Skip to content

Commit d373c11

Browse files
committed
Implement new custom object serialization mechanism
RFC: https://wiki.php.net/rfc/custom_object_serialization
1 parent e7e2056 commit d373c11

File tree

8 files changed

+457
-41
lines changed

8 files changed

+457
-41
lines changed

UPGRADING

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ PHP 7.4 UPGRADE NOTES
107107
This will enforce that $user->id can only be assigned integer and
108108
$user->name can only be assigned strings. For more information see the
109109
RFC: https://wiki.php.net/rfc/typed_properties_v2
110+
110111
. Added support for coalesce assign (??=) operator. For example:
111112

112113
$array['key'] ??= computeDefault();
@@ -156,6 +157,20 @@ PHP 7.4 UPGRADE NOTES
156157
. strip_tags() now also accepts an array of allowed tags: Instead of
157158
strip_tags($str, '<a><p>') you can now write strip_tags($str, ['a', 'p']).
158159

160+
. A new mechanism for custom object serialization has been added, which
161+
uses two new magic methods:
162+
163+
// Returns array containing all the necessary state of the object.
164+
public function __serialize(): array;
165+
166+
// Restores the object state from the given data array.
167+
public function __unserialize(array $data): void;
168+
169+
The new serialization mechanism supersedes the Serializable interface,
170+
which will be deprecated in the future.
171+
172+
RFC: https://wiki.php.net/rfc/custom_object_serialization
173+
159174
========================================
160175
3. Changes in SAPI modules
161176
========================================
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
__serialize() mechanism (001): Basics
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public $prop;
8+
public $prop2;
9+
public function __serialize() {
10+
return ["value" => $this->prop, 42 => $this->prop2];
11+
}
12+
public function __unserialize(array $data) {
13+
$this->prop = $data["value"];
14+
$this->prop2 = $data[42];
15+
}
16+
}
17+
18+
$test = new Test;
19+
$test->prop = "foobar";
20+
$test->prop2 = "barfoo";
21+
var_dump($s = serialize($test));
22+
var_dump(unserialize($s));
23+
24+
?>
25+
--EXPECT--
26+
string(58) "O:4:"Test":2:{s:5:"value";s:6:"foobar";i:42;s:6:"barfoo";}"
27+
object(Test)#2 (2) {
28+
["prop"]=>
29+
string(6) "foobar"
30+
["prop2"]=>
31+
string(6) "barfoo"
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
__serialize() mechanism (002): TypeError on invalid return type
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function __serialize() {
8+
return $this;
9+
}
10+
}
11+
12+
try {
13+
serialize(new Test);
14+
} catch (TypeError $e) {
15+
echo $e->getMessage(), "\n";
16+
}
17+
18+
?>
19+
--EXPECT--
20+
__serialize() must return an array
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
__serialize() mechanism (003): Interoperability of different serialization mechanisms
3+
--FILE--
4+
<?php
5+
6+
class Test implements Serializable {
7+
public function __sleep() {
8+
echo "__sleep() called\n";
9+
}
10+
11+
public function __wakeup() {
12+
echo "__wakeup() called\n";
13+
}
14+
15+
public function __serialize() {
16+
echo "__serialize() called\n";
17+
return ["key" => "value"];
18+
}
19+
20+
public function __unserialize(array $data) {
21+
echo "__unserialize() called\n";
22+
var_dump($data);
23+
}
24+
25+
public function serialize() {
26+
echo "serialize() called\n";
27+
return "payload";
28+
}
29+
30+
public function unserialize($payload) {
31+
echo "unserialize() called\n";
32+
var_dump($payload);
33+
}
34+
}
35+
36+
$test = new Test;
37+
var_dump($s = serialize($test));
38+
var_dump(unserialize($s));
39+
40+
var_dump(unserialize('C:4:"Test":7:{payload}'));
41+
42+
?>
43+
--EXPECT--
44+
__serialize() called
45+
string(37) "O:4:"Test":1:{s:3:"key";s:5:"value";}"
46+
__unserialize() called
47+
array(1) {
48+
["key"]=>
49+
string(5) "value"
50+
}
51+
object(Test)#2 (0) {
52+
}
53+
unserialize() called
54+
string(7) "payload"
55+
object(Test)#2 (0) {
56+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
--TEST--
2+
__serialize() mechanism (004): Delayed __unserialize() calls
3+
--FILE--
4+
<?php
5+
6+
class Wakeup {
7+
public $data;
8+
public function __construct(array $data) {
9+
$this->data = $data;
10+
}
11+
public function __wakeup() {
12+
echo "__wakeup() called\n";
13+
var_dump($this->data);
14+
$this->woken_up = true;
15+
}
16+
}
17+
18+
class Unserialize {
19+
public $data;
20+
public function __construct(array $data) {
21+
$this->data = $data;
22+
}
23+
public function __serialize() {
24+
return $this->data;
25+
}
26+
public function __unserialize(array $data) {
27+
$this->data = $data;
28+
echo "__unserialize() called\n";
29+
var_dump($this->data);
30+
$this->unserialized = true;
31+
}
32+
}
33+
34+
$obj = new Wakeup([new Unserialize([new Wakeup([new Unserialize([])])])]);
35+
var_dump($s = serialize($obj));
36+
var_dump(unserialize($s));
37+
38+
?>
39+
--EXPECT--
40+
string(126) "O:6:"Wakeup":1:{s:4:"data";a:1:{i:0;O:11:"Unserialize":1:{i:0;O:6:"Wakeup":1:{s:4:"data";a:1:{i:0;O:11:"Unserialize":0:{}}}}}}"
41+
__unserialize() called
42+
array(0) {
43+
}
44+
__wakeup() called
45+
array(1) {
46+
[0]=>
47+
object(Unserialize)#8 (2) {
48+
["data"]=>
49+
array(0) {
50+
}
51+
["unserialized"]=>
52+
bool(true)
53+
}
54+
}
55+
__unserialize() called
56+
array(1) {
57+
[0]=>
58+
object(Wakeup)#7 (2) {
59+
["data"]=>
60+
array(1) {
61+
[0]=>
62+
object(Unserialize)#8 (2) {
63+
["data"]=>
64+
array(0) {
65+
}
66+
["unserialized"]=>
67+
bool(true)
68+
}
69+
}
70+
["woken_up"]=>
71+
bool(true)
72+
}
73+
}
74+
__wakeup() called
75+
array(1) {
76+
[0]=>
77+
object(Unserialize)#6 (2) {
78+
["data"]=>
79+
array(1) {
80+
[0]=>
81+
object(Wakeup)#7 (2) {
82+
["data"]=>
83+
array(1) {
84+
[0]=>
85+
object(Unserialize)#8 (2) {
86+
["data"]=>
87+
array(0) {
88+
}
89+
["unserialized"]=>
90+
bool(true)
91+
}
92+
}
93+
["woken_up"]=>
94+
bool(true)
95+
}
96+
}
97+
["unserialized"]=>
98+
bool(true)
99+
}
100+
}
101+
object(Wakeup)#5 (2) {
102+
["data"]=>
103+
array(1) {
104+
[0]=>
105+
object(Unserialize)#6 (2) {
106+
["data"]=>
107+
array(1) {
108+
[0]=>
109+
object(Wakeup)#7 (2) {
110+
["data"]=>
111+
array(1) {
112+
[0]=>
113+
object(Unserialize)#8 (2) {
114+
["data"]=>
115+
array(0) {
116+
}
117+
["unserialized"]=>
118+
bool(true)
119+
}
120+
}
121+
["woken_up"]=>
122+
bool(true)
123+
}
124+
}
125+
["unserialized"]=>
126+
bool(true)
127+
}
128+
}
129+
["woken_up"]=>
130+
bool(true)
131+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
__serialize() mechanism (005): parent::__unserialize() is safe
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
private $data;
8+
public function __construct(array $data) {
9+
$this->data = $data;
10+
}
11+
public function __serialize() {
12+
return $this->data;
13+
}
14+
public function __unserialize(array $data) {
15+
$this->data = $data;
16+
}
17+
}
18+
19+
class B extends A {
20+
private $data2;
21+
public function __construct(array $data, array $data2) {
22+
parent::__construct($data);
23+
$this->data2 = $data2;
24+
}
25+
public function __serialize() {
26+
return [$this->data2, parent::__serialize()];
27+
}
28+
public function __unserialize(array $payload) {
29+
[$data2, $data] = $payload;
30+
parent::__unserialize($data);
31+
$this->data2 = $data2;
32+
}
33+
}
34+
35+
$common = new stdClass;
36+
$obj = new B([$common], [$common]);
37+
var_dump($s = serialize($obj));
38+
var_dump(unserialize($s));
39+
40+
?>
41+
--EXPECT--
42+
string(63) "O:1:"B":2:{i:0;a:1:{i:0;O:8:"stdClass":0:{}}i:1;a:1:{i:0;r:3;}}"
43+
object(B)#3 (2) {
44+
["data2":"B":private]=>
45+
array(1) {
46+
[0]=>
47+
object(stdClass)#4 (0) {
48+
}
49+
}
50+
["data":"A":private]=>
51+
array(1) {
52+
[0]=>
53+
object(stdClass)#4 (0) {
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)