1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details)

This commit is contained in:
Dmitry Stogov
2015-01-29 21:05:02 +03:00
parent 10a3260b1f
commit 15a23b1218
19 changed files with 426 additions and 152 deletions

View File

@@ -829,7 +829,7 @@ static int generate_free_loop_var(znode *var) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_FREE;
opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
SET_NODE(opline->op1, var);
SET_UNUSED(opline->op2);
}
@@ -3398,6 +3398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
opnum_reset = get_next_op_number(CG(active_op_array));
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
reset_node.flag = by_ref;
zend_stack_push(&CG(loop_var_stack), &reset_node);
opnum_fetch = get_next_op_number(CG(active_op_array));
@@ -3408,12 +3409,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL);
if (by_ref) {
/* Allocate temporary variable to keep HashTable value */
opline->op1_type = IS_TMP_VAR;
opline->op1.var = get_temporary_variable(CG(active_op_array));
}
if (key_ast) {
zend_make_tmp_result(&key_node, opline);
}
@@ -3504,6 +3499,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
expr_node.flag = 0;
zend_stack_push(&CG(loop_var_stack), &expr_node);
zend_begin_loop();

View File

@@ -95,7 +95,8 @@ typedef union _znode_op {
} znode_op;
typedef struct _znode { /* used only during compilation */
int op_type;
zend_uchar op_type;
zend_uchar flag;
union {
znode_op op;
zval constant; /* replaced by literal/zv */

View File

@@ -1584,6 +1584,14 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
}
} else if (brk_opline->opcode == ZEND_FE_FREE) {
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
}
array_offset = jmp_to->parent;

View File

@@ -183,6 +183,11 @@ void init_executor(void) /* {{{ */
EG(scope) = NULL;
EG(ht_iterators_count) = sizeof(EG(ht_iterators_slots)) / sizeof(HashTableIterator);
EG(ht_iterators_used) = 0;
EG(ht_iterators) = EG(ht_iterators_slots);
memset(EG(ht_iterators), 0, sizeof(EG(ht_iterators_slots)));
EG(active) = 1;
}
/* }}} */
@@ -373,6 +378,11 @@ void shutdown_executor(void) /* {{{ */
zend_shutdown_fpu();
EG(ht_iterators_used) = 0;
if (EG(ht_iterators) != EG(ht_iterators_slots)) {
efree(EG(ht_iterators));
}
EG(active) = 0;
}
/* }}} */

View File

@@ -60,6 +60,12 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato
if (brk_opline->opcode == ZEND_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
zval_ptr_dtor_nogc(var);
} else if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
}

View File

@@ -225,6 +225,11 @@ struct _zend_executor_globals {
zend_bool active;
zend_bool valid_symbol_table;
uint32_t ht_iterators_count; /* number of allocatd slots */
uint32_t ht_iterators_used; /* number of used slots */
HashTableIterator *ht_iterators;
HashTableIterator ht_iterators_slots[16];
void *saved_fpu_cw_ptr;
#if XPFPA_HAVE_CW
XPFPA_CW_DATATYPE saved_fpu_cw;

View File

@@ -193,6 +193,144 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt
}
}
ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators);
HashTableIterator *end = iter + EG(ht_iterators_count);
uint32_t idx;
if (EXPECTED(ht->u.v.nIteratorsCount != 255)) {
ht->u.v.nIteratorsCount++;
}
while (iter != end) {
if (iter->ht == NULL) {
iter->ht = ht;
iter->pos = ht->nInternalPointer;
idx = iter - EG(ht_iterators);
if (idx + 1 > EG(ht_iterators_used)) {
EG(ht_iterators_used) = idx + 1;
}
return idx;
}
iter++;
}
if (EG(ht_iterators) == EG(ht_iterators_slots)) {
EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count));
} else {
EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
}
iter = EG(ht_iterators) + EG(ht_iterators_count);
EG(ht_iterators_count) += 8;
iter->ht = ht;
iter->pos = ht->nInternalPointer;
memset(iter + 1, 0, sizeof(HashTableIterator) * 7);
idx = iter - EG(ht_iterators);
EG(ht_iterators_used) = idx + 1;
return idx;
}
ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators) + idx;
ZEND_ASSERT(idx != (uint32_t)-1);
if (iter->pos == INVALID_IDX) {
return INVALID_IDX;
} else if (UNEXPECTED(iter->ht != ht)) {
if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) {
iter->ht->u.v.nIteratorsCount--;
}
if (EXPECTED(ht->u.v.nIteratorsCount != 255)) {
ht->u.v.nIteratorsCount++;
}
iter->ht = ht;
iter->pos = ht->nInternalPointer;
}
return iter->pos;
}
ZEND_API void zend_hash_iterator_del(uint32_t idx)
{
HashTableIterator *iter = EG(ht_iterators) + idx;
ZEND_ASSERT(idx != (uint32_t)-1);
if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) {
iter->ht->u.v.nIteratorsCount--;
}
iter->ht = NULL;
if (idx == EG(ht_iterators_used) - 1) {
while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
idx--;
}
EG(ht_iterators_used) = idx;
}
}
static zend_never_inline void _iterators_del(HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators);
HashTableIterator *end = iter + EG(ht_iterators_used);
uint32_t idx;
while (iter != end) {
if (iter->ht == ht) {
iter->ht = NULL;
}
iter++;
}
idx = EG(ht_iterators_used);
while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
idx--;
}
EG(ht_iterators_used) = idx;
}
static zend_always_inline void iterators_del(HashTable *ht)
{
if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
_iterators_del(ht);
}
}
static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos)
{
HashTableIterator *iter = EG(ht_iterators);
HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht && iter->pos == ht->nInternalPointer) {
iter->pos = pos;
}
iter++;
}
}
static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos)
{
if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
_iterators_update(ht, pos);
}
}
ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos)
{
if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
HashTableIterator *iter = EG(ht_iterators);
HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht && iter->pos == ht->nInternalPointer) {
iter->pos = pos;
}
iter++;
}
}
}
static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key)
{
zend_ulong h;
@@ -303,6 +441,7 @@ add_to_hash:
idx = ht->nNumUsed++;
ht->nNumOfElements++;
if (ht->nInternalPointer == INVALID_IDX) {
iterators_update(ht, idx);
ht->nInternalPointer = idx;
}
p = ht->arData + idx;
@@ -470,6 +609,7 @@ add_to_packed:
}
ht->nNumOfElements++;
if (ht->nInternalPointer == INVALID_IDX) {
iterators_update(ht, h);
ht->nInternalPointer = h;
}
if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
@@ -512,6 +652,7 @@ add_to_hash:
idx = ht->nNumUsed++;
ht->nNumOfElements++;
if (ht->nInternalPointer == INVALID_IDX) {
iterators_update(ht, idx);
ht->nInternalPointer = idx;
}
if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
@@ -600,6 +741,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
if (i != j) {
ht->arData[j] = ht->arData[i];
if (ht->nInternalPointer == i) {
iterators_update(ht, j);
ht->nInternalPointer = j;
}
}
@@ -632,9 +774,11 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx,
while (1) {
idx++;
if (idx >= ht->nNumUsed) {
iterators_update(ht, INVALID_IDX);
ht->nInternalPointer = INVALID_IDX;
break;
} else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
iterators_update(ht, idx);
ht->nInternalPointer = idx;
break;
}
@@ -893,6 +1037,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
} while (++p != end);
}
}
iterators_del(ht);
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
return;
}
@@ -933,7 +1078,7 @@ ZEND_API void zend_array_destroy(HashTable *ht)
}
} while (++p != end);
}
iterators_del(ht);
SET_INCONSISTENT(HT_DESTROYED);
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
return;

View File

@@ -50,8 +50,6 @@ typedef struct _zend_hash_key {
typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam);
typedef uint32_t HashPosition;
BEGIN_EXTERN_C()
/* startup/shutdown */
@@ -227,6 +225,13 @@ void zend_hash_display(const HashTable *ht);
ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx);
ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht);
ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht);
ZEND_API void zend_hash_iterator_del(uint32_t idx);
ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos);
END_EXTERN_C()
#define ZEND_INIT_SYMTABLE(ht) \

View File

@@ -129,6 +129,7 @@ struct _zval_struct {
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
} u2;
};
@@ -161,10 +162,11 @@ typedef struct _Bucket {
typedef struct _HashTable {
union {
struct {
ZEND_ENDIAN_LOHI_3(
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
uint16_t reserve)
zend_uchar nIteratorsCount,
zend_uchar reserve)
} v;
uint32_t flags;
} u;
@@ -179,6 +181,13 @@ typedef struct _HashTable {
dtor_func_t pDestructor;
} HashTable;
typedef uint32_t HashPosition;
typedef struct _HashTableIterator {
HashTable *ht;
HashPosition pos;
} HashTableIterator;
struct _zend_array {
zend_refcounted gc;
HashTable ht;
@@ -265,6 +274,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_FE_POS(zval) (zval).u2.fe_pos
#define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p))
#define Z_FE_ITER(zval) (zval).u2.fe_iter_idx
#define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p))
#define Z_COUNTED(zval) (zval).value.counted
#define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))

View File

@@ -2138,6 +2138,21 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
{
zval *var;
USE_OPLINE
SAVE_OPLINE();
var = EX_VAR(opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST)
{
USE_OPLINE
@@ -3821,6 +3836,14 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
}
} else if (brk_opline->opcode == ZEND_FE_FREE) {
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
}
@@ -4758,8 +4781,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
iter->index = -1; /* will be set to 0 before using next handler */
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (OP1_TYPE == IS_VAR) {
FREE_OP1_VAR_PTR();
@@ -4798,6 +4820,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
while (1) {
if (pos >= fe_ht->nNumUsed) {
FREE_OP1_VAR_PTR();
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
@@ -4811,8 +4834,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
fe_ht->nInternalPointer = pos;
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
FREE_OP1_VAR_PTR();
CHECK_EXCEPTION();
@@ -4820,6 +4843,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (OP1_TYPE == IS_VAR) {
FREE_OP1_VAR_PTR();
} else {
@@ -4990,21 +5014,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
ZVAL_DEREF(array);
if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
fe_ht = Z_ARRVAL_P(array);
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
if (UNEXPECTED(pos == INVALID_IDX)) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
pos = fe_ht->nInternalPointer;
}
SEPARATE_ARRAY(array);
fe_ht = Z_ARRVAL_P(array);
Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
}
//??? if (pos != fe_ht->nInternalPointer) {
//??? //...
//??? }
pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@@ -5048,7 +5058,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
@@ -5059,19 +5070,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
zend_object *zobj = Z_OBJ_P(array);
fe_ht = Z_OBJPROP_P(array);
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
if (UNEXPECTED(pos == INVALID_IDX)) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
pos = fe_ht->nInternalPointer;
}
Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
}
//??? if (pos != fe_ht->nInternalPointer) {
//???
//??? }
pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@@ -5129,7 +5128,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else {
@@ -5963,6 +5963,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
}
} else if (brk_opline->opcode == ZEND_FE_FREE) {
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
} else if (brk_opline->opcode == ZEND_END_SILENCE) {
/* restore previous error_reporting value */
if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {

View File

@@ -1347,6 +1347,14 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
}
} else if (brk_opline->opcode == ZEND_FE_FREE) {
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
} else if (brk_opline->opcode == ZEND_END_SILENCE) {
/* restore previous error_reporting value */
if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
@@ -1805,6 +1813,14 @@ static int ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
}
} else if (brk_opline->opcode == ZEND_FE_FREE) {
if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
}
@@ -3175,8 +3191,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
iter->index = -1; /* will be set to 0 before using next handler */
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_CONST == IS_VAR) {
@@ -3215,6 +3230,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
while (1) {
if (pos >= fe_ht->nNumUsed) {
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
@@ -3228,14 +3244,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
fe_ht->nInternalPointer = pos;
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_CONST == IS_VAR) {
} else {
@@ -9109,8 +9126,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
iter->index = -1; /* will be set to 0 before using next handler */
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_TMP_VAR == IS_VAR) {
@@ -9149,6 +9165,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
while (1) {
if (pos >= fe_ht->nNumUsed) {
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
@@ -9162,14 +9179,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
fe_ht->nInternalPointer = pos;
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_TMP_VAR == IS_VAR) {
} else {
@@ -11945,8 +11963,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
iter->index = -1; /* will be set to 0 before using next handler */
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_VAR == IS_VAR) {
if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
@@ -11985,6 +12002,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
while (1) {
if (pos >= fe_ht->nNumUsed) {
if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
@@ -11998,8 +12016,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
fe_ht->nInternalPointer = pos;
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
CHECK_EXCEPTION();
@@ -12007,6 +12025,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_VAR == IS_VAR) {
if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
} else {
@@ -12177,21 +12196,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
ZVAL_DEREF(array);
if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
fe_ht = Z_ARRVAL_P(array);
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
if (UNEXPECTED(pos == INVALID_IDX)) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
pos = fe_ht->nInternalPointer;
}
SEPARATE_ARRAY(array);
fe_ht = Z_ARRVAL_P(array);
Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
}
//??? if (pos != fe_ht->nInternalPointer) {
//??? //...
//??? }
pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@@ -12235,7 +12240,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
@@ -12246,19 +12252,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
zend_object *zobj = Z_OBJ_P(array);
fe_ht = Z_OBJPROP_P(array);
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
if (UNEXPECTED(pos == INVALID_IDX)) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
pos = fe_ht->nInternalPointer;
}
Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
}
//??? if (pos != fe_ht->nInternalPointer) {
//???
//??? }
pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@@ -12316,7 +12310,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else {
@@ -24354,8 +24349,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
iter->index = -1; /* will be set to 0 before using next handler */
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_CV == IS_VAR) {
@@ -24394,6 +24388,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
while (1) {
if (pos >= fe_ht->nNumUsed) {
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
@@ -24407,14 +24402,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
fe_ht->nInternalPointer = pos;
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
if (IS_CV == IS_VAR) {
} else {
@@ -33144,6 +33140,21 @@ static int ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval *var;
USE_OPLINE
SAVE_OPLINE();
var = EX_VAR(opline->op1.var);
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -39719,16 +39730,16 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,

View File

@@ -149,7 +149,7 @@ const char *zend_vm_opcodes_map[170] = {
"ZEND_VERIFY_RETURN_TYPE",
"ZEND_FE_RESET_RW",
"ZEND_FE_FETCH_RW",
NULL,
"ZEND_FE_FREE",
NULL,
NULL,
NULL,

View File

@@ -151,6 +151,7 @@ END_EXTERN_C()
#define ZEND_VERIFY_RETURN_TYPE 124
#define ZEND_FE_RESET_RW 125
#define ZEND_FE_FETCH_RW 126
#define ZEND_FE_FREE 127
#define ZEND_PRE_INC_OBJ 132
#define ZEND_PRE_DEC_OBJ 133
#define ZEND_POST_INC_OBJ 134

View File

@@ -207,12 +207,15 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
for (i = 0; i< op_array->last_brk_cont; i++) {
if (op_array->brk_cont_array[i].start >= 0 &&
(op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
int parent = op_array->brk_cont_array[i].parent;
while (parent >= 0 &&
op_array->brk_cont_array[parent].start < 0 &&
op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE) {
(op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
parent = op_array->brk_cont_array[parent].parent;
}
op_array->brk_cont_array[i].parent = parent;
@@ -227,6 +230,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
for (i = 0; i< op_array->last_brk_cont; i++) {
if (op_array->brk_cont_array[i].start >= 0 &&
(op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
if (i != j) {
op_array->brk_cont_array[j] = op_array->brk_cont_array[i];

View File

@@ -66,12 +66,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
}
/* special puprose variable to keep HashTable* on VM stack */
if (opline->opcode == ZEND_OP_DATA &&
(opline-1)->opcode == ZEND_FE_FETCH_RW &&
opline->op1_type == IS_TMP_VAR) {
start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
}
opline--;
}
@@ -84,21 +78,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
while (opline >= end) {
if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
/* special puprose variable to keep HashPointer on VM stack */
if (opline->opcode == ZEND_OP_DATA &&
(opline-1)->opcode == ZEND_FE_FETCH_RW &&
opline->op1_type == IS_TMP_VAR) {
max++;
ZEND_OP1(opline).var = NUM_VAR(max + offset);
} else {
currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
if (!valid_T[currT]) {
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
}
ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
if (!valid_T[currT]) {
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
}
ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
}
/* Skip OP_DATA */

View File

@@ -203,7 +203,9 @@ void zend_optimizer_pass2(zend_op_array *op_array)
jmp_to = &op_array->brk_cont_array[array_offset];
array_offset = jmp_to->parent;
if (--nest_levels > 0) {
if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE) {
if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE ||
op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE ||
op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) {
dont_optimize = 1;
break;
}

View File

@@ -2203,6 +2203,9 @@ PHP_FUNCTION(array_shift)
q->key = NULL;
ZVAL_COPY_VALUE(&q->val, &p->val);
ZVAL_UNDEF(&p->val);
if (idx == Z_ARRVAL_P(stack)->nInternalPointer) {
zend_hash_iterators_update(Z_ARRVAL_P(stack), k);
}
}
k++;
}
@@ -2265,9 +2268,14 @@ PHP_FUNCTION(array_unshift)
}
} ZEND_HASH_FOREACH_END();
new_hash.nInternalPointer = Z_ARRVAL_P(stack)->nInternalPointer;
new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount;
Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0;
Z_ARRVAL_P(stack)->pDestructor = NULL;
zend_hash_destroy(Z_ARRVAL_P(stack));
*Z_ARRVAL_P(stack) = new_hash;
zend_hash_iterators_update(Z_ARRVAL_P(stack), 0);
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
/* Clean up and return the number of elements in the stack */
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));

View File

@@ -1,7 +1,5 @@
--TEST--
Directly modifying an unreferenced array when foreach'ing over it while using &$value syntax.
--XFAIL--
Needs major foreach changes to get sane behavior
--FILE--
<?php
@@ -70,7 +68,7 @@ withRefValue(3, $transform);
withRefValue(4, $transform);
?>
--EXPECTF--
--EXPECT--
Popping elements off end of an unreferenced array, using &$value.
---( Array with 1 element(s): )---
@@ -95,9 +93,10 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.0
--> State of array after loop:
array(0) {
array(1) {
[0]=>
&string(3) "v.0"
}
---( Array with 3 element(s): )---
@@ -134,10 +133,12 @@ array(4) {
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=0; $v=v.0
iteration 3: $k=0; $v=v.0
--> State of array after loop:
array(0) {
array(2) {
[0]=>
string(3) "v.0"
[1]=>
&string(3) "v.1"
}
@@ -289,12 +290,28 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=new.0
iteration 2: $k=2; $v=new.1
iteration 3: $k=3; $v=new.2
iteration 4: $k=4; $v=new.3
iteration 5: $k=5; $v=new.4
** Stuck in a loop! **
--> State of array after loop:
array(2) {
array(7) {
[0]=>
&string(3) "v.0"
string(3) "v.0"
[1]=>
string(5) "new.0"
[2]=>
string(5) "new.1"
[3]=>
string(5) "new.2"
[4]=>
string(5) "new.3"
[5]=>
&string(5) "new.4"
[6]=>
string(5) "new.5"
}
---( Array with 2 element(s): )---
@@ -428,12 +445,28 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=new.0
iteration 2: $k=0; $v=new.1
iteration 3: $k=0; $v=new.2
iteration 4: $k=0; $v=new.3
iteration 5: $k=0; $v=new.4
** Stuck in a loop! **
--> State of array after loop:
array(2) {
array(7) {
[0]=>
string(5) "new.0"
string(5) "new.5"
[1]=>
&string(3) "v.0"
&string(5) "new.4"
[2]=>
string(5) "new.3"
[3]=>
string(5) "new.2"
[4]=>
string(5) "new.1"
[5]=>
string(5) "new.0"
[6]=>
string(3) "v.0"
}
---( Array with 2 element(s): )---

View File

@@ -1,7 +1,5 @@
--TEST--
Directly modifying a REFERENCED array when foreach'ing over it while using &$value syntax.
--XFAIL--
Needs major foreach changes to get sane behavior
--FILE--
<?php
@@ -72,7 +70,7 @@ withRefValue(3, $transform);
withRefValue(4, $transform);
?>
--EXPECTF--
--EXPECT--
Popping elements off end of a referenced array, using &$value
---( Array with 1 element(s): )---
@@ -97,9 +95,10 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.0
--> State of array after loop:
array(0) {
array(1) {
[0]=>
&string(3) "v.0"
}
---( Array with 3 element(s): )---
@@ -136,10 +135,12 @@ array(4) {
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=0; $v=v.0
iteration 3: $k=0; $v=v.0
--> State of array after loop:
array(0) {
array(2) {
[0]=>
string(3) "v.0"
[1]=>
&string(3) "v.1"
}
@@ -291,12 +292,28 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=new.0
iteration 2: $k=2; $v=new.1
iteration 3: $k=3; $v=new.2
iteration 4: $k=4; $v=new.3
iteration 5: $k=5; $v=new.4
** Stuck in a loop! **
--> State of array after loop:
array(2) {
array(7) {
[0]=>
&string(3) "v.0"
string(3) "v.0"
[1]=>
string(5) "new.0"
[2]=>
string(5) "new.1"
[3]=>
string(5) "new.2"
[4]=>
string(5) "new.3"
[5]=>
&string(5) "new.4"
[6]=>
string(5) "new.5"
}
---( Array with 2 element(s): )---
@@ -430,12 +447,28 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=new.0
iteration 2: $k=0; $v=new.1
iteration 3: $k=0; $v=new.2
iteration 4: $k=0; $v=new.3
iteration 5: $k=0; $v=new.4
** Stuck in a loop! **
--> State of array after loop:
array(2) {
array(7) {
[0]=>
string(5) "new.0"
string(5) "new.5"
[1]=>
&string(3) "v.0"
&string(5) "new.4"
[2]=>
string(5) "new.3"
[3]=>
string(5) "new.2"
[4]=>
string(5) "new.1"
[5]=>
string(5) "new.0"
[6]=>
string(3) "v.0"
}
---( Array with 2 element(s): )---