2929#include "php_spl.h"
3030#include "spl_array_arginfo.h"
3131#include "spl_functions.h"
32- #include "spl_engine.h"
3332#include "spl_iterators.h"
3433#include "spl_array.h"
3534#include "spl_exceptions.h"
@@ -63,6 +62,8 @@ typedef struct _spl_array_object {
6362 uint32_t ht_iter ;
6463 int ar_flags ;
6564 unsigned char nApplyCount ;
65+ bool is_child ;
66+ Bucket * bucket ;
6667 zend_function * fptr_offset_get ;
6768 zend_function * fptr_offset_set ;
6869 zend_function * fptr_offset_has ;
@@ -167,6 +168,8 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o
167168 object_properties_init (& intern -> std , class_type );
168169
169170 intern -> ar_flags = 0 ;
171+ intern -> is_child = false;
172+ intern -> bucket = NULL ;
170173 intern -> ce_get_iterator = spl_ce_ArrayIterator ;
171174 if (orig ) {
172175 spl_array_object * other = spl_array_from_obj (orig );
@@ -477,6 +480,22 @@ static zval *spl_array_read_dimension(zend_object *object, zval *offset, int typ
477480 return spl_array_read_dimension_ex (1 , object , offset , type , rv );
478481} /* }}} */
479482
483+ /*
484+ * The assertion(HT_ASSERT_RC1(ht)) failed because the refcount was increased manually when intern->is_child is true.
485+ * We have to set the refcount to 1 to make assertion success and restore the refcount to the original value after
486+ * modifying the array when intern->is_child is true.
487+ */
488+ static uint32_t spl_array_set_refcount (bool is_child , HashTable * ht , uint32_t refcount ) /* {{{ */
489+ {
490+ uint32_t old_refcount = 0 ;
491+ if (is_child ) {
492+ old_refcount = GC_REFCOUNT (ht );
493+ GC_SET_REFCOUNT (ht , refcount );
494+ }
495+
496+ return old_refcount ;
497+ } /* }}} */
498+
480499static void spl_array_write_dimension_ex (int check_inherited , zend_object * object , zval * offset , zval * value ) /* {{{ */
481500{
482501 spl_array_object * intern = spl_array_from_obj (object );
@@ -500,9 +519,16 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
500519 }
501520
502521 Z_TRY_ADDREF_P (value );
522+
523+ uint32_t refcount = 0 ;
503524 if (!offset || Z_TYPE_P (offset ) == IS_NULL ) {
504525 ht = spl_array_get_hash_table (intern );
526+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
505527 zend_hash_next_index_insert (ht , value );
528+
529+ if (refcount ) {
530+ spl_array_set_refcount (intern -> is_child , ht , refcount );
531+ }
506532 return ;
507533 }
508534
@@ -513,12 +539,17 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
513539 }
514540
515541 ht = spl_array_get_hash_table (intern );
542+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
516543 if (key .key ) {
517544 zend_hash_update_ind (ht , key .key , value );
518545 spl_hash_key_release (& key );
519546 } else {
520547 zend_hash_index_update (ht , key .h , value );
521548 }
549+
550+ if (refcount ) {
551+ spl_array_set_refcount (intern -> is_child , ht , refcount );
552+ }
522553} /* }}} */
523554
524555static void spl_array_write_dimension (zend_object * object , zval * offset , zval * value ) /* {{{ */
@@ -548,6 +579,8 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
548579 }
549580
550581 ht = spl_array_get_hash_table (intern );
582+ uint32_t refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
583+
551584 if (key .key ) {
552585 zval * data = zend_hash_find (ht , key .key );
553586 if (data ) {
@@ -570,6 +603,10 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
570603 } else {
571604 zend_hash_index_del (ht , key .h );
572605 }
606+
607+ if (refcount ) {
608+ spl_array_set_refcount (intern -> is_child , ht , refcount );
609+ }
573610} /* }}} */
574611
575612static void spl_array_unset_dimension (zend_object * object , zval * offset ) /* {{{ */
@@ -1058,6 +1095,16 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar
10581095 } else {
10591096 //??? TODO: try to avoid array duplication
10601097 ZVAL_ARR (& intern -> array , zend_array_dup (Z_ARR_P (array )));
1098+
1099+ if (intern -> is_child ) {
1100+ Z_TRY_DELREF_P (& intern -> bucket -> val );
1101+ /*
1102+ * replace bucket->val with copied array, so the changes between
1103+ * parent and child object can affect each other.
1104+ */
1105+ intern -> bucket -> val = intern -> array ;
1106+ Z_TRY_ADDREF_P (& intern -> array );
1107+ }
10611108 }
10621109 } else {
10631110 if (Z_OBJ_HT_P (array ) == & spl_handler_ArrayObject || Z_OBJ_HT_P (array ) == & spl_handler_ArrayIterator ) {
@@ -1544,6 +1591,22 @@ PHP_METHOD(RecursiveArrayIterator, hasChildren)
15441591}
15451592/* }}} */
15461593
1594+ static void spl_instantiate_child_arg (zend_class_entry * pce , zval * retval , zval * arg1 , zval * arg2 ) /* {{{ */
1595+ {
1596+ object_init_ex (retval , pce );
1597+ spl_array_object * new_intern = Z_SPLARRAY_P (retval );
1598+ /*
1599+ * set new_intern->is_child is true to indicate that the object was created by
1600+ * RecursiveArrayIterator::getChildren() method.
1601+ */
1602+ new_intern -> is_child = true;
1603+
1604+ /* find the bucket of parent object. */
1605+ new_intern -> bucket = (Bucket * )((char * )(arg1 ) - XtOffsetOf (Bucket , val ));;
1606+ zend_call_known_instance_method_with_2_params (pce -> constructor , Z_OBJ_P (retval ), NULL , arg1 , arg2 );
1607+ }
1608+ /* }}} */
1609+
15471610/* {{{ Create a sub iterator for the current element (same class as $this) */
15481611PHP_METHOD (RecursiveArrayIterator , getChildren )
15491612{
@@ -1574,7 +1637,7 @@ PHP_METHOD(RecursiveArrayIterator, getChildren)
15741637 }
15751638
15761639 ZVAL_LONG (& flags , intern -> ar_flags );
1577- spl_instantiate_arg_ex2 (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
1640+ spl_instantiate_child_arg (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
15781641}
15791642/* }}} */
15801643
0 commit comments