Confusing pointers in PHP 4 and 5

php.internals

Taco van den Broek

22 years ago
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

Sara Golemon

22 years ago
> 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

Bert Slagter

22 years ago
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

Aidan Lister

22 years ago
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...

Stephan Schmidt

22 years ago
Hi,
> 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.
Yes, that is correct. Stephan

Derick Rethans

22 years ago
On Wed, 9 Jun 2004, Stephan Schmidt wrote:
> Hi, > > > 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. > Yes, that is correct.
No, it is not. 'copying' a variable copies the structure to contain the variable, but not the data, but does NOT make a reference as that's a different concept. You are right that the data associated with a variable container is only copied when it is modified. Derick

Bert Slagter

22 years ago
Derick Rethans wrote:
> On Wed, 9 Jun 2004, Stephan Schmidt wrote: > >>Yes, that is correct. > > > No, it is not. 'copying' a variable copies the structure to contain the > variable, but not the data, but does NOT make a reference as that's a > different concept. > > You are right that the data associated with a variable container is only > copied when it is modified. > > Derick
Hm! I should 've known this - someone told this on a PHP Conference :). I created a testcase where references to a string are a lot faster then normal copies by modifying the data after the assignment. But when I leave the data unaltered, I don't see a significant difference between a copy and a reference. So I assume that the cost of referencing is about equal to the cost of copying the structure..? But as a rule of thumb: references to primary types (i.e. strings, arrays) are only faster when the data is modified, am I right? Bert

Andi Gutmans

22 years ago
In general, if you don't mean for it to be a reference semantically, you are best off not using references at all. PHP will do the right thing. Andi At 10:32 AM 6/9/2004 +0200, Bert Slagter wrote:

Sara Golemon

22 years ago
> 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.

Derick Rethans

22 years ago
Hey, put this in thedocs somewhere? regards, Derick On Wed, 9 Jun 2004, Sara Golemon wrote: