1
 class a {
public $test="msg1";
}          

 $t1 = new a;
 echo "echo1: After Instantiation :<br/>";
 xdebug_debug_zval('t1');echo "<br/><br/>";

 $t2 = $t1;
 echo 'echo2: After assigning $t1 to $t2 :<br/>';
 xdebug_debug_zval('t2');echo "<br/><br/>";

 $t1->test="msg2";
 echo 'echo3: After assigning $t1->test = "msg2" :<br/>';
 xdebug_debug_zval('t1');echo "<br/>";
 xdebug_debug_zval('t2');echo "<br/><br/>";

 $t2->test="msg3";
 echo 'echo4: After assigning $t2->test="msg3" :<br/>';
 xdebug_debug_zval('t1');echo "<br/>";
 xdebug_debug_zval('t2');echo "<br/><br/>"; 

 $t2->test2 = "c*ap!";
 echo 'echo5: After injecting $test2 to $t2 :<br/>';
 xdebug_debug_zval('t1');echo "<br/>";
 xdebug_debug_zval('t2');echo "<br/><br/>";

The output:

echo1: After Instantiation :
t1: (refcount=1, is_ref=0)=class a { public $test = (refcount=2, is_ref=0)='msg1' }

echo2: After assigning $t1 to $t2 :
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=2, is_ref=0)='msg1' }

echo3: After assigning $t1->test = "msg2" :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg2' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg2' }

echo4: After assigning $t2->test="msg3" :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3' }

echo5: After injecting $test2 to $t2 :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3'; public $test2 = (refcount=1, is_ref=0)='cap!' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3'; public $test2 = (refcount=1, is_ref=0)='c
ap!' }

Ignoring echo1 & echo2 because of this: What is exactly happening when instantiating with 'new'? & expected behaviour.

Considering echo3:

echo3: After assigning $t1->test = "msg2" :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg2' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg2' }

This is understandable as I am just changing $t1->test variable and no direct change to &t2->test.

Considering echo4, where a direct change to $t2->test is done:

echo4: After assigning $t2->test="msg3" :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg3' }

No C.O.W takes place! and the change is reflected to $t1 as well even though is_ref is not set.

Considering echo5, where the variable $test2 is injected into $t2:

echo5: After injecting $test2 to $t2 :
t1: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg4'; public $test2 = (refcount=1, is_ref=0)='cap!' }
t2: (refcount=2, is_ref=0)=class a { public $test = (refcount=1, is_ref=0)='msg4'; public $test2 = (refcount=1, is_ref=0)='c
ap!' }

Again, no C.O.W takes place! and the change is reflected to $t1 as well even though is_ref is not set.

Why is this behaviour!?

Community
  • 1
  • 1
ThinkingMonkey
  • 12,539
  • 13
  • 57
  • 81

1 Answers1

2

It does but you're having the wrong expectation.

The value is an object identifier. You assign that to $t1 or $t2. The object identifier is copied on write, but it still refers to the same object, so the object isn't copied in any of the cases you outline in your question.

See Objects and references­Docs:

One of the key-points of PHP 5 OOP that is often mentioned is that "objects are passed by references by default". This is not completely true. [...] As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object.

C.O.W. is an optimization. PHP here sees that $t1->test and $t2->test are actually the same value. So if you change it, the optimization kicks in in the sense that there is nothing to copy at all.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • When an assignment is done, since both pointing to the same zval container, the change gets reflected. The point of having a `reference assignment`, is that if, $t1=new a; $t2=&$t1, $t2=new b; now, both $t1 & $t2 will be pointing to the zval container for an object of class b? – ThinkingMonkey Jan 01 '12 at 11:13
  • `$t2=&$t1` will make `$t2` an alias for `$t1`. So you can interchangeable use both variable names to access the same value. And both refer to the same zval container. See as well http://www.php.net/manual/en/features.gc.refcounting-basics.php which shows what happens when you create the reference (any type, not only object). But take care that an object "value" is represented as the bespoken object identifier. – hakre Jan 01 '12 at 12:02
  • Hmmmmm.Have read refcounting basics, even with your explanation, Still not clear in my mind :( but, thanks for the help. – ThinkingMonkey Jan 01 '12 at 12:27
  • I think I understood it. How C.O.W is implemented WRT PHP. :) – ThinkingMonkey Jan 01 '12 at 12:59
  • 1
    If you really want to dig in completely for the theory: [Copy-on-Write in the PHP Language (Jan 18 2009; by Akihiko Tozawa, Michiaki Tatsubori, Tamiya Onodera and Yasuhiko Minamide; PDF file)](http://www.research.ibm.com/trl/people/mich/pub/200901_popl2009phpsem.pdf) – hakre Jan 01 '12 at 13:01
  • I definitely want to. Thanks for the file. :) – ThinkingMonkey Jan 01 '12 at 13:05
  • 1
    And this might be interesting for how PHP manges these structures internally: http://www.php.net/manual/en/internals2.variables.intro.php – hakre Jan 01 '12 at 13:11