@@ -48,8 +48,6 @@ typedef struct _spl_fixedarray {
4848 zend_long size ;
4949 /* It is possible to resize this, so this can't be combined with the object */
5050 zval * elements ;
51- /* True if this was modified after the last call to get_properties or the hash table wasn't rebuilt. */
52- bool should_rebuild_properties ;
5351} spl_fixedarray ;
5452
5553typedef struct _spl_fixedarray_object {
@@ -107,7 +105,6 @@ static void spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_lon
107105 array -> size = 0 ; /* reset size in case ecalloc() fails */
108106 array -> elements = size ? safe_emalloc (size , sizeof (zval ), 0 ) : NULL ;
109107 array -> size = size ;
110- array -> should_rebuild_properties = true;
111108}
112109
113110static void spl_fixedarray_init (spl_fixedarray * array , zend_long size )
@@ -177,7 +174,6 @@ static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
177174 /* nothing to do */
178175 return ;
179176 }
180- array -> should_rebuild_properties = true;
181177
182178 /* first initialization */
183179 if (array -> size == 0 ) {
@@ -211,47 +207,41 @@ static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, i
211207 return ht ;
212208}
213209
214- static HashTable * spl_fixedarray_object_get_properties (zend_object * obj )
210+ static HashTable * spl_fixedarray_object_get_properties_for (zend_object * obj , zend_prop_purpose purpose )
215211{
216- spl_fixedarray_object * intern = spl_fixed_array_from_obj (obj );
217- HashTable * ht = zend_std_get_properties (obj );
218-
219- if (!spl_fixedarray_empty (& intern -> array )) {
220- /*
221- * Usually, the reference count of the hash table is 1,
222- * except during cyclic reference cycles.
223- *
224- * Maintain the DEBUG invariant that a hash table isn't modified during iteration,
225- * and avoid unnecessary work rebuilding a hash table for unmodified properties.
226- *
227- * See https://github.com/php/php-src/issues/8079 and ext/spl/tests/fixedarray_022.phpt
228- * Also see https://github.com/php/php-src/issues/8044 for alternate considered approaches.
229- */
230- if (!intern -> array .should_rebuild_properties ) {
231- /* Return the same hash table so that recursion cycle detection works in internal functions. */
232- return ht ;
233- }
234- intern -> array .should_rebuild_properties = false;
212+ /* This has __serialize, so the purpose is not ZEND_PROP_PURPOSE_SERIALIZE, which would expect a non-null return value */
213+ ZEND_ASSERT (purpose != ZEND_PROP_PURPOSE_SERIALIZE );
235214
236- zend_long j = zend_hash_num_elements (ht );
215+ const spl_fixedarray_object * intern = spl_fixed_array_from_obj (obj );
216+ /*
217+ * SplFixedArray can be subclassed or have dynamic properties (With or without AllowDynamicProperties in subclasses).
218+ * Instances of subclasses with declared properties may have properties but not yet have a property table.
219+ */
220+ HashTable * source_properties = obj -> properties ? obj -> properties : (obj -> ce -> default_properties_count ? zend_std_get_properties (obj ) : NULL );
237221
238- if (GC_REFCOUNT (ht ) > 1 ) {
239- intern -> std .properties = zend_array_dup (ht );
240- GC_TRY_DELREF (ht );
241- }
242- for (zend_long i = 0 ; i < intern -> array .size ; i ++ ) {
243- zend_hash_index_update (ht , i , & intern -> array .elements [i ]);
244- Z_TRY_ADDREF (intern -> array .elements [i ]);
245- }
246- if (j > intern -> array .size ) {
247- for (zend_long i = intern -> array .size ; i < j ; ++ i ) {
248- zend_hash_index_del (ht , i );
222+ const zend_long size = intern -> array .size ;
223+ if (size == 0 && (!source_properties || !zend_hash_num_elements (source_properties ))) {
224+ return NULL ;
225+ }
226+ zval * const elements = intern -> array .elements ;
227+ HashTable * ht = zend_new_array (size );
228+
229+ for (zend_long i = 0 ; i < size ; i ++ ) {
230+ Z_TRY_ADDREF_P (& elements [i ]);
231+ zend_hash_next_index_insert (ht , & elements [i ]);
232+ }
233+ if (source_properties && zend_hash_num_elements (source_properties ) > 0 ) {
234+ zend_long nkey ;
235+ zend_string * skey ;
236+ zval * value ;
237+ ZEND_HASH_MAP_FOREACH_KEY_VAL_IND (source_properties , nkey , skey , value ) {
238+ Z_TRY_ADDREF_P (value );
239+ if (skey ) {
240+ zend_hash_add_new (ht , skey , value );
241+ } else {
242+ zend_hash_index_update (ht , nkey , value );
249243 }
250- }
251- if (HT_IS_PACKED (ht )) {
252- /* Engine doesn't expet packed array */
253- zend_hash_packed_to_hash (ht );
254- }
244+ } ZEND_HASH_FOREACH_END ();
255245 }
256246
257247 return ht ;
@@ -394,9 +384,6 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off
394384 }
395385
396386 spl_fixedarray_object * intern = spl_fixed_array_from_obj (object );
397- if (type != BP_VAR_IS && type != BP_VAR_R ) {
398- intern -> array .should_rebuild_properties = true;
399- }
400387 return spl_fixedarray_object_read_dimension_helper (intern , offset );
401388}
402389
@@ -420,7 +407,6 @@ static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *
420407 zend_throw_exception (spl_ce_RuntimeException , "Index invalid or out of range" , 0 );
421408 return ;
422409 } else {
423- intern -> array .should_rebuild_properties = true;
424410 /* Fix #81429 */
425411 zval * ptr = & (intern -> array .elements [index ]);
426412 zval tmp ;
@@ -461,7 +447,6 @@ static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *
461447 zend_throw_exception (spl_ce_RuntimeException , "Index invalid or out of range" , 0 );
462448 return ;
463449 } else {
464- intern -> array .should_rebuild_properties = true;
465450 zval_ptr_dtor (& (intern -> array .elements [index ]));
466451 ZVAL_NULL (& intern -> array .elements [index ]);
467452 }
@@ -973,7 +958,7 @@ PHP_MINIT_FUNCTION(spl_fixedarray)
973958 spl_handler_SplFixedArray .unset_dimension = spl_fixedarray_object_unset_dimension ;
974959 spl_handler_SplFixedArray .has_dimension = spl_fixedarray_object_has_dimension ;
975960 spl_handler_SplFixedArray .count_elements = spl_fixedarray_object_count_elements ;
976- spl_handler_SplFixedArray .get_properties = spl_fixedarray_object_get_properties ;
961+ spl_handler_SplFixedArray .get_properties_for = spl_fixedarray_object_get_properties_for ;
977962 spl_handler_SplFixedArray .get_gc = spl_fixedarray_object_get_gc ;
978963 spl_handler_SplFixedArray .free_obj = spl_fixedarray_object_free_storage ;
979964
0 commit comments