Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions ext/standard/tests/serialize/__serialize_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
__serialize() mechanism (001): Basics
--FILE--
<?php

class Test {
public $prop;
public $prop2;
public function __serialize() {
return ["value" => $this->prop, 42 => $this->prop2];
}
public function __unserialize(array $data) {
$this->prop = $data["value"];
$this->prop2 = $data[42];
}
}

$test = new Test;
$test->prop = "foobar";
$test->prop2 = "barfoo";
var_dump($s = serialize($test));
var_dump(unserialize($s));

?>
--EXPECT--
string(58) "O:4:"Test":2:{s:5:"value";s:6:"foobar";i:42;s:6:"barfoo";}"
object(Test)#2 (2) {
["prop"]=>
string(6) "foobar"
["prop2"]=>
string(6) "barfoo"
}
20 changes: 20 additions & 0 deletions ext/standard/tests/serialize/__serialize_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
__serialize() mechanism (002): TypeError on invalid return type
--FILE--
<?php

class Test {
public function __serialize() {
return $this;
}
}

try {
serialize(new Test);
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
__serialize() must return an array
56 changes: 56 additions & 0 deletions ext/standard/tests/serialize/__serialize_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
__serialize() mechanism (003): Interoperability of different serialization mechanisms
--FILE--
<?php

class Test implements Serializable {
public function __sleep() {
echo "__sleep() called\n";
}

public function __wakeup() {
echo "__wakeup() called\n";
}

public function __serialize() {
echo "__serialize() called\n";
return ["key" => "value"];
}

public function __unserialize(array $data) {
echo "__unserialize() called\n";
var_dump($data);
}

public function serialize() {
echo "serialize() called\n";
return "payload";
}

public function unserialize($payload) {
echo "unserialize() called\n";
var_dump($payload);
}
}

$test = new Test;
var_dump($s = serialize($test));
var_dump(unserialize($s));

var_dump(unserialize('C:4:"Test":7:{payload}'));

?>
--EXPECT--
__serialize() called
string(37) "O:4:"Test":1:{s:3:"key";s:5:"value";}"
__unserialize() called
array(1) {
["key"]=>
string(5) "value"
}
object(Test)#2 (0) {
}
unserialize() called
string(7) "payload"
object(Test)#2 (0) {
}
131 changes: 131 additions & 0 deletions ext/standard/tests/serialize/__serialize_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
--TEST--
__serialize() mechanism (004): Delayed __unserialize() calls
--FILE--
<?php

class Wakeup {
public $data;
public function __construct(array $data) {
$this->data = $data;
}
public function __wakeup() {
echo "__wakeup() called\n";
var_dump($this->data);
$this->woken_up = true;
}
}

class Unserialize {
public $data;
public function __construct(array $data) {
$this->data = $data;
}
public function __serialize() {
return $this->data;
}
public function __unserialize(array $data) {
$this->data = $data;
echo "__unserialize() called\n";
var_dump($this->data);
$this->unserialized = true;
}
}

$obj = new Wakeup([new Unserialize([new Wakeup([new Unserialize([])])])]);
var_dump($s = serialize($obj));
var_dump(unserialize($s));

?>
--EXPECT--
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:{}}}}}}"
__unserialize() called
array(0) {
}
__wakeup() called
array(1) {
[0]=>
object(Unserialize)#8 (2) {
["data"]=>
array(0) {
}
["unserialized"]=>
bool(true)
}
}
__unserialize() called
array(1) {
[0]=>
object(Wakeup)#7 (2) {
["data"]=>
array(1) {
[0]=>
object(Unserialize)#8 (2) {
["data"]=>
array(0) {
}
["unserialized"]=>
bool(true)
}
}
["woken_up"]=>
bool(true)
}
}
__wakeup() called
array(1) {
[0]=>
object(Unserialize)#6 (2) {
["data"]=>
array(1) {
[0]=>
object(Wakeup)#7 (2) {
["data"]=>
array(1) {
[0]=>
object(Unserialize)#8 (2) {
["data"]=>
array(0) {
}
["unserialized"]=>
bool(true)
}
}
["woken_up"]=>
bool(true)
}
}
["unserialized"]=>
bool(true)
}
}
object(Wakeup)#5 (2) {
["data"]=>
array(1) {
[0]=>
object(Unserialize)#6 (2) {
["data"]=>
array(1) {
[0]=>
object(Wakeup)#7 (2) {
["data"]=>
array(1) {
[0]=>
object(Unserialize)#8 (2) {
["data"]=>
array(0) {
}
["unserialized"]=>
bool(true)
}
}
["woken_up"]=>
bool(true)
}
}
["unserialized"]=>
bool(true)
}
}
["woken_up"]=>
bool(true)
}
56 changes: 56 additions & 0 deletions ext/standard/tests/serialize/__serialize_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
__serialize() mechanism (005): parent::__unserialize() is safe
--FILE--
<?php

class A {
private $data;
public function __construct(array $data) {
$this->data = $data;
}
public function __serialize() {
return $this->data;
}
public function __unserialize(array $data) {
$this->data = $data;
}
}

class B extends A {
private $data2;
public function __construct(array $data, array $data2) {
parent::__construct($data);
$this->data2 = $data2;
}
public function __serialize() {
return [$this->data2, parent::__serialize()];
}
public function __unserialize(array $payload) {
[$data2, $data] = $payload;
parent::__unserialize($data);
$this->data2 = $data2;
}
}

$common = new stdClass;
$obj = new B([$common], [$common]);
var_dump($s = serialize($obj));
var_dump(unserialize($s));

?>
--EXPECT--
string(63) "O:1:"B":2:{i:0;a:1:{i:0;O:8:"stdClass":0:{}}i:1;a:1:{i:0;r:3;}}"
object(B)#3 (2) {
["data2":"B":private]=>
array(1) {
[0]=>
object(stdClass)#4 (0) {
}
}
["data":"A":private]=>
array(1) {
[0]=>
object(stdClass)#4 (0) {
}
}
}
63 changes: 63 additions & 0 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,32 @@ static int php_var_serialize_call_sleep(zval *retval, zval *struc) /* {{{ */
}
/* }}} */

static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{{ */
{
zval fname;
int res;

ZVAL_STRINGL(&fname, "__serialize", sizeof("__serialize") - 1);
BG(serialize_lock)++;
res = call_user_function(CG(function_table), obj, &fname, retval, 0, 0);
BG(serialize_lock)--;
zval_ptr_dtor_str(&fname);

if (res == FAILURE || Z_ISUNDEF_P(retval)) {
zval_ptr_dtor(retval);
return FAILURE;
}

if (Z_TYPE_P(retval) != IS_ARRAY) {
zval_ptr_dtor(retval);
zend_type_error("__serialize() must return an array");
return FAILURE;
}

return SUCCESS;
}
/* }}} */

static void php_var_serialize_collect_names(HashTable *ht, HashTable *src) /* {{{ */
{
zval *val;
Expand Down Expand Up @@ -904,6 +930,43 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
case IS_OBJECT: {
zend_class_entry *ce = Z_OBJCE_P(struc);

if (zend_hash_str_exists(&ce->function_table, "__serialize", sizeof("__serialize")-1)) {
zval retval, obj;
zend_string *key;
zval *data;
zend_ulong index;

ZVAL_COPY(&obj, struc);
if (php_var_serialize_call_magic_serialize(&retval, &obj) == FAILURE) {
if (!EG(exception)) {
smart_str_appendl(buf, "N;", 2);
}
zval_ptr_dtor(&obj);
return;
}

php_var_serialize_class_name(buf, &obj);
smart_str_append_unsigned(buf, zend_array_count(Z_ARRVAL(retval)));
smart_str_appendl(buf, ":{", 2);
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(retval), index, key, data) {
if (!key) {
php_var_serialize_long(buf, index);
} else {
php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
}

if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
data = Z_REFVAL_P(data);
}
php_var_serialize_intern(buf, data, var_hash);
} ZEND_HASH_FOREACH_END();
smart_str_appendc(buf, '}');

zval_ptr_dtor(&obj);
zval_ptr_dtor(&retval);
return;
}

if (ce->serialize != NULL) {
/* has custom handler */
unsigned char *serialized_data = NULL;
Expand Down
Loading