mirror of
https://github.com/php/php-src.git
synced 2024-11-27 03:44:07 +08:00
Fix "forech" statemt behaviour according to https://wiki.php.net/rfc/php7_foreach
Squashed commit of the following: commit1e41295097
Author: Dmitry Stogov <dmitry@zend.com> Date: Sat Jan 31 07:28:58 2015 +0300 Generalize HashTableIterator API to allows its usage without involvement of HashTable.nInternalPonter commit5406f21b11
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 18:08:43 2015 +0300 Reduced alghorithms complexity commitb37f1d58d2
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 18:08:30 2015 +0300 Fixed test name commitfb2d079645
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 18:08:05 2015 +0300 API cleanup commit08302c0d6d
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 14:20:46 2015 +0300 Make array_splice() to preserve foreach hash position commitcc4b7be41e
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 12:24:31 2015 +0300 Make internal function, operation on array passed by reference, to preserve foreach hash position commit5aa9712b0a
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 09:49:35 2015 +0300 Implement consistent behavior for foreach by value over plain object commit4c5b385ff5
Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jan 30 07:56:37 2015 +0300 More careful iterators update. commit721fc9e80d
Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Jan 29 21:43:28 2015 +0300 Added new test commit15a23b1218
Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Jan 29 21:05:02 2015 +0300 Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details) commit10a3260b1f
Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Jan 29 21:04:44 2015 +0300 New test commiteef80c5837
Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Jan 28 16:52:21 2015 +0300 Fixed foreach by reference iteration over constant array commit61e7391873
Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Jan 28 14:59:54 2015 +0300 Fixed temporary variable re-allocation pass commit92e90c09f0
Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Jan 28 12:44:57 2015 +0300 Fixed operand destruction in case of exceptions in iterator commitdd2a36a207
Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Jan 28 10:02:34 2015 +0300 Use GET_OP1_ZVAL_PTR_DEREF() (IS_TMP_VAR and IS_CONST can't be IS_REFERENCE) commit4638f7b914
Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Jan 28 07:43:28 2015 +0300 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:
parent
741b5952c6
commit
97fe15db43
@ -23,4 +23,4 @@ var_dump(key($arr["v"]));
|
||||
int(0)
|
||||
int(0)
|
||||
int(0)
|
||||
NULL
|
||||
int(0)
|
||||
|
@ -23,4 +23,4 @@ int(0)
|
||||
int(0)
|
||||
int(1)
|
||||
int(2)
|
||||
NULL
|
||||
int(0)
|
||||
|
71
Zend/tests/foreach_003.phpt
Normal file
71
Zend/tests/foreach_003.phpt
Normal file
@ -0,0 +1,71 @@
|
||||
--TEST--
|
||||
Iterator exceptions in foreach by value
|
||||
--FILE--
|
||||
<?php
|
||||
class IT implements Iterator {
|
||||
private $n = 0;
|
||||
private $count = 0;
|
||||
private $trap = null;
|
||||
|
||||
function __construct($count, $trap = null) {
|
||||
$this->count = $count;
|
||||
$this->trap = $trap;
|
||||
}
|
||||
|
||||
function trap($trap) {
|
||||
if ($trap === $this->trap) {
|
||||
throw new Exception($trap);
|
||||
}
|
||||
}
|
||||
|
||||
function rewind() {$this->trap(__FUNCTION__); $this->n = 0;}
|
||||
function valid() {$this->trap(__FUNCTION__); return $this->n < $this->count;}
|
||||
function key() {$this->trap(__FUNCTION__); return $this->n;}
|
||||
function current() {$this->trap(__FUNCTION__); return $this->n;}
|
||||
function next() {$this->trap(__FUNCTION__); $this->n++;}
|
||||
}
|
||||
|
||||
foreach(['rewind', 'valid', 'key', 'current', 'next'] as $trap) {
|
||||
$obj = new IT(3, $trap);
|
||||
try {
|
||||
// IS_CV
|
||||
foreach ($obj as $key => $val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
unset($obj);
|
||||
|
||||
try {
|
||||
// IS_VAR
|
||||
foreach (new IT(3, $trap) as $key => $val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
// IS_TMP_VAR
|
||||
foreach ((object)new IT(2, $trap) as $key => $val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
rewind
|
||||
rewind
|
||||
rewind
|
||||
valid
|
||||
valid
|
||||
valid
|
||||
key
|
||||
key
|
||||
key
|
||||
current
|
||||
current
|
||||
current
|
||||
0
|
||||
next
|
||||
0
|
||||
next
|
||||
0
|
||||
next
|
65
Zend/tests/foreach_004.phpt
Normal file
65
Zend/tests/foreach_004.phpt
Normal file
@ -0,0 +1,65 @@
|
||||
--TEST--
|
||||
Iterator exceptions in foreach by reference
|
||||
--FILE--
|
||||
<?php
|
||||
class IT extends ArrayIterator {
|
||||
private $n = 0;
|
||||
|
||||
function __construct($trap = null) {
|
||||
parent::__construct([0, 1]);
|
||||
$this->trap = $trap;
|
||||
}
|
||||
|
||||
function trap($trap) {
|
||||
if ($trap === $this->trap) {
|
||||
throw new Exception($trap);
|
||||
}
|
||||
}
|
||||
|
||||
function rewind() {$this->trap(__FUNCTION__); return parent::rewind();}
|
||||
function valid() {$this->trap(__FUNCTION__); return parent::valid();}
|
||||
function key() {$this->trap(__FUNCTION__); return parent::key();}
|
||||
function next() {$this->trap(__FUNCTION__); return parent::next();}
|
||||
}
|
||||
|
||||
foreach(['rewind', 'valid', 'key', 'next'] as $trap) {
|
||||
$obj = new IT($trap);
|
||||
try {
|
||||
// IS_CV
|
||||
foreach ($obj as $key => &$val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
unset($obj);
|
||||
|
||||
try {
|
||||
// IS_VAR
|
||||
foreach (new IT($trap) as $key => &$val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
// IS_TMP_VAR
|
||||
foreach ((object)new IT($trap) as $key => &$val) echo "$val\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
rewind
|
||||
rewind
|
||||
rewind
|
||||
valid
|
||||
valid
|
||||
valid
|
||||
key
|
||||
key
|
||||
key
|
||||
0
|
||||
next
|
||||
0
|
||||
next
|
||||
0
|
||||
next
|
22
Zend/tests/foreach_005.phpt
Normal file
22
Zend/tests/foreach_005.phpt
Normal file
@ -0,0 +1,22 @@
|
||||
--TEST--
|
||||
Nested foreach by reference on the same array
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3];
|
||||
foreach($a as &$x) {
|
||||
foreach($a as &$y) {
|
||||
echo "$x-$y\n";
|
||||
$y++;
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1-1
|
||||
2-2
|
||||
2-3
|
||||
3-2
|
||||
3-3
|
||||
4-4
|
||||
5-3
|
||||
5-4
|
||||
5-5
|
20
Zend/tests/foreach_006.phpt
Normal file
20
Zend/tests/foreach_006.phpt
Normal file
@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
Foreach by reference on constant
|
||||
--FILE--
|
||||
<?php
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
foreach ([1,2,3] as &$val) {
|
||||
echo "$val\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
13
Zend/tests/foreach_007.phpt
Normal file
13
Zend/tests/foreach_007.phpt
Normal file
@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Foreach by reference and inserting new element when we are already at the end
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
$a[1]=2;
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
21
Zend/tests/foreach_008.phpt
Normal file
21
Zend/tests/foreach_008.phpt
Normal file
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Nested foreach by reference and array modification
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [0, 1, 2, 3];
|
||||
foreach ($a as &$x) {
|
||||
foreach ($a as &$y) {
|
||||
echo "$x - $y\n";
|
||||
if ($x == 0 && $y == 1) {
|
||||
unset($a[2]);
|
||||
unset($a[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
0 - 0
|
||||
0 - 1
|
||||
0 - 3
|
||||
3 - 0
|
||||
3 - 3
|
40
Zend/tests/foreach_009.phpt
Normal file
40
Zend/tests/foreach_009.phpt
Normal file
@ -0,0 +1,40 @@
|
||||
--TEST--
|
||||
Nested foreach by reference and array modification with resize
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
unset($a[0], $a[1], $a[2], $a[3]);
|
||||
foreach ($a as &$ref) {
|
||||
foreach ($a as &$ref2) {
|
||||
echo "$ref-$ref2\n";
|
||||
if ($ref == 5 && $ref2 == 6) {
|
||||
$a[42] = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
4-4
|
||||
4-5
|
||||
4-6
|
||||
4-7
|
||||
5-4
|
||||
5-5
|
||||
5-6
|
||||
5-7
|
||||
5-8
|
||||
6-4
|
||||
6-5
|
||||
6-6
|
||||
6-7
|
||||
6-8
|
||||
7-4
|
||||
7-5
|
||||
7-6
|
||||
7-7
|
||||
7-8
|
||||
8-4
|
||||
8-5
|
||||
8-6
|
||||
8-7
|
||||
8-8
|
40
Zend/tests/foreach_010.phpt
Normal file
40
Zend/tests/foreach_010.phpt
Normal file
@ -0,0 +1,40 @@
|
||||
--TEST--
|
||||
Nested foreach by value over object and object modification with resize
|
||||
--FILE--
|
||||
<?php
|
||||
$o = (object)['a'=>0, 'b'=>1, 'c'=>2, 'd'=>3, 'e'=>4, 'f'=>5, 'g'=>6, 'h'=>7];
|
||||
unset($o->a, $o->b, $o->c, $o->d);
|
||||
foreach ($o as $v1) {
|
||||
foreach ($o as $v2) {
|
||||
echo "$v1-$v2\n";
|
||||
if ($v1 == 5 && $v2 == 6) {
|
||||
$o->i = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
4-4
|
||||
4-5
|
||||
4-6
|
||||
4-7
|
||||
5-4
|
||||
5-5
|
||||
5-6
|
||||
5-7
|
||||
5-8
|
||||
6-4
|
||||
6-5
|
||||
6-6
|
||||
6-7
|
||||
6-8
|
||||
7-4
|
||||
7-5
|
||||
7-6
|
||||
7-7
|
||||
7-8
|
||||
8-4
|
||||
8-5
|
||||
8-6
|
||||
8-7
|
||||
8-8
|
19
Zend/tests/foreach_011.phpt
Normal file
19
Zend/tests/foreach_011.phpt
Normal file
@ -0,0 +1,19 @@
|
||||
--TEST--
|
||||
sort() functions precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3,4,5,0];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($v == 3) {
|
||||
rsort($a);
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
2
|
||||
1
|
||||
0
|
18
Zend/tests/foreach_012.phpt
Normal file
18
Zend/tests/foreach_012.phpt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
array_walk() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3,4,5];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($v == 3) {
|
||||
array_walk($a, function (&$x) {$x+=10;});
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
14
|
||||
15
|
17
Zend/tests/foreach_013.phpt
Normal file
17
Zend/tests/foreach_013.phpt
Normal file
@ -0,0 +1,17 @@
|
||||
--TEST--
|
||||
array_push() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($v == 3) {
|
||||
array_push($a, 4);
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
15
Zend/tests/foreach_014.phpt
Normal file
15
Zend/tests/foreach_014.phpt
Normal file
@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
array_pop() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($v == 2) {
|
||||
array_pop($a);
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
18
Zend/tests/foreach_015.phpt
Normal file
18
Zend/tests/foreach_015.phpt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
array_shift() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
array_shift($a);
|
||||
}
|
||||
var_dump($a);
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
array(0) {
|
||||
}
|
18
Zend/tests/foreach_016.phpt
Normal file
18
Zend/tests/foreach_016.phpt
Normal file
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
array_unshift() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
$a = [1,2,3];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($v == 2) {
|
||||
array_unshift($a, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
var_dump(count($a));
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
int(11)
|
111
Zend/tests/foreach_017.phpt
Normal file
111
Zend/tests/foreach_017.phpt
Normal file
@ -0,0 +1,111 @@
|
||||
--TEST--
|
||||
array_splice() function precerve foreach by reference iterator pointer
|
||||
--FILE--
|
||||
<?php
|
||||
/* remove before */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if (!$done && $v == 3) {
|
||||
$done = 1;
|
||||
array_splice($a, 1, 2);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
/* remove after */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if (!$done && $v == 0) {
|
||||
$done = 1;
|
||||
array_splice($a, 2, 2);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
/* remove current */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if (!$done && $v == 2) {
|
||||
$done = 1;
|
||||
array_splice($a, 1, 3);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
$replacement = ['x', 'y', 'z'];
|
||||
|
||||
/* replace before */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if ($done && $v == 3) {
|
||||
$done = 1;
|
||||
array_splice($a, 1, 2, $replacement);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
/* replace after */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if (!$done && $v == 0) {
|
||||
$done = 1;
|
||||
array_splice($a, 2, 2, $replacement);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
/* replace current */
|
||||
$done = 0;
|
||||
$a = [0,1,2,3,4];
|
||||
foreach($a as &$v) {
|
||||
echo "$v\n";
|
||||
if (!$done && $v == 2) {
|
||||
$done = 1;
|
||||
array_splice($a, 1, 3, $replacement);
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
|
||||
0
|
||||
1
|
||||
4
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
4
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
|
||||
0
|
||||
1
|
||||
x
|
||||
y
|
||||
z
|
||||
4
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
4
|
@ -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);
|
||||
}
|
||||
@ -3438,32 +3438,19 @@ 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);
|
||||
|
||||
reset_node.flag = 1; /* generate FE_FREE */
|
||||
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 */
|
||||
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) {
|
||||
zend_make_tmp_result(&key_node, opline);
|
||||
}
|
||||
@ -3554,6 +3541,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();
|
||||
|
@ -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 */
|
||||
@ -831,9 +832,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
|
||||
|
@ -1628,6 +1628,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_TYPE_P(var) != IS_ARRAY && 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;
|
||||
|
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -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_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(var));
|
||||
}
|
||||
zval_ptr_dtor_nogc(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
202
Zend/zend_hash.c
202
Zend/zend_hash.c
@ -193,6 +193,140 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht, HashPosition pos)
|
||||
{
|
||||
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 = pos;
|
||||
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 = pos;
|
||||
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 _zend_hash_iterators_remove(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 zend_hash_iterators_remove(HashTable *ht)
|
||||
{
|
||||
if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
|
||||
_zend_hash_iterators_remove(ht);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start)
|
||||
{
|
||||
HashTableIterator *iter = EG(ht_iterators);
|
||||
HashTableIterator *end = iter + EG(ht_iterators_used);
|
||||
HashPosition res = INVALID_IDX;
|
||||
uint32_t idx;
|
||||
|
||||
while (iter != end) {
|
||||
if (iter->ht == ht) {
|
||||
if (iter->pos >= start && iter->pos < res) {
|
||||
res = iter->pos;
|
||||
}
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to)
|
||||
{
|
||||
HashTableIterator *iter = EG(ht_iterators);
|
||||
HashTableIterator *end = iter + EG(ht_iterators_used);
|
||||
|
||||
while (iter != end) {
|
||||
if (iter->ht == ht && iter->pos == from) {
|
||||
iter->pos = to;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key)
|
||||
{
|
||||
zend_ulong h;
|
||||
@ -305,6 +439,7 @@ add_to_hash:
|
||||
if (ht->nInternalPointer == INVALID_IDX) {
|
||||
ht->nInternalPointer = idx;
|
||||
}
|
||||
zend_hash_iterators_update(ht, INVALID_IDX, idx);
|
||||
p = ht->arData + idx;
|
||||
p->h = h = zend_string_hash_val(key);
|
||||
p->key = key;
|
||||
@ -472,6 +607,7 @@ add_to_packed:
|
||||
if (ht->nInternalPointer == INVALID_IDX) {
|
||||
ht->nInternalPointer = h;
|
||||
}
|
||||
zend_hash_iterators_update(ht, INVALID_IDX, h);
|
||||
if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
|
||||
ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
|
||||
}
|
||||
@ -514,6 +650,7 @@ add_to_hash:
|
||||
if (ht->nInternalPointer == INVALID_IDX) {
|
||||
ht->nInternalPointer = idx;
|
||||
}
|
||||
zend_hash_iterators_update(ht, INVALID_IDX, idx);
|
||||
if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
|
||||
ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
|
||||
}
|
||||
@ -594,19 +731,42 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
|
||||
}
|
||||
|
||||
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
|
||||
for (i = 0, j = 0; i < ht->nNumUsed; i++) {
|
||||
p = ht->arData + i;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (i != j) {
|
||||
ht->arData[j] = ht->arData[i];
|
||||
if (ht->nInternalPointer == i) {
|
||||
ht->nInternalPointer = j;
|
||||
if (EXPECTED(ht->u.v.nIteratorsCount == 0)) {
|
||||
for (i = 0, j = 0; i < ht->nNumUsed; i++) {
|
||||
p = ht->arData + i;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (i != j) {
|
||||
ht->arData[j] = ht->arData[i];
|
||||
if (ht->nInternalPointer == i) {
|
||||
ht->nInternalPointer = j;
|
||||
}
|
||||
}
|
||||
nIndex = ht->arData[j].h & ht->nTableMask;
|
||||
Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex];
|
||||
ht->arHash[nIndex] = j;
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, 0);
|
||||
|
||||
for (i = 0, j = 0; i < ht->nNumUsed; i++) {
|
||||
p = ht->arData + i;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (i != j) {
|
||||
ht->arData[j] = ht->arData[i];
|
||||
if (ht->nInternalPointer == i) {
|
||||
ht->nInternalPointer = j;
|
||||
}
|
||||
if (i == iter_pos) {
|
||||
zend_hash_iterators_update(ht, i, j);
|
||||
iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1);
|
||||
}
|
||||
}
|
||||
nIndex = ht->arData[j].h & ht->nTableMask;
|
||||
Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex];
|
||||
ht->arHash[nIndex] = j;
|
||||
j++;
|
||||
}
|
||||
nIndex = ht->arData[j].h & ht->nTableMask;
|
||||
Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex];
|
||||
ht->arHash[nIndex] = j;
|
||||
j++;
|
||||
}
|
||||
ht->nNumUsed = j;
|
||||
return SUCCESS;
|
||||
@ -628,17 +788,22 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx,
|
||||
} while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF));
|
||||
}
|
||||
ht->nNumOfElements--;
|
||||
if (ht->nInternalPointer == idx) {
|
||||
if (ht->nInternalPointer == idx || UNEXPECTED(ht->u.v.nIteratorsCount)) {
|
||||
uint32_t new_idx = idx;
|
||||
|
||||
while (1) {
|
||||
idx++;
|
||||
if (idx >= ht->nNumUsed) {
|
||||
ht->nInternalPointer = INVALID_IDX;
|
||||
new_idx++;
|
||||
if (new_idx >= ht->nNumUsed) {
|
||||
new_idx = INVALID_IDX;
|
||||
break;
|
||||
} else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
|
||||
ht->nInternalPointer = idx;
|
||||
} else if (Z_TYPE(ht->arData[new_idx].val) != IS_UNDEF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ht->nInternalPointer == idx) {
|
||||
ht->nInternalPointer = new_idx;
|
||||
}
|
||||
zend_hash_iterators_update(ht, idx, new_idx);
|
||||
}
|
||||
if (p->key) {
|
||||
zend_string_release(p->key);
|
||||
@ -893,6 +1058,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
|
||||
} while (++p != end);
|
||||
}
|
||||
}
|
||||
zend_hash_iterators_remove(ht);
|
||||
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
|
||||
return;
|
||||
}
|
||||
@ -933,7 +1099,7 @@ ZEND_API void zend_array_destroy(HashTable *ht)
|
||||
}
|
||||
} while (++p != end);
|
||||
}
|
||||
|
||||
zend_hash_iterators_remove(ht);
|
||||
SET_INCONSISTENT(HT_DESTROYED);
|
||||
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
|
||||
return;
|
||||
|
@ -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 */
|
||||
@ -171,13 +169,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) \
|
||||
@ -234,6 +225,21 @@ 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, HashPosition pos);
|
||||
ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht);
|
||||
ZEND_API void zend_hash_iterator_del(uint32_t idx);
|
||||
ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start);
|
||||
ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to);
|
||||
|
||||
static zend_always_inline void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to)
|
||||
{
|
||||
if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
|
||||
_zend_hash_iterators_update(ht, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#define ZEND_INIT_SYMTABLE(ht) \
|
||||
|
@ -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:
|
||||
|
@ -128,6 +128,8 @@ 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 */
|
||||
uint32_t fe_iter_idx; /* foreach iterator index */
|
||||
} u2;
|
||||
};
|
||||
|
||||
@ -160,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;
|
||||
@ -178,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;
|
||||
@ -260,6 +270,12 @@ 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_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))
|
||||
|
||||
|
@ -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_TYPE_P(var) != IS_ARRAY && 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
|
||||
@ -3828,6 +3843,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_TYPE_P(var) != IS_ARRAY && 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));
|
||||
}
|
||||
@ -4627,245 +4650,296 @@ 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, *result;
|
||||
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);
|
||||
ZVAL_DEREF(array_ptr);
|
||||
if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
|
||||
SEPARATE_ARRAY(array_ptr);
|
||||
if (!Z_ISREF_P(array_ref)) {
|
||||
array_ptr = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
|
||||
if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
|
||||
result = EX_VAR(opline->result.var);
|
||||
ZVAL_COPY_VALUE(result, array_ptr);
|
||||
if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) {
|
||||
Z_ADDREF_P(array_ptr);
|
||||
}
|
||||
Z_FE_POS_P(result) = 0;
|
||||
|
||||
FREE_OP1_IF_VAR();
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
HashPosition pos = 0;
|
||||
Bucket *p;
|
||||
|
||||
result = EX_VAR(opline->result.var);
|
||||
ZVAL_COPY_VALUE(result, array_ptr);
|
||||
if (OP1_TYPE != IS_TMP_VAR) {
|
||||
Z_ADDREF_P(array_ptr);
|
||||
}
|
||||
fe_ht = Z_OBJPROP_P(array_ptr);
|
||||
pos = 0;
|
||||
while (1) {
|
||||
if (pos >= fe_ht->nNumUsed) {
|
||||
FREE_OP1_IF_VAR();
|
||||
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;
|
||||
if ((Z_TYPE(p->val) != IS_UNDEF &&
|
||||
(Z_TYPE(p->val) != IS_INDIRECT ||
|
||||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) &&
|
||||
(!p->key ||
|
||||
zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
fe_ht->nInternalPointer = pos;
|
||||
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
|
||||
|
||||
FREE_OP1_IF_VAR();
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else {
|
||||
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 (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
|
||||
FREE_OP1();
|
||||
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)) {
|
||||
OBJ_RELEASE(&iter->std);
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
||||
is_empty = iter->funcs->valid(iter) != SUCCESS;
|
||||
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
OBJ_RELEASE(&iter->std);
|
||||
FREE_OP1();
|
||||
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_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
|
||||
|
||||
FREE_OP1();
|
||||
if (is_empty) {
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
|
||||
} else {
|
||||
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;
|
||||
FREE_OP1();
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_free_op free_op1;
|
||||
zval *array_ptr, *array_ref;
|
||||
HashTable *fe_ht;
|
||||
HashPosition pos = 0;
|
||||
Bucket *p;
|
||||
|
||||
SAVE_OPLINE();
|
||||
|
||||
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
|
||||
array_ref = array_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
|
||||
if (Z_ISREF_P(array_ref)) {
|
||||
array_ptr = Z_REFVAL_P(array_ref);
|
||||
}
|
||||
} else {
|
||||
array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
}
|
||||
|
||||
if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
|
||||
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
|
||||
if (array_ptr == 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;
|
||||
Z_ADDREF_P(array_ref);
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
|
||||
} else {
|
||||
if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
|
||||
array_ptr = EX_VAR(opline->result.var);
|
||||
ZVAL_COPY_VALUE(array_ptr, 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 (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)) {
|
||||
FREE_OP1_IF_VAR();
|
||||
}
|
||||
if (iter && EXPECTED(EG(exception) == NULL)) {
|
||||
ZVAL_OBJ(&iterator, &iter->std);
|
||||
array_ptr = array_ref = &iterator;
|
||||
if (OP1_TYPE == IS_CONST) {
|
||||
zval_copy_ctor_func(array_ptr);
|
||||
} 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();
|
||||
SEPARATE_ARRAY(array_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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;
|
||||
|
||||
fe_ht = Z_ARRVAL_P(array_ptr);
|
||||
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();
|
||||
}
|
||||
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;
|
||||
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) {
|
||||
if (Z_TYPE(p->val) != IS_UNDEF &&
|
||||
(Z_TYPE(p->val) != IS_INDIRECT ||
|
||||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) {
|
||||
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;
|
||||
}
|
||||
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
|
||||
|
||||
if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
|
||||
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 (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
array_ptr = Z_REFVAL_P(array_ref);
|
||||
}
|
||||
Z_ADDREF_P(array_ref);
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
|
||||
} else {
|
||||
array_ptr = EX_VAR(opline->result.var);
|
||||
ZVAL_COPY_VALUE(array_ptr, array_ref);
|
||||
}
|
||||
fe_ht = Z_OBJPROP_P(array_ptr);
|
||||
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;
|
||||
if ((Z_TYPE(p->val) != IS_UNDEF &&
|
||||
(Z_TYPE(p->val) != IS_INDIRECT ||
|
||||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) &&
|
||||
(!p->key ||
|
||||
zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
fe_ht->nInternalPointer = pos;
|
||||
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
|
||||
|
||||
FREE_OP1_VAR_PTR();
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else {
|
||||
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))) {
|
||||
if (OP1_TYPE == IS_VAR) {
|
||||
FREE_OP1_VAR_PTR();
|
||||
} else {
|
||||
FREE_OP1();
|
||||
}
|
||||
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)) {
|
||||
OBJ_RELEASE(&iter->std);
|
||||
if (OP1_TYPE == IS_VAR) {
|
||||
FREE_OP1_VAR_PTR();
|
||||
} else {
|
||||
FREE_OP1();
|
||||
}
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
||||
is_empty = iter->funcs->valid(iter) != SUCCESS;
|
||||
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
OBJ_RELEASE(&iter->std);
|
||||
if (OP1_TYPE == IS_VAR) {
|
||||
FREE_OP1_VAR_PTR();
|
||||
} else {
|
||||
FREE_OP1();
|
||||
}
|
||||
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_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
|
||||
|
||||
if (OP1_TYPE == IS_VAR) {
|
||||
FREE_OP1_VAR_PTR();
|
||||
} else {
|
||||
FREE_OP1();
|
||||
}
|
||||
if (is_empty) {
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
|
||||
} else {
|
||||
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 (OP1_TYPE == IS_VAR) {
|
||||
FREE_OP1_VAR_PTR();
|
||||
} else {
|
||||
FREE_OP1();
|
||||
}
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
|
||||
ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_free_op free_op1;
|
||||
zval *array, *array_ref;
|
||||
zval *array;
|
||||
zval *value;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
@ -4873,46 +4947,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);
|
||||
}
|
||||
if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
|
||||
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();
|
||||
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);
|
||||
}
|
||||
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)) {
|
||||
@ -4923,30 +4978,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 = 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 */
|
||||
@ -4955,61 +4987,54 @@ 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);
|
||||
}
|
||||
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]) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!p->key ||
|
||||
zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
do {
|
||||
ZVAL_COPY(EX_VAR(opline->result.var), value);
|
||||
while (1) {
|
||||
pos++;
|
||||
if (pos >= fe_ht->nNumUsed) {
|
||||
fe_ht->nInternalPointer = ptr->pos = INVALID_IDX;
|
||||
ZEND_VM_INC_OPCODE();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
pos = INVALID_IDX;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@ -5019,7 +5044,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();
|
||||
}
|
||||
}
|
||||
@ -5027,32 +5052,26 @@ 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) {
|
||||
ZVAL_COPY(EX_VAR(opline->result.var), 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_ref);
|
||||
zval_ptr_dtor(array);
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
@ -5068,6 +5087,192 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
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 = 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 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
/* !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));
|
||||
}
|
||||
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 {
|
||||
ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
|
||||
}
|
||||
}
|
||||
ZVAL_MAKE_REF(value);
|
||||
Z_ADDREF_P(value);
|
||||
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
|
||||
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(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
|
||||
{
|
||||
USE_OPLINE
|
||||
@ -5847,6 +6052,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_TYPE_P(var) != IS_ARRAY && 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) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,9 +147,9 @@ const char *zend_vm_opcodes_map[170] = {
|
||||
"ZEND_DEFINED",
|
||||
"ZEND_TYPE_CHECK",
|
||||
"ZEND_VERIFY_RETURN_TYPE",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"ZEND_FE_RESET_RW",
|
||||
"ZEND_FE_FETCH_RW",
|
||||
"ZEND_FE_FREE",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
@ -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,9 @@ 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_FE_FREE 127
|
||||
#define ZEND_PRE_INC_OBJ 132
|
||||
#define ZEND_PRE_DEC_OBJ 133
|
||||
#define ZEND_POST_INC_OBJ 134
|
||||
|
@ -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;
|
||||
@ -205,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;
|
||||
@ -225,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];
|
||||
@ -293,11 +299,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 +627,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);
|
||||
|
@ -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:
|
||||
|
@ -66,16 +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 HashPointer on VM stack */
|
||||
if (opline->opcode == ZEND_OP_DATA &&
|
||||
(opline-1)->opcode == ZEND_FE_FETCH &&
|
||||
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;
|
||||
}
|
||||
}
|
||||
opline--;
|
||||
}
|
||||
|
||||
@ -88,25 +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 &&
|
||||
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();
|
||||
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 */
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1877,26 +1877,54 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
|
||||
hash = Z_ARRVAL_P(array);
|
||||
n_left = n_elems;
|
||||
|
||||
if (hash->nNumUsed != hash->nNumOfElements) {
|
||||
for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
|
||||
p = hash->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (j != idx) {
|
||||
hash->arData[j] = *p;
|
||||
if (EXPECTED(hash->u.v.nIteratorsCount == 0)) {
|
||||
if (hash->nNumUsed != hash->nNumOfElements) {
|
||||
for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
|
||||
p = hash->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (j != idx) {
|
||||
hash->arData[j] = *p;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (--n_left) {
|
||||
rnd_idx = php_rand();
|
||||
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
|
||||
if (rnd_idx != n_left) {
|
||||
temp = hash->arData[n_left];
|
||||
hash->arData[n_left] = hash->arData[rnd_idx];
|
||||
hash->arData[rnd_idx] = temp;
|
||||
while (--n_left) {
|
||||
rnd_idx = php_rand();
|
||||
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
|
||||
if (rnd_idx != n_left) {
|
||||
temp = hash->arData[n_left];
|
||||
hash->arData[n_left] = hash->arData[rnd_idx];
|
||||
hash->arData[rnd_idx] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
|
||||
|
||||
if (hash->nNumUsed != hash->nNumOfElements) {
|
||||
for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
|
||||
p = hash->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (j != idx) {
|
||||
hash->arData[j] = *p;
|
||||
if (idx == iter_pos) {
|
||||
zend_hash_iterators_update(hash, idx, j);
|
||||
iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (--n_left) {
|
||||
rnd_idx = php_rand();
|
||||
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
|
||||
if (rnd_idx != n_left) {
|
||||
temp = hash->arData[n_left];
|
||||
hash->arData[n_left] = hash->arData[rnd_idx];
|
||||
hash->arData[rnd_idx] = temp;
|
||||
zend_hash_iterators_update(hash, rnd_idx, n_left);
|
||||
}
|
||||
}
|
||||
}
|
||||
HANDLE_BLOCK_INTERRUPTIONS();
|
||||
hash->nNumUsed = n_elems;
|
||||
hash->nInternalPointer = 0;
|
||||
@ -1941,6 +1969,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
uint idx;
|
||||
Bucket *p; /* Pointer to hash bucket */
|
||||
zval *entry; /* Hash entry */
|
||||
uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
|
||||
|
||||
/* Get number of entries in the input hash */
|
||||
num_in = zend_hash_num_elements(in_hash);
|
||||
@ -1966,7 +1995,6 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
|
||||
p = in_hash->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
pos++;
|
||||
/* Get entry and increase reference count */
|
||||
entry = &p->val;
|
||||
|
||||
@ -1976,6 +2004,13 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
} else {
|
||||
zend_hash_add_new(&out_hash, p->key, entry);
|
||||
}
|
||||
if (idx == iter_pos) {
|
||||
if (idx != pos) {
|
||||
zend_hash_iterators_update(in_hash, idx, pos);
|
||||
}
|
||||
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
/* If hash for removed entries exists, go until offset+length and copy the entries to it */
|
||||
@ -2001,10 +2036,12 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
}
|
||||
}
|
||||
} else { /* otherwise just skip those entries */
|
||||
for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
|
||||
int pos2 = pos;
|
||||
|
||||
for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
|
||||
p = in_hash->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
pos++;
|
||||
pos2++;
|
||||
if (p->key == NULL) {
|
||||
zend_hash_index_del(in_hash, p->h);
|
||||
} else {
|
||||
@ -2016,12 +2053,14 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
}
|
||||
}
|
||||
}
|
||||
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
|
||||
|
||||
/* If there are entries to insert.. */
|
||||
if (replace) {
|
||||
ZEND_HASH_FOREACH_VAL_IND(replace, entry) {
|
||||
if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
|
||||
zend_hash_next_index_insert_new(&out_hash, entry);
|
||||
pos++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
@ -2035,13 +2074,31 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
|
||||
} else {
|
||||
zend_hash_add_new(&out_hash, p->key, entry);
|
||||
}
|
||||
if (idx == iter_pos) {
|
||||
if (idx != pos) {
|
||||
zend_hash_iterators_update(in_hash, idx, pos);
|
||||
}
|
||||
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
zend_hash_internal_pointer_reset(&out_hash);
|
||||
|
||||
/* replace HashTable data */
|
||||
in_hash->u.v.nIteratorsCount = 0;
|
||||
in_hash->pDestructor = NULL;
|
||||
zend_hash_destroy(in_hash);
|
||||
*in_hash = out_hash;
|
||||
|
||||
in_hash->u.v.flags = out_hash.u.v.flags;
|
||||
in_hash->nTableSize = out_hash.nTableSize;
|
||||
in_hash->nTableMask = out_hash.nTableMask;
|
||||
in_hash->nNumUsed = out_hash.nNumUsed;
|
||||
in_hash->nNumOfElements = out_hash.nNumOfElements;
|
||||
in_hash->nNextFreeElement = out_hash.nNextFreeElement;
|
||||
in_hash->arData = out_hash.arData;
|
||||
in_hash->arHash = out_hash.arHash;
|
||||
in_hash->pDestructor = out_hash.pDestructor;
|
||||
|
||||
zend_hash_internal_pointer_reset(in_hash);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -2194,17 +2251,38 @@ PHP_FUNCTION(array_shift)
|
||||
if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) {
|
||||
uint32_t k = 0;
|
||||
|
||||
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
|
||||
p = Z_ARRVAL_P(stack)->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (idx != k) {
|
||||
Bucket *q = Z_ARRVAL_P(stack)->arData + k;
|
||||
q->h = k;
|
||||
q->key = NULL;
|
||||
ZVAL_COPY_VALUE(&q->val, &p->val);
|
||||
ZVAL_UNDEF(&p->val);
|
||||
if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
|
||||
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
|
||||
p = Z_ARRVAL_P(stack)->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (idx != k) {
|
||||
Bucket *q = Z_ARRVAL_P(stack)->arData + k;
|
||||
q->h = k;
|
||||
q->key = NULL;
|
||||
ZVAL_COPY_VALUE(&q->val, &p->val);
|
||||
ZVAL_UNDEF(&p->val);
|
||||
}
|
||||
k++;
|
||||
}
|
||||
} else {
|
||||
uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
|
||||
|
||||
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
|
||||
p = Z_ARRVAL_P(stack)->arData + idx;
|
||||
if (Z_TYPE(p->val) == IS_UNDEF) continue;
|
||||
if (idx != k) {
|
||||
Bucket *q = Z_ARRVAL_P(stack)->arData + k;
|
||||
q->h = k;
|
||||
q->key = NULL;
|
||||
ZVAL_COPY_VALUE(&q->val, &p->val);
|
||||
ZVAL_UNDEF(&p->val);
|
||||
if (idx == iter_pos) {
|
||||
zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
|
||||
iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
Z_ARRVAL_P(stack)->nNumUsed = k;
|
||||
Z_ARRVAL_P(stack)->nNextFreeElement = k;
|
||||
@ -2257,17 +2335,50 @@ PHP_FUNCTION(array_unshift)
|
||||
}
|
||||
zend_hash_next_index_insert_new(&new_hash, &args[i]);
|
||||
}
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
|
||||
if (key) {
|
||||
zend_hash_add_new(&new_hash, key, value);
|
||||
} else {
|
||||
zend_hash_next_index_insert_new(&new_hash, value);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
|
||||
if (key) {
|
||||
zend_hash_add_new(&new_hash, key, value);
|
||||
} else {
|
||||
zend_hash_next_index_insert_new(&new_hash, value);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
} else {
|
||||
uint32_t old_idx;
|
||||
uint32_t new_idx = i;
|
||||
uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
|
||||
if (key) {
|
||||
zend_hash_add_new(&new_hash, key, value);
|
||||
} else {
|
||||
zend_hash_next_index_insert_new(&new_hash, value);
|
||||
}
|
||||
old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData;
|
||||
if (old_idx == iter_pos) {
|
||||
zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx);
|
||||
iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
|
||||
}
|
||||
new_idx++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
/* replace HashTable data */
|
||||
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;
|
||||
|
||||
Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags;
|
||||
Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
|
||||
Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
|
||||
Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
|
||||
Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
|
||||
Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
|
||||
Z_ARRVAL_P(stack)->arData = new_hash.arData;
|
||||
Z_ARRVAL_P(stack)->arHash = new_hash.arHash;
|
||||
Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
|
||||
|
||||
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)));
|
||||
|
@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null)
|
||||
?>
|
||||
--EXPECT--
|
||||
string(3) "one"
|
||||
bool(false)
|
||||
string(3) "one"
|
||||
|
@ -60,5 +60,5 @@ string(1) "f"
|
||||
int(2)
|
||||
string(1) "f"
|
||||
|
||||
bool(false)
|
||||
string(1) "a"
|
||||
bool(false)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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): )---
|
||||
@ -446,30 +463,17 @@ 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=2; $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]=>
|
||||
string(3) "v.1"
|
||||
[3]=>
|
||||
&string(3) "v.1"
|
||||
}
|
||||
|
||||
---( Array with 3 element(s): )---
|
||||
@ -484,32 +488,19 @@ 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=3; $v=v.2
|
||||
--> State of array after loop:
|
||||
array(9) {
|
||||
array(5) {
|
||||
[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"
|
||||
[8]=>
|
||||
string(3) "v.2"
|
||||
[4]=>
|
||||
&string(3) "v.2"
|
||||
}
|
||||
|
||||
---( Array with 4 element(s): )---
|
||||
@ -526,32 +517,19 @@ 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=4; $v=v.3
|
||||
--> State of array after loop:
|
||||
array(10) {
|
||||
array(6) {
|
||||
[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"
|
||||
[8]=>
|
||||
[4]=>
|
||||
string(3) "v.2"
|
||||
[9]=>
|
||||
string(3) "v.3"
|
||||
[5]=>
|
||||
&string(3) "v.3"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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): )---
|
||||
@ -448,30 +465,17 @@ 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=2; $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]=>
|
||||
string(3) "v.1"
|
||||
[3]=>
|
||||
&string(3) "v.1"
|
||||
}
|
||||
|
||||
---( Array with 3 element(s): )---
|
||||
@ -486,32 +490,19 @@ 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=3; $v=v.2
|
||||
--> State of array after loop:
|
||||
array(9) {
|
||||
array(5) {
|
||||
[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"
|
||||
[8]=>
|
||||
string(3) "v.2"
|
||||
[4]=>
|
||||
&string(3) "v.2"
|
||||
}
|
||||
|
||||
---( Array with 4 element(s): )---
|
||||
@ -528,32 +519,19 @@ 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=4; $v=v.3
|
||||
--> State of array after loop:
|
||||
array(10) {
|
||||
array(6) {
|
||||
[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"
|
||||
[8]=>
|
||||
[4]=>
|
||||
string(3) "v.2"
|
||||
[9]=>
|
||||
string(3) "v.3"
|
||||
[5]=>
|
||||
&string(3) "v.3"
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user