Last active
January 7, 2017 23:05
-
-
Save laszlokorte/3948f40873346cc1fd9b8c11ab06ae04 to your computer and use it in GitHub Desktop.
Php Serializable bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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