|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2016-05-17 09:40 UTC] laruence@php.net
[2016-05-17 09:40 UTC] laruence@php.net
-Status: Open
+Status: Closed
[2016-05-17 09:41 UTC] laruence@php.net
[2016-07-20 11:31 UTC] davey@php.net
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Wed Oct 29 15:00:02 2025 UTC |
Description: ------------ The Bug concerns serialization of an object: in certain cases a "real" reference (with a '&' like &$x) is wrongly introduced. In terms of the string as an output of the serialization: instead of an 'r:' an 'R:' is used. Run the test script twice: first without the marked two lines (see code) and second with these two lines. What is expected: In the sample code the $Obj1 shows the expected result. $Obj2 should also show this result. What the actual result is: With the two marked lines uncommented in code, $Obj2 shows a wrong result. DESCRIPTION: Without the two lines in the contructor all is correct. The produced string ($txt1) by serialization is: O:2:"C1":2:{s:4:"arr1";a:1:{i:0;r:1;}s:4:"arr2";a:1:{i:0;r:1;}} Unserialization of this string creates an object ($Obj2) which is identical with the original object ($Obj1). This object "behaves" quite correct: after assigning the value '50' to its member arr2[0] the content of its member arr1[0] remains unchanged, i.e. it still holds a reference to the object itself ($Obj2). That's correct because in the constructor there is an assigning "by value", i.e. $this->arr2[0] does NOT refer to the array element $this->arr1[0]. With the two lines in the constructor the result is wrong. The produced string ($txt1) by serialization is: O:2:"C1":2:{s:4:"arr1";a:1:{i:0;R:1;}s:4:"arr2";a:1:{i:0;r:1;}} Notice: the occurence of the first 'r:' in the middle of the string changed to 'R:' (from lower case to upper case letter!). The two lines simply introduce a reference of a local variable to arr1[0] and remove (unset) it immediately after. Therefore the resulting object should be exactly the same as without these two lines. But the output string of the serialization now shows a "real" reference from arr2[0] to arr1[0]! The concequence of this bug is, that after unserialization (creation of an object from the serialized string) there is a "connection" (a real reference) from the member arr2[0] to the member arr1[0], which was not the case in the original object! Now changing the content of arr2[0] also (wrongly) changes the content of arr1[0]. It seems that this bug only occures when a recursion exists. The line '$this->arr1[0] = $this;' in the constructor introduces this recursion. This was done here in this simple way only for demonstration purpose. The bug also occures when the recursion exists much more complicate/indirect (i.e. via a chain of many other objects) and also if it's done in any other method of the objects than the constructor. Because recursion is needed for this bug, as a concequence the reported bug only occures when an object is assigned to the member variable. The bug does not occure when simple values are assigned (because values can't form a recursion). I consider this bug as very serious! Because PHP uses serialization for saving (session-)variables between script calls all objects containing a recursion will be wrong after repetitive script calls! Test script: --------------- <?php class C1 { public $arr1 = array(); public $arr2 = array(); public function __construct() { $this->arr1[0] = $this; $this->arr2[0] = $this->arr1[0]; // ***** Run the test without and with the following two lines: $var1 = &$this->arr1[0]; // Set a reference... unset($var1); // ... and unset it. } } echo '<pre>'; $Obj1 = new C1(); $txt1 = serialize($Obj1); $Obj2 = unserialize($txt1); $Obj1->arr2[0] = 50; print_r($Obj1); $Obj2->arr2[0] = 50; print_r($Obj2); echo '</pre>'; ?> Expected result: ---------------- C1 Object ( [arr1] => Array ( [0] => C1 Object *RECURSION* ) [arr2] => Array ( [0] => 50 ) ) C1 Object ( [arr1] => Array ( [0] => C1 Object *RECURSION* ) [arr2] => Array ( [0] => 50 ) ) Actual result: -------------- C1 Object ( [arr1] => Array ( [0] => C1 Object *RECURSION* ) [arr2] => Array ( [0] => 50 ) ) C1 Object ( [arr1] => Array ( [0] => 50 ) [arr2] => Array ( [0] => 50 ) )