diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index cf304e738a4da..bf9ec4e695fb4 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -611,8 +611,13 @@ PHP_METHOD(SplFixedArray, __serialize) /* members */ if (intern->std.properties) { ZEND_HASH_FOREACH_STR_KEY_VAL(intern->std.properties, key, current) { - zend_hash_add(Z_ARRVAL_P(return_value), key, current); - Z_TRY_ADDREF_P(current); + /* The properties hash table can also contain the array elements if the properties table was already rebuilt. + * In this case we'd have a NULL key. We can't simply use the properties table in all cases because it's + * potentially out of sync (missing elements, or containing removed elements) and might need a rebuild. */ + if (key != NULL) { + zend_hash_add_new(Z_ARRVAL_P(return_value), key, current); + Z_TRY_ADDREF_P(current); + } } ZEND_HASH_FOREACH_END(); } } diff --git a/ext/spl/tests/gh10907.phpt b/ext/spl/tests/gh10907.phpt new file mode 100644 index 0000000000000..034c5f1d5a33c --- /dev/null +++ b/ext/spl/tests/gh10907.phpt @@ -0,0 +1,139 @@ +--TEST-- +GH-10907 (Unable to serialize processed SplFixedArrays in PHP 8.2.4) +--FILE-- +my_dynamic_property = "my_dynamic_property_value"; +$array[0] = "test value 1"; +$array[1] = "test value 2"; +var_dump(serialize($array)); +var_dump(unserialize(serialize($array))); +var_dump($array); +?> +--EXPECT-- +Test without rebuilding properties +string(73) "O:13:"SplFixedArray":2:{i:0;s:12:"test value 1";i:1;s:12:"test value 2";}" +object(SplFixedArray)#2 (2) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" +} +object(SplFixedArray)#1 (2) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" +} +================= +Test with rebuilding properties +object(SplFixedArray)#2 (2) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" +} +string(73) "O:13:"SplFixedArray":2:{i:0;s:12:"test value 1";i:1;s:12:"test value 2";}" +object(SplFixedArray)#1 (2) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" +} +object(SplFixedArray)#2 (2) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" +} +================= +Test with partially rebuilding properties +object(SplFixedArray)#1 (3) { + [0]=> + string(12) "test value 1" + [1]=> + NULL + [2]=> + NULL +} +string(79) "O:13:"SplFixedArray":3:{i:0;s:12:"test value 1";i:1;s:12:"test value 2";i:2;N;}" +object(SplFixedArray)#2 (3) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" + [2]=> + NULL +} +object(SplFixedArray)#1 (3) { + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" + [2]=> + NULL +} +================= +Test with adding members +string(161) "O:15:"MySplFixedArray":5:{i:0;s:12:"test value 1";i:1;s:12:"test value 2";i:2;N;s:9:"my_string";i:0;s:19:"my_dynamic_property";s:25:"my_dynamic_property_value";}" +object(MySplFixedArray)#1 (5) { + ["my_string"]=> + int(0) + ["my_dynamic_property"]=> + string(25) "my_dynamic_property_value" + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" + [2]=> + NULL +} +object(MySplFixedArray)#2 (5) { + ["my_string"]=> + string(15) "my_string_value" + ["my_dynamic_property"]=> + string(25) "my_dynamic_property_value" + [0]=> + string(12) "test value 1" + [1]=> + string(12) "test value 2" + [2]=> + NULL +}