[php-src] Issue #12265: Cloning breaks serialization recursion
| From: | azuradara | 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