|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2017-11-23 23:16 UTC] pmmaga@php.net
-Status: Open
+Status: Duplicate
[2017-11-23 23:16 UTC] pmmaga@php.net
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Oct 28 06:00:01 2025 UTC |
Description: ------------ Deserializing a class that has attributes with access modifiers different from the ones used during deserialization creates attributes with duplicate attribute names, and converting such objects to arrays using get_object_vars creates arrays with duplicate keys. This can also be considered a memory leak, since the attributes with the old access modifiers cannot be unset or modified. Test script: --------------- // a.php <?php class a { public $a = 'public'; public function dump() { return get_object_vars($this); } } $a = new a(); file_put_contents('test', serialize($a)); var_dump($a, $a->dump()); // b.php <?php class a { protected $a = 'protected'; public function dump() { return get_object_vars($this); } } $a = unserialize(file_get_contents('test')); file_put_contents('test', serialize($a)); var_dump($a, $a->dump()); // c.php <?php class a { private $a = 'private'; public function dump() { return get_object_vars($this); } public function adump() { return (array)$this; } public function vdump() { foreach ($this as $key => $value) { var_dump($key.' => '.$value); } } public function unset() { var_dump('UNSETTING $this->a'); unset($this->a); } } $a = unserialize(file_get_contents('test')); file_put_contents('test', serialize($a)); $array = $a->dump(); var_dump($a, $array, $array['a']); var_dump('UNSETTING $array["a"]'); unset($array['a']); var_dump($array); var_dump('UNSETTING $array["a"]'); unset($array['a']); var_dump($array); var_dump('UNSETTING $array["a"]'); unset($array['a']); var_dump($array); var_dump((object)$a->adump()); $a->vdump(); $a->unset(); // The private property is being unset $a->vdump(); $a->unset(); // Nothing is being unset $a->vdump(); Expected result: ---------------- // php a.php && php b.php && php c.php // a.php object(a)#1 (1) { ["a"]=> string(6) "public" } array(1) { ["a"]=> string(6) "public" } // b.php object(a)#1 (2) { ["a":protected]=> string(9) "protected" ["a"]=> string(6) "public" } array(2) { // NICE ["a"]=> string(9) "protected" ["a"]=> string(6) "public" } // c.php object(a)#1 (3) { ["a":"a":private]=> string(7) "private" ["a":protected]=> string(9) "protected" ["a"]=> string(6) "public" } array(3) { ["a"]=> string(7) "private" ["a"]=> string(9) "protected" ["a"]=> string(6) "public" } string(6) "public" // The last element is always fetched/unset/modified string(21) "UNSETTING $array["a"]" array(2) { ["a"]=> string(7) "private" ["a"]=> string(9) "protected" } string(21) "UNSETTING $array["a"]" array(1) { ["a"]=> string(7) "private" } string(21) "UNSETTING $array["a"]" array(0) { } object(stdClass)#2 (3) { // Since the array generated using the array cast contains the access modifier indicators (private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name.), casting that array back to an object works (so the behaviour can be replicated manually without serialize) ["a":"a":private]=> string(7) "private" ["a":protected]=> string(9) "protected" ["a"]=> string(6) "public" } string(12) "a => private" string(14) "a => protected" string(11) "a => public" string(18) "UNSETTING $this->a" string(14) "a => protected" string(11) "a => public" string(18) "UNSETTING $this->a" string(14) "a => protected" string(11) "a => public"