- Fixed bug #35106 (nested foreach fails when array variable has a reference).

- 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).
This commit is contained in:
Dmitry Stogov 2007-01-10 15:58:08 +00:00
parent 4e0f1992ed
commit e470e22e20
16 changed files with 231 additions and 37 deletions

7
NEWS
View file

@ -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 Executable file
View file

@ -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 Executable file
View file

@ -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)
}

View file

@ -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 Executable file
View file

@ -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 Executable file
View file

@ -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

View file

@ -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,10 +3718,6 @@ 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) {
@ -3729,13 +3729,6 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno
dummy_opline.op1 = tmp;
}
zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op));
}
}
void zend_do_foreach_fetch(znode *foreach_token, znode *open_brackets_token, znode *as_token TSRMLS_DC)
{
zend_op *opline;
/* 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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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) \

View file

@ -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 '}'

View file

@ -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;

View file

@ -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:

View file

@ -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;