Change "foreach" statement behavior (this is just a PoC yet)

- "foreach by value" don't relay on internal array/object pointer and doesnt perform array duplication. It just locks it incrementing reference counter. If the original array is modified by some code, the copy on write is performed and "foreach" still work with the old copy.

- it makes no difference if array given to "foreach by value" is reference itself

- "foreach by reference" still use internal array/object pointer and should work similar to PHP-5. (This id not completely implemented)
This commit is contained in:
Dmitry Stogov 2015-01-28 07:43:28 +03:00
parent 638d0cb753
commit 4638f7b914
24 changed files with 1328 additions and 1275 deletions

View File

@ -23,4 +23,4 @@ var_dump(key($arr["v"]));
int(0)
int(0)
int(0)
NULL
int(0)

View File

@ -23,4 +23,4 @@ int(0)
int(0)
int(1)
int(2)
NULL
int(0)

View File

@ -3396,30 +3396,22 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
}
opnum_reset = get_next_op_number(CG(active_op_array));
opline = zend_emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL);
if (by_ref && is_variable) {
opline->extended_value = ZEND_FE_FETCH_BYREF;
}
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
zend_stack_push(&CG(loop_var_stack), &reset_node);
opnum_fetch = get_next_op_number(CG(active_op_array));
opline = zend_emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL);
if (by_ref) {
opline->extended_value |= ZEND_FE_FETCH_BYREF;
}
opline = zend_emit_op(&value_node, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
if (key_ast) {
opline->extended_value |= ZEND_FE_FETCH_WITH_KEY;
opline->extended_value = 1;
}
opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL);
/* Allocate enough space to keep HashPointer on VM stack */
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 (sizeof(HashPointer) > sizeof(zval)) {
/* Make sure 1 zval is enough for HashPointer (2 must be enough) */
get_temporary_variable(CG(active_op_array));
}
if (key_ast) {

View File

@ -828,9 +828,6 @@ int zend_add_literal(zend_op_array *op_array, zval *zv);
#define ZEND_FETCH_ARG_MASK 0x000fffff
#define ZEND_FE_FETCH_BYREF 1
#define ZEND_FE_FETCH_WITH_KEY 2
#define EXT_TYPE_FREE_ON_RETURN (1<<2)
#define ZEND_MEMBER_FUNC_CALL 1<<0

View File

@ -171,13 +171,6 @@ ZEND_API zval *zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos);
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos);
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos);
typedef struct _HashPointer {
HashPosition pos;
HashTable *ht;
zend_ulong h;
zend_string *key;
} HashPointer;
#define zend_hash_has_more_elements(ht) \
zend_hash_has_more_elements_ex(ht, &(ht)->nInternalPointer)
#define zend_hash_move_forward(ht) \

View File

@ -766,8 +766,10 @@ ZEND_API int pass_two(zend_op_array *op_array)
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_NEW:
case ZEND_FE_RESET:
case ZEND_FE_FETCH:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
break;
case ZEND_VERIFY_RETURN_TYPE:

View File

@ -128,6 +128,7 @@ struct _zval_struct {
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
} u2;
};
@ -261,6 +262,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_CACHE_SLOT(zval) (zval).u2.cache_slot
#define Z_CACHE_SLOT_P(zval_p) Z_CACHE_SLOT(*(zval_p))
#define Z_FE_POS(zval) (zval).u2.fe_pos
#define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p))
#define Z_COUNTED(zval) (zval).value.counted
#define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))

View File

@ -4620,245 +4620,200 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY)
ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *array_ptr, *array_ref, iterator, tmp;
zval *array_ptr;
HashTable *fe_ht;
zend_object_iterator *iter = NULL;
zend_class_entry *ce = NULL;
zend_bool is_empty = 0;
SAVE_OPLINE();
if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) &&
(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
array_ptr = array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
ZVAL_DEREF(array_ptr);
if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
SEPARATE_ARRAY(array_ptr);
if (!Z_ISREF_P(array_ref)) {
ZVAL_NEW_REF(array_ref, array_ref);
array_ptr = Z_REFVAL_P(array_ref);
}
if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
} else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
ce = Z_OBJCE_P(array_ptr);
if (ce->get_iterator == NULL) {
Z_ADDREF_P(array_ptr);
}
array_ref = array_ptr;
} else {
if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
}
} else {
array_ptr = array_ref = GET_OP1_ZVAL_PTR(BP_VAR_R);
if (OP1_TYPE & (IS_VAR|IS_CV)) {
ZVAL_DEREF(array_ptr);
}
if (OP1_TYPE == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&tmp, array_ptr);
if (Z_OPT_IMMUTABLE_P(&tmp)) {
zval_copy_ctor_func(&tmp);
}
array_ref = array_ptr = &tmp;
if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
ce = Z_OBJCE_P(array_ptr);
if (ce && ce->get_iterator) {
Z_DELREF_P(array_ref);
}
}
} else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
ce = Z_OBJCE_P(array_ptr);
if (!ce->get_iterator) {
if (OP1_TYPE == IS_CV) {
Z_ADDREF_P(array_ref);
}
}
} else if (Z_IMMUTABLE_P(array_ref)) {
if (OP1_TYPE == IS_CV) {
zval_copy_ctor_func(array_ref);
Z_ADDREF_P(array_ref);
} else {
ZVAL_COPY_VALUE(&tmp, array_ref);
zval_copy_ctor_func(&tmp);
array_ptr = array_ref = &tmp;
}
} else if (Z_REFCOUNTED_P(array_ref)) {
if (OP1_TYPE == IS_CONST ||
(OP1_TYPE == IS_CV &&
!Z_ISREF_P(array_ref) &&
Z_REFCOUNT_P(array_ref) > 1) ||
(OP1_TYPE == IS_VAR &&
!Z_ISREF_P(array_ref) &&
Z_REFCOUNT_P(array_ref) > 2)) {
if (OP1_TYPE == IS_VAR) {
Z_DELREF_P(array_ref);
}
ZVAL_DUP(&tmp, array_ref);
array_ptr = array_ref = &tmp;
} else if (OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) {
if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) {
ZVAL_UNREF(array_ref);
array_ptr = array_ref;
}
if (Z_IMMUTABLE_P(array_ptr)) {
zval_copy_ctor_func(array_ptr);
} else if (Z_ISREF_P(array_ref) &&
Z_COPYABLE_P(array_ptr) &&
Z_REFCOUNT_P(array_ptr) > 1) {
Z_DELREF_P(array_ptr);
zval_copy_ctor_func(array_ptr);
}
if (OP1_TYPE == IS_CV) {
Z_ADDREF_P(array_ref);
}
}
}
}
if (OP1_TYPE != IS_CONST &&
Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(array_ptr);
zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0);
zend_bool is_empty;
if (ce && ce->get_iterator) {
iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
if (OP1_TYPE == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
FREE_OP1_IF_VAR();
}
if (iter && EXPECTED(EG(exception) == NULL)) {
ZVAL_OBJ(&iterator, &iter->std);
array_ptr = array_ref = &iterator;
} else {
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
FREE_OP1_VAR_PTR();
}
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
}
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
if (iter) {
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
FREE_OP1_VAR_PTR();
}
FREE_OP1_IF_VAR();
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
is_empty = iter->funcs->valid(iter) != SUCCESS;
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
FREE_OP1_VAR_PTR();
}
FREE_OP1_IF_VAR();
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
iter->index = -1; /* will be set to 0 before using next handler */
} else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var);
HashPosition pos = 0;
Bucket *p;
while (1) {
if (pos >= fe_ht->nNumUsed) {
is_empty = 1;
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
FREE_OP1_VAR_PTR();
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
if (Z_TYPE(p->val) == IS_UNDEF ||
(Z_TYPE(p->val) == IS_INDIRECT &&
Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) {
pos++;
continue;
}
if (!ce ||
!p->key ||
zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
break;
}
pos++;
}
fe_ht->nInternalPointer = pos;
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()");
is_empty = 1;
}
ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
FREE_OP1_VAR_PTR();
}
FREE_OP1_IF_VAR();
if (is_empty) {
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
if (OP1_TYPE != IS_TMP_VAR) {
if (Z_REFCOUNTED_P(array_ptr)) {
Z_ADDREF_P(array_ptr);
}
}
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr);
Z_FE_POS_P(EX_VAR(opline->result.var)) = 0;
FREE_OP1_IF_VAR();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
FREE_OP1();
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
}
ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *array, *array_ref;
zval *value;
zval *array_ptr, *array_ref;
HashTable *fe_ht;
HashPointer *ptr;
HashPosition pos;
Bucket *p;
array = array_ref = EX_VAR(opline->op1.var);
if (Z_ISREF_P(array)) {
array = Z_REFVAL_P(array);
// TODO: referenced value might be changed to different array ???
if (Z_IMMUTABLE_P(array)) {
zval_copy_ctor_func(array);
}
}
SAVE_OPLINE();
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
ZVAL_MAKE_REF(array_ref);
array_ptr = Z_REFVAL_P(array_ref);
if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
SEPARATE_ARRAY(array_ptr);
}
} else {
array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
}
if (OP1_TYPE != IS_CONST &&
Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(array_ptr);
zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1);
zend_bool is_empty;
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
FREE_OP1_VAR_PTR();
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
FREE_OP1_VAR_PTR();
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
is_empty = iter->funcs->valid(iter) != SUCCESS;
if (UNEXPECTED(EG(exception) != NULL)) {
FREE_OP1_VAR_PTR();
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
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);
FREE_OP1_VAR_PTR();
if (is_empty) {
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
HashPosition pos = 0;
Bucket *p;
if (OP1_TYPE != IS_TMP_VAR) {
if (Z_REFCOUNTED_P(array_ref)) {
Z_ADDREF_P(array_ref);
}
}
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
while (1) {
if (pos >= fe_ht->nNumUsed) {
FREE_OP1_VAR_PTR();
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
if ((Z_TYPE(p->val) != IS_UNDEF &&
(Z_TYPE(p->val) != IS_INDIRECT ||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) &&
(Z_TYPE_P(array_ptr) != IS_OBJECT ||
!p->key ||
zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) {
break;
}
pos++;
}
Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
FREE_OP1_VAR_PTR();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZVAL_UNDEF(EX_VAR(opline->result.var));
FREE_OP1_VAR_PTR();
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
}
ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *array;
zval *value;
HashTable *fe_ht;
HashPosition pos;
Bucket *p;
array = EX_VAR(opline->op1.var);
SAVE_OPLINE();
if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
fe_ht = Z_ARRVAL_P(array);
ptr = (HashPointer*)EX_VAR((opline+1)->op1.var);
pos = ptr->pos;
if (UNEXPECTED(pos == INVALID_IDX)) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else if (UNEXPECTED(ptr->ht != fe_ht)) {
ptr->ht = fe_ht;
pos = 0;
} else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) {
if (fe_ht->u.flags & HASH_FLAG_PACKED) {
pos = ptr->h;
} else {
pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask];
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);
}
}
}
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@ -4866,46 +4821,27 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
}
p = fe_ht->arData + pos;
value = &p->val;
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
} else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) {
} else if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
}
}
if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
} else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
break;
}
if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
if (opline->extended_value) {
if (!p->key) {
ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
} else {
ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
}
}
break;
}
do {
pos++;
if (pos >= fe_ht->nNumUsed) {
fe_ht->nInternalPointer = ptr->pos = INVALID_IDX;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
}
p = fe_ht->arData + pos;
} while (Z_TYPE(p->val) == IS_UNDEF ||
(Z_TYPE(p->val) == IS_INDIRECT &&
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;
ZVAL_COPY(EX_VAR(opline->result.var), value);
Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
@ -4916,30 +4852,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
zend_object *zobj = Z_OBJ_P(array);
fe_ht = Z_OBJPROP_P(array);
ptr = (HashPointer*)EX_VAR((opline+1)->op1.var);
pos = ptr->pos;
if (pos == INVALID_IDX) {
/* reached end of iteration */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else if (UNEXPECTED(ptr->ht != fe_ht)) {
ptr->ht = fe_ht;
pos = 0;
} else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) {
if (fe_ht->u.flags & HASH_FLAG_PACKED) {
pos = ptr->h;
} else {
pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask];
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);
}
}
}
pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@ -4948,25 +4861,26 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
p = fe_ht->arData + pos;
value = &p->val;
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
} else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) {
} else if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
}
}
if (UNEXPECTED(!p->key)) {
if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
}
if (!p->key ||
zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) {
break;
} else if (zend_check_property_access(zobj, p->key) == SUCCESS) {
if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
if (p->key->val[0]) {
}
pos++;
}
if (opline->extended_value) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
} else if (p->key->val[0]) {
ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
} else {
const char *class_name, *prop_name;
@ -4976,33 +4890,8 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len);
}
}
break;
}
pos++;
}
if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
} else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
do {
pos++;
if (pos >= fe_ht->nNumUsed) {
fe_ht->nInternalPointer = ptr->pos = INVALID_IDX;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
}
p = fe_ht->arData + pos;
} while (Z_TYPE(p->val) == IS_UNDEF ||
(Z_TYPE(p->val) == IS_INDIRECT &&
Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) ||
(EXPECTED(p->key != NULL) &&
zend_check_property_access(zobj, p->key) == FAILURE));
fe_ht->nInternalPointer = ptr->pos = pos;
ptr->h = fe_ht->arData[pos].h;
ptr->key = fe_ht->arData[pos].key;
Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else {
@ -5012,7 +4901,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
}
@ -5020,32 +4909,236 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
} else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
if (opline->extended_value) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array_ref);
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
}
}
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
}
ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *array;
zval *value;
HashTable *fe_ht;
HashPosition pos;
Bucket *p;
array = EX_VAR(opline->op1.var);
SAVE_OPLINE();
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) {
//??? //...
//??? }
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
fe_ht->nInternalPointer = INVALID_IDX;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
value = &p->val;
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
} else if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
}
}
break;
}
if (opline->extended_value) {
if (!p->key) {
ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
} else {
ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
}
}
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
while (1) {
pos++;
if (pos >= fe_ht->nNumUsed) {
pos = INVALID_IDX;
break;
}
p = fe_ht->arData + pos;
if (Z_TYPE(p->val) != IS_UNDEF &&
(Z_TYPE(p->val) != IS_INDIRECT ||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) {
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
zend_object_iterator *iter;
if ((iter = zend_iterator_unwrap(array)) == NULL) {
/* plain object */
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) {
//???
//??? }
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
fe_ht->nInternalPointer = INVALID_IDX;
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
p = fe_ht->arData + pos;
value = &p->val;
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
} else if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (Z_TYPE_P(value) == IS_UNDEF) {
pos++;
continue;
}
}
if (!p->key ||
zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) {
break;
}
pos++;
}
if (opline->extended_value) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
} else if (p->key->val[0]) {
ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
} else {
const char *class_name, *prop_name;
size_t prop_name_len;
zend_unmangle_property_name_ex(
p->key, &class_name, &prop_name, &prop_name_len);
ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len);
}
}
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
while (1) {
pos++;
if (pos >= fe_ht->nNumUsed) {
pos = INVALID_IDX;
break;
}
p = fe_ht->arData + pos;
if ((Z_TYPE(p->val) != IS_UNDEF &&
(Z_TYPE(p->val) != IS_INDIRECT ||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) &&
(Z_TYPE_P(array) != IS_OBJECT ||
!p->key ||
zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) {
break;
}
}
Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
} else {
/* !iter happens from exception */
if (iter && ++iter->index > 0) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
}
/* If index is zero we come from FE_RESET and checked valid() already. */
if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
if (opline->extended_value) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {

File diff suppressed because it is too large Load Diff

View File

@ -99,8 +99,8 @@ const char *zend_vm_opcodes_map[170] = {
"ZEND_UNSET_VAR",
"ZEND_UNSET_DIM",
"ZEND_UNSET_OBJ",
"ZEND_FE_RESET",
"ZEND_FE_FETCH",
"ZEND_FE_RESET_R",
"ZEND_FE_FETCH_R",
"ZEND_EXIT",
"ZEND_FETCH_R",
"ZEND_FETCH_DIM_R",
@ -147,8 +147,8 @@ const char *zend_vm_opcodes_map[170] = {
"ZEND_DEFINED",
"ZEND_TYPE_CHECK",
"ZEND_VERIFY_RETURN_TYPE",
NULL,
NULL,
"ZEND_FE_RESET_RW",
"ZEND_FE_FETCH_RW",
NULL,
NULL,
NULL,

View File

@ -101,8 +101,8 @@ END_EXTERN_C()
#define ZEND_UNSET_VAR 74
#define ZEND_UNSET_DIM 75
#define ZEND_UNSET_OBJ 76
#define ZEND_FE_RESET 77
#define ZEND_FE_FETCH 78
#define ZEND_FE_RESET_R 77
#define ZEND_FE_FETCH_R 78
#define ZEND_EXIT 79
#define ZEND_FETCH_R 80
#define ZEND_FETCH_DIM_R 81
@ -149,6 +149,8 @@ END_EXTERN_C()
#define ZEND_DEFINED 122
#define ZEND_TYPE_CHECK 123
#define ZEND_VERIFY_RETURN_TYPE 124
#define ZEND_FE_RESET_RW 125
#define ZEND_FE_FETCH_RW 126
#define ZEND_PRE_INC_OBJ 132
#define ZEND_PRE_DEC_OBJ 133
#define ZEND_POST_INC_OBJ 134

View File

@ -166,14 +166,16 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
START_BLOCK_OP(ZEND_OP2(opline).opline_num);
START_BLOCK_OP(opno + 1);
break;
case ZEND_FE_FETCH:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
START_BLOCK_OP(ZEND_OP2(opline).opline_num);
START_BLOCK_OP(opno + 2);
break;
@ -293,11 +295,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_FE_FETCH:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
/* break missing intentionally */
default:
@ -619,7 +623,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array,
ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */
opline->opcode != ZEND_FETCH_LIST &&
opline->opcode != ZEND_FE_RESET &&
(opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) &&
opline->opcode != ZEND_FREE
) {
zend_op *src = VAR_SOURCE(opline->op1);

View File

@ -93,8 +93,10 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_FETCH:
case ZEND_FE_RESET:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:

View File

@ -66,15 +66,12 @@ 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 HashPointer on VM stack */
/* special puprose variable to keep HashTable* on VM stack */
if (opline->opcode == ZEND_OP_DATA &&
(opline-1)->opcode == ZEND_FE_FETCH &&
(opline-1)->opcode == ZEND_FE_FETCH_RW &&
(opline-2)->opcode == ZEND_FE_RESET_RW &&
opline->op1_type == IS_TMP_VAR) {
start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
if (sizeof(HashPointer) > sizeof(zval)) {
/* Make shure 1 zval is enough for HashPointer (2 must be enough) */
start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline;
}
start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline - 2;
}
opline--;
}
@ -88,17 +85,6 @@ 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 &&
opline->op1_type == IS_TMP_VAR) {
max++;
ZEND_OP1(opline).var = NUM_VAR(max + offset);
if (sizeof(HashPointer) > sizeof(zval)) {
/* Make shure 1 zval is enough for HashPointer (2 must be enough) */
max++;
}
} else {
currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
if (!valid_T[currT]) {
GET_AVAILABLE_T();
@ -107,7 +93,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
}
ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
}
}
/* Skip OP_DATA */
if (opline->opcode == ZEND_OP_DATA &&

View File

@ -602,8 +602,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET:
case ZEND_FE_FETCH:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:

View File

@ -328,7 +328,8 @@ continue_jmp_ex_optimization:
op->opcode == ZEND_RETURN ||
op->opcode == ZEND_RETURN_BY_REF ||
op->opcode == ZEND_FAST_RET ||
op->opcode == ZEND_FE_FETCH ||
op->opcode == ZEND_FE_FETCH_R ||
op->opcode == ZEND_FE_FETCH_RW ||
op->opcode == ZEND_EXIT) {
break;
}
@ -363,7 +364,8 @@ continue_jmp_ex_optimization:
op->opcode == ZEND_RETURN ||
op->opcode == ZEND_RETURN_BY_REF ||
op->opcode == ZEND_FAST_RET ||
op->opcode == ZEND_FE_FETCH ||
op->opcode == ZEND_FE_FETCH_R ||
op->opcode == ZEND_FE_FETCH_RW ||
op->opcode == ZEND_EXIT) {
break;
}

View File

@ -450,8 +450,10 @@ static void zend_accel_optimize(zend_op_array *op_array,
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_NEW:
case ZEND_FE_RESET:
case ZEND_FE_FETCH:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
break;
}
@ -488,8 +490,10 @@ static void zend_accel_optimize(zend_op_array *op_array,
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_NEW:
case ZEND_FE_RESET:
case ZEND_FE_FETCH:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
break;
}

View File

@ -378,8 +378,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_NEW:
case ZEND_FE_RESET:
case ZEND_FE_FETCH:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
break;
}

View File

@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null)
?>
--EXPECT--
string(3) "one"
bool(false)
string(3) "one"

View File

@ -60,5 +60,5 @@ string(1) "f"
int(2)
string(1) "f"
bool(false)
string(1) "a"
bool(false)

View File

@ -55,6 +55,7 @@ foreach ($refedArray as $k=>&$v4) {
Remove elements from a referenced array during loop
key: 0; value: original.0
key: 1; value: original.1
key: 2; value: original.2
Remove elements from a referenced array during loop, using &$value
key: 0; value: original.0
@ -64,11 +65,6 @@ Add elements to a referenced array during loop
key: 0; value: original.0
key: 1; value: original.1
key: 2; value: original.2
key: 3; value: new.0
key: 4; value: new.1
key: 5; value: new.2
key: 6; value: new.3
Loop detected, as expected.
Add elements to a referenced array during loop, using &$value
key: 0; value: original.0

View File

@ -25,10 +25,9 @@ foreach ($a as $v) {
Change from array to non iterable:
int(1)
Warning: Invalid argument supplied for foreach() in %s on line 5
int(2)
int(3)
Change from object to non iterable:
int(1)
Warning: Invalid argument supplied for foreach() in %s on line 15
int(2)

View File

@ -1,7 +1,5 @@
--TEST--
Directly modifying a REFERENCED array when foreach'ing over it.
--XFAIL--
Needs major foreach changes to get sane behavior
--FILE--
<?php
@ -96,7 +94,7 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
--> State of array after loop:
array(0) {
}
@ -114,10 +112,9 @@ array(3) {
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
--> State of array after loop:
array(1) {
[0]=>
string(3) "v.0"
array(0) {
}
---( Array with 4 element(s): )---
@ -135,8 +132,8 @@ 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
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=v.3
--> State of array after loop:
array(0) {
}
@ -166,7 +163,7 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.1
iteration 1: $k=1; $v=v.1
--> State of array after loop:
array(0) {
}
@ -183,8 +180,8 @@ array(3) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.1
iteration 2: $k=0; $v=v.2
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
--> State of array after loop:
array(0) {
}
@ -203,9 +200,9 @@ array(4) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=0; $v=v.1
iteration 2: $k=0; $v=v.2
iteration 3: $k=0; $v=v.3
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=v.3
--> State of array after loop:
array(0) {
}
@ -309,13 +306,8 @@ array(2) {
--> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=new.0
iteration 3: $k=3; $v=new.1
iteration 4: $k=4; $v=new.2
iteration 5: $k=5; $v=new.3
** Stuck in a loop! **
--> State of array after loop:
array(8) {
array(4) {
[0]=>
string(3) "v.0"
[1]=>
@ -324,14 +316,6 @@ array(8) {
string(5) "new.0"
[3]=>
string(5) "new.1"
[4]=>
string(5) "new.2"
[5]=>
string(5) "new.3"
[6]=>
string(5) "new.4"
[7]=>
string(5) "new.5"
}
---( Array with 3 element(s): )---
@ -348,12 +332,8 @@ array(3) {
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=new.0
iteration 4: $k=4; $v=new.1
iteration 5: $k=5; $v=new.2
** Stuck in a loop! **
--> State of array after loop:
array(9) {
array(6) {
[0]=>
string(3) "v.0"
[1]=>
@ -366,12 +346,6 @@ array(9) {
string(5) "new.1"
[5]=>
string(5) "new.2"
[6]=>
string(5) "new.3"
[7]=>
string(5) "new.4"
[8]=>
string(5) "new.5"
}
---( Array with 4 element(s): )---
@ -391,11 +365,8 @@ array(4) {
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=v.3
iteration 4: $k=4; $v=new.0
iteration 5: $k=5; $v=new.1
** Stuck in a loop! **
--> State of array after loop:
array(10) {
array(8) {
[0]=>
string(3) "v.0"
[1]=>
@ -412,10 +383,6 @@ array(10) {
string(5) "new.2"
[7]=>
string(5) "new.3"
[8]=>
string(5) "new.4"
[9]=>
string(5) "new.5"
}
@ -447,29 +414,16 @@ array(2) {
}
--> 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! **
iteration 1: $k=1; $v=v.1
--> State of array after loop:
array(8) {
array(4) {
[0]=>
string(5) "new.5"
[1]=>
string(5) "new.4"
[2]=>
string(5) "new.3"
[3]=>
string(5) "new.2"
[4]=>
string(5) "new.1"
[5]=>
[1]=>
string(5) "new.0"
[6]=>
[2]=>
string(3) "v.0"
[7]=>
[3]=>
string(3) "v.1"
}
@ -485,31 +439,21 @@ array(3) {
}
--> 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! **
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
--> State of array after loop:
array(9) {
array(6) {
[0]=>
string(5) "new.5"
[1]=>
string(5) "new.4"
[2]=>
string(5) "new.3"
[3]=>
string(5) "new.2"
[4]=>
[1]=>
string(5) "new.1"
[5]=>
[2]=>
string(5) "new.0"
[6]=>
[3]=>
string(3) "v.0"
[7]=>
[4]=>
string(3) "v.1"
[8]=>
[5]=>
string(3) "v.2"
}
@ -527,32 +471,25 @@ array(4) {
}
--> 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! **
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=v.3
--> State of array after loop:
array(10) {
array(8) {
[0]=>
string(5) "new.5"
[1]=>
string(5) "new.4"
[2]=>
string(5) "new.3"
[3]=>
[1]=>
string(5) "new.2"
[4]=>
[2]=>
string(5) "new.1"
[5]=>
[3]=>
string(5) "new.0"
[6]=>
[4]=>
string(3) "v.0"
[7]=>
[5]=>
string(3) "v.1"
[8]=>
[6]=>
string(3) "v.2"
[9]=>
[7]=>
string(3) "v.3"
}

View File

@ -70,16 +70,12 @@ var_dump($obj);
?>
--EXPECTF--
Substituting the iterated object for a different object.
string(10) "Original a"
string(10) "Original b"
string(5) "new a"
string(5) "new b"
string(5) "new c"
string(5) "new d"
string(5) "new e"
string(5) "new f"
string(10) "Original c"
string(10) "Original d"
string(10) "Original e"
object(stdClass)#%d (6) {
["a"]=>
string(5) "new a"
@ -98,14 +94,9 @@ object(stdClass)#%d (6) {
Substituting the iterated object for an array.
string(10) "Original a"
string(10) "Original b"
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
string(10) "Original c"
string(10) "Original d"
string(10) "Original e"
array(8) {
[0]=>
int(1)
@ -128,11 +119,12 @@ array(8) {
Substituting the iterated array for an object.
int(1)
int(2)
string(10) "Original a"
string(10) "Original b"
string(10) "Original c"
string(10) "Original d"
string(10) "Original e"
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
object(C)#%d (5) {
["a"]=>
string(10) "Original a"