Skip to content

Instantly share code, notes, and snippets.

@laszlokorte
Last active January 7, 2017 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laszlokorte/3948f40873346cc1fd9b8c11ab06ae04 to your computer and use it in GitHub Desktop.
Save laszlokorte/3948f40873346cc1fd9b8c11ab06ae04 to your computer and use it in GitHub Desktop.
Php Serializable bug
<?php
// The root container object
class Root implements Serializable {
private $nodes;
private $mainNode;
public function __construct() {
$this->nodes = new IdentifierMap();
}
// Adds child object
public function addNode(Identifier $id, $value) {
$newNode = new Node($value);
$this->nodes[$id] = $newNode;
return $newNode;
}
// sets some property
public function setMainNode(Identifier $id) {
$this->mainNode = $id;
}
public function serialize() {
return serialize([
$this->nodes,
$this->mainNode,
]);
}
public function unserialize($data) {
list(
$this->nodes,
$this->mainNode,
) = unserialize($data);
}
}
// Some child object of the root
// stores some value
class Node implements Serializable {
private $value;
private $props;
private $mainProp = NULL;
public function __construct($value) {
$this->props = new IdentifierMap();
$this->value = $value;
}
public function serialize() {
return serialize([
$this->props,
$this->value,
$this->mainProp,
]);
}
public function unserialize($data) {
list(
$this->props,
$this->value,
$this->mainProp,
) = unserialize($data);
}
public function defineProp(Identifier $id, $val) {
$this->props[$id] = $val;
}
public function setMainProp(Identifier $id) {
$this->mainProp = $id;
}
}
final class IdentifierMap extends SplObjectStorage implements Serializable {
public function getHash($o) {
return $o->hash();
}
public function serialize() {
return serialize([
'parent' => parent::serialize()
]);
}
public function unserialize($data) {
parent::unserialize(unserialize($data)['parent']);
}
}
final class Identifier implements Serializable {
private $name;
public function __construct($name) {
if(!is_string($name)) {
throw new \Exception(sprintf("Identifier must be a string %s given", gettype($name)));
}
$this->name = $name;
}
public function __toString() {
return $this->name;
}
public function hash() {
return $this->name;
}
public function serialize() {
return serialize($this->name);
}
public function unserialize($data) {
$this->name = unserialize($data);
}
}
echo "<pre>";
$root = new Root();
foreach (['fooNode','mainNode'] as $name) {
$reusedNodeId = new Identifier($name);
$node = $root->addNode($reusedNodeId, 'some value');
if($name == 'mainNode') {
$root->setMainNode($reusedNodeId);
// if in the call above ^ $reusedNodeId is cloned it works fine
}
foreach(['fooProp','mainProp'] AS $prop) {
$reusedPropId = new Identifier($prop);
$node->defineProp($reusedPropId, 'propValue');
if($prop === 'mainProp') {
$node->setMainProp($reusedPropId);
// if in the call above ^ $reusedPropId is cloned it works fine
}
}
}
$unserializedRoot = unserialize(serialize($root));
echo "Expected:\n";
var_dump($root);
echo "Actual:\n";
var_dump($unserializedRoot);
Expected:
object(Root)#1 (2) {
["nodes":"Root":private]=>
object(IdentifierMap)#2 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fd20000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#3 (1) {
["name":"Identifier":private]=>
string(7) "fooNode"
}
["inf"]=>
object(Node)#4 (3) {
["value":"Node":private]=>
string(10) "some value"
["props":"Node":private]=>
object(IdentifierMap)#5 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fd70000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#6 (1) {
["name":"Identifier":private]=>
string(7) "fooProp"
}
["inf"]=>
string(9) "propValue"
}
["000000003acf8fd60000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#7 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
["inf"]=>
string(9) "propValue"
}
}
}
["mainProp":"Node":private]=>
object(Identifier)#7 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
}
}
["000000003acf8fd90000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#8 (1) {
["name":"Identifier":private]=>
string(8) "mainNode"
}
["inf"]=>
object(Node)#9 (3) {
["value":"Node":private]=>
string(10) "some value"
["props":"Node":private]=>
object(IdentifierMap)#10 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fda0000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#11 (1) {
["name":"Identifier":private]=>
string(7) "fooProp"
}
["inf"]=>
string(9) "propValue"
}
["000000003acf8fdd0000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#12 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
["inf"]=>
string(9) "propValue"
}
}
}
["mainProp":"Node":private]=>
object(Identifier)#12 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
}
}
}
}
["mainNode":"Root":private]=>
object(Identifier)#8 (1) {
["name":"Identifier":private]=>
string(8) "mainNode"
}
}
Actual:
object(Root)#13 (2) {
["nodes":"Root":private]=>
object(IdentifierMap)#14 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fde0000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#15 (1) {
["name":"Identifier":private]=>
string(7) "fooNode"
}
["inf"]=>
object(Node)#16 (3) {
["value":"Node":private]=>
string(10) "some value"
["props":"Node":private]=>
object(IdentifierMap)#17 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fc30000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#18 (1) {
["name":"Identifier":private]=>
string(7) "fooProp"
}
["inf"]=>
string(9) "propValue"
}
["000000003acf8fc20000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#19 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
["inf"]=>
string(9) "propValue"
}
}
}
["mainProp":"Node":private]=>
int(2) <<<<<<<<<<<<<<<<< note that mainProp is an integer and not an Identifier value
}
}
["000000003acf8fc50000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#20 (1) {
["name":"Identifier":private]=>
string(8) "mainNode"
}
["inf"]=>
object(Node)#21 (3) {
["value":"Node":private]=>
string(10) "some value"
["props":"Node":private]=>
object(IdentifierMap)#22 (1) {
["storage":"SplObjectStorage":private]=>
array(2) {
["000000003acf8fc60000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#23 (1) {
["name":"Identifier":private]=>
string(7) "fooProp"
}
["inf"]=>
string(9) "propValue"
}
["000000003acf8fc90000000051ee4838"]=>
array(2) {
["obj"]=>
object(Identifier)#24 (1) {
["name":"Identifier":private]=>
string(8) "mainProp"
}
["inf"]=>
string(9) "propValue"
}
}
}
["mainProp":"Node":private]=>
int(2) <<<<<<<<<<<<<<<<< note that mainProp is an integer and not an Identifier value
}
}
}
}
["mainNode":"Root":private]=>
string(10) "some value" <<<<<<<<<<<<<<<<< note that mainNode is a string and not an IdentifierObject
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment