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 ;
@@ -150,6 +151,8 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o
150
151
object_properties_init (& intern -> std , class_type );
151
152
152
153
intern -> ar_flags = 0 ;
154
+ intern -> is_child = false;
155
+ intern -> bucket = NULL ;
153
156
intern -> ce_get_iterator = spl_ce_ArrayIterator ;
154
157
if (orig ) {
155
158
spl_array_object * other = spl_array_from_obj (orig );
@@ -440,6 +443,22 @@ static zval *spl_array_read_dimension(zend_object *object, zval *offset, int typ
440
443
return spl_array_read_dimension_ex (1 , object , offset , type , rv );
441
444
} /* }}} */
442
445
446
+ /*
447
+ * The assertion(HT_ASSERT_RC1(ht)) failed because the refcount was increased manually when intern->is_child is true.
448
+ * We have to set the refcount to 1 to make assertion success and restore the refcount to the original value after
449
+ * modifying the array when intern->is_child is true.
450
+ */
451
+ static uint32_t spl_array_set_refcount (bool is_child , HashTable * ht , uint32_t refcount ) /* {{{ */
452
+ {
453
+ uint32_t old_refcount = 0 ;
454
+ if (is_child ) {
455
+ old_refcount = GC_REFCOUNT (ht );
456
+ GC_SET_REFCOUNT (ht , refcount );
457
+ }
458
+
459
+ return old_refcount ;
460
+ } /* }}} */
461
+
443
462
static void spl_array_write_dimension_ex (int check_inherited , zend_object * object , zval * offset , zval * value ) /* {{{ */
444
463
{
445
464
spl_array_object * intern = spl_array_from_obj (object );
@@ -463,9 +482,16 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
463
482
}
464
483
465
484
Z_TRY_ADDREF_P (value );
485
+
486
+ uint32_t refcount = 0 ;
466
487
if (!offset || Z_TYPE_P (offset ) == IS_NULL ) {
467
488
ht = spl_array_get_hash_table (intern );
489
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
468
490
zend_hash_next_index_insert (ht , value );
491
+
492
+ if (refcount ) {
493
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
494
+ }
469
495
return ;
470
496
}
471
497
@@ -476,12 +502,17 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
476
502
}
477
503
478
504
ht = spl_array_get_hash_table (intern );
505
+ refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
479
506
if (key .key ) {
480
507
zend_hash_update_ind (ht , key .key , value );
481
508
spl_hash_key_release (& key );
482
509
} else {
483
510
zend_hash_index_update (ht , key .h , value );
484
511
}
512
+
513
+ if (refcount ) {
514
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
515
+ }
485
516
} /* }}} */
486
517
487
518
static void spl_array_write_dimension (zend_object * object , zval * offset , zval * value ) /* {{{ */
@@ -511,6 +542,8 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
511
542
}
512
543
513
544
ht = spl_array_get_hash_table (intern );
545
+ uint32_t refcount = spl_array_set_refcount (intern -> is_child , ht , 1 );
546
+
514
547
if (key .key ) {
515
548
zval * data = zend_hash_find (ht , key .key );
516
549
if (data ) {
@@ -533,6 +566,10 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
533
566
} else {
534
567
zend_hash_index_del (ht , key .h );
535
568
}
569
+
570
+ if (refcount ) {
571
+ spl_array_set_refcount (intern -> is_child , ht , refcount );
572
+ }
536
573
} /* }}} */
537
574
538
575
static void spl_array_unset_dimension (zend_object * object , zval * offset ) /* {{{ */
@@ -994,6 +1031,16 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar
994
1031
} else {
995
1032
//??? TODO: try to avoid array duplication
996
1033
ZVAL_ARR (& intern -> array , zend_array_dup (Z_ARR_P (array )));
1034
+
1035
+ if (intern -> is_child ) {
1036
+ Z_TRY_DELREF_P (& intern -> bucket -> val );
1037
+ /*
1038
+ * replace bucket->val with copied array, so the changes between
1039
+ * parent and child object can affect each other.
1040
+ */
1041
+ intern -> bucket -> val = intern -> array ;
1042
+ Z_TRY_ADDREF_P (& intern -> array );
1043
+ }
997
1044
}
998
1045
} else {
999
1046
if (Z_OBJ_HT_P (array ) == & spl_handler_ArrayObject || Z_OBJ_HT_P (array ) == & spl_handler_ArrayIterator ) {
@@ -1469,6 +1516,22 @@ PHP_METHOD(RecursiveArrayIterator, hasChildren)
1469
1516
}
1470
1517
/* }}} */
1471
1518
1519
+ static void spl_instantiate_child_arg (zend_class_entry * pce , zval * retval , zval * arg1 , zval * arg2 ) /* {{{ */
1520
+ {
1521
+ object_init_ex (retval , pce );
1522
+ spl_array_object * new_intern = Z_SPLARRAY_P (retval );
1523
+ /*
1524
+ * set new_intern->is_child is true to indicate that the object was created by
1525
+ * RecursiveArrayIterator::getChildren() method.
1526
+ */
1527
+ new_intern -> is_child = true;
1528
+
1529
+ /* find the bucket of parent object. */
1530
+ new_intern -> bucket = (Bucket * )((char * )(arg1 ) - XtOffsetOf (Bucket , val ));;
1531
+ zend_call_known_instance_method_with_2_params (pce -> constructor , Z_OBJ_P (retval ), NULL , arg1 , arg2 );
1532
+ }
1533
+ /* }}} */
1534
+
1472
1535
/* {{{ Create a sub iterator for the current element (same class as $this) */
1473
1536
PHP_METHOD (RecursiveArrayIterator , getChildren )
1474
1537
{
@@ -1499,7 +1562,7 @@ PHP_METHOD(RecursiveArrayIterator, getChildren)
1499
1562
}
1500
1563
1501
1564
ZVAL_LONG (& flags , intern -> ar_flags );
1502
- spl_instantiate_arg_ex2 (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
1565
+ spl_instantiate_child_arg (Z_OBJCE_P (ZEND_THIS ), return_value , entry , & flags );
1503
1566
}
1504
1567
/* }}} */
1505
1568
0 commit comments