From ed18d67c5e69e4b5713e940568e5b3d0a7392a8d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 15 Oct 2014 17:02:54 +0400 Subject: [PATCH] Fixed bug #68215 (Behavior of foreach has changed) --- Zend/tests/bug68215.phpt | 91 ++++++++++++++++++++++++++++++++++++++++ Zend/zend_hash.h | 7 ++-- Zend/zend_vm_def.h | 17 ++++++-- Zend/zend_vm_execute.h | 20 +++++++-- 4 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 Zend/tests/bug68215.phpt diff --git a/Zend/tests/bug68215.phpt b/Zend/tests/bug68215.phpt new file mode 100644 index 0000000000..c4e8da8515 --- /dev/null +++ b/Zend/tests/bug68215.phpt @@ -0,0 +1,91 @@ +--TEST-- +Bug #68215 (Behavior of foreach has changed) +--FILE-- + array( + 'a' => 'apple', + 'b' => 'banana', + 'c' => 'cranberry', + 'd' => 'mango', + 'e' => 'pineapple' + ), + 'b' => array( + 'a' => 'apple', + 'b' => 'banana', + 'c' => 'cranberry', + 'd' => 'mango', + 'e' => 'pineapple' + ), + 'c' => 'cranberry', + 'd' => 'mango', + 'e' => 'pineapple' +); + +function test(&$child, $entry) +{ + $i = 1; + + foreach ($child AS $key => $fruit) + { + if (!is_numeric($key)) + { + $child[$i] = $fruit; + unset($child[$key]); + $i++; + } + } +} + +$i = 1; + +foreach ($arr AS $key => $fruit) +{ + $arr[$i] = $fruit; + + if (is_array($fruit)) + { + test($arr[$i], $fruit); + } + + unset($arr[$key]); + $i++; +} + +var_dump($arr); +?> +--EXPECT-- +array(5) { + [1]=> + array(5) { + [1]=> + string(5) "apple" + [2]=> + string(6) "banana" + [3]=> + string(9) "cranberry" + [4]=> + string(5) "mango" + [5]=> + string(9) "pineapple" + } + [2]=> + array(5) { + [1]=> + string(5) "apple" + [2]=> + string(6) "banana" + [3]=> + string(9) "cranberry" + [4]=> + string(5) "mango" + [5]=> + string(9) "pineapple" + } + [3]=> + string(9) "cranberry" + [4]=> + string(5) "mango" + [5]=> + string(9) "pineapple" +} diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index aaa677943d..5a36ddf28f 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -170,9 +170,10 @@ ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *p ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos); typedef struct _HashPointer { - HashPosition pos; - HashTable *ht; - zend_ulong h; + HashPosition pos; + HashTable *ht; + zend_ulong h; + zend_string *key; } HashPointer; #define zend_hash_has_more_elements(ht) \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index af532e88e4..ae2f0e7161 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4576,6 +4576,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) ptr->pos = pos; ptr->ht = fe_ht; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); @@ -4630,8 +4631,11 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) pos = ptr->h; } else { pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (pos != INVALID_IDX) { - if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + while (1) { + if (pos == INVALID_IDX) { + pos = fe_ht->nInternalPointer; + break; + } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { break; } pos = Z_NEXT(fe_ht->arData[pos].val); @@ -4684,6 +4688,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); fe_ht->nInternalPointer = ptr->pos = pos; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -4707,8 +4712,11 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) pos = ptr->h; } else { pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (pos != INVALID_IDX) { - if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + while (1) { + if (pos == INVALID_IDX) { + pos = fe_ht->nInternalPointer; + break; + } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { break; } pos = Z_NEXT(fe_ht->arData[pos].val); @@ -4777,6 +4785,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) zend_check_property_access(zobj, p->key TSRMLS_CC) == FAILURE)); fe_ht->nInternalPointer = ptr->pos = pos; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0768aa6df2..5d448c34d4 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3222,6 +3222,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A ptr->pos = pos; ptr->ht = fe_ht; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); @@ -10032,6 +10033,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG ptr->pos = pos; ptr->ht = fe_ht; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); @@ -16747,6 +16749,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG ptr->pos = pos; ptr->ht = fe_ht; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); @@ -16801,8 +16804,11 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG pos = ptr->h; } else { pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (pos != INVALID_IDX) { - if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + while (1) { + if (pos == INVALID_IDX) { + pos = fe_ht->nInternalPointer; + break; + } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { break; } pos = Z_NEXT(fe_ht->arData[pos].val); @@ -16855,6 +16861,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); fe_ht->nInternalPointer = ptr->pos = pos; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -16878,8 +16885,11 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG pos = ptr->h; } else { pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (pos != INVALID_IDX) { - if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + while (1) { + if (pos == INVALID_IDX) { + pos = fe_ht->nInternalPointer; + break; + } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { break; } pos = Z_NEXT(fe_ht->arData[pos].val); @@ -16948,6 +16958,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG zend_check_property_access(zobj, p->key TSRMLS_CC) == FAILURE)); fe_ht->nInternalPointer = ptr->pos = pos; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -34388,6 +34399,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS ptr->pos = pos; ptr->ht = fe_ht; ptr->h = fe_ht->arData[pos].h; + ptr->key = fe_ht->arData[pos].key; is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); -- 2.40.0