[php-src] master: Merge branch 'PHP-8.6'

From: Date: Wed, 30 Sep 2026 20:23:55 +0000
Subject: [php-src] master: Merge branch 'PHP-8.6'
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-141821@lists.php.net to get a copy of this message
Author: Máté Kocsis (kocsismate) Date: 2026-09-30T22:23:27+02:00 Commit: https://github.com/php/php-src/commit/fe3d1ba04bebb0e670eaf6b6e64151260c225e4d Raw diff: https://github.com/php/php-src/commit/fe3d1ba04bebb0e670eaf6b6e64151260c225e4d.diff Merge branch 'PHP-8.6' * PHP-8.6: standard: Create incomplete objects again from the C format (#24020) Changed paths: A ext/standard/tests/serialize/incomplete_class_c_format.phpt A ext/standard/tests/serialize/incomplete_class_c_format_allowed_classes.phpt M ext/standard/tests/serialize/serialization_objects_009.phpt M ext/standard/var_unserializer.re Diff: diff --git a/ext/standard/tests/serialize/incomplete_class_c_format.phpt b/ext/standard/tests/serialize/incomplete_class_c_format.phpt new file mode 100644 index 000000000000..258e76921c4a --- /dev/null +++ b/ext/standard/tests/serialize/incomplete_class_c_format.phpt @@ -0,0 +1,36 @@ +--TEST-- +unserialize() turns C: objects of missing classes into incomplete objects +--FILE-- +<?php + +var_dump(unserialize('a:2:{i:0;C:7:"Missing":4:{abcd}i:1;s:4:"tail";}')); + +function callback(string $class): void +{ + echo __FUNCTION__, "($class)\n"; +} + +ini_set('unserialize_callback_func', 'callback'); + +var_dump(unserialize('C:7:"Missing":4:{abcd}')); +?> +--EXPECTF-- +Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d +array(2) { + [0]=> + object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(7) "Missing" + } + [1]=> + string(4) "tail" +} +callback(Missing) + +Warning: unserialize(): Function callback() hasn't defined the class it was called for in %s on line %d + +Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d +object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(7) "Missing" +} diff --git a/ext/standard/tests/serialize/incomplete_class_c_format_allowed_classes.phpt b/ext/standard/tests/serialize/incomplete_class_c_format_allowed_classes.phpt new file mode 100644 index 000000000000..54a68a935ef1 --- /dev/null +++ b/ext/standard/tests/serialize/incomplete_class_c_format_allowed_classes.phpt @@ -0,0 +1,63 @@ +--TEST-- +unserialize() with allowed_classes turns C: objects of classes that are not allowed into incomplete objects +--FILE-- +<?php + +final class Kept +{ + public $a = 1; +} + +final class Blocked implements Serializable +{ + public function serialize(): string + { + return ''; + } + + public function unserialize(string $data): void + { + echo __METHOD__, "\n"; + } + + public function __serialize(): array + { + return []; + } + + public function __unserialize(array $data): void + { + echo __METHOD__, "\n"; + } +} + +$payload = 'a:4:{i:0;O:4:"Kept":1:{s:1:"a";i:2;}i:1;C:7:"Blocked":4:{abcd}i:2;C:7:"Missing":4:{abcd}i:3;r:4;}'; + +var_dump(unserialize($payload, ['allowed_classes' => ['Kept']])); +?> +--EXPECTF-- +Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d + +Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d +array(4) { + [0]=> + object(Kept)#%d (1) { + ["a"]=> + int(2) + } + [1]=> + object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(7) "Blocked" + } + [2]=> + object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(7) "Missing" + } + [3]=> + object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(7) "Blocked" + } +} diff --git a/ext/standard/tests/serialize/serialization_objects_009.phpt b/ext/standard/tests/serialize/serialization_objects_009.phpt index 95b85ccd80f7..27da8e53ebb4 100644 --- a/ext/standard/tests/serialize/serialization_objects_009.phpt +++ b/ext/standard/tests/serialize/serialization_objects_009.phpt @@ -13,11 +13,12 @@ echo "Done"; --EXPECTF-- Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d -Warning: unserialize(): Error at offset 11 of 18 bytes in %s on line %d - Warning: Class C has no unserializer in %s on line %d Warning: unserialize(): Error at offset 11 of 18 bytes in %s on line %d -bool(false) +object(__PHP_Incomplete_Class)#%d (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(1) "C" +} bool(false) Done diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 0138471aa63e..e10b067c68d8 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -769,7 +769,11 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); - return 0; + /* __PHP_Incomplete_Class has no internal state, an empty instance is safe. */ + if (ce != PHP_IC_ENTRY) { + return 0; + } + object_init_ex(rval, ce); } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { return 0; }

« previous php.cvs (#141821) next »