28
28
29
29
#include "php_spl.h"
30
30
#include "spl_functions.h"
31
- #include "spl_engine.h"
32
31
#include "spl_iterators.h"
33
32
#include "spl_array.h"
34
33
#include "spl_array_arginfo.h"
@@ -46,6 +45,8 @@ typedef struct _spl_array_object {
46
45
uint32_t ht_iter ;
47
46
int ar_flags ;
48
47
unsigned char nApplyCount ;
48
+ bool is_child ;
49
+ Bucket * bucket ;
49
50
zend_function * fptr_offset_get ;
50
51
zend_function * fptr_offset_set ;
51
52
zend_function * fptr_offset_has ;
@@ -165,6 +166,8 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o
165
166
object_properties_init (& intern -> std , class_type );
166
167
167
168
intern -> ar_flags = 0 ;
169
+ intern -> is_child = false;
170
+ intern -> bucket = NULL ;
168
171
intern -> ce_get_iterator = spl_ce_ArrayIterator ;
169
172
if (orig ) {
170
173
spl_array_object * other = spl_array_from_obj (orig );
@@ -451,6 +454,22 @@ static zval *spl_array_read_dimension(zend_object *object, zval *offset, int typ
451
454
return spl_array_read_dimension_ex (1 , object , offset , type , rv );
452
455
} /* }}} */
453
456
457
+ /*
458
+ * The assertion(HT_ASSERT_RC1(ht)) failed because the refcount was increased manually when intern->is_child is true.
459
+ * We have to set the refcount to 1 to make assertion success and restore the refcount to the original value after
460
+ * modifying the array when intern->is_child is true.
461
+ */
462
+ static uint32_t spl_array_set_refcount (bool is_child , HashTable * ht , uint32_t refcount ) /* {{{ */
463
+ {
464
+ uint32_t old_refcount = 0 ;
465
+ if (is_child ) {
466
+ old_refcount = GC_REFCOUNT (ht );
467
+ GC_SET_REFCOUNT (ht , refcount );
468
+ }
469
+
470
+ return old_refcount ;
471
+ } /* }}} */
472
+
454
473
static void spl_array_write_dimension_ex (int check_inherited , zend_object * object , zval * offset , zval * value ) /* {{{ */
455
474
{
456
475
spl_array_object * intern = spl_array_from_obj (object );
@@ -474,9 +493,16 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
474
493
}
475
494
476
495
Z_TRY_ADDREF_P (value );
496
+
497
+ uint32_t refcount = 0 ;
477
498
if (!offset || Z_TYPE_P (offset ) == IS_NULL ) {
478
499
ht = spl_array_get_hash_table (intern );
500
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
479
501
zend_hash_next_index_insert (ht , value );
502
+
503
+ if (refcount ) {
504
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
505
+ }
480
506
return ;
481
507
}
482
508
@@ -487,12 +513,17 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
487
513
}
488
514
489
515
ht = spl_array_get_hash_table (intern );
516
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
490
517
if (key .key ) {
491
518
zend_hash_update_ind (ht , key .key , value );
492
519
spl_hash_key_release (& key );
493
520
} else {
494
521
zend_hash_index_update (ht , key .h , value );
495
522
}
523
+
524
+ if (refcount ) {
525
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
526
+ }
496
527
} /* }}} */
497
528
498
529
static void spl_array_write_dimension (zend_object * object , zval * offset , zval * value ) /* {{{ */
@@ -522,6 +553,8 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
522
553
}
523
554
524
555
ht = spl_array_get_hash_table (intern );
556
+ uint32_t refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
557
+
525
558
if (key .key ) {
526
559
zval * data = zend_hash_find (ht , key .key );
527
560
if (data ) {
@@ -544,6 +577,10 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
544
577
} else {
545
578
zend_hash_index_del (ht , key .h );
546
579
}
580
+
581
+ if (refcount ) {
582
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
583
+ }
547
584
} /* }}} */
548
585
549
586
static void spl_array_unset_dimension (zend_object * object , zval * offset ) /* {{{ */
@@ -1005,6 +1042,16 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar
1005
1042
} else {
1006
1043
//??? TODO: try to avoid array duplication
1007
1044
ZVAL_ARR (& intern -> array , zend_array_dup (Z_ARR_P (array )));
1045
+
1046
+ if (intern -> is_child ) {
1047
+ Z_TRY_DELREF_P (& intern -> bucket -> val );
1048
+ /*
1049
+ * replace bucket->val with copied array, so the changes between
1050
+ * parent and child object can affect each other.
1051
+ */
1052
+ intern -> bucket -> val = intern -> array ;
1053
+ Z_TRY_ADDREF_P (& intern -> array );
1054
+ }
1008
1055
}
1009
1056
} else {
1010
1057
if (Z_OBJ_HT_P (array ) == & spl_handler_ArrayObject || Z_OBJ_HT_P (array ) == & spl_handler_ArrayIterator ) {
@@ -1480,6 +1527,22 @@ PHP_METHOD(RecursiveArrayIterator, hasChildren)
1480
1527
}
1481
1528
/* }}} */
1482
1529
1530
+ static void spl_instantiate_child_arg (zend_class_entry * pce , zval * retval , zval * arg1 , zval * arg2 ) /* {{{ */
1531
+ {
1532
+ object_init_ex (retval , pce );
1533
+ spl_array_object * new_intern = Z_SPLARRAY_P (retval );
1534
+ /*
1535
+ * set new_intern->is_child is true to indicate that the object was created by
1536
+ * RecursiveArrayIterator::getChildren() method.
1537
+ */
1538
+ new_intern -> is_child = true;
1539
+
1540
+ /* find the bucket of parent object. */
1541
+ new_intern -> bucket = (Bucket * )((char * )(arg1 ) - XtOffsetOf (Bucket , val ));;
1542
+ zend_call_known_instance_method_with_2_params (pce -> constructor , Z_OBJ_P (retval ), NULL , arg1 , arg2 );
1543
+ }
1544
+ /* }}} */
1545
+
1483
1546
/* {{{ Create a sub iterator for the current element (same class as $this) */
1484
1547
PHP_METHOD (RecursiveArrayIterator , getChildren )
1485
1548
{
@@ -1510,7 +1573,7 @@ PHP_METHOD(RecursiveArrayIterator, getChildren)
1510
1573
}
1511
1574
1512
1575
ZVAL_LONG (& flags , intern -> ar_flags );
1513
- spl_instantiate_arg_ex2 (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
1576
+ spl_instantiate_child_arg (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
1514
1577
}
1515
1578
/* }}} */
1516
1579
0 commit comments