[php-src] Issue #12265: Cloning breaks serialization recursion

From: Date: Fri, 22 Sep 2023 00:06:12 +0000
Subject: [php-src] Issue #12265: Cloning breaks serialization recursion
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-245416@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/12265 Author: azuradara ### Description ### Overview Starting from 8.1.0 cloning recursive objects inline when calling serialize() breaks the serialization recursion. For example: ```php serialize(clone $obj); ``` However, assigning the clone to a variable and then passing it to the serialize() function will not break. ```php $clone = clone $obj; serialize($clone); ``` Additionally, there is some undefined behavior related to defining and "instance" property on the object and assigning the object's instance to it within the __clone() magic method which somehow fixes everything. Not sure about this part, but this is the only way I got it to work consistently in my usecase. Here's a 3v4l link: https://3v4l.org/6FKnp#v810 ### Reproduction The following code: <details> <summary>Click to expand codeblock</summary> ```php <?php class A { public B|C $x; public function __construct(B|C $x) { $this->x = $x; } public function __sleep() { return ['x']; } } class B { public A $a; public function __serialize() { return ['a' => new A($this)]; } } class C { public A $a; private self $instance; public function __serialize() { return ['a' => new A($this)]; } public function __clone() { $this->instance = $this; } } // instantiate $b = new B(); $c = new C(); // clone and assign $ib = clone $b; $ic = clone $c; // serialize $sb = serialize($b); $scb = serialize(clone $b); $sib = serialize($ib); $sc = serialize($c); $scc = serialize(clone $c); $sic = serialize($ic); // unserialize $ub = unserialize($sb); $ucb = unserialize($scb); $uib = unserialize($sib); $uc = unserialize($sc); $ucc = unserialize($scc); $uic = unserialize($sic); // expected behavior is b == b->a->x printf("original ids: b:%s, b->a:%s, b->a->x:%s \n", spl_object_id($ub), spl_object_id($ub->a), spl_object_id($ub->a->x)); printf("cloned ids: b:%s, b->a:%s, b->a->x:%s \n", spl_object_id($ucb), spl_object_id($ucb->a), spl_object_id($ucb->a->x)); printf("cloned & assigned ids: b:%s, b->a:%s, b->a->x:%s \n", spl_object_id($uib), spl_object_id($uib->a), spl_object_id($uib->a->x)); printf(PHP_EOL); // expected behavior is c == c->a->x printf("original ids: c:%s, c->a:%s, c->a->x:%s \n", spl_object_id($uc), spl_object_id($uc->a), spl_object_id($uc->a->x)); printf("cloned ids: c:%s, c->a:%s, c->a->x:%s \n", spl_object_id($ucc), spl_object_id($ucc->a), spl_object_id($ucc->a->x)); printf("cloned & assigned ids: c:%s, c->a:%s, c->a->x:%s \n", spl_object_id($uic), spl_object_id($uic->a), spl_object_id($uic->a->x)); printf(PHP_EOL); printf("serialized original: %s\n", $sb); printf("serialized clone : %s\n", $scb); ?> ``` </details> Outputs this: ``` original ids: b:6, b->a:7, b->a->x:6 cloned ids: b:8, b->a:9, b->a->x:10 cloned & assigned ids: b:12, b->a:13, b->a->x:12 original ids: c:14, c->a:15, c->a->x:14 cloned ids: c:16, c->a:17, c->a->x:16 cloned & assigned ids: c:18, c->a:19, c->a->x:18 serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} serialized clone : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:3;}}}} ``` But I expected this output instead: ``` original ids: b:6, b->a:7, b->a->x:6 cloned ids: b:8, b->a:9, b->a->x:8 cloned & assigned ids: b:10, b->a:11, b->a->x:10 original ids: c:12, c->a:13, c->a->x:12 cloned ids: c:14, c->a:15, c->a->x:14 cloned & assigned ids: c:16, c->a:17, c->a->x:16 serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} serialized clone : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} ``` ### PHP Version 8.2.0 ### Operating System Debian 12

« previous php.bugs (#245416) next »