From b6fb60e0e9aba373e654dd6ef7cb54c894e33bef Mon Sep 17 00:00:00 2001 From: Dmytro Kulyk Date: Sun, 19 Oct 2025 22:02:49 +0000 Subject: [PATCH 1/4] Update `__sleep()` serialization behavior to support `null` or no return values --- ext/standard/tests/serialize/bug21957.phpt | 9 +++++++-- ext/standard/tests/serialize/bug79526.phpt | 4 ++-- ext/standard/tests/serialize/bug81163.phpt | 2 +- ext/standard/var.c | 19 +++++++------------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ext/standard/tests/serialize/bug21957.phpt b/ext/standard/tests/serialize/bug21957.phpt index 9d761a6699c96..80bc8cd661846 100644 --- a/ext/standard/tests/serialize/bug21957.phpt +++ b/ext/standard/tests/serialize/bug21957.phpt @@ -40,10 +40,15 @@ array(2) { int(2) } } -a:2:{s:3:"one";s:3:"ABC";s:3:"two";N;} +a:2:{s:3:"one";s:3:"ABC";s:3:"two";O:4:"test":2:{s:1:"a";i:7;s:1:"b";i:0;}} array(2) { ["one"]=> string(3) "ABC" ["two"]=> - NULL + object(test)#2 (2) { + ["a"]=> + int(7) + ["b"]=> + int(0) + } } diff --git a/ext/standard/tests/serialize/bug79526.phpt b/ext/standard/tests/serialize/bug79526.phpt index 440c18772d53d..14b8846f3bce1 100644 --- a/ext/standard/tests/serialize/bug79526.phpt +++ b/ext/standard/tests/serialize/bug79526.phpt @@ -24,9 +24,9 @@ serialize(new B()); ?> Done --EXPECTF-- -Warning: serialize(): A::__sleep() should return an array only containing the names of instance-variables to serialize in %s on line %d +Warning: serialize(): A::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d -Warning: serialize(): B::__sleep() should return an array only containing the names of instance-variables to serialize in %s on line %d +Warning: serialize(): B::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d Warning: serialize(): "1" returned as member variable from __sleep() but does not exist in %s on line %d Done diff --git a/ext/standard/tests/serialize/bug81163.phpt b/ext/standard/tests/serialize/bug81163.phpt index 855fc1c953ed2..60f13f9f69847 100644 --- a/ext/standard/tests/serialize/bug81163.phpt +++ b/ext/standard/tests/serialize/bug81163.phpt @@ -18,4 +18,4 @@ class bar extends foo serialize(new bar()); ?> --EXPECTF-- -Warning: serialize(): bar::__sleep() should return an array only containing the names of instance-variables to serialize in %s on line %d +Warning: serialize(): bar::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d diff --git a/ext/standard/var.c b/ext/standard/var.c index a1ef60410a338..ae3bd6ebfb2a1 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -832,14 +832,14 @@ static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function * zend_call_known_instance_method(fn, obj, &retval, /* param_count */ 0, /* params */ NULL); BG(serialize_lock)--; - if (Z_ISUNDEF(retval) || EG(exception)) { + if (Z_ISUNDEF(retval) || Z_ISNULL(retval) || EG(exception)) { zval_ptr_dtor(&retval); return NULL; } if (Z_TYPE(retval) != IS_ARRAY) { zval_ptr_dtor(&retval); - php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(obj->ce->name)); + php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value", ZSTR_VAL(obj->ce->name)); return NULL; } @@ -916,7 +916,7 @@ static int php_var_serialize_get_sleep_props( ZVAL_DEREF(name_val); if (Z_TYPE_P(name_val) != IS_STRING) { php_error_docref(NULL, E_WARNING, - "%s::__sleep() should return an array only containing the names of instance-variables to serialize", + "%s::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value", ZSTR_VAL(ce->name)); } @@ -1215,20 +1215,15 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ zval tmp; ZVAL_OBJ_COPY(&tmp, Z_OBJ_P(struc)); - if (!(ht = php_var_serialize_call_sleep(Z_OBJ(tmp), Z_FUNC_P(zv)))) { - if (!EG(exception)) { - /* we should still add element even if it's not OK, - * since we already wrote the length of the array before */ - smart_str_appendl(buf, "N;", 2); - } + ht = php_var_serialize_call_sleep(Z_OBJ(tmp), Z_FUNC_P(zv)); + if (ht) { + php_var_serialize_class(buf, &tmp, ht, var_hash); + zend_array_release(ht); OBJ_RELEASE(Z_OBJ(tmp)); return; } - php_var_serialize_class(buf, &tmp, ht, var_hash); - zend_array_release(ht); OBJ_RELEASE(Z_OBJ(tmp)); - return; } } From 839147ab556cf62894bfa1a8b116653cece35404 Mon Sep 17 00:00:00 2001 From: Dmytro Kulyk Date: Tue, 21 Oct 2025 03:25:13 +0300 Subject: [PATCH 2/4] Update __sleep() warnings and add a test for missing return values --- ext/standard/tests/serialize/007.phpt | 38 ++++++++++++++++++++++ ext/standard/tests/serialize/bug79526.phpt | 4 +-- ext/standard/tests/serialize/bug81163.phpt | 2 +- ext/standard/var.c | 4 +-- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/serialize/007.phpt diff --git a/ext/standard/tests/serialize/007.phpt b/ext/standard/tests/serialize/007.phpt new file mode 100644 index 0000000000000..f7614245e96a9 --- /dev/null +++ b/ext/standard/tests/serialize/007.phpt @@ -0,0 +1,38 @@ +--TEST-- +serialize() with __sleep() without props +--FILE-- +a = 2; + } +} + +class FooNull { + public $a = 1; + function __sleep() { + $this->a = 2; + return null; + } +} + +class Bar { + public $a = 1; + function __sleep() { + $this->a = 2; + return 1; + } +} + +var_dump(serialize(new Foo())); +var_dump(serialize(new FooNull())); +var_dump(serialize(new Bar())); +?> +--EXPECTF-- +string(31) "O:3:"Foo":1:{s:6:"%0Foo%0a";i:2;}" +string(30) "O:7:"FooNull":1:{s:1:"a";i:2;}" + +Warning: serialize(): Bar::__sleep() should return an array of property names, or return null/void to delegate to default serialization in /Users/dkulyk/Projects/src/php-src/ext/standard/tests/serialize/007.php on line 28 +string(26) "O:3:"Bar":1:{s:1:"a";i:2;}" diff --git a/ext/standard/tests/serialize/bug79526.phpt b/ext/standard/tests/serialize/bug79526.phpt index 14b8846f3bce1..a7307cbcf260b 100644 --- a/ext/standard/tests/serialize/bug79526.phpt +++ b/ext/standard/tests/serialize/bug79526.phpt @@ -24,9 +24,9 @@ serialize(new B()); ?> Done --EXPECTF-- -Warning: serialize(): A::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d +Warning: serialize(): A::__sleep() should return an array of property names, or return null/void to delegate to default serialization in %s on line %d -Warning: serialize(): B::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d +Warning: serialize(): B::__sleep() should return an array only containing the names of instance-variables to serialize in %s on line %d Warning: serialize(): "1" returned as member variable from __sleep() but does not exist in %s on line %d Done diff --git a/ext/standard/tests/serialize/bug81163.phpt b/ext/standard/tests/serialize/bug81163.phpt index 60f13f9f69847..fe222a10384f3 100644 --- a/ext/standard/tests/serialize/bug81163.phpt +++ b/ext/standard/tests/serialize/bug81163.phpt @@ -18,4 +18,4 @@ class bar extends foo serialize(new bar()); ?> --EXPECTF-- -Warning: serialize(): bar::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value in %s on line %d +Warning: serialize(): bar::__sleep() should return an array of property names, or return null/void to delegate to default serialization in %s on line %d diff --git a/ext/standard/var.c b/ext/standard/var.c index ae3bd6ebfb2a1..491d6068cded9 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -839,7 +839,7 @@ static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function * if (Z_TYPE(retval) != IS_ARRAY) { zval_ptr_dtor(&retval); - php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value", ZSTR_VAL(obj->ce->name)); + php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array of property names, or return null/void to delegate to default serialization", ZSTR_VAL(obj->ce->name)); return NULL; } @@ -916,7 +916,7 @@ static int php_var_serialize_get_sleep_props( ZVAL_DEREF(name_val); if (Z_TYPE_P(name_val) != IS_STRING) { php_error_docref(NULL, E_WARNING, - "%s::__sleep() should return an array only containing the names of instance-variables to serialize, null, or no value", + "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(ce->name)); } From b7a36d5d3d9092c4b955b1d9b6340e628338fe65 Mon Sep 17 00:00:00 2001 From: Dmytro Kulyk Date: Tue, 21 Oct 2025 03:48:56 +0300 Subject: [PATCH 3/4] Cleanup the test --- ext/standard/tests/serialize/007.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/serialize/007.phpt b/ext/standard/tests/serialize/007.phpt index f7614245e96a9..2876ca1570c68 100644 --- a/ext/standard/tests/serialize/007.phpt +++ b/ext/standard/tests/serialize/007.phpt @@ -34,5 +34,5 @@ var_dump(serialize(new Bar())); string(31) "O:3:"Foo":1:{s:6:"%0Foo%0a";i:2;}" string(30) "O:7:"FooNull":1:{s:1:"a";i:2;}" -Warning: serialize(): Bar::__sleep() should return an array of property names, or return null/void to delegate to default serialization in /Users/dkulyk/Projects/src/php-src/ext/standard/tests/serialize/007.php on line 28 +Warning: serialize(): Bar::__sleep() should return an array of property names, or return null/void to delegate to default serialization in %s on line %d string(26) "O:3:"Bar":1:{s:1:"a";i:2;}" From 3be0c282aabafe4d4b53c0fa81bd419ce2525e04 Mon Sep 17 00:00:00 2001 From: Dmytro Kulyk Date: Tue, 21 Oct 2025 13:00:41 +0300 Subject: [PATCH 4/4] Handle exception on sleep hook --- ext/standard/var.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/standard/var.c b/ext/standard/var.c index 491d6068cded9..aeea583ad7bca 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -1224,6 +1224,9 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ } OBJ_RELEASE(Z_OBJ(tmp)); + if (EG(exception)) { + return; + } } }