|
Spotting ReferencesMany syntax constructs in PHP are implemented via referencing mechanisms, so everything told above about reference binding also apply to these constructs. Some constructs, like passing and returning by-reference, are mentioned above. Other constructs that use references are: When you declare variable as global $var you are in fact creating reference to a global variable. That means, this is the same as: <?php
That means, for example, that unsetting Code Examples / Notes » language.references.spotsergio santana: ssantana
Sometimes an object's method returning a reference to itself is required. Here is a way to code it: <?php class MyClass { public $datum; public $other; function &MyRef($d) { // the method $this->datum = $d; return $this; // returns the reference } } $a = new MyClass; $b = $a->MyRef(25); // creates the reference echo "This is object \$a: \n"; print_r($a); echo "This is object \$b: \n"; print_r($b); $b->other = 50; echo "This is object \$a, modified" . " indirectly by modifying ref \$b: \n"; print_r($a); ?> This code outputs: This is object $a: MyClass Object ( [datum] => 25 [other] => ) This is object $b: MyClass Object ( [datum] => 25 [other] => ) This is object $a, modified indirectly by modifying ref $b: MyClass Object ( [datum] => 25 [other] => 50 ) mildred
Someone said that BenBe's function is_ref() was broken for testing objects. I didn't test it but I rewrote it and I know it works for objects. Here is the new version : <?php function is_ref(&$a, &$b){ if(gettype($a) !== gettype($b)) return false; $same = false; if(is_array($a)){ //Look for an unused index in $a $key = uniqid("is_ref_", true); while(isset($a[$key]))$key = uniqid("is_ref_", true); //The two variables differ in content ... They can't be the same if(isset($b[$key])) return false; //The arrays point to the same data if changes are reflected in $b $data = uniqid("is_ref_data_", true); $a[$key] =& $data; //There seems to be a modification ... $same = ((isset($b[$key])) and ($b[$key] === $data)); //Undo our changes ... unset($a[$key]); }elseif(is_object($a)){ //The same objects are required to have equal class names ;-) if(get_class($a) !== get_class($b)) return false; //Look for an unused property in $a $key = uniqid("is_ref_", true); while(isset($a->$key))$key = uniqid("is_ref_", true); //The two variables differ in content ... They can't be the same if(isset($b->$key)) return false; //The arrays point to the same data if changes are reflected in $b $data = uniqid("is_ref_data_", true); $a->$key =& $data; //There seems to be a modification ... $same = ((isset($b->$key)) and ($b->$key === $data)); //Undo our changes ... unset($a->$key); }elseif(is_resource($a)){ if(get_resource_type($a) !== get_resource_type($b))return false; $same = ((string) $var1) === ((string) $var2); }else{ if($a !== $b) return false; //To check for a reference of a variable with simple type //simply store its old value and check against modifications of the second variable ;-) $data = uniqid("is_ref_", true); while($data === $a) $data = uniqid("is_ref_", true); $save = $a; //WE NEED A COPY HERE!!! $a = $data; //Set a to the value of $data (copy) $same = ($a === $b); //Check if $var2 was modified too ... $a = $save; //Undo our changes ... } return $same; } ?> ksamvel
One may check reference to any object by simple operator==( object). Example: class A {} $oA1 = new A(); $roA = & $oA1; echo "roA and oA1 are " . ( $roA == $oA1 ? "same" : "not same") . " "; $oA2 = new A(); $roA = & $roA2; echo "roA and oA1 are " . ( $roA == $oA1 ? "same" : "not same") . " "; Output: roA and oA1 are same roA and oA1 are not same Current technique might be useful for caching in objects when inheritance is used and only base part of extended class should be copied (analog of C++: oB = oA): class A { /* Any function changing state of A should set $bChanged to true */ public function isChanged() { return $this->bChanged; } private $bChanged; //... } class B extends A { // ... public function set( &$roObj) { if( $roObj instanceof A) { if( $this->roAObj == $roObj && $roObj->isChanged()) { /* Object was not changed do not need to copy A part of B */ } else { /* Copy A part of B */ $this->roAObj = &$roObj; } } } private $roAObj; } benbe
Hi, If you want to check if two variables are referencing each other (i.e. point to the same memory) you can use a function like this: <?php function same_type(&$var1, &$var2){ return gettype($var1) === gettype($var2); } function is_ref(&$var1, &$var2) { //If a reference exists, the type IS the same if(!same_type($var1, $var2)) { return false; } $same = false; //We now only need to ask for var1 to be an array ;-) if(is_array($var1)) { //Look for an unused index in $var1 do { $key = uniqid("is_ref_", true); } while(array_key_exists($key, $var1)); //The two variables differ in content ... They can't be the same if(array_key_exists($key, $var2)) { return false; } //The arrays point to the same data if changes are reflected in $var2 $data = uniqid("is_ref_data_", true); $var1[$key] =& $data; //There seems to be a modification ... if(array_key_exists($key, $var2)) { if($var2[$key] === $data) { $same = true; } } //Undo our changes ... unset($var1[$key]); } elseif(is_object($var1)) { //The same objects are required to have equal class names ;-) if(get_class($var1) !== get_class($var2)) { return false; } $obj1 = array_keys(get_object_vars($var1)); $obj2 = array_keys(get_object_vars($var2)); //Look for an unused index in $var1 do { $key = uniqid("is_ref_", true); } while(in_array($key, $obj1)); //The two variables differ in content ... They can't be the same if(in_array($key, $obj2)) { return false; } //The arrays point to the same data if changes are reflected in $var2 $data = uniqid("is_ref_data_", true); $var1->$key =& $data; //There seems to be a modification ... if(isset($var2->$key)) { if($var2[$key] === $data) { $same = true; } } //Undo our changes ... unset($var1->$key); } elseif (is_resource($var1)) { if(get_resource_type($var1) !== get_resource_type($var2)) { return false; } return ((string) $var1) === ((string) $var2); } else { //Simple variables ... if($var1!==$var2) { //Data mismatch ... They can't be the same ... return false; } //To check for a reference of a variable with simple type //simply store its old value and check against modifications of the second variable ;-) do { $key = uniqid("is_ref_", true); } while($key === $var1); $tmp = $var1; //WE NEED A COPY HERE!!! $var1 = $key; //Set var1 to the value of $key (copy) $same = $var1 === $var2; //Check if $var2 was modified too ... $var1 = $tmp; //Undo our changes ... } return $same; } ?> Although this implementation is quite complete, it can't handle function references and some other minor stuff ATM. This function is especially useful if you want to serialize a recursive array by hand. The usage is something like: <?php $a = 5; $b = 5; var_dump(is_ref($a, $b)); //false $a = 5; $b = $a; var_dump(is_ref($a, $b)); //false $a = 5; $b =& $a; var_dump(is_ref($a, $b)); //true echo "---\n"; $a = array(); var_dump(is_ref($a, $a)); //true $a[] =& $a; var_dump(is_ref($a, $a[0])); // true echo "---\n"; $b = array(array()); var_dump(is_ref($b, $b)); //true var_dump(is_ref($b, $b[0])); //false echo "---\n"; $b = array(); $b[] = $b; var_dump(is_ref($b, $b)); //true var_dump(is_ref($b, $b[0])); //false var_dump(is_ref($b[0], $b[0][0])); //true* echo "---\n"; var_dump($a); var_dump($b); ?> * Please note the internal behaviour of PHP that seems to do the reference assignment BEFORE actually copying the variable!!! Thus you get an array containing a (different) recursive array for the last testcase, instead of an array containing an empty array as you could expect. BenBE. ludvig dot ericson
For the sake of clarity: $this is a PSEUDO VARIABLE - thus not a real variable. ZE treats is in other ways then normal variables, and that means that some advanced variable-things won't work on it (for obvious reasons): <?php class Test { var $monkeys = 0; function doFoobar() { $var = "this"; $$var->monkeys++; // Will fail on this line. } } $obj = new Test; $obj->doFoobar(); // Will fail in this call. var_dump($obj->monkeys); // Will return int(0) if it even reaches here. ?> steve clay
BenBE's function is broken for testing objects. Here's a variation on it just for testing objects: <?php function same_obj(&$var1, &$var2) { if ($var1 !== $var2) { return false; } $same = false; $key = uniqid("same_obj_", true); $var1->$key = $key; if (isset($var2->$key) && $var2->$key == $key) { $same = true; } unset($var1->$key); return $same; } // PHP4 references are risilient as array members $f1 =& new Foo; $f2 = array(&$f1); $f3 = $f2; echo (int)same_obj($f1, $f3[0]); // 1! ?> sergio santana: ssantana
*** WARNING about OBJECTS TRICKY REFERENCES *** ----------------------------------------------- The use of references in the context of classes and objects, though well defined in the documentation, is somehow tricky, so one must be very careful when using objects. Let's examine the following two examples: <?php class y { public $d; } $A = new y; $A->d = 18; echo "Object \$A before operation:\n"; var_dump($A); $B = $A; // This is not an explicit (=&) reference assignment, // however, $A and $B refer to the same instance // though they are not equivalent names $C =& $A; // Explicit reference assignment, $A and $C refer to // the same instance and they have become equivalent // names of the same instance $B->d = 1234; echo "\nObject \$B after operation:\n"; var_dump($B); echo "\nObject \$A implicitly modified after operation:\n"; var_dump($A); echo "\nObject \$C implicitly modified after operation:\n"; var_dump($C); // Let's make $A refer to another instance $A = new y; $A->d = 25200; echo "\nObject \$B after \$A modification:\n"; var_dump($B); // $B doesn't change echo "\nObject \$A after \$A modification:\n"; var_dump($A); echo "\nObject \$C implicitly modified after \$A modification:\n"; var_dump($C); // $C changes as $A changes ?> Thus, note the difference between assignments $X = $Y and $X =& $Y. When $Y is anything but an object instance, the first assignment means that $X will hold an independent copy of $Y, and the second, means that $X and $Y will refer to the same thing, so they are tight together until either $X or $Y is forced to refer to another thing. However, when $Y happens to be an object instance, the semantic of $X = $Y changes and becomes only slightly different to that of $X =& $Y, since in both cases $X and $Y become references to the same object. See what this example outputs: Object $A before operation: object(y)#1 (1) { ["d"]=> int(18) } Object $B after operation: object(y)#1 (1) { ["d"]=> int(1234) } Object $A implicitly modified after operation: object(y)#1 (1) { ["d"]=> int(1234) } Object $C implicitly modified after operation: object(y)#1 (1) { ["d"]=> int(1234) } Object $B after $A modification: object(y)#1 (1) { ["d"]=> int(1234) } Object $A after $A modification: object(y)#2 (1) { ["d"]=> int(25200) } Object $C implicitly modified after $A modification: object(y)#2 (1) { ["d"]=> int(25200) } Let's review a SECOND EXAMPLE: <?php class yy { public $d; function yy($x) { $this->d = $x; } } function modify($v) { $v->d = 1225; } $A = new yy(3); var_dump($A); modify($A); var_dump($A); ?> Although, in general, a formal argument declared as $v in the function 'modify' shown above, implies that the actual argument $A, passed when calling the function, is not modified, this is not the case when $A is an object instance. See what the example code outputs when executed: object(yy)#3 (1) { ["d"]=> int(3) } object(yy)#3 (1) { ["d"]=> int(1225) } |