0

To give some context; this process was working prior to adding in the Serializable interface. The TestAction2 is not keeping a reference to itself after unserializing; and I have tried adding the Serializable to the Action class and telling it to serialize the $parent but still no difference. It is correctly unserializing the $method field though.

Class Action serializes the reference in $parent field when the serialization is being done on the class that is referencing it (class B), but not when it references itself. Something with serializing happening on Class A or B but not the Action class itself.

class A implements Serializable { private static $label; public function serialize() { return serialize(self::$label); } public function unserialize($serialized) { self::$label = unserialize($serialized); } } class B extends A { private $actions; public function serialize() { return serialize([ 'actions' => $this->actions, 'parent' => parent::serialize()]); } public function unserialize($serialized) { $data = unserialize($serialized); $this->actions = $data['actions']; parent::unserialize($data['parent']); } public function addAction($anAction) { $this->actions[] = $anAction; } public function process() { $this->addAction(new TestAction1($this, 'test1')); // WORKS FINE $this->addAction(new TestAction2($this, 'test2')); // WORKS NOT! } } class Action { private $parent; private $method; public function __construct($cParent, $sMethod) { $this->parent = $cParent; } } class TestAction1 extends Action { public function __construct($cParent, $sMethod) { parent::__construct($cParent, $sMethod); } } class TestAction2 extends Action { private $maybeNeedLater; public function __construct($cParent, $sMethod) { $this->maybeNeedLater = $cParent; parent::__construct($this, $sMethod); // pass $this instead } } $testThis = new B(); $testThis->process(); $serialized = serialize($testThis); $testThis = unserialize($serialized); 

The Action class TestAction2 will have a null $parent field in the $actions field of $testThis

4
  • Typo in parent::__constsruct? Commented Jan 5, 2018 at 0:03
  • excuse the typo; this code was not copy/paste. Just trying to get input on what may be causing the Action class to not serialize itself; why is it null? Commented Jan 5, 2018 at 0:06
  • You need to implement the __sleep() and __wakeup() magic methods to save and recreate the links between objects. Commented Jan 5, 2018 at 0:39
  • @Barmar PHP documentation specifies I can use the Serializable interface in place of the magic methods; and this was all working before I had implemented the Serializable interface on the class A that is holding reference to the Action instances. Commented Jan 5, 2018 at 0:49

1 Answer 1

1

Your two different Action classes a bit of a red herring, because they are not the problem. Test2 just happens to use a reference other than $testThis; and many other reference would be wrong as well.

The real problem is that the order of serialization in your A/B classes is different from the order of unserialization. During serialization PHP encodes references with r (or R) and a number (r and R are different in many ways but the difference would just distract from the counting issue). In your example $needThis can be referenced with r:1;, A::label with r:2;, the actions array with r:3;, and so on. The $this reference to the TestAction2 object is r:8; (you can see this when you echo $serialized.)

When you call unserialize inside your unserialize function, there is no A::label (yet). Because of that all numbers greater than 1 will be off by one. r:8; now points to whatever is in maybeNeedLater.

Now things get a little complicated, because PHP normally does not create such strings. In PHP5 it seems to create some kind of error that is printed as NULL. In PHP7 parent will actually be a reference to $needThis.

Luckily there are a few easy ways to solve this:

Option 1:

Use a different format to encode your parent class:

class A implements Serializable { private static $label; public function serialize() { return json_encode(self::$label); } public function unserialize($serialized) { self::$label = json_decode($serialized); } } 

Option 2:

Use double serialization in your child class and make sure the order fits:

class B extends A { private $actions; public function serialize() { return json_encode([ 'actions' => serialize($this->actions), 'parent' => parent::serialize()]); } public function unserialize($serialized) { $data = json_decode($serialized, true); $this->actions = unserialize($data['actions']); parent::unserialize($data['parent']); } ... 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.