29
29
#include "php_spl.h"
30
30
#include "spl_array_arginfo.h"
31
31
#include "spl_functions.h"
32
- #include "spl_engine.h"
33
32
#include "spl_iterators.h"
34
33
#include "spl_array.h"
35
34
#include "spl_exceptions.h"
@@ -63,6 +62,8 @@ typedef struct _spl_array_object {
63
62
uint32_t ht_iter ;
64
63
int ar_flags ;
65
64
unsigned char nApplyCount ;
65
+ bool is_child ;
66
+ Bucket * bucket ;
66
67
zend_function * fptr_offset_get ;
67
68
zend_function * fptr_offset_set ;
68
69
zend_function * fptr_offset_has ;
@@ -167,6 +168,8 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o
167
168
object_properties_init (& intern -> std , class_type );
168
169
169
170
intern -> ar_flags = 0 ;
171
+ intern -> is_child = false;
172
+ intern -> bucket = NULL ;
170
173
intern -> ce_get_iterator = spl_ce_ArrayIterator ;
171
174
if (orig ) {
172
175
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
477
480
return spl_array_read_dimension_ex (1 , object , offset , type , rv );
478
481
} /* }}} */
479
482
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
+
480
499
static void spl_array_write_dimension_ex (int check_inherited , zend_object * object , zval * offset , zval * value ) /* {{{ */
481
500
{
482
501
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
500
519
}
501
520
502
521
Z_TRY_ADDREF_P (value );
522
+
523
+ uint32_t refcount = 0 ;
503
524
if (!offset || Z_TYPE_P (offset ) == IS_NULL ) {
504
525
ht = spl_array_get_hash_table (intern );
526
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
505
527
zend_hash_next_index_insert (ht , value );
528
+
529
+ if (refcount ) {
530
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
531
+ }
506
532
return ;
507
533
}
508
534
@@ -513,12 +539,17 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
513
539
}
514
540
515
541
ht = spl_array_get_hash_table (intern );
542
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
516
543
if (key .key ) {
517
544
zend_hash_update_ind (ht , key .key , value );
518
545
spl_hash_key_release (& key );
519
546
} else {
520
547
zend_hash_index_update (ht , key .h , value );
521
548
}
549
+
550
+ if (refcount ) {
551
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
552
+ }
522
553
} /* }}} */
523
554
524
555
static 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
548
579
}
549
580
550
581
ht = spl_array_get_hash_table (intern );
582
+ uint32_t refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
583
+
551
584
if (key .key ) {
552
585
zval * data = zend_hash_find (ht , key .key );
553
586
if (data ) {
@@ -570,6 +603,10 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
570
603
} else {
571
604
zend_hash_index_del (ht , key .h );
572
605
}
606
+
607
+ if (refcount ) {
608
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
609
+ }
573
610
} /* }}} */
574
611
575
612
static 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
1058
1095
} else {
1059
1096
//??? TODO: try to avoid array duplication
1060
1097
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
+ }
1061
1108
}
1062
1109
} else {
1063
1110
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)
1544
1591
}
1545
1592
/* }}} */
1546
1593
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
+
1547
1610
/* {{{ Create a sub iterator for the current element (same class as $this) */
1548
1611
PHP_METHOD (RecursiveArrayIterator , getChildren )
1549
1612
{
@@ -1574,7 +1637,7 @@ PHP_METHOD(RecursiveArrayIterator, getChildren)
1574
1637
}
1575
1638
1576
1639
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 );
1578
1641
}
1579
1642
/* }}} */
1580
1643
0 commit comments