While venus is crossing the sun I'm trying to model a tree using two
arrays; one containing the structure of the tree and the other
containing the data of the nodes.
The second array also contains pointers to 'subtrees' of the first
array. Anyway, I discovered a strange behavior when I dumped the tree
before and after making the pointers.
Example:
<?php
$tree = array( 1 => array( 2 => 'foo' ) );
var_dump($tree);
$b = &$tree[1];
var_dump($tree);
?>
This code will output the following:
array(1) {
[1]=>
array(1) {
[2]=>
string(3) "foo"
}
}
array(1) {
[1]=>
&array(1) {
[2]=>
string(3) "foo"
}
}
As you can see the original tree now contains a pointer, how can that
be, I didn't change the original array!?
So what I'd like to know is: Is this intended behavior, and if so: why
does it work like this and would it make some operations impossible?
Taco
As you can see the original tree now contains a pointer, how can that
be, I didn't change the original array!?
So what I'd like to know is: Is this intended behavior, and if so: why
does it work like this and would it make some operations impossible?
The truth of it is, this was true in PHP4 as well, we just didn't show it at
the time, and yes: It's intended behavior.
Every "variable" in PHP is a pair. The $foo label that you use to refer to
it in your script, and the actual value in memory (referred to as a zval).
When you make a reference (using the & operator) the new label is created to
point to the same zval as the original label. The zval itself doesn't know
which label was first and which came second, it only knows that two labels
refer to it in a reference manner (there are non-reference manners as well
but let's not confuse the issue here). So when the value is output
(regardless of which label was followed to output it), PHP says "This value
is referenced by two or more labels." and throws that '&' indicator onto the
output.
$foo = 1;
/* $foo (label) --------> 1 (value) (is_ref=0, refcount=1) */
$bar = &$foo;
/* $foo (label) ----------> 1 (value) /
/ $bar (label) -------/ is_ref=1, refcount=2 */
Hope that helps.
-Sara
Sara Golemon wrote:
Every "variable" in PHP is a pair.
[cut]
$foo = 1;
/* $foo (label) --------> 1 (value) (is_ref=0, refcount=1) */
$bar = &$foo;
/* $foo (label) ----------> 1 (value) /
/ $bar (label) -------/ is_ref=1, refcount=2 */Hope that helps.
-Sara
Thanks for the clear explanation :). I understood that in PHP 5 objects
are automatically referenced when assigned, and 'primary types' like
int, bool, string are normally copied when assigned.
When I do a (very rough) benchmark with strings/ints, assigning (and
thus copying) a 1000 bytes string isn't significantly slower than
referencing it. Also: memory usage is exactly the same.
Is there any situation in which one might manually want to reference a
variable instead of assigning it (like: $x =& $y)?
Bert
I think, and I could be completely wrong, that copying a variable actually
creates a reference. The data is only copied when the variable referenced is
modified.
"Bert Slagter" bert@procurios.nl wrote in message
news:20040609072455.27050.qmail@pb1.pair.com...
Sara Golemon wrote:
Every "variable" in PHP is a pair.
[cut]
$foo = 1;
/* $foo (label) --------> 1 (value) (is_ref=0, refcount=1) */
$bar = &$foo;
/* $foo (label) ----------> 1 (value) /
/ $bar (label) -------/ is_ref=1, refcount=2 */Hope that helps.
-Sara
Thanks for the clear explanation :). I understood that in PHP 5 objects
are automatically referenced when assigned, and 'primary types' like
int, bool, string are normally copied when assigned.When I do a (very rough) benchmark with strings/ints, assigning (and
thus copying) a 1000 bytes string isn't significantly slower than
referencing it. Also: memory usage is exactly the same.Is there any situation in which one might manually want to reference a
variable instead of assigning it (like: $x =& $y)?Bert
I think, and I could be completely wrong, that copying a variable actually
creates a reference. The data is only copied when the variable referenced
is
modified.
That's true. What I left out of my explanation (in order to keep it simple)
is that when you "copy" a variable, a new label is created to point to the
same zval, and the zval's refcount is incrmented but the is_ref flag is
not set (I referred to this offhand as non-reference manner of multiple
labels referring to the same value). Then when one of the referring labels
says "I want to change my copy
of the data." It notices that someone else
is also referring to this value (in a non-reference manner) and "separates"
the zval: This amounts to making a true copy of the zval (with a refcount
of 1, and an is_ref of 0) and decrements the refcount of the original zval
(since one fewer label is referring to it). This is the process known as
"copy on change".
$foo = 1;
/* $foo (label) ------> 1(value) (is_ref:0 refcount:1) */
$bar = $foo;
/* $foo (label) -------> 1(value) /
/ $bar (label) ---/ (is_ref:0 refcount:2) */
$foo = 2;
/* $bar (label) -------> 1(value) (is_ref:0 refcount:1) /
/ $foo (label) -------> 2(value) (is_ref:0 refcount:1) */
-Sara
Ya just had to make it complicated didn't you.
Hey,
put this in thedocs somewhere?
regards,
Derick
That's true. What I left out of my explanation (in order to keep it simple)
is that when you "copy" a variable, a new label is created to point to the
same zval, and the zval's refcount is incrmented but the is_ref flag is
not set (I referred to this offhand as non-reference manner of multiple
labels referring to the same value). Then when one of the referring labels
says "I want to change mycopy
of the data." It notices that someone else
is also referring to this value (in a non-reference manner) and "separates"
the zval: This amounts to making a true copy of the zval (with a refcount
of 1, and an is_ref of 0) and decrements the refcount of the original zval
(since one fewer label is referring to it). This is the process known as
"copy on change".$foo = 1;
/* $foo (label) ------> 1(value) (is_ref:0 refcount:1) */
$bar = $foo;
/* $foo (label) -------> 1(value) /
/ $bar (label) ---/ (is_ref:0 refcount:2) */$foo = 2;
/* $bar (label) -------> 1(value) (is_ref:0 refcount:1) /
/ $foo (label) -------> 2(value) (is_ref:0 refcount:1) */-Sara
Ya just had to make it complicated didn't you.