/** * test shallowCopy vs deepCopy * * shallowCopy: * \code * * - array with closure * - property with object * - property with object * - array with object * - property with object * - property with object * - private var * - public var * - static object * - protected var * \endcode * * Actions of shallowCopy: * - create new Test2 * - copy values from * - don't touch statics * - property with object (not closure): copy reference, not the object * - property with object closure: rebind closure to * - property array with objects of type closure: recursively search closures and rebind them to * * deepCopy: * \code * * - array with closure * - property with object * - property with object * - array with object * - property with object * - property with object * - private var * - public var * - static object * - protected var * \endcode * * Actions of deepCopy: * - create new Test2 * - copy values from * - don't touch statics * - property with object (not closure): object->deepCopy, clone other objects * - property with object closure: rebind closure to * - property array with objects (not closure): object->deepCopy, clone other objects * - property array with objects of type closure: recursively search closures and rebind them to * * \todo How to handle closures in traversable objects? */ class test extends DefaultObject { private $name = 'base.name.notSet'; // name in multiple subjects are identical when closures are in old context public $aV = 'base.av.notSet'; // av: arrayValue public $sV = 'base.sv.notSet'; // sv: selfValue private $private = 0; static $static = 0; protected $alice = NULL; // object as property protected $fnA = []; // closure in array protected $fnC = NULL; // closure as property function __construct($name, Alice $alice, $private, DefaultObject $static) { $this->name = $name; $this->alice = $alice; $this->private = $private; self::$static = $static; $this->fnA['test']['self'] = function(){ return $this->aV; }; $this->fnC = function(){ return $this->sV; }; $this->set('init'.$name); } function __debugInfo() { return [ 'name' => $this->name, //'aV' => $this->aV, //'sV' => $this->sV, 'private' => $this->private, 'alice' => $this->alice, 'static' => self::$static, 'arrayClosure' => $this->fnA['test']['self'], 'propClosure' => $this->fnC, ]; } function setClassName($n) { $this->name = $n; } function setAV($self, $alice) { $this->aV = 'base.av.'.$self; $this->alice->aV = 'alic.av.'.$alice; } function setSV($self, $alice) { $this->sV = 'base.sv.'.$self; $this->alice->sV = 'alic.sv.'.$alice; } function set($n) { $this->setAV('arrayClosure.'.$this->name.'.'.$n, 'arrayClosure.'.$this->name.'.'.$n); $this->setSV('propertyClosure.'.$this->name.'.'.$n, 'propertyClosure.'.$this->name.'.'.$n); } function closureInArray() { $fn = $this->fnA['test']['self']; echo 'closure in array: '.$fn().PHP_EOL .' '.$this->alice->aT(); } function closureAsProperty() { $fn = $this->fnC; echo 'closure as property:'.$fn().PHP_EOL .' '.$this->alice->sT(); } function testClosureScope() { $this->closureInArray(); $this->closureAsProperty(); } function compare(test $__Test) { $r = []; $r['Test'] = $this === $__Test; $r['Test->fnA'] = $this->fnA === $__Test->fnA; $r['Test->fnC'] = $this->fnC === $__Test->fnC; $r['Test->alice'] = $this->alice->compare($__Test->alice); $r['Test::static'] = self::$static === $__Test::$static; return ["Compare {$this->name} with {$__Test->name}" => $r]; } } class bob { } class alice extends DefaultObject { public $aV = 'alice.av.notSet'; public $sV = 'alice.sv.notSet'; protected $fnA = []; protected $fnC = NULL; protected $__bob; function __construct() { $this->fnA['test']['alice'] = function(){ return $this->aV;}; $this->fnC = function(){ return $this->sV; }; $this->__bob = new bob; } function __debugInfo() { return [ //'aV' => $this->aV, //'sV' => $this->sV, 'arrayClosure' => $this->fnA['test']['alice'], //'propClosure' => $this->fnC, ]; } function aT() { $fn = $this->fnA['test']['alice']; return $fn().PHP_EOL; } function sT() { $fn = $this->fnC; return $fn().PHP_EOL; } function compare(alice $__Test) { $r = []; $r['alice'] = $this === $__Test; $r['alice->fnA'] = $this->fnA === $__Test->fnA; $r['alice->fnC'] = $this->fnC === $__Test->fnC; $r['alice->__bob'] = $this->__bob === $__Test->__bob; return $r; } } $refTest = 'foooooooooooobar'; echo '#--start closure test', PHP_EOL; $T = new Test('ORIG', new alice, $refTest, new DefaultObject); $T->set('$T.ready'); $T->closureAsProperty(); // base name 'orig', alice name 'orig' echo '#----should be the same as above', PHP_EOL; $T->closureInArray(); // base name 'orig', alice name 'orig' echo '#--create shallow copy $D', PHP_EOL; $D = $T->shallowCopy(); $T->set('$T.afterShallowCopy'); $D->set('$D.afterShallowCopy'); $T->closureAsProperty(); // base name 'orig', alice name 'orig' $D->closureAsProperty(); // base name 'orig', alice name 'orig' echo '#----should be the same as above', PHP_EOL; $T->closureInArray(); // base name 'orig', alice name 'orig' $D->closureInArray(); // base name 'orig', alice name 'orig' echo '#--change name of $D to "COPY"', PHP_EOL; $D->setClassName('COPY'); $T->set('$T.afterNameChange$D'); $D->set('$D.afterNameChange$D'); echo '#----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object', PHP_EOL; $T->closureAsProperty(); // base name 'orig', alice name 'copy' $D->closureAsProperty(); // base name 'copy', alice name 'copy' echo '#----should be the same as above', PHP_EOL; $T->closureInArray(); // base name 'orig', alice name 'copy' $D->closureInArray(); // base name 'copy', alice name 'copy' echo '#--create deep $E', PHP_EOL; $E = $T->deepCopy(); $T->set('$T.afterDeepCopy'); $D->set('$D.afterDeepCopy'); $E->set('$E.afterDeepCopy'); echo '#----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object', PHP_EOL; $T->closureAsProperty(); // base name 'orig', alice name 'orig' $D->closureAsProperty(); // base name 'orig', alice name 'orig' $E->closureAsProperty(); // base name 'orig', alice name 'orig' $T->closureInArray(); // base name 'orig', alice name 'orig' $D->closureInArray(); // base name 'orig', alice name 'orig' $E->closureInArray(); // base name 'orig', alice name 'orig' echo '#--change name of $E to "DEEP"', PHP_EOL; $E->setClassName('DEEP'); $T->set('$T.afterNameChange$E'); $D->set('$D.afterNameChange$E'); $E->set('$E.afterNameChange$E'); echo '#----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object', PHP_EOL; $T->closureAsProperty(); // base name 'orig', alice name 'orig' $D->closureAsProperty(); // base name 'orig', alice name 'orig' echo '#----both should be DEEP', PHP_EOL; $E->closureAsProperty(); // base name 'orig', alice name 'orig' echo '#----should be the same as above', PHP_EOL; $T->closureInArray(); // base name 'orig', alice name 'orig' $D->closureInArray(); // base name 'orig', alice name 'orig' $E->closureInArray(); // base name 'orig', alice name 'orig' var_dump($T->compare($D)); var_dump($T->compare($E)); echo '#--dump of "ORIG" ############################################################################', PHP_EOL; var_dump($T); echo '#--dump of "COPY" ############################################################################', PHP_EOL; var_dump($D); echo '#--dump of "DEEP" ############################################################################', PHP_EOL; var_dump($E); // output: /* #--start closure test closure as property:base.sv.propertyClosure.ORIG.$T.ready alic.sv.propertyClosure.ORIG.$T.ready #----should be the same as above closure in array: base.av.arrayClosure.ORIG.$T.ready alic.av.arrayClosure.ORIG.$T.ready #--create shallow copy $D closure as property:base.sv.propertyClosure.ORIG.$T.afterShallowCopy alic.sv.propertyClosure.ORIG.$D.afterShallowCopy closure as property:base.sv.propertyClosure.ORIG.$D.afterShallowCopy alic.sv.propertyClosure.ORIG.$D.afterShallowCopy #----should be the same as above closure in array: base.av.arrayClosure.ORIG.$T.afterShallowCopy alic.av.arrayClosure.ORIG.$D.afterShallowCopy closure in array: base.av.arrayClosure.ORIG.$D.afterShallowCopy alic.av.arrayClosure.ORIG.$D.afterShallowCopy #--change name of $D to "COPY" #----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object closure as property:base.sv.propertyClosure.ORIG.$T.afterNameChange$D alic.sv.propertyClosure.COPY.$D.afterNameChange$D closure as property:base.sv.propertyClosure.COPY.$D.afterNameChange$D alic.sv.propertyClosure.COPY.$D.afterNameChange$D #----should be the same as above closure in array: base.av.arrayClosure.ORIG.$T.afterNameChange$D alic.av.arrayClosure.COPY.$D.afterNameChange$D closure in array: base.av.arrayClosure.COPY.$D.afterNameChange$D alic.av.arrayClosure.COPY.$D.afterNameChange$D #--create deep $E #----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object closure as property:base.sv.propertyClosure.ORIG.$T.afterDeepCopy alic.sv.propertyClosure.COPY.$D.afterDeepCopy closure as property:base.sv.propertyClosure.COPY.$D.afterDeepCopy alic.sv.propertyClosure.COPY.$D.afterDeepCopy closure as property:base.sv.propertyClosure.ORIG.$E.afterDeepCopy alic.sv.propertyClosure.ORIG.$E.afterDeepCopy closure in array: base.av.arrayClosure.ORIG.$T.afterDeepCopy alic.av.arrayClosure.COPY.$D.afterDeepCopy closure in array: base.av.arrayClosure.COPY.$D.afterDeepCopy alic.av.arrayClosure.COPY.$D.afterDeepCopy closure in array: base.av.arrayClosure.ORIG.$E.afterDeepCopy alic.av.arrayClosure.ORIG.$E.afterDeepCopy #--change name of $E to "DEEP" #----closure in ALICE returns COPY: because ORIG and COPY referencing to the same ALICE object closure as property:base.sv.propertyClosure.ORIG.$T.afterNameChange$E alic.sv.propertyClosure.COPY.$D.afterNameChange$E closure as property:base.sv.propertyClosure.COPY.$D.afterNameChange$E alic.sv.propertyClosure.COPY.$D.afterNameChange$E #----both should be DEEP closure as property:base.sv.propertyClosure.DEEP.$E.afterNameChange$E alic.sv.propertyClosure.DEEP.$E.afterNameChange$E #----should be the same as above closure in array: base.av.arrayClosure.ORIG.$T.afterNameChange$E alic.av.arrayClosure.COPY.$D.afterNameChange$E closure in array: base.av.arrayClosure.COPY.$D.afterNameChange$E alic.av.arrayClosure.COPY.$D.afterNameChange$E closure in array: base.av.arrayClosure.DEEP.$E.afterNameChange$E alic.av.arrayClosure.DEEP.$E.afterNameChange$E array(1) { ["Compare ORIG with COPY"]=> array(5) { ["Test"]=> bool(false) ["Test->fnA"]=> bool(false) ["Test->fnC"]=> bool(false) ["Test->alice"]=> array(4) { ["alice"]=> bool(true) ["alice->fnA"]=> bool(true) ["alice->fnC"]=> bool(true) ["alice->__bob"]=> bool(true) } ["Test::static"]=> bool(true) } } array(1) { ["Compare ORIG with DEEP"]=> array(5) { ["Test"]=> bool(false) ["Test->fnA"]=> bool(false) ["Test->fnC"]=> bool(false) ["Test->alice"]=> array(4) { ["alice"]=> bool(false) ["alice->fnA"]=> bool(false) ["alice->fnC"]=> bool(false) ["alice->__bob"]=> bool(false) } ["Test::static"]=> bool(true) } } #--dump of "ORIG" ############################################################################ object(web\test)#2 (6) { ["name"]=> string(4) "ORIG" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#8 (1) { ["this"]=> object(web\test)#2 (6) { ["name"]=> string(4) "ORIG" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> object(Closure)#9 (1) { ["this"]=> object(web\test)#2 (6) { ["name"]=> string(4) "ORIG" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } } } ["propClosure"]=> object(Closure)#9 (1) { ["this"]=> object(web\test)#2 (6) { ["name"]=> string(4) "ORIG" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#8 (1) { ["this"]=> object(web\test)#2 (6) { ["name"]=> string(4) "ORIG" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } ["propClosure"]=> *RECURSION* } } } #--dump of "COPY" ############################################################################ object(web\test)#12 (6) { ["name"]=> string(4) "COPY" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#29 (1) { ["this"]=> object(web\test)#12 (6) { ["name"]=> string(4) "COPY" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> object(Closure)#30 (1) { ["this"]=> object(web\test)#12 (6) { ["name"]=> string(4) "COPY" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } } } ["propClosure"]=> object(Closure)#30 (1) { ["this"]=> object(web\test)#12 (6) { ["name"]=> string(4) "COPY" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#29 (1) { ["this"]=> object(web\test)#12 (6) { ["name"]=> string(4) "COPY" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> object(Closure)#4 (1) { ["this"]=> object(web\alice)#3 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } ["propClosure"]=> *RECURSION* } } } #--dump of "DEEP" ############################################################################ object(web\test)#26 (6) { ["name"]=> string(4) "DEEP" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> object(Closure)#45 (1) { ["this"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#44 (1) { ["this"]=> object(web\test)#26 (6) { ["name"]=> string(4) "DEEP" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> object(Closure)#45 (1) { ["this"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> object(Closure)#43 (1) { ["this"]=> object(web\test)#26 (6) { ["name"]=> string(4) "DEEP" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> object(Closure)#45 (1) { ["this"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } } } ["propClosure"]=> object(Closure)#43 (1) { ["this"]=> object(web\test)#26 (6) { ["name"]=> string(4) "DEEP" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> object(Closure)#45 (1) { ["this"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> object(Closure)#44 (1) { ["this"]=> object(web\test)#26 (6) { ["name"]=> string(4) "DEEP" ["private"]=> string(16) "foooooooooooobar" ["alice"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> object(Closure)#45 (1) { ["this"]=> object(web\alice)#34 (1) { ["arrayClosure"]=> *RECURSION* } } } ["static"]=> object(ILLI\Core\Std\DefaultObject)#7 (0) { } ["arrayClosure"]=> *RECURSION* ["propClosure"]=> *RECURSION* } } ["propClosure"]=> *RECURSION* } } }