summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPGRADING3
-rw-r--r--UPGRADING.INTERNALS14
-rw-r--r--Zend/tests/gc_032.phpt13
-rw-r--r--Zend/zend.c42
-rw-r--r--Zend/zend_builtin_functions.c8
-rw-r--r--Zend/zend_exceptions.c8
-rw-r--r--Zend/zend_hash.c78
-rw-r--r--Zend/zend_hash.h14
-rw-r--r--Zend/zend_object_handlers.c44
-rw-r--r--Zend/zend_operators.c2
-rw-r--r--Zend/zend_ts_hash.c10
-rw-r--r--Zend/zend_ts_hash.h3
-rw-r--r--Zend/zend_types.h39
-rw-r--r--ext/filter/filter.c6
-rw-r--r--ext/json/json_encoder.c32
-rw-r--r--ext/mbstring/mbstring.c29
-rw-r--r--ext/mbstring/tests/bug66964.phpt15
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c3
-rw-r--r--ext/opcache/zend_persist.c2
-rw-r--r--ext/soap/php_encoding.c6
-rw-r--r--ext/spl/spl_observer.c11
-rw-r--r--ext/standard/array.c92
-rw-r--r--ext/standard/http.c10
-rw-r--r--ext/standard/php_array.h2
-rw-r--r--ext/standard/tests/array/compact_variation1.phpt4
-rw-r--r--ext/standard/tests/array/count_variation3.phpt2
-rw-r--r--ext/standard/tests/general_functions/debug_zval_dump_o.phpt492
-rw-r--r--ext/standard/var.c87
-rw-r--r--ext/wddx/wddx.c44
-rw-r--r--ext/xmlrpc/xmlrpc-epi-php.c19
-rw-r--r--sapi/phpdbg/phpdbg_utils.c15
31 files changed, 327 insertions, 822 deletions
diff --git a/UPGRADING b/UPGRADING
index de2dc91016..f9d5916705 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -55,6 +55,9 @@ BCMath:
5. Changed Functions
========================================
+ . debug_zval_dump() was changed to display recursive arrays and objects
+ in the same way as var_dump(). Now, it doesn't display them twice.
+
========================================
6. New Functions
========================================
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index efb9920bfb..1b6ba2adbb 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -11,6 +11,7 @@ PHP 7.2 INTERNALS UPGRADE NOTES
h. valid_symbol_table removed
i. array_init() and array_init_size()
j. Run-time constant operand addressing
+ k. Array/Object recursion protection
2. Build system changes
a. Unix build system changes
@@ -70,6 +71,19 @@ PHP 7.2 INTERNALS UPGRADE NOTES
RT_CONSTANT_EX, EX_CONSTANT should be substituted by RT_CONSTANT than now
use "opline" (instead of "op_array") as first argument.
+ k. Protection from recursion during processing circular data structures was
+ refactored. HashTable.nApplyCount and IS_OBJ_APPLY_COUNT are replaced by
+ single flag GC_PROTECTED. Corresponding macros Z_OBJ_APPLY_COUNT,
+ Z_OBJ_INC_APPLY_COUNT, Z_OBJ_DEC_APPLY_COUNT, ZEND_HASH_GET_APPLY_COUNT,
+ ZEND_HASH_INC_APPLY_COUNT, ZEND_HASH_DEC_APPLY_COUNT are replaced with
+ GC_IS_RECURSIVE, GC_PROTECT_RECURSION, GC_UNPROTECT_RECURSION,
+ Z_IS_RECURSIVE, Z_PROTECT_RECURSION, Z_UNPROTECT_RECURSION.
+
+ HASH_FLAG_APPLY_PROTECTION flag and ZEND_HASH_APPLY_PROTECTION() macro
+ are removed. All mutable arrays should use recursion protection.
+ Corresponding checks should be replaced by Z_REFCOUNTED() or
+ !(GC_GLAGS(p) & GC_IMMUTABLE).
+
========================
2. Build system changes
diff --git a/Zend/tests/gc_032.phpt b/Zend/tests/gc_032.phpt
index cd30ed7cb6..b11a6655f1 100644
--- a/Zend/tests/gc_032.phpt
+++ b/Zend/tests/gc_032.phpt
@@ -18,10 +18,7 @@ array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
- array(1) refcount(%d){
- [0]=>
- *RECURSION*
- }
+ *RECURSION*
}
}
array(1) refcount(%d){
@@ -30,13 +27,7 @@ array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
- array(1) refcount(%d){
- [0]=>
- array(1) refcount(%d){
- [0]=>
- *RECURSION*
- }
- }
+ *RECURSION*
}
}
}
diff --git a/Zend/zend.c b/Zend/zend.c
index 014b9744d7..2621851941 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -334,16 +334,17 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
switch (Z_TYPE_P(expr)) {
case IS_ARRAY:
ZEND_PUTS("Array (");
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
- ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
- ZEND_PUTS(" *RECURSION*");
- Z_ARRVAL_P(expr)->u.v.nApplyCount--;
- return;
+ if (Z_REFCOUNTED_P(expr)) {
+ if (Z_IS_RECURSIVE_P(expr)) {
+ ZEND_PUTS(" *RECURSION*");
+ return;
+ }
+ Z_PROTECT_RECURSION_P(expr);
}
print_flat_hash(Z_ARRVAL_P(expr));
ZEND_PUTS(")");
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
- Z_ARRVAL_P(expr)->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(expr)) {
+ Z_UNPROTECT_RECURSION_P(expr);
}
break;
case IS_OBJECT:
@@ -353,7 +354,7 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
zend_printf("%s Object (", ZSTR_VAL(class_name));
zend_string_release(class_name);
- if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
+ if (Z_IS_RECURSIVE_P(expr)) {
ZEND_PUTS(" *RECURSION*");
return;
}
@@ -362,9 +363,9 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
properties = Z_OBJPROP_P(expr);
}
if (properties) {
- Z_OBJ_INC_APPLY_COUNT_P(expr);
+ Z_PROTECT_RECURSION_P(expr);
print_flat_hash(properties);
- Z_OBJ_DEC_APPLY_COUNT_P(expr);
+ Z_UNPROTECT_RECURSION_P(expr);
}
ZEND_PUTS(")");
break;
@@ -384,15 +385,16 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
switch (Z_TYPE_P(expr)) {
case IS_ARRAY:
smart_str_appends(buf, "Array\n");
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
- ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
- smart_str_appends(buf, " *RECURSION*");
- Z_ARRVAL_P(expr)->u.v.nApplyCount--;
- return;
+ if (Z_REFCOUNTED_P(expr)) {
+ if (Z_IS_RECURSIVE_P(expr)) {
+ smart_str_appends(buf, " *RECURSION*");
+ return;
+ }
+ Z_PROTECT_RECURSION_P(expr);
}
print_hash(buf, Z_ARRVAL_P(expr), indent, 0);
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
- Z_ARRVAL_P(expr)->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(expr)) {
+ Z_UNPROTECT_RECURSION_P(expr);
}
break;
case IS_OBJECT:
@@ -405,7 +407,7 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
zend_string_release(class_name);
smart_str_appends(buf, " Object\n");
- if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
+ if (Z_IS_RECURSIVE_P(expr)) {
smart_str_appends(buf, " *RECURSION*");
return;
}
@@ -413,9 +415,9 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
break;
}
- Z_OBJ_INC_APPLY_COUNT_P(expr);
+ Z_PROTECT_RECURSION_P(expr);
print_hash(buf, properties, indent, 1);
- Z_OBJ_DEC_APPLY_COUNT_P(expr);
+ Z_UNPROTECT_RECURSION_P(expr);
if (is_temp) {
zend_hash_destroy(properties);
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index d0f0b7cf69..73b1b64c82 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -727,13 +727,13 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
int ret = 1;
zval *val;
- ht->u.v.nApplyCount++;
+ GC_PROTECT_RECURSION(ht);
ZEND_HASH_FOREACH_VAL_IND(ht, val) {
ZVAL_DEREF(val);
if (Z_REFCOUNTED_P(val)) {
if (Z_TYPE_P(val) == IS_ARRAY) {
if (Z_REFCOUNTED_P(val)) {
- if (Z_ARRVAL_P(val)->u.v.nApplyCount > 0) {
+ if (Z_IS_RECURSIVE_P(val)) {
zend_error(E_WARNING, "Constants cannot be recursive arrays");
ret = 0;
break;
@@ -749,7 +749,7 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
}
}
} ZEND_HASH_FOREACH_END();
- ht->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(ht);
return ret;
}
/* }}} */
@@ -1137,7 +1137,7 @@ ZEND_FUNCTION(get_object_vars)
zobj = Z_OBJ_P(obj);
- if (!zobj->ce->default_properties_count && properties == zobj->properties && !ZEND_HASH_GET_APPLY_COUNT(properties)) {
+ if (!zobj->ce->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) {
/* fast copy */
if (EXPECTED(zobj->handlers == &std_object_handlers)) {
RETURN_ARR(zend_proptable_to_symtable(properties, 0));
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index 4bf2b746cb..d9ea885a62 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -711,9 +711,9 @@ ZEND_METHOD(exception, __toString)
zend_string_release(file);
zval_ptr_dtor(&trace);
- Z_OBJPROP_P(exception)->u.v.nApplyCount++;
+ Z_PROTECT_RECURSION_P(exception);
exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
- if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_OBJPROP_P(exception)->u.v.nApplyCount > 0) {
+ if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) {
break;
}
}
@@ -722,8 +722,8 @@ ZEND_METHOD(exception, __toString)
exception = getThis();
/* Reset apply counts */
while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(exception)) && instanceof_function(Z_OBJCE_P(exception), base_ce)) {
- if (Z_OBJPROP_P(exception)->u.v.nApplyCount) {
- Z_OBJPROP_P(exception)->u.v.nApplyCount--;
+ if (Z_IS_RECURSIVE_P(exception)) {
+ Z_UNPROTECT_RECURSION_P(exception);
} else {
break;
}
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 5f03ce86b0..92f8358a00 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -72,19 +72,6 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
#define SET_INCONSISTENT(n)
#endif
-#define HASH_PROTECT_RECURSION(ht) \
- if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
- if (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >= (3 << ZEND_HASH_APPLY_SHIFT)) {\
- zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");\
- } \
- ZEND_HASH_INC_APPLY_COUNT(ht); \
- }
-
-#define HASH_UNPROTECT_RECURSION(ht) \
- if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
- ZEND_HASH_DEC_APPLY_COUNT(ht); \
- }
-
#define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
if ((ht)->nNumUsed >= (ht)->nTableSize) { \
zend_hash_do_resize(ht); \
@@ -170,11 +157,11 @@ static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
{HT_INVALID_IDX, HT_INVALID_IDX};
-static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
+static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
{
GC_REFCOUNT(ht) = 1;
GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
- ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | (bApplyProtection ? HASH_FLAG_APPLY_PROTECTION : 0) | HASH_FLAG_STATIC_KEYS;
+ ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_STATIC_KEYS;
ht->nTableMask = HT_MIN_MASK;
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
ht->nNumUsed = 0;
@@ -187,13 +174,13 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
{
- _zend_hash_init_int(ht, nSize, pDestructor, persistent, 1);
+ _zend_hash_init_int(ht, nSize, pDestructor, persistent);
}
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize ZEND_FILE_LINE_DC)
{
HashTable *ht = emalloc(sizeof(HashTable));
- _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0, 1);
+ _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0);
return ht;
}
@@ -245,11 +232,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
pefree(old_data, (ht)->u.flags & HASH_FLAG_PERSISTENT);
}
-ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
-{
- _zend_hash_init_int(ht, nSize, pDestructor, persistent, bApplyProtection);
-}
-
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed)
{
HT_ASSERT_RC1(ht);
@@ -317,15 +299,6 @@ ZEND_API uint32_t zend_array_count(HashTable *ht)
}
/* }}} */
-ZEND_API void ZEND_FASTCALL zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
-{
- if (bApplyProtection) {
- ht->u.flags |= HASH_FLAG_APPLY_PROTECTION;
- } else {
- ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
- }
-}
-
ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
{
HashTableIterator *iter = EG(ht_iterators);
@@ -1508,7 +1481,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
IS_CONSISTENT(ht);
- HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1522,7 +1494,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
break;
}
}
- HASH_UNPROTECT_RECURSION(ht);
}
@@ -1534,7 +1505,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
IS_CONSISTENT(ht);
- HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1548,7 +1518,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
break;
}
}
- HASH_UNPROTECT_RECURSION(ht);
}
@@ -1562,8 +1531,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
IS_CONSISTENT(ht);
- HASH_PROTECT_RECURSION(ht);
-
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1583,8 +1550,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
}
va_end(args);
}
-
- HASH_UNPROTECT_RECURSION(ht);
}
@@ -1596,7 +1561,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
IS_CONSISTENT(ht);
- HASH_PROTECT_RECURSION(ht);
idx = ht->nNumUsed;
while (idx > 0) {
idx--;
@@ -1613,7 +1577,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
break;
}
}
- HASH_UNPROTECT_RECURSION(ht);
}
@@ -1776,7 +1739,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->pDestructor = source->pDestructor;
if (source->nNumUsed == 0) {
- target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
+ target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT)) | HASH_FLAG_STATIC_KEYS;
target->nTableMask = HT_MIN_MASK;
target->nNumUsed = 0;
target->nNumOfElements = 0;
@@ -1784,7 +1747,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = HT_INVALID_IDX;
HT_SET_DATA_ADDR(target, &uninitialized_bucket);
} else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) {
- target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
+ target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNumUsed = source->nNumUsed;
target->nNumOfElements = source->nNumOfElements;
@@ -1801,7 +1764,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = idx;
}
} else if (source->u.flags & HASH_FLAG_PACKED) {
- target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
+ target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNumUsed = source->nNumUsed;
target->nNumOfElements = source->nNumOfElements;
@@ -1824,7 +1787,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = idx;
}
} else {
- target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
+ target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNextFreeElement = source->nNextFreeElement;
target->nInternalPointer = source->nInternalPointer;
@@ -2402,11 +2365,28 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
IS_CONSISTENT(ht1);
IS_CONSISTENT(ht2);
- HASH_PROTECT_RECURSION(ht1);
- HASH_PROTECT_RECURSION(ht2);
+ if (ht1 == ht2) {
+ return 0;
+ }
+
+ /* use bitwise OR to make only one conditional jump */
+ if (UNEXPECTED(GC_IS_RECURSIVE(ht1) | GC_IS_RECURSIVE(ht2))) {
+ zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
+ }
+
+ if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
+ GC_PROTECT_RECURSION(ht1);
+ }
+ if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
+ GC_PROTECT_RECURSION(ht2);
+ }
result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
- HASH_UNPROTECT_RECURSION(ht1);
- HASH_UNPROTECT_RECURSION(ht2);
+ if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht1);
+ }
+ if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht2);
+ }
return result;
}
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index e362b5557b..e99224a0b4 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -36,7 +36,6 @@
#define HASH_ADD_NEXT (1<<4)
#define HASH_FLAG_PERSISTENT (1<<0)
-#define HASH_FLAG_APPLY_PROTECTION (1<<1)
#define HASH_FLAG_PACKED (1<<2)
#define HASH_FLAG_INITIALIZED (1<<3)
#define HASH_FLAG_STATIC_KEYS (1<<4) /* long and interned strings */
@@ -69,14 +68,13 @@ BEGIN_EXTERN_C()
/* startup/shutdown */
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
-ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht);
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
_zend_hash_init((ht), (nSize), (pDestructor), (persistent))
#define zend_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection) \
- _zend_hash_init_ex((ht), (nSize), (pDestructor), (persistent), (bApplyProtection))
+ _zend_hash_init((ht), (nSize), (pDestructor), (persistent))
ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed);
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht);
@@ -985,16 +983,6 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
_key = _p->key; \
_val = _z;
-#define ZEND_HASH_APPLY_PROTECTION(ht) \
- ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION)
-
-#define ZEND_HASH_APPLY_SHIFT 8
-#define ZEND_HASH_APPLY_COUNT_MASK 0xff00
-#define ZEND_HASH_GET_APPLY_COUNT(ht) (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >> ZEND_HASH_APPLY_SHIFT)
-#define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->u.flags += (1 << ZEND_HASH_APPLY_SHIFT))
-#define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT))
-
-
/* The following macros are useful to insert a sequence of new elements
* of packed array. They may be use insted of series of
* zend_hash_next_index_insert_new()
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index a11664ff0e..f1412ef0d2 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -44,18 +44,6 @@
#define IN_UNSET (1<<2)
#define IN_ISSET (1<<3)
-#define Z_OBJ_PROTECT_RECURSION(zval_p) \
- do { \
- if (Z_OBJ_APPLY_COUNT_P(zval_p) >= 3) { \
- zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?"); \
- } \
- Z_OBJ_INC_APPLY_COUNT_P(zval_p); \
- } while (0)
-
-
-#define Z_OBJ_UNPROTECT_RECURSION(zval_p) \
- Z_OBJ_DEC_APPLY_COUNT_P(zval_p)
-
/*
__X accessors explanation:
@@ -1444,6 +1432,9 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
zobj1 = Z_OBJ_P(o1);
zobj2 = Z_OBJ_P(o2);
+ if (zobj1 == zobj2) {
+ return 0; /* the same object */
+ }
if (zobj1->ce != zobj2->ce) {
return 1; /* different classes */
}
@@ -1456,40 +1447,45 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
p1 = zobj1->properties_table;
p2 = zobj2->properties_table;
end = p1 + zobj1->ce->default_properties_count;
- Z_OBJ_PROTECT_RECURSION(o1);
- Z_OBJ_PROTECT_RECURSION(o2);
+
+ /* use bitwise OR to make only one conditional jump */
+ if (UNEXPECTED(Z_IS_RECURSIVE_P(o1) | Z_IS_RECURSIVE_P(o2))) {
+ zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
+ }
+ Z_PROTECT_RECURSION_P(o1);
+ Z_PROTECT_RECURSION_P(o2);
do {
if (Z_TYPE_P(p1) != IS_UNDEF) {
if (Z_TYPE_P(p2) != IS_UNDEF) {
zval result;
if (compare_function(&result, p1, p2)==FAILURE) {
- Z_OBJ_UNPROTECT_RECURSION(o1);
- Z_OBJ_UNPROTECT_RECURSION(o2);
+ Z_UNPROTECT_RECURSION_P(o1);
+ Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
if (Z_LVAL(result) != 0) {
- Z_OBJ_UNPROTECT_RECURSION(o1);
- Z_OBJ_UNPROTECT_RECURSION(o2);
+ Z_UNPROTECT_RECURSION_P(o1);
+ Z_UNPROTECT_RECURSION_P(o2);
return Z_LVAL(result);
}
} else {
- Z_OBJ_UNPROTECT_RECURSION(o1);
- Z_OBJ_UNPROTECT_RECURSION(o2);
+ Z_UNPROTECT_RECURSION_P(o1);
+ Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
} else {
if (Z_TYPE_P(p2) != IS_UNDEF) {
- Z_OBJ_UNPROTECT_RECURSION(o1);
- Z_OBJ_UNPROTECT_RECURSION(o2);
+ Z_UNPROTECT_RECURSION_P(o1);
+ Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
}
p1++;
p2++;
} while (p1 != end);
- Z_OBJ_UNPROTECT_RECURSION(o1);
- Z_OBJ_UNPROTECT_RECURSION(o2);
+ Z_UNPROTECT_RECURSION_P(o1);
+ Z_UNPROTECT_RECURSION_P(o2);
return 0;
} else {
if (!zobj1->properties) {
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index 781a1825e9..8a20d1ca28 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -607,7 +607,7 @@ try_again:
/* fast copy */
if (!Z_OBJCE_P(op)->default_properties_count &&
obj_ht == Z_OBJ_P(op)->properties &&
- !ZEND_HASH_GET_APPLY_COUNT(Z_OBJ_P(op)->properties) &&
+ !GC_IS_RECURSIVE(obj_ht) &&
EXPECTED(Z_OBJ_P(op)->handlers == &std_object_handlers)) {
arr = zend_proptable_to_symtable(obj_ht, 0);
} else {
diff --git a/Zend/zend_ts_hash.c b/Zend/zend_ts_hash.c
index 66af862884..931236936d 100644
--- a/Zend/zend_ts_hash.c
+++ b/Zend/zend_ts_hash.c
@@ -69,16 +69,6 @@ ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pD
_zend_hash_init(TS_HASH(ht), nSize, pDestructor, persistent);
}
-ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
-{
-#ifdef ZTS
- ht->mx_reader = tsrm_mutex_alloc();
- ht->mx_writer = tsrm_mutex_alloc();
- ht->reader = 0;
-#endif
- _zend_hash_init_ex(TS_HASH(ht), nSize, pDestructor, persistent, bApplyProtection);
-}
-
ZEND_API void zend_ts_hash_destroy(TsHashTable *ht)
{
begin_write(ht);
diff --git a/Zend/zend_ts_hash.h b/Zend/zend_ts_hash.h
index 3128096cd3..20e66d3890 100644
--- a/Zend/zend_ts_hash.h
+++ b/Zend/zend_ts_hash.h
@@ -38,14 +38,13 @@ BEGIN_EXTERN_C()
/* startup/shutdown */
ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
-ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
ZEND_API void zend_ts_hash_destroy(TsHashTable *ht);
ZEND_API void zend_ts_hash_clean(TsHashTable *ht);
#define zend_ts_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
_zend_ts_hash_init(ht, nSize, pDestructor, persistent)
#define zend_ts_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection) \
- _zend_ts_hash_init_ex(ht, nSize, pDestructor, persistent, bApplyProtection)
+ _zend_ts_hash_init(ht, nSize, pDestructor, persistent)
/* additions/updates/changes */
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 4118154289..d7381ab2d7 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -239,7 +239,7 @@ struct _zend_array {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
- zend_uchar nApplyCount,
+ zend_uchar _unused,
zend_uchar nIteratorsCount,
zend_uchar consistency)
} v;
@@ -455,7 +455,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define GC_INFO_SHIFT 16
#define GC_INFO_MASK 0xffff0000
-/* zval.value->gc.u.v.flags */
+/* zval.value->gc.u.v.flags (common flags) */
+#define GC_PROTECTED (1<<0) /* used for array/object recursion detection */
+#define GC_IMMUTABLE (1<<1) /* string/array can't be canged in place */
#define GC_COLLECTABLE (1<<7)
#define GC_ARRAY (IS_ARRAY | (GC_COLLECTABLE << GC_FLAGS_SHIFT))
@@ -490,37 +492,38 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
/* string flags (zval.value->gc.u.flags) */
#define IS_STR_PERSISTENT (1<<0) /* allocated using malloc */
-#define IS_STR_INTERNED (1<<1) /* interned string */
+#define IS_STR_INTERNED GC_IMMUTABLE /* interned string */
#define IS_STR_PERMANENT (1<<2) /* relives request boundary */
/* array flags */
-#define IS_ARRAY_IMMUTABLE (1<<1)
+#define IS_ARRAY_PROTECTED GC_PROTECTED
+#define IS_ARRAY_IMMUTABLE GC_IMMUTABLE
/* object flags (zval.value->gc.u.flags) */
-#define IS_OBJ_APPLY_COUNT 0x07
+#define IS_OBJ_PROTECTED GC_PROTECTED
#define IS_OBJ_DESTRUCTOR_CALLED (1<<3)
#define IS_OBJ_FREE_CALLED (1<<4)
#define IS_OBJ_USE_GUARDS (1<<5)
#define IS_OBJ_HAS_GUARDS (1<<6)
-#define Z_OBJ_APPLY_COUNT(zval) \
- (Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT)
+/* Recursion protection macros must be used only for arrays and objects */
+#define GC_IS_RECURSIVE(p) \
+ (GC_FLAGS(p) & GC_PROTECTED)
-#define Z_OBJ_INC_APPLY_COUNT(zval) do { \
- Z_GC_FLAGS(zval) = \
- (Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
- ((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) + 1); \
+#define GC_PROTECT_RECURSION(p) do { \
+ GC_FLAGS(p) |= GC_PROTECTED; \
} while (0)
-#define Z_OBJ_DEC_APPLY_COUNT(zval) do { \
- Z_GC_FLAGS(zval) = \
- (Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
- ((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) - 1); \
+#define GC_UNPROTECT_RECURSION(p) do { \
+ GC_FLAGS(p) &= ~GC_PROTECTED; \
} while (0)
-#define Z_OBJ_APPLY_COUNT_P(zv) Z_OBJ_APPLY_COUNT(*(zv))
-#define Z_OBJ_INC_APPLY_COUNT_P(zv) Z_OBJ_INC_APPLY_COUNT(*(zv))
-#define Z_OBJ_DEC_APPLY_COUNT_P(zv) Z_OBJ_DEC_APPLY_COUNT(*(zv))
+#define Z_IS_RECURSIVE(zval) GC_IS_RECURSIVE(Z_COUNTED(zval))
+#define Z_PROTECT_RECURSION(zval) GC_PROTECT_RECURSION(Z_COUNTED(zval))
+#define Z_UNPROTECT_RECURSION(zval) GC_UNPROTECT_RECURSION(Z_COUNTED(zval))
+#define Z_IS_RECURSIVE_P(zv) Z_IS_RECURSIVE(*(zv))
+#define Z_PROTECT_RECURSION_P(zv) Z_PROTECT_RECURSION(*(zv))
+#define Z_UNPROTECT_RECURSION_P(zv) Z_UNPROTECT_RECURSION(*(zv))
/* All data types < IS_STRING have their constructor/destructors skipped */
#define Z_CONSTANT(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_CONSTANT) != 0)
diff --git a/ext/filter/filter.c b/ext/filter/filter.c
index cdc5e15bb6..17fc500a9d 100644
--- a/ext/filter/filter.c
+++ b/ext/filter/filter.c
@@ -503,21 +503,21 @@ static void php_zval_filter_recursive(zval *value, zend_long filter, zend_long f
if (Z_TYPE_P(value) == IS_ARRAY) {
zval *element;
- if (Z_ARRVAL_P(value)->u.v.nApplyCount > 1) {
+ if (Z_IS_RECURSIVE_P(value)) {
return;
}
+ Z_PROTECT_RECURSION_P(value);
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), element) {
ZVAL_DEREF(element);
SEPARATE_ZVAL_NOREF(element);
if (Z_TYPE_P(element) == IS_ARRAY) {
- Z_ARRVAL_P(element)->u.v.nApplyCount++;
php_zval_filter_recursive(element, filter, flags, options, charset, copy);
- Z_ARRVAL_P(element)->u.v.nApplyCount--;
} else {
php_zval_filter(element, filter, flags, options, charset, copy);
}
} ZEND_HASH_FOREACH_END();
+ Z_UNPROTECT_RECURSION_P(value);
} else {
php_zval_filter(value, filter, flags, options, charset, copy);
}
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c
index 1fa2344204..2c3843fdd2 100644
--- a/ext/json/json_encoder.c
+++ b/ext/json/json_encoder.c
@@ -113,17 +113,17 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
}
/* }}} */
-#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \
+#define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
do { \
- if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
- ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \
+ if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
+ GC_PROTECT_RECURSION(_tmp_ht); \
} \
} while (0)
-#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \
+#define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
do { \
- if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
- ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \
+ if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
+ GC_UNPROTECT_RECURSION(_tmp_ht); \
} \
} while (0)
@@ -140,13 +140,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
r = PHP_JSON_OUTPUT_OBJECT;
}
- if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
+ if (myht && GC_IS_RECURSIVE(myht)) {
encoder->error_code = PHP_JSON_ERROR_RECURSION;
smart_str_appendl(buf, "null", 4);
return FAILURE;
}
- PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
+ PHP_JSON_HASH_PROTECT_RECURSION(myht);
if (r == PHP_JSON_OUTPUT_ARRAY) {
smart_str_appendc(buf, '[');
@@ -212,13 +212,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
return FAILURE;
}
} ZEND_HASH_FOREACH_END();
}
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
if (encoder->depth > encoder->max_depth) {
encoder->error_code = PHP_JSON_ERROR_DEPTH;
@@ -445,7 +445,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
zval retval, fname;
int return_code;
- if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
+ if (myht && GC_IS_RECURSIVE(myht)) {
encoder->error_code = PHP_JSON_ERROR_RECURSION;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
@@ -453,7 +453,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
return FAILURE;
}
- PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
+ PHP_JSON_HASH_PROTECT_RECURSION(myht);
ZVAL_STRING(&fname, "jsonSerialize");
@@ -466,7 +466,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
return FAILURE;
}
@@ -478,19 +478,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
return FAILURE;
}
if ((Z_TYPE(retval) == IS_OBJECT) &&
(Z_OBJ(retval) == Z_OBJ_P(val))) {
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
return_code = php_json_encode_array(buf, &retval, options, encoder);
} else {
/* All other types, encode as normal */
return_code = php_json_encode_zval(buf, &retval, options, encoder);
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+ PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
}
zval_ptr_dtor(&retval);
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index 0bb18e9b57..6d1103aff7 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -3129,11 +3129,12 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons
return NULL;
}
- if (input->u.v.nApplyCount++ > 1) {
- input->u.v.nApplyCount--;
+ if (GC_IS_RECURSIVE(input)) {
+ GC_UNPROTECT_RECURSION(input);
php_error_docref(NULL, E_WARNING, "Cannot convert recursively referenced values");
return NULL;
}
+ GC_PROTECT_RECURSION(input);
output = zend_new_array(zend_hash_num_elements(input));
ZEND_HASH_FOREACH_KEY_VAL(input, idx, key, entry) {
/* convert key */
@@ -3177,7 +3178,7 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons
zend_hash_index_add(output, idx, &entry_tmp);
}
} ZEND_HASH_FOREACH_END();
- input->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(input);
return output;
}
@@ -3764,11 +3765,11 @@ PHP_FUNCTION(mb_convert_variables)
if (target_hash != NULL) {
while ((hash_entry = zend_hash_get_current_data(target_hash)) != NULL) {
if (Z_REFCOUNTED_P(var)) {
- if (++target_hash->u.v.nApplyCount > 1) {
- --target_hash->u.v.nApplyCount;
+ if (GC_IS_RECURSIVE(target_hash)) {
recursion_error = 1;
goto detect_end;
}
+ GC_PROTECT_RECURSION(target_hash);
}
zend_hash_move_forward(target_hash);
if (Z_TYPE_P(hash_entry) == IS_INDIRECT) {
@@ -3813,9 +3814,7 @@ detect_end:
if (recursion_error) {
while(stack_level-- && (var = &stack[stack_level])) {
if (Z_REFCOUNTED_P(var)) {
- if (HASH_OF(var)->u.v.nApplyCount > 1) {
- HASH_OF(var)->u.v.nApplyCount--;
- }
+ Z_UNPROTECT_RECURSION_P(var);
}
}
efree(stack);
@@ -3880,11 +3879,11 @@ detect_end:
ZVAL_DEREF(hash_entry);
if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
if (Z_REFCOUNTED_P(hash_entry)) {
- if (++(HASH_OF(hash_entry)->u.v.nApplyCount) > 1) {
- --(HASH_OF(hash_entry)->u.v.nApplyCount);
+ if (Z_IS_RECURSIVE_P(hash_entry)) {
recursion_error = 1;
goto conv_end;
}
+ Z_PROTECT_RECURSION_P(hash_entry);
}
if (stack_level >= stack_max) {
stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
@@ -3933,9 +3932,7 @@ conv_end:
if (recursion_error) {
while(stack_level-- && (var = &stack[stack_level])) {
if (Z_REFCOUNTED_P(var)) {
- if (HASH_OF(var)->u.v.nApplyCount > 1) {
- HASH_OF(var)->u.v.nApplyCount--;
- }
+ Z_UNPROTECT_RECURSION_P(var);
}
}
efree(stack);
@@ -4787,12 +4784,12 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str
return 0;
}
- if (vars->u.v.nApplyCount++ > 1) {
- vars->u.v.nApplyCount--;
+ if (GC_IS_RECURSIVE(vars)) {
mbfl_buffer_converter_delete(convd);
php_error_docref(NULL, E_WARNING, "Cannot not handle circular references");
return 0;
}
+ GC_PROTECT_RECURSION(vars);
ZEND_HASH_FOREACH_KEY_VAL(vars, idx, key, entry) {
ZVAL_DEREF(entry);
if (key) {
@@ -4826,7 +4823,7 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str
break;
}
} ZEND_HASH_FOREACH_END();
- vars->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(vars);
mbfl_buffer_converter_delete(convd);
return valid;
}
diff --git a/ext/mbstring/tests/bug66964.phpt b/ext/mbstring/tests/bug66964.phpt
index e982aa2e01..c33bb67c49 100644
--- a/ext/mbstring/tests/bug66964.phpt
+++ b/ext/mbstring/tests/bug66964.phpt
@@ -49,5 +49,16 @@ array(5) {
[3]=>
string(21) "日本語テキスト"
[4]=>
- *RECURSION*
-} \ No newline at end of file
+ &array(5) {
+ [0]=>
+ string(21) "日本語テキスト"
+ [1]=>
+ string(21) "日本語テキスト"
+ [2]=>
+ string(21) "日本語テキスト"
+ [3]=>
+ string(21) "日本語テキスト"
+ [4]=>
+ *RECURSION*
+ }
+}
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index 930bcceaf2..6926798ab6 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -181,7 +181,7 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
ht->nNumOfElements = source->nNumOfElements;
ht->nNextFreeElement = source->nNextFreeElement;
ht->pDestructor = ZVAL_PTR_DTOR;
- ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION;
+ ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX;
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
@@ -378,7 +378,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
/* constants table */
zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table);
- ce->constants_table.u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
/* interfaces aren't really implemented, so we create a new table */
if (ce->num_interfaces) {
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index 2ce4c05ba3..cd4077279f 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -305,7 +305,6 @@ static void zend_persist_zval(zval *z)
GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
- Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
}
}
break;
@@ -374,7 +373,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
GC_REFCOUNT(op_array->static_variables) = 2;
GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8);
op_array->static_variables->u.flags |= HASH_FLAG_STATIC_KEYS;
- op_array->static_variables->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
}
}
diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index a88d14fb1a..6d69d81c33 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -453,7 +453,7 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml
} else {
if (check_class_map && SOAP_GLOBAL(class_map) && data &&
Z_TYPE_P(data) == IS_OBJECT &&
- !ZEND_HASH_GET_APPLY_COUNT(Z_OBJPROP_P(data))) {
+ !GC_IS_RECURSIVE(Z_OBJPROP_P(data))) {
zend_class_entry *ce = Z_OBJCE_P(data);
zval *tmp;
zend_string *type_name;
@@ -1859,9 +1859,9 @@ static xmlNodePtr to_xml_object(encodeTypePtr type, zval *data, int style, xmlNo
sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_LIST &&
sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_UNION) {
- if (prop) ZEND_HASH_INC_APPLY_COUNT(prop);
+ if (prop) {GC_PROTECT_RECURSION(prop);}
xmlParam = master_to_xml(sdlType->encode, data, style, parent);
- if (prop) ZEND_HASH_DEC_APPLY_COUNT(prop);
+ if (prop) {GC_UNPROTECT_RECURSION(prop);}
} else {
zval rv;
zval *tmp = get_zval_property(data, "_", &rv);
diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c
index c6d366515b..e9dc418b53 100644
--- a/ext/spl/spl_observer.c
+++ b/ext/spl/spl_observer.c
@@ -569,12 +569,13 @@ SPL_METHOD(SplObjectStorage, count)
}
if (mode == COUNT_RECURSIVE) {
- zend_long ret = zend_hash_num_elements(&intern->storage);
- zval *element;
+ zend_long ret;
- ZEND_HASH_FOREACH_VAL(&intern->storage, element) {
- ret += php_count_recursive(element, mode);
- } ZEND_HASH_FOREACH_END();
+ if (mode != COUNT_RECURSIVE) {
+ ret = zend_hash_num_elements(&intern->storage);
+ } else {
+ ret = php_count_recursive(&intern->storage);
+ }
RETURN_LONG(ret);
return;
diff --git a/ext/standard/array.c b/ext/standard/array.c
index e55924c66b..18a8a2e77f 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -743,30 +743,29 @@ PHP_FUNCTION(ksort)
}
/* }}} */
-PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */
+PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
{
zend_long cnt = 0;
zval *element;
- if (Z_TYPE_P(array) == IS_ARRAY) {
- if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) {
+ if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(ht)) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
}
+ GC_PROTECT_RECURSION(ht);
+ }
- cnt = zend_array_count(Z_ARRVAL_P(array));
- if (mode == COUNT_RECURSIVE) {
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
- Z_ARRVAL_P(array)->u.v.nApplyCount++;
- }
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
- ZVAL_DEREF(element);
- cnt += php_count_recursive(element, COUNT_RECURSIVE);
- } ZEND_HASH_FOREACH_END();
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
- Z_ARRVAL_P(array)->u.v.nApplyCount--;
- }
+ cnt = zend_array_count(ht);
+ ZEND_HASH_FOREACH_VAL(ht, element) {
+ ZVAL_DEREF(element);
+ if (Z_TYPE_P(element) == IS_ARRAY) {
+ cnt += php_count_recursive(Z_ARRVAL_P(element));
}
+ } ZEND_HASH_FOREACH_END();
+
+ if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht);
}
return cnt;
@@ -794,12 +793,10 @@ PHP_FUNCTION(count)
RETURN_LONG(0);
break;
case IS_ARRAY:
- cnt = zend_array_count(Z_ARRVAL_P(array));
- if (mode == COUNT_RECURSIVE) {
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
- ZVAL_DEREF(element);
- cnt += php_count_recursive(element, COUNT_RECURSIVE);
- } ZEND_HASH_FOREACH_END();
+ if (mode != COUNT_RECURSIVE) {
+ cnt = zend_array_count(Z_ARRVAL_P(array));
+ } else {
+ cnt = php_count_recursive(Z_ARRVAL_P(array));
}
RETURN_LONG(cnt);
break;
@@ -1431,7 +1428,7 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
ZVAL_DEREF(zv);
SEPARATE_ARRAY(zv);
thash = Z_ARRVAL_P(zv);
- if (thash->u.v.nApplyCount > 1) {
+ if (GC_IS_RECURSIVE(thash)) {
php_error_docref(NULL, E_WARNING, "recursion detected");
result = FAILURE;
break;
@@ -1442,12 +1439,12 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
Z_ADDREF(ref);
- thash->u.v.nApplyCount++;
+ GC_PROTECT_RECURSION(thash);
result = php_array_walk(zv, userdata, recursive);
if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
/* If the hashtable changed in the meantime, we'll "leak" this apply count
* increment -- our reference to thash is no longer valid. */
- thash->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(thash);
}
zval_ptr_dtor(&ref);
@@ -2577,19 +2574,18 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
}
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
- if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) {
- php_error_docref(NULL, E_WARNING, "recursion detected");
- return;
- }
-
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
- Z_ARRVAL_P(entry)->u.v.nApplyCount++;
+ if (Z_REFCOUNTED_P(entry)) {
+ if (Z_IS_RECURSIVE_P(entry)) {
+ php_error_docref(NULL, E_WARNING, "recursion detected");
+ return;
+ }
+ Z_PROTECT_RECURSION_P(entry);
}
ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
php_compact_var(eg_active_symbol_table, return_value, value_ptr);
} ZEND_HASH_FOREACH_END();
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
- Z_ARRVAL_P(entry)->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(entry)) {
+ Z_UNPROTECT_RECURSION_P(entry);
}
}
}
@@ -3636,7 +3632,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
ZVAL_DEREF(src_zval);
ZVAL_DEREF(dest_zval);
thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
- if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
+ if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
}
@@ -3662,12 +3658,12 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
src_zval = &tmp;
}
if (Z_TYPE_P(src_zval) == IS_ARRAY) {
- if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
- thash->u.v.nApplyCount++;
+ if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
+ GC_PROTECT_RECURSION(thash);
}
ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
- if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
- thash->u.v.nApplyCount--;
+ if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(thash);
}
if (!ret) {
return 0;
@@ -3761,8 +3757,8 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
dest_zval = dest_entry;
ZVAL_DEREF(dest_zval);
- if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 ||
- Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 ||
+ if (Z_IS_RECURSIVE_P(dest_zval) ||
+ Z_IS_RECURSIVE_P(src_zval) ||
(Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
@@ -3772,20 +3768,20 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
SEPARATE_ZVAL(dest_entry);
dest_zval = dest_entry;
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
- Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++;
+ if (Z_REFCOUNTED_P(dest_zval)) {
+ Z_PROTECT_RECURSION_P(dest_zval);
}
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
- Z_ARRVAL_P(src_zval)->u.v.nApplyCount++;
+ if (Z_REFCOUNTED_P(src_zval)) {
+ Z_PROTECT_RECURSION_P(src_zval);
}
ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
- Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(dest_zval)) {
+ Z_UNPROTECT_RECURSION_P(dest_zval);
}
- if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
- Z_ARRVAL_P(src_zval)->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(src_zval)) {
+ Z_UNPROTECT_RECURSION_P(src_zval);
}
if (!ret) {
diff --git a/ext/standard/http.c b/ext/standard/http.c
index e270342c7b..afbb77d1fa 100644
--- a/ext/standard/http.c
+++ b/ext/standard/http.c
@@ -42,7 +42,7 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
return FAILURE;
}
- if (ht->u.v.nApplyCount > 0) {
+ if (GC_IS_RECURSIVE(ht)) {
/* Prevent recursion */
return SUCCESS;
}
@@ -136,12 +136,12 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
*(p++) = 'B';
*p = '\0';
}
- if (ZEND_HASH_APPLY_PROTECTION(ht)) {
- ht->u.v.nApplyCount++;
+ if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ GC_PROTECT_RECURSION(ht);
}
php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, newprefix, newprefix_len, "%5D", 3, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type);
- if (ZEND_HASH_APPLY_PROTECTION(ht)) {
- ht->u.v.nApplyCount--;
+ if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht);
}
efree(newprefix);
} else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h
index 2b58db71e3..d9c5eedc18 100644
--- a/ext/standard/php_array.h
+++ b/ext/standard/php_array.h
@@ -107,7 +107,7 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src);
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src);
PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src);
PHPAPI int php_multisort_compare(const void *a, const void *b);
-PHPAPI zend_long php_count_recursive(zval *array, zend_long mode);
+PHPAPI zend_long php_count_recursive(HashTable *ht);
#define PHP_SORT_REGULAR 0
#define PHP_SORT_NUMERIC 1
diff --git a/ext/standard/tests/array/compact_variation1.phpt b/ext/standard/tests/array/compact_variation1.phpt
index ea48132857..d2b9fc45df 100644
--- a/ext/standard/tests/array/compact_variation1.phpt
+++ b/ext/standard/tests/array/compact_variation1.phpt
@@ -37,10 +37,6 @@ array(1) {
Warning: compact(): recursion detected in %s on line %d
Warning: compact(): recursion detected in %s on line %d
-
-Warning: compact(): recursion detected in %s on line %d
-
-Warning: compact(): recursion detected in %s on line %d
array(2) {
["a"]=>
int(1)
diff --git a/ext/standard/tests/array/count_variation3.phpt b/ext/standard/tests/array/count_variation3.phpt
index e11b4c2449..9c89bcd16b 100644
--- a/ext/standard/tests/array/count_variation3.phpt
+++ b/ext/standard/tests/array/count_variation3.phpt
@@ -35,5 +35,5 @@ int(4)
-- $mode = 1: --
Warning: count(): recursion detected in %s on line %d
-int(12)
+int(4)
Done
diff --git a/ext/standard/tests/general_functions/debug_zval_dump_o.phpt b/ext/standard/tests/general_functions/debug_zval_dump_o.phpt
index c06dff556d..e2c84dae9b 100644
--- a/ext/standard/tests/general_functions/debug_zval_dump_o.phpt
+++ b/ext/standard/tests/general_functions/debug_zval_dump_o.phpt
@@ -135,25 +135,7 @@ object(object_class)#%d (6) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 2 --
object(no_member_class)#%d (0) refcount(%d){
@@ -184,25 +166,7 @@ object(contains_object_class)#%d (9) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
["class_object2"]=>
object(object_class)#%d (6) refcount(%d){
@@ -222,25 +186,7 @@ object(contains_object_class)#%d (9) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
["class_object3":"contains_object_class":private]=>
object(object_class)#%d (6) refcount(%d){
@@ -260,25 +206,7 @@ object(contains_object_class)#%d (9) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
["class_object4":protected]=>
object(object_class)#%d (6) refcount(%d){
@@ -298,195 +226,13 @@ object(contains_object_class)#%d (9) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
["no_member_class_object"]=>
object(no_member_class)#%d (0) refcount(%d){
}
["class_object5"]=>
- object(contains_object_class)#%d (9) refcount(%d){
- ["p"]=>
- int(30)
- ["p1":protected]=>
- int(40)
- ["p2":"contains_object_class":private]=>
- int(50)
- ["class_object1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
- }
- ["class_object2"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
- }
- ["class_object3":"contains_object_class":private]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
- }
- ["class_object4":protected]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
- }
- ["no_member_class_object"]=>
- object(no_member_class)#%d (0) refcount(%d){
- }
- ["class_object5"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 4 --
object(object_class)#%d (6) refcount(%d){
@@ -506,25 +252,7 @@ object(object_class)#%d (6) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 5 --
object(object_class)#%d (6) refcount(%d){
@@ -544,25 +272,7 @@ object(object_class)#%d (6) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 6 --
object(no_member_class)#%d (0) refcount(%d){
@@ -587,25 +297,7 @@ object(object_class)#%d (6) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 9 --
object(object_class)#%d (6) refcount(%d){
@@ -625,25 +317,7 @@ object(object_class)#%d (6) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (6) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- }
+ *RECURSION*
}
-- Iteration 10 --
int(30)
@@ -674,67 +348,7 @@ object(object_class)#%d (7) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- &object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- *RECURSION*
- }
- ["obj"]=>
- *RECURSION*
- }
- }
+ *RECURSION*
["obj"]=>
&object(object_class)#%d (7) refcount(%d){
["value1"]=>
@@ -753,89 +367,9 @@ object(object_class)#%d (7) refcount(%d){
int(3)
}
["object_class1"]=>
- object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- &object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- *RECURSION*
- }
- }
+ *RECURSION*
["obj"]=>
- &object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- &object(object_class)#%d (7) refcount(%d){
- ["value1"]=>
- int(5)
- ["value2":"object_class":private]=>
- int(10)
- ["value3":protected]=>
- int(20)
- ["value4"]=>
- int(30)
- ["array_var"]=>
- array(2) refcount(%d){
- ["key1"]=>
- int(1)
- ["key2 "]=>
- int(3)
- }
- ["object_class1"]=>
- *RECURSION*
- ["obj"]=>
- *RECURSION*
- }
- }
+ *RECURSION*
}
}
Done
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 0ae7991412..e28afe9177 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -119,10 +119,12 @@ again:
break;
case IS_ARRAY:
myht = Z_ARRVAL_P(struc);
- if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
- PUTS("*RECURSION*\n");
- --myht->u.v.nApplyCount;
- return;
+ if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(myht)) {
+ PUTS("*RECURSION*\n");
+ return;
+ }
+ GC_PROTECT_RECURSION(myht);
}
count = zend_array_count(myht);
php_printf("%sarray(%d) {\n", COMMON, count);
@@ -131,8 +133,8 @@ again:
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) {
php_array_element_dump(val, num, key, level);
} ZEND_HASH_FOREACH_END();
- if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
- --myht->u.v.nApplyCount;
+ if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(myht);
}
if (is_temp) {
zend_hash_destroy(myht);
@@ -144,11 +146,11 @@ again:
PUTS("}\n");
break;
case IS_OBJECT:
- if (Z_OBJ_APPLY_COUNT_P(struc) > 0) {
+ if (Z_IS_RECURSIVE_P(struc)) {
PUTS("*RECURSION*\n");
return;
}
- Z_OBJ_INC_APPLY_COUNT_P(struc);
+ Z_PROTECT_RECURSION_P(struc);
myht = Z_OBJDEBUG_P(struc, is_temp);
class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
@@ -172,7 +174,7 @@ again:
php_printf("%*c", level-1, ' ');
}
PUTS("}\n");
- Z_OBJ_DEC_APPLY_COUNT_P(struc);
+ Z_UNPROTECT_RECURSION_P(struc);
break;
case IS_RESOURCE: {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
@@ -289,18 +291,20 @@ again:
break;
case IS_ARRAY:
myht = Z_ARRVAL_P(struc);
- if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 1) {
- myht->u.v.nApplyCount--;
- PUTS("*RECURSION*\n");
- return;
+ if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(myht)) {
+ PUTS("*RECURSION*\n");
+ return;
+ }
+ GC_PROTECT_RECURSION(myht);
}
count = zend_array_count(myht);
php_printf("%sarray(%d) refcount(%u){\n", COMMON, count, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1);
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
zval_array_element_dump(val, index, key, level);
} ZEND_HASH_FOREACH_END();
- if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
- myht->u.v.nApplyCount--;
+ if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(myht);
}
if (is_temp) {
zend_hash_destroy(myht);
@@ -314,12 +318,11 @@ again:
case IS_OBJECT:
myht = Z_OBJDEBUG_P(struc, is_temp);
if (myht) {
- if (myht->u.v.nApplyCount > 1) {
+ if (GC_IS_RECURSIVE(myht)) {
PUTS("*RECURSION*\n");
return;
- } else {
- myht->u.v.nApplyCount++;
}
+ GC_PROTECT_RECURSION(myht);
}
class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc));
@@ -328,7 +331,7 @@ again:
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
zval_object_property_dump(val, index, key, level);
} ZEND_HASH_FOREACH_END();
- myht->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(myht);
if (is_temp) {
zend_hash_destroy(myht);
efree(myht);
@@ -487,11 +490,13 @@ again:
break;
case IS_ARRAY:
myht = Z_ARRVAL_P(struc);
- if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) {
- myht->u.v.nApplyCount--;
- smart_str_appendl(buf, "NULL", 4);
- zend_error(E_WARNING, "var_export does not handle circular references");
- return;
+ if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(myht)) {
+ smart_str_appendl(buf, "NULL", 4);
+ zend_error(E_WARNING, "var_export does not handle circular references");
+ return;
+ }
+ GC_PROTECT_RECURSION(myht);
}
if (level > 1) {
smart_str_appendc(buf, '\n');
@@ -501,8 +506,8 @@ again:
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
php_array_element_export(val, index, key, level, buf);
} ZEND_HASH_FOREACH_END();
- if (ZEND_HASH_APPLY_PROTECTION(myht)) {
- myht->u.v.nApplyCount--;
+ if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(myht);
}
if (level > 1) {
buffer_append_spaces(buf, level - 1);
@@ -514,12 +519,12 @@ again:
case IS_OBJECT:
myht = Z_OBJPROP_P(struc);
if (myht) {
- if (myht->u.v.nApplyCount > 0) {
+ if (GC_IS_RECURSIVE(myht)) {
smart_str_appendl(buf, "NULL", 4);
zend_error(E_WARNING, "var_export does not handle circular references");
return;
} else {
- myht->u.v.nApplyCount++;
+ GC_PROTECT_RECURSION(myht);
}
}
if (level > 1) {
@@ -534,7 +539,7 @@ again:
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
php_object_element_export(val, index, key, level, buf);
} ZEND_HASH_FOREACH_END();
- myht->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(myht);
}
if (level > 1) {
buffer_append_spaces(buf, level - 1);
@@ -951,18 +956,22 @@ again:
/* we should still add element even if it's not OK,
* since we already wrote the length of the array before */
- if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))
- || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1)
- ) {
- smart_str_appendl(buf, "N;", 2);
- } else {
- if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
- Z_ARRVAL_P(data)->u.v.nApplyCount++;
+ if (Z_TYPE_P(data) == IS_ARRAY) {
+ if (Z_TYPE_P(data) == IS_ARRAY
+ && (UNEXPECTED(Z_IS_RECURSIVE_P(data))
+ || UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)))) {
+ smart_str_appendl(buf, "N;", 2);
+ } else {
+ if (Z_REFCOUNTED_P(data)) {
+ Z_PROTECT_RECURSION_P(data);
+ }
+ php_var_serialize_intern(buf, data, var_hash);
+ if (Z_REFCOUNTED_P(data)) {
+ Z_UNPROTECT_RECURSION_P(data);
+ }
}
+ } else {
php_var_serialize_intern(buf, data, var_hash);
- if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
- Z_ARRVAL_P(data)->u.v.nApplyCount--;
- }
}
} ZEND_HASH_FOREACH_END();
}
diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c
index b6af00edd7..556af05112 100644
--- a/ext/wddx/wddx.c
+++ b/ext/wddx/wddx.c
@@ -640,28 +640,28 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name)
case IS_ARRAY:
ht = Z_ARRVAL_P(var);
- if (ht->u.v.nApplyCount > 1) {
- zend_throw_error(NULL, "WDDX doesn't support circular references");
- return;
- }
- if (ZEND_HASH_APPLY_PROTECTION(ht)) {
- ht->u.v.nApplyCount++;
+ if (Z_REFCOUNTED_P(var)) {
+ if (GC_IS_RECURSIVE(ht)) {
+ zend_throw_error(NULL, "WDDX doesn't support circular references");
+ return;
+ }
+ GC_PROTECT_RECURSION(ht);
}
php_wddx_serialize_array(packet, var);
- if (ZEND_HASH_APPLY_PROTECTION(ht)) {
- ht->u.v.nApplyCount--;
+ if (Z_REFCOUNTED_P(var)) {
+ GC_UNPROTECT_RECURSION(ht);
}
break;
case IS_OBJECT:
ht = Z_OBJPROP_P(var);
- if (ht->u.v.nApplyCount > 1) {
+ if (GC_IS_RECURSIVE(ht)) {
zend_throw_error(NULL, "WDDX doesn't support circular references");
return;
}
- ht->u.v.nApplyCount++;
+ GC_PROTECT_RECURSION(ht);
php_wddx_serialize_object(packet, var);
- ht->u.v.nApplyCount--;
+ GC_UNPROTECT_RECURSION(ht);
break;
}
@@ -691,28 +691,26 @@ static void php_wddx_add_var(wddx_packet *packet, zval *name_var)
target_hash = HASH_OF(name_var);
- if (is_array && target_hash->u.v.nApplyCount > 1) {
- php_error_docref(NULL, E_WARNING, "recursion detected");
- return;
- }
-
if (!Z_REFCOUNTED_P(name_var)) {
ZEND_HASH_FOREACH_VAL(target_hash, val) {
php_wddx_add_var(packet, val);
} ZEND_HASH_FOREACH_END();
} else {
- ZEND_HASH_FOREACH_VAL(target_hash, val) {
- if (is_array) {
- target_hash->u.v.nApplyCount++;
+ if (is_array) {
+ if (GC_IS_RECURSIVE(target_hash)) {
+ php_error_docref(NULL, E_WARNING, "recursion detected");
+ return;
}
-
+ GC_PROTECT_RECURSION(target_hash);
+ }
+ ZEND_HASH_FOREACH_VAL(target_hash, val) {
ZVAL_DEREF(val);
php_wddx_add_var(packet, val);
- if (is_array) {
- target_hash->u.v.nApplyCount--;
- }
} ZEND_HASH_FOREACH_END();
+ if (is_array) {
+ GC_UNPROTECT_RECURSION(target_hash);
+ }
}
}
}
diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c
index 2b1a642c05..e20b6d6872 100644
--- a/ext/xmlrpc/xmlrpc-epi-php.c
+++ b/ext/xmlrpc/xmlrpc-epi-php.c
@@ -556,9 +556,12 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
XMLRPC_VECTOR_TYPE vtype;
ht = HASH_OF(&val);
- if (ht && ht->u.v.nApplyCount > 1) {
- zend_throw_error(NULL, "XML-RPC doesn't support circular references");
- return NULL;
+ if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(ht)) {
+ zend_throw_error(NULL, "XML-RPC doesn't support circular references");
+ return NULL;
+ }
+ GC_PROTECT_RECURSION(ht);
}
ZVAL_COPY(&val_arr, &val);
@@ -569,10 +572,6 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(val_arr), num_index, my_key, pIter) {
ZVAL_DEREF(pIter);
- ht = HASH_OF(pIter);
- if (ht) {
- ht->u.v.nApplyCount++;
- }
if (my_key == NULL) {
char *num_str = NULL;
@@ -587,10 +586,10 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
} else {
XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(ZSTR_VAL(my_key), pIter, depth++));
}
- if (ht) {
- ht->u.v.nApplyCount--;
- }
} ZEND_HASH_FOREACH_END();
+ if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht);
+ }
zval_ptr_dtor(&val_arr);
}
break;
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 9326b79b6c..bdcb173c24 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -687,10 +687,12 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
break;
case IS_ARRAY:
myht = Z_ARRVAL_P(zv);
- if (ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
- phpdbg_xml("<recursion />");
- --myht->u.v.nApplyCount;
- break;
+ if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(myht)) {
+ phpdbg_xml("<recursion />");
+ break;
+ }
+ GC_PROTECT_RECURSION(myht);
}
phpdbg_xml("<array refstatus=\"%s\" num=\"%d\">", COMMON, zend_hash_num_elements(myht));
element_dump_func = phpdbg_xml_array_element_dump;
@@ -698,9 +700,8 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
goto head_done;
case IS_OBJECT:
myht = Z_OBJDEBUG_P(zv, is_temp);
- if (myht && ++myht->u.v.nApplyCount > 1) {
+ if (myht && GC_IS_RECURSIVE(myht)) {
phpdbg_xml("<recursion />");
- --myht->u.v.nApplyCount;
break;
}
@@ -715,7 +716,7 @@ head_done:
element_dump_func(val, key, num);
} ZEND_HASH_FOREACH_END();
zend_hash_apply_with_arguments(myht, (apply_func_args_t) element_dump_func, 0);
- --myht->u.v.nApplyCount;
+ GC_UNPROTECT_RECURSION(myht);
if (is_temp) {
zend_hash_destroy(myht);
efree(myht);