- Fixed bug #36214 (__get method works properly only when conditional operator is used).
- Fixed bug #39449 (Overloaded array properties do not work correctly).
- Fixed bug #39990 (Cannot "foreach" over overloaded properties).
--- /dev/null
+--TEST--
+Bug #35106 (nested foreach fails when array variable has a reference)
+--FILE--
+<?php
+$a=array("1","2");
+$b=&$a;
+foreach($a as $i){
+ echo $i;
+ foreach($a as $p);
+}
+echo "\n";
+?>
+--EXPECT--
+12
--- /dev/null
+--TEST--
+Bug #36214 (__get method works properly only when conditional operator is used)
+--FILE--
+<?php
+class context {
+ public $stack = array();
+
+ public function __set($name,$var) {
+ $this->stack[$name] = $var;return;
+ }
+
+ public function &__get($name) {
+ return $this->stack[$name];
+ }
+}
+
+$ctx = new context;
+$ctx->comment_preview = array();
+$ctx->comment_preview[0] = 1;
+$ctx->comment_preview[1] = 2;
+var_dump($ctx->comment_preview);
+
+$comment_preview = array();
+$comment_preview[0] = 1;
+$comment_preview[1] = 2;
+$ctx->comment_preview = $comment_preview;
+var_dump($ctx->comment_preview);
+
+$ctx->comment_preview = new ArrayObject();
+$ctx->comment_preview[0] = 1;
+$ctx->comment_preview[1] = 2;
+var_dump($ctx->comment_preview);
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+}
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+}
+object(ArrayObject)#2 (2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+}
print "$key => $value\n";
}
?>
---EXPECTF--
-Notice: Indirect modification of overloaded property foo::$bar has no effect in %sbug38146.php on line 10
+--EXPECT--
foo => bar
bar => foo
--- /dev/null
+--TEST--
+Bug #39449 (Overloaded array properties do not work correctly)
+--FILE--
+<?php
+class A {
+ private $keys = array();
+ public function & __get($val) {
+ return $this->keys[$val];
+ }
+ public function __set($k, $v) {
+ $this->keys[$k] = $v;
+ }
+}
+
+$a =new A();
+$a->arr = array('a','b','c');
+
+$b = &$a->arr;
+$b[]= 'd';
+
+foreach ($a->arr as $k => $v) {
+ echo "$k => $v\n";
+}
+
+$a->arr[]='d';
+
+foreach ($a->arr as $k => $v) {
+ echo "$k => $v\n";
+}
+?>
+--EXPECT--
+0 => a
+1 => b
+2 => c
+3 => d
+0 => a
+1 => b
+2 => c
+3 => d
+4 => d
--- /dev/null
+--TEST--
+Bug #39990 (Cannot "foreach" over overloaded properties)
+--FILE--
+<?php
+ class Foo {
+ public function __get($name) {
+ return array('Hello', 'World');
+ }
+ }
+
+ $obj=new Foo();
+ foreach($obj->arr as $value)
+ echo "$value\n";
+?>
+--EXPECT--
+Hello
+World
}
-void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, znode *array, int variable TSRMLS_DC)
+void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, znode *array, znode *as_token, int variable TSRMLS_DC)
{
zend_op *opline;
zend_bool is_variable;
zend_bool push_container = 0;
+ zend_op dummy_opline;
if (variable) {
if (zend_is_function_or_method_call(array)) {
} else {
is_variable = 1;
}
+ /* save the location of FETCH_W instruction(s) */
+ open_brackets_token->u.opline_num = get_next_op_number(CG(active_op_array));
zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC);
if (CG(active_op_array)->last > 0 &&
CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode == ZEND_FETCH_OBJ_W) {
}
} else {
is_variable = 0;
+ open_brackets_token->u.opline_num = get_next_op_number(CG(active_op_array));
}
/* save the location of FE_RESET */
opline->op1 = *array;
SET_UNUSED(opline->op2);
opline->extended_value = is_variable ? ZEND_FE_RESET_VARIABLE : 0;
- *open_brackets_token = opline->result;
- {
- zend_op dummy_opline;
-
- dummy_opline.result = opline->result;
- if (push_container) {
- dummy_opline.op1 = CG(active_op_array)->opcodes[CG(active_op_array)->last-2].op1;
- } else {
- znode tmp;
+ dummy_opline.result = opline->result;
+ if (push_container) {
+ dummy_opline.op1 = CG(active_op_array)->opcodes[CG(active_op_array)->last-2].op1;
+ } else {
+ znode tmp;
- tmp.op_type = IS_UNUSED;
- dummy_opline.op1 = tmp;
- }
- zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op));
+ tmp.op_type = IS_UNUSED;
+ dummy_opline.op1 = tmp;
}
-}
-
-
-void zend_do_foreach_fetch(znode *foreach_token, znode *open_brackets_token, znode *as_token TSRMLS_DC)
-{
- zend_op *opline;
+ zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op));
/* save the location of FE_FETCH */
as_token->u.opline_num = get_next_op_number(CG(active_op_array));
opline->opcode = ZEND_FE_FETCH;
opline->result.op_type = IS_VAR;
opline->result.u.var = get_temporary_variable(CG(active_op_array));
- opline->op1 = *open_brackets_token;
+ opline->op1 = dummy_opline.result;
opline->extended_value = 0;
SET_UNUSED(opline->op2);
}
-void zend_do_foreach_cont(znode *foreach_token, znode *as_token, znode *value, znode *key TSRMLS_DC)
+void zend_do_foreach_cont(znode *foreach_token, znode *open_brackets_token, znode *as_token, znode *value, znode *key TSRMLS_DC)
{
zend_op *opline;
znode dummy, value_node;
/* Mark extended_value for assign-by-reference */
opline->extended_value |= ZEND_FE_FETCH_BYREF;
CG(active_op_array)->opcodes[foreach_token->u.opline_num].extended_value |= ZEND_FE_RESET_REFERENCE;
+ } else {
+ zend_op *foreach_copy;
+ zend_op *fetch = &CG(active_op_array)->opcodes[foreach_token->u.opline_num];
+ zend_op *end = &CG(active_op_array)->opcodes[open_brackets_token->u.opline_num];
+
+ /* Change "write context" into "read context" */
+ fetch->extended_value = 0; /* reset ZEND_FE_RESET_VARIABLE */
+ while (fetch != end) {
+ (--fetch)->opcode -= 3; /* FETCH_W -> FETCH_R */
+ }
+ /* prevent double SWITCH_FREE */
+ zend_stack_top(&CG(foreach_copy_stack), (void **) &foreach_copy);
+ foreach_copy->op1.op_type = IS_UNUSED;
}
value_node = opline->result;
void zend_do_instanceof(znode *result, znode *expr, znode *class_znode, int type TSRMLS_DC);
-void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, znode *array, int variable TSRMLS_DC);
-void zend_do_foreach_fetch(znode *foreach_token, znode *open_brackets_token, znode *as_token TSRMLS_DC);
-void zend_do_foreach_cont(znode *foreach_token, znode *as_token, znode *value, znode *key TSRMLS_DC);
+void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, znode *array, znode *as_token, int variable TSRMLS_DC);
+void zend_do_foreach_cont(znode *foreach_token, znode *open_brackets_token, znode *as_token, znode *value, znode *key TSRMLS_DC);
void zend_do_foreach_end(znode *foreach_token, znode *as_token TSRMLS_DC);
void zend_do_declare_begin(TSRMLS_D);
overloaded_result = Z_OBJ_HT_P(container)->read_dimension(container, dim, type TSRMLS_CC);
if (overloaded_result) {
- if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
+ if (!overloaded_result->is_ref &&
+ (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
if (overloaded_result->refcount > 0) {
zval *tmp = overloaded_result;
zval *str;
zend_uint offset;
} str_offset;
+ struct {
+ zval **ptr_ptr;
+ zval *ptr;
+ zend_bool fcall_returned_reference;
+ HashPointer fe_pos;
+ } fe;
zend_class_entry *class_entry;
} temp_variable;
}
+ZEND_API int zend_hash_get_pointer(HashTable *ht, HashPointer *ptr)
+{
+ ptr->pos = ht->pInternalPointer;
+ if (ht->pInternalPointer) {
+ ptr->h = ht->pInternalPointer->h;
+ return 1;
+ } else {
+ ptr->h = 0;
+ return 0;
+ }
+}
+
+ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
+{
+ if (ht->pInternalPointer != ptr->pos) {
+ Bucket *p;
+
+ IS_CONSISTENT(ht);
+ p = ht->arBuckets[ptr->h & ht->nTableMask];
+ while (p != NULL) {
+ if (p == ptr->pos) {
+ ht->pInternalPointer = p;
+ return 1;
+ }
+ p = p->pNext;
+ }
+ return 0;
+ }
+ return 1;
+}
+
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
{
IS_CONSISTENT(ht);
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos);
ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, zstr str_index, uint str_length, ulong num_index, HashPosition *pos);
+typedef struct _HashPointer {
+ HashPosition pos;
+ ulong h;
+} HashPointer;
+
+ZEND_API int zend_hash_get_pointer(HashTable *ht, HashPointer *ptr);
+ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr);
+
#define zend_hash_has_more_elements(ht) \
zend_hash_has_more_elements_ex(ht, NULL)
#define zend_hash_move_forward(ht) \
| expr ';' { zend_do_free(&$1 TSRMLS_CC); }
| T_USE use_filename ';' { zend_error(E_COMPILE_ERROR,"use: Not yet supported. Please use include_once() or require_once()"); zval_dtor(&$2.u.constant); }
| T_UNSET '(' unset_variables ')' ';'
- | T_FOREACH '(' variable { zend_do_foreach_begin(&$1, &$2, &$3, 1 TSRMLS_CC); } T_AS
- { zend_do_foreach_fetch(&$1, &$2, &$5 TSRMLS_CC); }
- foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$5, &$7, &$8 TSRMLS_CC); }
- foreach_statement { zend_do_foreach_end(&$1, &$5 TSRMLS_CC); }
- | T_FOREACH '(' expr_without_variable { zend_do_foreach_begin(&$1, &$2, &$3, 0 TSRMLS_CC); } T_AS
- { zend_do_foreach_fetch(&$1, &$2, &$5 TSRMLS_CC); }
- variable foreach_optional_arg ')' { zend_check_writable_variable(&$7); zend_do_foreach_cont(&$1, &$5, &$7, &$8 TSRMLS_CC); }
- foreach_statement { zend_do_foreach_end(&$1, &$5 TSRMLS_CC); }
+ | T_FOREACH '(' variable T_AS
+ { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); }
+ foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
+ foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
+ | T_FOREACH '(' expr_without_variable T_AS
+ { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); }
+ variable foreach_optional_arg ')' { zend_check_writable_variable(&$6); zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
+ foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
| T_DECLARE { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); }
| ';' /* empty statement */
| T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}'
if (rv) {
retval = &rv;
- if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
+ if (!rv->is_ref &&
+ (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
if (rv->refcount > 0) {
zval *tmp = rv;
}
}
is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
is_empty = 1;
zend_object *zobj = zend_objects_get_address(array TSRMLS_CC);
fe_ht = HASH_OF(array);
+ zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
do {
if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
/* reached end of iteration */
} while (key_type == HASH_KEY_NON_EXISTANT ||
(key_type != HASH_KEY_IS_LONG &&
zend_check_property_access(zobj, key_type == HASH_KEY_IS_UNICODE?IS_UNICODE:IS_STRING, str_key, str_key_len-1 TSRMLS_CC) != SUCCESS));
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
if (use_key && key_type != HASH_KEY_IS_LONG) {
zend_u_unmangle_property_name(key_type == HASH_KEY_IS_UNICODE?IS_UNICODE:IS_STRING, str_key, str_key_len-1, &class_name, &prop_name);
if (key_type == HASH_KEY_IS_UNICODE) {
case ZEND_ITER_PLAIN_ARRAY:
fe_ht = HASH_OF(array);
+ zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
/* reached end of iteration */
ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.u.opline_num);
key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL);
}
zend_hash_move_forward(fe_ht);
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
break;
case ZEND_ITER_OBJECT:
zval *z = &opline->op1.u.constant;
UErrorCode status = U_ZERO_ERROR;
- /* Convert inline HTML blocks to the output encoding, but only if necessary. */
+ /* UTODO: review this
+ * Convert inline HTML blocks to the output encoding, but only if necessary. */
if (opline->extended_value &&
strcmp(ucnv_getName(ZEND_U_CONVERTER(UG(output_encoding_conv)), &status),
EX(op_array)->script_encoding)) {
}
}
is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
is_empty = 1;
zval *z = _get_zval_ptr_tmp(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
UErrorCode status = U_ZERO_ERROR;
- /* Convert inline HTML blocks to the output encoding, but only if necessary. */
+ /* UTODO: review this
+ * Convert inline HTML blocks to the output encoding, but only if necessary. */
if (opline->extended_value &&
strcmp(ucnv_getName(ZEND_U_CONVERTER(UG(output_encoding_conv)), &status),
EX(op_array)->script_encoding)) {
}
}
is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
is_empty = 1;
zval *z = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
UErrorCode status = U_ZERO_ERROR;
- /* Convert inline HTML blocks to the output encoding, but only if necessary. */
+ /* UTODO: review this
+ * Convert inline HTML blocks to the output encoding, but only if necessary. */
if (opline->extended_value &&
strcmp(ucnv_getName(ZEND_U_CONVERTER(UG(output_encoding_conv)), &status),
EX(op_array)->script_encoding)) {
}
}
is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
is_empty = 1;
zend_object *zobj = zend_objects_get_address(array TSRMLS_CC);
fe_ht = HASH_OF(array);
+ zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
do {
if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
/* reached end of iteration */
} while (key_type == HASH_KEY_NON_EXISTANT ||
(key_type != HASH_KEY_IS_LONG &&
zend_check_property_access(zobj, key_type == HASH_KEY_IS_UNICODE?IS_UNICODE:IS_STRING, str_key, str_key_len-1 TSRMLS_CC) != SUCCESS));
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
if (use_key && key_type != HASH_KEY_IS_LONG) {
zend_u_unmangle_property_name(key_type == HASH_KEY_IS_UNICODE?IS_UNICODE:IS_STRING, str_key, str_key_len-1, &class_name, &prop_name);
if (key_type == HASH_KEY_IS_UNICODE) {
case ZEND_ITER_PLAIN_ARRAY:
fe_ht = HASH_OF(array);
+ zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
/* reached end of iteration */
ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.u.opline_num);
key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL);
}
zend_hash_move_forward(fe_ht);
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
break;
case ZEND_ITER_OBJECT:
zval *z = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
UErrorCode status = U_ZERO_ERROR;
- /* Convert inline HTML blocks to the output encoding, but only if necessary. */
+ /* UTODO: review this
+ * Convert inline HTML blocks to the output encoding, but only if necessary. */
if (opline->extended_value &&
strcmp(ucnv_getName(ZEND_U_CONVERTER(UG(output_encoding_conv)), &status),
EX(op_array)->script_encoding)) {
}
}
is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;
+ zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
is_empty = 1;