Skip to content

Cloning an object breaks serialization recursion #12265

Closed

Description

@azuradara

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

Activity

  1. changed the title [-]Cloning breaks serialization recursion[/-] [+]Cloning an object breaks serialization recursion[/+] on Sep 21, 2023
  2. ndossche commented on Sep 23, 2023

    @ndossche
    Member

    Can confirm in 8.1+, broke in 6c5942f

  3. added 2 commits that reference this issue on Sep 8, 2025
    8ef02b7
    f6f1748
  4. added 2 commits that reference this issue on Sep 10, 2025
    23a7ba1
    75e320f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions