Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

- Fixed bug #35106 (nested foreach fails when array variable has a re…

…ference).

- 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).
  • Loading branch information...
commit e470e22e20fa327ac7331c560dc034522ca89aae 1 parent 4e0f199
Dmitry Stogov authored
7 NEWS
View
@@ -13,13 +13,20 @@ PHP NEWS
- Fixed bug #40036 (empty() does not work correctly with ArrayObject when using
ARRAY_AS_PROPS). (Ilia)
- Fixed bug #40002 (Try/Catch performs poorly). (Dmitry)
+- Fixed bug #39990 (Cannot "foreach" over overloaded properties). (Dmitry)
- Fixed bug #39979 (PGSQL_CONNECT_FORCE_NEW will causes next connect to
establish a new connection). (Ilia)
- Fixed bug #39504 (xmlwriter_write_dtd_entity() creates Attlist tag,
not entity). (Hannes)
+- Fixed bug #39449 (Overloaded array properties do not work correctly).
+ (Dmitry)
- Fixed bug #39394 (Missing check for older variants of openssl). (Ilia)
- Fixed bug #38325 (spl_autoload_register() gaves wrong line for "class
not found"). (Ilia)
+- Fixed bug #36214 (__get method works properly only when conditional operator
+ is used). (Dmitry)
+- Fixed bug #35106 (nested foreach fails when array variable has a reference).
+ (Dmitry)
04 Jan 2007, PHP 5.2.1RC2
- Small optimization of the date() function (Matt,Ilia)
14 Zend/tests/bug35106.phpt
View
@@ -0,0 +1,14 @@
+--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
52 Zend/tests/bug36214.phpt
View
@@ -0,0 +1,52 @@
+--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)
+}
3  Zend/tests/bug38146.phpt
View
@@ -14,7 +14,6 @@ foreach($f->bar as $key => $value) {
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
40 Zend/tests/bug39449.phpt
View
@@ -0,0 +1,40 @@
+--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
17 Zend/tests/bug39990.phpt
View
@@ -0,0 +1,17 @@
+--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
50 Zend/zend_compile.c
View
@@ -3677,11 +3677,12 @@ void zend_do_instanceof(znode *result, znode *expr, znode *class_znode, int type
}
-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)) {
@@ -3689,6 +3690,8 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno
} 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) {
@@ -3700,6 +3703,7 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno
}
} else {
is_variable = 0;
+ open_brackets_token->u.opline_num = get_next_op_number(CG(active_op_array));
}
/* save the location of FE_RESET */
@@ -3714,28 +3718,17 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno
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));
@@ -3744,7 +3737,7 @@ void zend_do_foreach_fetch(znode *foreach_token, znode *open_brackets_token, zno
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);
@@ -3756,7 +3749,7 @@ void zend_do_foreach_fetch(znode *foreach_token, znode *open_brackets_token, zno
}
-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;
@@ -3787,6 +3780,19 @@ void zend_do_foreach_cont(znode *foreach_token, znode *as_token, znode *value, z
/* 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;
5 Zend/zend_compile.h
View
@@ -476,9 +476,8 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC
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);
3  Zend/zend_execute.c
View
@@ -1163,7 +1163,8 @@ static void zend_fetch_dimension_address(temp_variable *result, zval **container
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;
6 Zend/zend_execute.h
View
@@ -41,6 +41,12 @@ typedef union _temp_variable {
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;
31 Zend/zend_hash.c
View
@@ -1006,6 +1006,37 @@ ZEND_API int zend_hash_num_elements(HashTable *ht)
}
+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);
8 Zend/zend_hash.h
View
@@ -176,6 +176,14 @@ 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);
ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, char *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) \
16 Zend/zend_language_parser.y
View
@@ -214,14 +214,14 @@ unticked_statement:
| 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 '}'
3  Zend/zend_object_handlers.c
View
@@ -334,7 +334,8 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
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;
5 Zend/zend_vm_def.h
View
@@ -3207,6 +3207,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY)
}
}
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;
@@ -3251,6 +3252,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
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 */
@@ -3262,6 +3264,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
} while (key_type == HASH_KEY_NON_EXISTANT ||
(key_type != HASH_KEY_IS_LONG &&
zend_check_property_access(zobj, 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_unmangle_property_name(str_key, str_key_len-1, &class_name, &prop_name);
str_key_len = strlen(prop_name);
@@ -3273,6 +3276,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
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);
@@ -3281,6 +3285,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
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:
8 Zend/zend_vm_execute.h
View
@@ -2256,6 +2256,7 @@ static int ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}
}
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;
@@ -4796,6 +4797,7 @@ static int ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}
}
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;
@@ -7926,6 +7928,7 @@ static int ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}
}
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;
@@ -7970,6 +7973,7 @@ static int ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
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 */
@@ -7981,6 +7985,7 @@ static int ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
} while (key_type == HASH_KEY_NON_EXISTANT ||
(key_type != HASH_KEY_IS_LONG &&
zend_check_property_access(zobj, 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_unmangle_property_name(str_key, str_key_len-1, &class_name, &prop_name);
str_key_len = strlen(prop_name);
@@ -7992,6 +7997,7 @@ static int ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
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);
@@ -8000,6 +8006,7 @@ static int ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
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:
@@ -19982,6 +19989,7 @@ static int ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}
}
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;
Please sign in to comment.
Something went wrong with that request. Please try again.