Open
Description
Description
Overview
Starting from 8.1.0
cloning recursive objects inline when calling serialize()
breaks the serialization recursion. For example:
serialize(clone $obj);
However, assigning the clone to a variable and then passing it to the serialize()
function will not break.
$clone = clone $obj;
serialize($clone);
Additionally, there is some undefined behavior related to defining an "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:
Click to expand codeblock
<?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);
?>
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