]> granicus.if.org Git - php/commitdiff
Fix "forech" statemt behaviour according to https://wiki.php.net/rfc/php7_foreach
authorDmitry Stogov <dmitry@zend.com>
Thu, 12 Feb 2015 10:57:12 +0000 (13:57 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 12 Feb 2015 10:57:12 +0000 (13:57 +0300)
Squashed commit of the following:

commit 1e41295097576dbce6c197ddb7507c07ccae3cbe
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

commit 5406f21b11e563069d64045e599693b51c444b63
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:43 2015 +0300

    Reduced alghorithms complexity

commit b37f1d58d2a141b6e1d980a461ccb588d4317d2e
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:30 2015 +0300

    Fixed test name

commit fb2d079645829b12ed4e55a461034df6400bc430
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:05 2015 +0300

    API cleanup

commit 08302c0d6d1cab279b9f2129df03a057baddf2ff
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 14:20:46 2015 +0300

    Make array_splice() to preserve foreach hash position

commit cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e
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

commit 5aa9712b0a30303aadfe3bdd8ae1f072ca3e6ba1
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

commit 4c5b385ff53ae9f0b52572e98c4db801f56603b0
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 07:56:37 2015 +0300

    More careful iterators update.

commit 721fc9e80d2ee8f2cd79c8c3cdceffae2c72de92
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jan 29 21:43:28 2015 +0300

    Added new test

commit 15a23b1218b3e38630d677751a975907daa2cd54
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)

commit 10a3260b1f16b6075fd8140f673dfef4d5efea91
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jan 29 21:04:44 2015 +0300

    New test

commit eef80c583762d1e98d177cdbb27e3a8a6b0c4539
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 16:52:21 2015 +0300

    Fixed foreach by reference iteration over constant array

commit 61e739187391661e2d541947bec25d7dcc4479f3
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 14:59:54 2015 +0300

    Fixed temporary variable re-allocation pass

commit 92e90c09f085c22707ff4a59201f016f56e0ef8b
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 12:44:57 2015 +0300

    Fixed operand destruction in case of exceptions in iterator

commit dd2a36a2074bbb0cb31de00b66dcf2812d6d753f
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)

commit 4638f7b91407c48710007af82a68da0007c820f2
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)

48 files changed:
Zend/tests/bug40509.phpt
Zend/tests/bug40705.phpt
Zend/tests/foreach_003.phpt [new file with mode: 0644]
Zend/tests/foreach_004.phpt [new file with mode: 0644]
Zend/tests/foreach_005.phpt [new file with mode: 0644]
Zend/tests/foreach_006.phpt [new file with mode: 0644]
Zend/tests/foreach_007.phpt [new file with mode: 0644]
Zend/tests/foreach_008.phpt [new file with mode: 0644]
Zend/tests/foreach_009.phpt [new file with mode: 0644]
Zend/tests/foreach_010.phpt [new file with mode: 0644]
Zend/tests/foreach_011.phpt [new file with mode: 0644]
Zend/tests/foreach_012.phpt [new file with mode: 0644]
Zend/tests/foreach_013.phpt [new file with mode: 0644]
Zend/tests/foreach_014.phpt [new file with mode: 0644]
Zend/tests/foreach_015.phpt [new file with mode: 0644]
Zend/tests/foreach_016.phpt [new file with mode: 0644]
Zend/tests/foreach_017.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_execute_API.c
Zend/zend_generators.c
Zend/zend_globals.h
Zend/zend_hash.c
Zend/zend_hash.h
Zend/zend_opcode.c
Zend/zend_types.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/nop_removal.c
ext/opcache/Optimizer/optimize_temp_vars_5.c
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/pass2.c
ext/opcache/Optimizer/pass3.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/zend_persist.c
ext/standard/array.c
tests/lang/bug23624.phpt
tests/lang/foreachLoop.001.phpt
tests/lang/foreachLoop.009.phpt
tests/lang/foreachLoop.011.phpt
tests/lang/foreachLoop.013.phpt
tests/lang/foreachLoop.014.phpt
tests/lang/foreachLoop.015.phpt
tests/lang/foreachLoopObjects.006.phpt

index 21eaae94448c8c57a686d4484fd3490a1a2f09d1..65e32533ef111ab4e8851d98047a7838cfe8258c 100644 (file)
@@ -23,4 +23,4 @@ var_dump(key($arr["v"]));
 int(0)
 int(0)
 int(0)
-NULL
+int(0)
index 374f73b75eb63a6d6bd29f2ce9ec9ff8f6bf7fdf..8a679654d5413b7cc449f1d4b81a9ca94cc1585e 100644 (file)
@@ -23,4 +23,4 @@ int(0)
 int(0)
 int(1)
 int(2)
-NULL
+int(0)
diff --git a/Zend/tests/foreach_003.phpt b/Zend/tests/foreach_003.phpt
new file mode 100644 (file)
index 0000000..71b0f2a
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_004.phpt b/Zend/tests/foreach_004.phpt
new file mode 100644 (file)
index 0000000..1f754a7
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_005.phpt b/Zend/tests/foreach_005.phpt
new file mode 100644 (file)
index 0000000..6ed9fe9
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_006.phpt b/Zend/tests/foreach_006.phpt
new file mode 100644 (file)
index 0000000..65d6fdc
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_007.phpt b/Zend/tests/foreach_007.phpt
new file mode 100644 (file)
index 0000000..b99bc73
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_008.phpt b/Zend/tests/foreach_008.phpt
new file mode 100644 (file)
index 0000000..c68bcd8
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_009.phpt b/Zend/tests/foreach_009.phpt
new file mode 100644 (file)
index 0000000..6ce8384
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_010.phpt b/Zend/tests/foreach_010.phpt
new file mode 100644 (file)
index 0000000..6ba7e7e
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach_011.phpt
new file mode 100644 (file)
index 0000000..e91426f
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach_012.phpt
new file mode 100644 (file)
index 0000000..5e5538c
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach_013.phpt
new file mode 100644 (file)
index 0000000..cfbb3d7
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach_014.phpt
new file mode 100644 (file)
index 0000000..8d0ac58
--- /dev/null
@@ -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
diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach_015.phpt
new file mode 100644 (file)
index 0000000..adc8085
--- /dev/null
@@ -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) {
+}
\ No newline at end of file
diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach_016.phpt
new file mode 100644 (file)
index 0000000..423c8dd
--- /dev/null
@@ -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)
diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach_017.phpt
new file mode 100644 (file)
index 0000000..55eeeb0
--- /dev/null
@@ -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
index 8ce35c08d523d831f7de8705ec583e31de599b98..1fcc7e1b31c566e6f133e990803a2fce0c909c13 100644 (file)
@@ -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();
index 171fdcfbf29874b141adc0f470457f8962775963..d39f1285f4ed2b30997767fa5868b39f2edb9fcb 100644 (file)
@@ -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
index 62064dcd5f5877fc151424098b9d14dfc7748e59..73c1d4d17cdc8d37d555d16b013d3434a83bb9a8 100644 (file)
@@ -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;
index c9da46d819382e0ab48bcdd20057ba5bebec0c50..c6a0d8f0c9abaa00450f26ebe5f801f195b3e66f 100644 (file)
@@ -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;
 }
 /* }}} */
index f19bb850138b5e119097b9e507bf3b94a053cc61..971d4e7bc8c6ce91cdab8cb6e2cf5d14a18b14f5 100644 (file)
@@ -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);
                                }
                        }
                }
index beca5ad631b332ea12089ad427056553a00a7d1c..abebb19ae39022ffec67c1b48b673f283909cbd0 100644 (file)
@@ -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;
index 480ac64dd16d1d4f5c0fea298246a4fbccb0ef4e..195b5e48f0e1ae32812b85c0931da85821e2f1cc 100644 (file)
@@ -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;
index 377f508658ad5b55a06340b3b17392997689b1a0..602cd7a2e457593613f7a2fe0c0191b90a2f1532 100644 (file)
@@ -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)                                                         \
index a6d041f131209ddd4c4570634a7340c2dad0eca6..c1e3adb3ec06f7c28cba98b7c7d2a931b43efa10 100644 (file)
@@ -766,8 +766,10 @@ ZEND_API int pass_two(zend_op_array *op_array)
                        case ZEND_JMP_SET:
                        case ZEND_COALESCE:
                        case ZEND_NEW:
-                       case ZEND_FE_RESET:
-                       case ZEND_FE_FETCH:
+                       case ZEND_FE_RESET_R:
+                       case ZEND_FE_RESET_RW:
+                       case ZEND_FE_FETCH_R:
+                       case ZEND_FE_FETCH_RW:
                                ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
                                break;
                        case ZEND_VERIFY_RETURN_TYPE:
index 80e17003739f2ea6433875b3f071bc166e2a93cd..d05a6adcb979375eef7c2af1997016bd3a0eacf3 100644 (file)
@@ -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))
 
index f437f430d5f1fef430a275e227d2e592c08428ee..81a9bbb6584ad8e3c41e17ed6e332cc60a1d6445 100644 (file)
@@ -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,292 +4650,505 @@ 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)) {
-                               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) {
+       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);
                        }
-                       array_ref = 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 {
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               }
-       } else {
-               array_ptr = array_ref = GET_OP1_ZVAL_PTR(BP_VAR_R);
-               if (OP1_TYPE & (IS_VAR|IS_CV)) {
-                       ZVAL_DEREF(array_ptr);
-               }
-               if (OP1_TYPE == IS_TMP_VAR) {
-                       ZVAL_COPY_VALUE(&tmp, array_ptr);
-                       if (Z_OPT_IMMUTABLE_P(&tmp)) {
-                               zval_copy_ctor_func(&tmp);
-                       }
-                       array_ref = array_ptr = &tmp;
-                       if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                               ce = Z_OBJCE_P(array_ptr);
-                               if (ce && ce->get_iterator) {
-                                       Z_DELREF_P(array_ref);
+                       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();
                        }
-               } 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);
+
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       FREE_OP1();
+                                       HANDLE_EXCEPTION();
                                }
                        }
-               } else if (Z_IMMUTABLE_P(array_ref)) {
-                       if (OP1_TYPE == IS_CV) {
-                               zval_copy_ctor_func(array_ref);
-                               Z_ADDREF_P(array_ref);
+
+                       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 {
-                               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);
-                               }
+                               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;
 
-       if (ce && ce->get_iterator) {
-               iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
+       SAVE_OPLINE();
 
-               if (OP1_TYPE == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-                       FREE_OP1_IF_VAR();
+       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);
+                       }
+                       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);
                }
-               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) {
+                       SEPARATE_ARRAY(array_ptr);
+               }
+               fe_ht = Z_ARRVAL_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));
                        }
-                       if (!EG(exception)) {
-                               zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
+                       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;
                        }
-                       zend_throw_exception_internal(NULL);
-                       HANDLE_EXCEPTION();
+                       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 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();
+                       }
 
-       ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
+                       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 (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) {
+                               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)) {
-                       zval_ptr_dtor(array_ref);
-                       if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       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();
                        }
-                       HANDLE_EXCEPTION();
+                       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();
                }
-               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;
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+       }
+}
+
+ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *array;
+       zval *value;
+       HashTable *fe_ht;
+       HashPosition pos;
+       Bucket *p;
 
+       array = EX_VAR(opline->op1.var);
+       SAVE_OPLINE();
+       if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
+               fe_ht = Z_ARRVAL_P(array);
+               pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
                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();
-                               }
+                       if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
+                               /* reached end of iteration */
                                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)) {
+                       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 (!ce ||
-                           !p->key ||
-                           zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
-                               break;
+                       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);
                        }
-                       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;
+               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)) {
+               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 */
+                                       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_COPY(EX_VAR(opline->result.var), 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));
+                       }
+                       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);
+                                               HANDLE_EXCEPTION();
+                                       }
+                               } else {
+                                       ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
+                               }
+                       }
+                       ZEND_VM_INC_OPCODE();
+                       ZEND_VM_NEXT_OPCODE();
+               }
        } else {
                zend_error(E_WARNING, "Invalid argument supplied for foreach()");
-               is_empty = 1;
-       }
-
-       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();
        }
 }
 
-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, *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();
 
+       ZVAL_DEREF(array);
        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 = 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 (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));
+                       break;
+               }
+               if (opline->extended_value) {
+                       if (!p->key) {
+                               ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
                        } 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);
-                               }
+                               ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
                        }
-                       break;
                }
-               do {
+               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) {
-                               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));
-               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)) {
+                               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)) {
@@ -4923,93 +5159,66 @@ 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 */
+                                       fe_ht->nInternalPointer = INVALID_IDX;
                                        ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                                }
 
                                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_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) {
-                                       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 +5228,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,38 +5236,34 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
                        if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) {
                                /* reached end of iteration */
                                if (UNEXPECTED(EG(exception) != NULL)) {
-                                       zval_ptr_dtor(array_ref);
+                                       zval_ptr_dtor(array);
                                        HANDLE_EXCEPTION();
                                }
                                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
                        value = iter->funcs->get_current_data(iter);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
+                               zval_ptr_dtor(array);
                                HANDLE_EXCEPTION();
                        }
                        if (!value) {
                                /* failure in get_current_data */
                                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
-                               ZVAL_MAKE_REF(value);
-                               Z_ADDREF_P(value);
-                               ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
-                       } else {
-                               ZVAL_COPY(EX_VAR(opline->result.var), value);
-                       }
-                       if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
+                       if (opline->extended_value) {
                                if (iter->funcs->get_current_key) {
                                        iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var));
                                        if (UNEXPECTED(EG(exception) != NULL)) {
-                                               zval_ptr_dtor(array_ref);
+                                               zval_ptr_dtor(array);
                                                HANDLE_EXCEPTION();
                                        }
                                } else {
                                        ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
                                }
                        }
+                       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();
                }
@@ -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) {
index 3e32191a034e7bf06ce4620bdb515f8d5525d393..0121d65faf170904d71ba4f0af0b7020feecb57e 100644 (file)
@@ -1353,6 +1353,14 @@ static int ZEND_FASTCALL  ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
                                        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) {
@@ -1811,6 +1819,14 @@ static int ZEND_FASTCALL  ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                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));
 }
@@ -3045,194 +3061,273 @@ static int ZEND_FASTCALL  ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HA
        ZEND_VM_NEXT_OPCODE();
 }
 
-static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static int ZEND_FASTCALL  ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
 
-       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 ((IS_CONST == IS_CV || IS_CONST == IS_VAR) &&
-           (opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-               array_ptr = array_ref = NULL;
-               ZVAL_DEREF(array_ptr);
-               if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
-                       SEPARATE_ARRAY(array_ptr);
-                       if (!Z_ISREF_P(array_ref)) {
-                               ZVAL_NEW_REF(array_ref, array_ref);
-                               array_ptr = Z_REFVAL_P(array_ref);
-                       }
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (ce->get_iterator == NULL) {
+       array_ptr = EX_CONSTANT(opline->op1);
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               result = EX_VAR(opline->result.var);
+               ZVAL_COPY_VALUE(result, array_ptr);
+               if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) {
+                       Z_ADDREF_P(array_ptr);
+               }
+               Z_FE_POS_P(result) = 0;
+
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_CONST != 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 (IS_CONST != IS_TMP_VAR) {
                                Z_ADDREF_P(array_ptr);
                        }
-                       array_ref = array_ptr;
-               } else {
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               }
-       } else {
-               array_ptr = array_ref = EX_CONSTANT(opline->op1);
-               if (IS_CONST & (IS_VAR|IS_CV)) {
-                       ZVAL_DEREF(array_ptr);
-               }
-               if (IS_CONST == IS_TMP_VAR) {
-                       ZVAL_COPY_VALUE(&tmp, array_ptr);
-                       if (Z_OPT_IMMUTABLE_P(&tmp)) {
-                               zval_copy_ctor_func(&tmp);
+                       fe_ht = Z_OBJPROP_P(array_ptr);
+                       pos = 0;
+                       while (1) {
+                               if (pos >= fe_ht->nNumUsed) {
+
+                                       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++;
                        }
-                       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);
+                       fe_ht->nInternalPointer = pos;
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
+
+                       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))) {
+
+                               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();
                        }
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (!ce->get_iterator) {
-                               if (IS_CONST == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
+
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+
+                                       HANDLE_EXCEPTION();
                                }
                        }
-               } else if (Z_IMMUTABLE_P(array_ref)) {
-                       if (IS_CONST == IS_CV) {
-                               zval_copy_ctor_func(array_ref);
-                               Z_ADDREF_P(array_ref);
+
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+
+                               HANDLE_EXCEPTION();
+                       }
+                       iter->index = -1; /* will be set to 0 before using next handler */
+
+                       ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        } 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 (IS_CONST == IS_CONST ||
-                                  (IS_CONST == IS_CV &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 1) ||
-                                  (IS_CONST == IS_VAR &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 2)) {
-                               if (IS_CONST == IS_VAR) {
-                                       Z_DELREF_P(array_ref);
-                               }
-                               ZVAL_DUP(&tmp, array_ref);
-                               array_ptr = array_ref = &tmp;
-                       } else if (IS_CONST == IS_CV || IS_CONST == 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 (IS_CONST == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
-                               }
+                               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;
+
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
        }
+}
+
+static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+
+       zval *array_ptr, *array_ref;
+       HashTable *fe_ht;
+       HashPosition pos = 0;
+       Bucket *p;
 
-       if (ce && ce->get_iterator) {
-               iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
+       SAVE_OPLINE();
 
-               if (IS_CONST == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
+       if (IS_CONST == IS_VAR || IS_CONST == IS_CV) {
+               array_ref = array_ptr = NULL;
+               if (Z_ISREF_P(array_ref)) {
+                       array_ptr = Z_REFVAL_P(array_ref);
+               }
+       } else {
+               array_ref = array_ptr = EX_CONSTANT(opline->op1);
+       }
 
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               if (IS_CONST == IS_VAR || IS_CONST == 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);
                }
-               if (iter && EXPECTED(EG(exception) == NULL)) {
-                       ZVAL_OBJ(&iterator, &iter->std);
-                       array_ptr = array_ref = &iterator;
+               if (IS_CONST == IS_CONST) {
+                       zval_copy_ctor_func(array_ptr);
                } else {
-                       if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       SEPARATE_ARRAY(array_ptr);
+               }
+               fe_ht = Z_ARRVAL_P(array_ptr);
+               while (1) {
+                       if (pos >= fe_ht->nNumUsed) {
 
+                               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (!EG(exception)) {
-                               zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
+                       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;
                        }
-                       zend_throw_exception_internal(NULL);
-                       HANDLE_EXCEPTION();
+                       pos++;
                }
-       }
+               fe_ht->nInternalPointer = pos;
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
 
-       ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
+               if (!Z_OBJCE_P(array_ptr)->get_iterator) {
+                       if (IS_CONST == IS_VAR || IS_CONST == 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) {
 
-       if (iter) {
-               iter->index = 0;
-               if (iter->funcs->rewind) {
-                       iter->funcs->rewind(iter);
-                       if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
-                               if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                                       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);
+
+                       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 (IS_CONST == IS_VAR) {
+
+                               } else {
 
                                }
+                               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();
                        }
-               }
-               is_empty = iter->funcs->valid(iter) != SUCCESS;
-               if (UNEXPECTED(EG(exception) != NULL)) {
-                       zval_ptr_dtor(array_ref);
-                       if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
 
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       if (IS_CONST == IS_VAR) {
+
+                                       } else {
+
+                                       }
+                                       HANDLE_EXCEPTION();
+                               }
                        }
-                       HANDLE_EXCEPTION();
-               }
-               iter->index = -1; /* will be set to 0 before using next handler */
-       } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
-               HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var);
-               HashPosition pos = 0;
-               Bucket *p;
 
-               while (1) {
-                       if (pos >= fe_ht->nNumUsed) {
-                               is_empty = 1;
-                               if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+                               if (IS_CONST == IS_VAR) {
+
+                               } else {
 
                                }
-                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                               HANDLE_EXCEPTION();
                        }
-                       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;
+                       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 (IS_CONST == IS_VAR) {
+
+                       } else {
+
                        }
-                       if (!ce ||
-                           !p->key ||
-                           zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
-                               break;
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                       } else {
+                               CHECK_EXCEPTION();
+                               ZEND_VM_NEXT_OPCODE();
                        }
-                       pos++;
                }
-               fe_ht->nInternalPointer = pos;
-               ptr->pos = pos;
-               ptr->ht = fe_ht;
-               ptr->h = fe_ht->arData[pos].h;
-               ptr->key = fe_ht->arData[pos].key;
-               is_empty = 0;
        } else {
                zend_error(E_WARNING, "Invalid argument supplied for foreach()");
-               is_empty = 1;
-       }
+               ZVAL_UNDEF(EX_VAR(opline->result.var));
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+               if (IS_CONST == IS_VAR) {
 
-       if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+               } else {
 
-       }
-       if (is_empty) {
+               }
                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-       } else {
-               CHECK_EXCEPTION();
-               ZEND_VM_NEXT_OPCODE();
        }
 }
 
@@ -8964,194 +9059,274 @@ static int ZEND_FASTCALL  ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
        ZEND_VM_NEXT_OPCODE();
 }
 
-static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static int ZEND_FASTCALL  ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        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 ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) &&
-           (opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-               array_ptr = array_ref = NULL;
-               ZVAL_DEREF(array_ptr);
-               if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
-                       SEPARATE_ARRAY(array_ptr);
-                       if (!Z_ISREF_P(array_ref)) {
-                               ZVAL_NEW_REF(array_ref, array_ref);
-                               array_ptr = Z_REFVAL_P(array_ref);
-                       }
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (ce->get_iterator == NULL) {
+       array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               result = EX_VAR(opline->result.var);
+               ZVAL_COPY_VALUE(result, array_ptr);
+               if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) {
+                       Z_ADDREF_P(array_ptr);
+               }
+               Z_FE_POS_P(result) = 0;
+
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_TMP_VAR != 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 (IS_TMP_VAR != IS_TMP_VAR) {
                                Z_ADDREF_P(array_ptr);
                        }
-                       array_ref = array_ptr;
-               } else {
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               }
-       } else {
-               array_ptr = array_ref = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
-               if (IS_TMP_VAR & (IS_VAR|IS_CV)) {
-                       ZVAL_DEREF(array_ptr);
-               }
-               if (IS_TMP_VAR == IS_TMP_VAR) {
-                       ZVAL_COPY_VALUE(&tmp, array_ptr);
-                       if (Z_OPT_IMMUTABLE_P(&tmp)) {
-                               zval_copy_ctor_func(&tmp);
+                       fe_ht = Z_OBJPROP_P(array_ptr);
+                       pos = 0;
+                       while (1) {
+                               if (pos >= fe_ht->nNumUsed) {
+
+                                       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++;
                        }
-                       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);
+                       fe_ht->nInternalPointer = pos;
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
+
+                       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))) {
+                               zval_ptr_dtor_nogc(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();
                        }
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (!ce->get_iterator) {
-                               if (IS_TMP_VAR == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
+
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       zval_ptr_dtor_nogc(free_op1);
+                                       HANDLE_EXCEPTION();
                                }
                        }
-               } else if (Z_IMMUTABLE_P(array_ref)) {
-                       if (IS_TMP_VAR == IS_CV) {
-                               zval_copy_ctor_func(array_ref);
-                               Z_ADDREF_P(array_ref);
+
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+                               zval_ptr_dtor_nogc(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;
+
+                       zval_ptr_dtor_nogc(free_op1);
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        } 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 (IS_TMP_VAR == IS_CONST ||
-                                  (IS_TMP_VAR == IS_CV &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 1) ||
-                                  (IS_TMP_VAR == IS_VAR &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 2)) {
-                               if (IS_TMP_VAR == IS_VAR) {
-                                       Z_DELREF_P(array_ref);
-                               }
-                               ZVAL_DUP(&tmp, array_ref);
-                               array_ptr = array_ref = &tmp;
-                       } else if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == 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 (IS_TMP_VAR == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
-                               }
+                               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;
+               zval_ptr_dtor_nogc(free_op1);
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
        }
+}
+
+static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *array_ptr, *array_ref;
+       HashTable *fe_ht;
+       HashPosition pos = 0;
+       Bucket *p;
 
-       if (ce && ce->get_iterator) {
-               iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
+       SAVE_OPLINE();
 
-               if (IS_TMP_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
+       if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) {
+               array_ref = array_ptr = NULL;
+               if (Z_ISREF_P(array_ref)) {
+                       array_ptr = Z_REFVAL_P(array_ref);
+               }
+       } else {
+               array_ref = array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+       }
 
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == 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);
                }
-               if (iter && EXPECTED(EG(exception) == NULL)) {
-                       ZVAL_OBJ(&iterator, &iter->std);
-                       array_ptr = array_ref = &iterator;
+               if (IS_TMP_VAR == IS_CONST) {
+                       zval_copy_ctor_func(array_ptr);
                } else {
-                       if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       SEPARATE_ARRAY(array_ptr);
+               }
+               fe_ht = Z_ARRVAL_P(array_ptr);
+               while (1) {
+                       if (pos >= fe_ht->nNumUsed) {
 
+                               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (!EG(exception)) {
-                               zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
+                       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;
                        }
-                       zend_throw_exception_internal(NULL);
-                       HANDLE_EXCEPTION();
+                       pos++;
                }
-       }
+               fe_ht->nInternalPointer = pos;
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
 
-       ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
+               if (!Z_OBJCE_P(array_ptr)->get_iterator) {
+                       if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == 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) {
 
-       if (iter) {
-               iter->index = 0;
-               if (iter->funcs->rewind) {
-                       iter->funcs->rewind(iter);
-                       if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
-                               if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                                       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);
+
+                       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 (IS_TMP_VAR == IS_VAR) {
 
+                               } else {
+                                       zval_ptr_dtor_nogc(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();
                        }
-               }
-               is_empty = iter->funcs->valid(iter) != SUCCESS;
-               if (UNEXPECTED(EG(exception) != NULL)) {
-                       zval_ptr_dtor(array_ref);
-                       if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
 
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       if (IS_TMP_VAR == IS_VAR) {
+
+                                       } else {
+                                               zval_ptr_dtor_nogc(free_op1);
+                                       }
+                                       HANDLE_EXCEPTION();
+                               }
                        }
-                       HANDLE_EXCEPTION();
-               }
-               iter->index = -1; /* will be set to 0 before using next handler */
-       } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
-               HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var);
-               HashPosition pos = 0;
-               Bucket *p;
 
-               while (1) {
-                       if (pos >= fe_ht->nNumUsed) {
-                               is_empty = 1;
-                               if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
 
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+                               if (IS_TMP_VAR == IS_VAR) {
+
+                               } else {
+                                       zval_ptr_dtor_nogc(free_op1);
                                }
-                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                               HANDLE_EXCEPTION();
                        }
-                       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;
+                       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 (IS_TMP_VAR == IS_VAR) {
+
+                       } else {
+                               zval_ptr_dtor_nogc(free_op1);
                        }
-                       if (!ce ||
-                           !p->key ||
-                           zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
-                               break;
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                       } else {
+                               CHECK_EXCEPTION();
+                               ZEND_VM_NEXT_OPCODE();
                        }
-                       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;
-       }
-
-       if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+               ZVAL_UNDEF(EX_VAR(opline->result.var));
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+               if (IS_TMP_VAR == IS_VAR) {
 
-       }
-       if (is_empty) {
+               } else {
+                       zval_ptr_dtor_nogc(free_op1);
+               }
                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-       } else {
-               CHECK_EXCEPTION();
-               ZEND_VM_NEXT_OPCODE();
        }
 }
 
@@ -11784,292 +11959,505 @@ static int ZEND_FASTCALL  ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
        ZEND_VM_NEXT_OPCODE();
 }
 
-static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static int ZEND_FASTCALL  ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        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 ((IS_VAR == IS_CV || IS_VAR == IS_VAR) &&
-           (opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-               array_ptr = array_ref = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
-               ZVAL_DEREF(array_ptr);
-               if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
-                       SEPARATE_ARRAY(array_ptr);
-                       if (!Z_ISREF_P(array_ref)) {
-                               ZVAL_NEW_REF(array_ref, array_ref);
-                               array_ptr = Z_REFVAL_P(array_ref);
-                       }
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (ce->get_iterator == NULL) {
+       array_ptr = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1);
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               result = EX_VAR(opline->result.var);
+               ZVAL_COPY_VALUE(result, array_ptr);
+               if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) {
+                       Z_ADDREF_P(array_ptr);
+               }
+               Z_FE_POS_P(result) = 0;
+
+               zval_ptr_dtor_nogc(free_op1);
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_VAR != 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 (IS_VAR != IS_TMP_VAR) {
                                Z_ADDREF_P(array_ptr);
                        }
-                       array_ref = array_ptr;
-               } else {
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               }
-       } else {
-               array_ptr = array_ref = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
-               if (IS_VAR & (IS_VAR|IS_CV)) {
-                       ZVAL_DEREF(array_ptr);
-               }
-               if (IS_VAR == IS_TMP_VAR) {
-                       ZVAL_COPY_VALUE(&tmp, array_ptr);
-                       if (Z_OPT_IMMUTABLE_P(&tmp)) {
-                               zval_copy_ctor_func(&tmp);
+                       fe_ht = Z_OBJPROP_P(array_ptr);
+                       pos = 0;
+                       while (1) {
+                               if (pos >= fe_ht->nNumUsed) {
+                                       zval_ptr_dtor_nogc(free_op1);
+                                       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++;
                        }
-                       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);
+                       fe_ht->nInternalPointer = pos;
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
+
+                       zval_ptr_dtor_nogc(free_op1);
+                       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))) {
+                               zval_ptr_dtor_nogc(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();
                        }
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (!ce->get_iterator) {
-                               if (IS_VAR == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
+
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       zval_ptr_dtor_nogc(free_op1);
+                                       HANDLE_EXCEPTION();
                                }
                        }
-               } else if (Z_IMMUTABLE_P(array_ref)) {
-                       if (IS_VAR == IS_CV) {
-                               zval_copy_ctor_func(array_ref);
-                               Z_ADDREF_P(array_ref);
+
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+                               zval_ptr_dtor_nogc(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;
+
+                       zval_ptr_dtor_nogc(free_op1);
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        } 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 (IS_VAR == IS_CONST ||
-                                  (IS_VAR == IS_CV &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 1) ||
-                                  (IS_VAR == IS_VAR &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 2)) {
-                               if (IS_VAR == IS_VAR) {
-                                       Z_DELREF_P(array_ref);
-                               }
-                               ZVAL_DUP(&tmp, array_ref);
-                               array_ptr = array_ref = &tmp;
-                       } else if (IS_VAR == IS_CV || IS_VAR == 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 (IS_VAR == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
-                               }
+                               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;
+               zval_ptr_dtor_nogc(free_op1);
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
        }
+}
+
+static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *array_ptr, *array_ref;
+       HashTable *fe_ht;
+       HashPosition pos = 0;
+       Bucket *p;
 
-       if (ce && ce->get_iterator) {
-               iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
+       SAVE_OPLINE();
 
-               if (IS_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-                       zval_ptr_dtor_nogc(free_op1);
+       if (IS_VAR == IS_VAR || IS_VAR == IS_CV) {
+               array_ref = array_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+               if (Z_ISREF_P(array_ref)) {
+                       array_ptr = Z_REFVAL_P(array_ref);
+               }
+       } else {
+               array_ref = array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+       }
+
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               if (IS_VAR == IS_VAR || IS_VAR == 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);
                }
-               if (iter && EXPECTED(EG(exception) == NULL)) {
-                       ZVAL_OBJ(&iterator, &iter->std);
-                       array_ptr = array_ref = &iterator;
+               if (IS_VAR == IS_CONST) {
+                       zval_copy_ctor_func(array_ptr);
                } else {
-                       if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       SEPARATE_ARRAY(array_ptr);
+               }
+               fe_ht = Z_ARRVAL_P(array_ptr);
+               while (1) {
+                       if (pos >= fe_ht->nNumUsed) {
                                if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (!EG(exception)) {
-                               zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
+                       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;
                        }
-                       zend_throw_exception_internal(NULL);
-                       HANDLE_EXCEPTION();
+                       pos++;
                }
-       }
+               fe_ht->nInternalPointer = pos;
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
 
-       ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
+               if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
+               if (!Z_OBJCE_P(array_ptr)->get_iterator) {
+                       if (IS_VAR == IS_VAR || IS_VAR == 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) {
+                                       if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                                       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);
+
+                       if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                       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 (IS_VAR == IS_VAR) {
+                                       if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                               } else {
+                                       zval_ptr_dtor_nogc(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 (IS_VAR == IS_VAR) {
+                                               if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                                       } else {
+                                               zval_ptr_dtor_nogc(free_op1);
+                                       }
+                                       HANDLE_EXCEPTION();
+                               }
+                       }
+
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
 
-       if (iter) {
-               iter->index = 0;
-               if (iter->funcs->rewind) {
-                       iter->funcs->rewind(iter);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
-                               if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                               OBJ_RELEASE(&iter->std);
+                               if (IS_VAR == IS_VAR) {
                                        if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                               } else {
+                                       zval_ptr_dtor_nogc(free_op1);
                                }
                                HANDLE_EXCEPTION();
                        }
-               }
-               is_empty = iter->funcs->valid(iter) != SUCCESS;
-               if (UNEXPECTED(EG(exception) != NULL)) {
-                       zval_ptr_dtor(array_ref);
-                       if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       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 (IS_VAR == IS_VAR) {
                                if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+                       } else {
+                               zval_ptr_dtor_nogc(free_op1);
+                       }
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                       } else {
+                               CHECK_EXCEPTION();
+                               ZEND_VM_NEXT_OPCODE();
                        }
-                       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;
+       } 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 (IS_VAR == IS_VAR) {
+                       if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+               } else {
+                       zval_ptr_dtor_nogc(free_op1);
+               }
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+       }
+}
 
+static int ZEND_FASTCALL  ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+
+       zval *array;
+       zval *value;
+       HashTable *fe_ht;
+       HashPosition pos;
+       Bucket *p;
+
+       array = EX_VAR(opline->op1.var);
+       SAVE_OPLINE();
+       if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
+               fe_ht = Z_ARRVAL_P(array);
+               pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
                while (1) {
-                       if (pos >= fe_ht->nNumUsed) {
-                               is_empty = 1;
-                               if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
-                                       if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
-                               }
+                       if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
+                               /* reached end of iteration */
                                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)) {
+                       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 (!ce ||
-                           !p->key ||
-                           zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
-                               break;
+                       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);
                        }
-                       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;
+               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)) {
+               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 */
+                                       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_COPY(EX_VAR(opline->result.var), 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));
+                       }
+                       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);
+                                               HANDLE_EXCEPTION();
+                                       }
+                               } else {
+                                       ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
+                               }
+                       }
+                       ZEND_VM_INC_OPCODE();
+                       ZEND_VM_NEXT_OPCODE();
+               }
        } else {
                zend_error(E_WARNING, "Invalid argument supplied for foreach()");
-               is_empty = 1;
-       }
-
-       if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
-               if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
-       }
-       if (is_empty) {
                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-       } else {
-               CHECK_EXCEPTION();
-               ZEND_VM_NEXT_OPCODE();
        }
 }
 
-static int ZEND_FASTCALL  ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static int ZEND_FASTCALL  ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
 
-       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();
 
+       ZVAL_DEREF(array);
        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 = 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 (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));
+                       break;
+               }
+               if (opline->extended_value) {
+                       if (!p->key) {
+                               ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h);
                        } else {
-                               ZVAL_COPY(EX_VAR(opline->result.var), value);
+                               ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key);
                        }
-                       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 {
+               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) {
-                               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));
-               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)) {
+                               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)) {
@@ -12080,93 +12468,66 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
                        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 */
+                                       fe_ht->nInternalPointer = INVALID_IDX;
                                        ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                                }
 
                                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_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) {
-                                       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 {
@@ -12176,7 +12537,7 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
                                 * 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();
                                }
                        }
@@ -12184,38 +12545,34 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
                        if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) {
                                /* reached end of iteration */
                                if (UNEXPECTED(EG(exception) != NULL)) {
-                                       zval_ptr_dtor(array_ref);
+                                       zval_ptr_dtor(array);
                                        HANDLE_EXCEPTION();
                                }
                                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
                        value = iter->funcs->get_current_data(iter);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
+                               zval_ptr_dtor(array);
                                HANDLE_EXCEPTION();
                        }
                        if (!value) {
                                /* failure in get_current_data */
                                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
-                               ZVAL_MAKE_REF(value);
-                               Z_ADDREF_P(value);
-                               ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
-                       } else {
-                               ZVAL_COPY(EX_VAR(opline->result.var), value);
-                       }
-                       if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) {
+                       if (opline->extended_value) {
                                if (iter->funcs->get_current_key) {
                                        iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var));
                                        if (UNEXPECTED(EG(exception) != NULL)) {
-                                               zval_ptr_dtor(array_ref);
+                                               zval_ptr_dtor(array);
                                                HANDLE_EXCEPTION();
                                        }
                                } else {
                                        ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index);
                                }
                        }
+                       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();
                }
@@ -24074,194 +24431,273 @@ static int ZEND_FASTCALL  ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL
        ZEND_VM_NEXT_OPCODE();
 }
 
-static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static int ZEND_FASTCALL  ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
 
-       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 ((IS_CV == IS_CV || IS_CV == IS_VAR) &&
-           (opline->extended_value & ZEND_FE_FETCH_BYREF)) {
-               array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
-               ZVAL_DEREF(array_ptr);
-               if (Z_TYPE_P(array_ptr) == IS_ARRAY) {
-                       SEPARATE_ARRAY(array_ptr);
-                       if (!Z_ISREF_P(array_ref)) {
-                               ZVAL_NEW_REF(array_ref, array_ref);
-                               array_ptr = Z_REFVAL_P(array_ref);
-                       }
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (ce->get_iterator == NULL) {
+       array_ptr = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var);
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               result = EX_VAR(opline->result.var);
+               ZVAL_COPY_VALUE(result, array_ptr);
+               if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) {
+                       Z_ADDREF_P(array_ptr);
+               }
+               Z_FE_POS_P(result) = 0;
+
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_CV != 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 (IS_CV != IS_TMP_VAR) {
                                Z_ADDREF_P(array_ptr);
                        }
-                       array_ref = array_ptr;
-               } else {
-                       if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
-               }
-       } else {
-               array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
-               if (IS_CV & (IS_VAR|IS_CV)) {
-                       ZVAL_DEREF(array_ptr);
-               }
-               if (IS_CV == IS_TMP_VAR) {
-                       ZVAL_COPY_VALUE(&tmp, array_ptr);
-                       if (Z_OPT_IMMUTABLE_P(&tmp)) {
-                               zval_copy_ctor_func(&tmp);
+                       fe_ht = Z_OBJPROP_P(array_ptr);
+                       pos = 0;
+                       while (1) {
+                               if (pos >= fe_ht->nNumUsed) {
+
+                                       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++;
                        }
-                       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);
+                       fe_ht->nInternalPointer = pos;
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
+
+                       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))) {
+
+                               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();
                        }
-               } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
-                       ce = Z_OBJCE_P(array_ptr);
-                       if (!ce->get_iterator) {
-                               if (IS_CV == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
+
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+
+                                       HANDLE_EXCEPTION();
                                }
                        }
-               } else if (Z_IMMUTABLE_P(array_ref)) {
-                       if (IS_CV == IS_CV) {
-                               zval_copy_ctor_func(array_ref);
-                               Z_ADDREF_P(array_ref);
+
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+
+                               HANDLE_EXCEPTION();
+                       }
+                       iter->index = -1; /* will be set to 0 before using next handler */
+
+                       ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
+                       Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        } 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 (IS_CV == IS_CONST ||
-                                  (IS_CV == IS_CV &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 1) ||
-                                  (IS_CV == IS_VAR &&
-                                   !Z_ISREF_P(array_ref) &&
-                                   Z_REFCOUNT_P(array_ref) > 2)) {
-                               if (IS_CV == IS_VAR) {
-                                       Z_DELREF_P(array_ref);
-                               }
-                               ZVAL_DUP(&tmp, array_ref);
-                               array_ptr = array_ref = &tmp;
-                       } else if (IS_CV == IS_CV || IS_CV == 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 (IS_CV == IS_CV) {
-                                       Z_ADDREF_P(array_ref);
-                               }
+                               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;
+
+               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
        }
+}
 
-       if (ce && ce->get_iterator) {
-               iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
+static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+
+       zval *array_ptr, *array_ref;
+       HashTable *fe_ht;
+       HashPosition pos = 0;
+       Bucket *p;
 
-               if (IS_CV == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
+       SAVE_OPLINE();
 
+       if (IS_CV == IS_VAR || IS_CV == IS_CV) {
+               array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+               if (Z_ISREF_P(array_ref)) {
+                       array_ptr = Z_REFVAL_P(array_ref);
                }
-               if (iter && EXPECTED(EG(exception) == NULL)) {
-                       ZVAL_OBJ(&iterator, &iter->std);
-                       array_ptr = array_ref = &iterator;
+       } else {
+               array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+       }
+
+       if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
+               if (IS_CV == IS_VAR || IS_CV == 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 {
-                       if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       array_ptr = EX_VAR(opline->result.var);
+                       ZVAL_COPY_VALUE(array_ptr, array_ref);
+               }
+               if (IS_CV == IS_CONST) {
+                       zval_copy_ctor_func(array_ptr);
+               } else {
+                       SEPARATE_ARRAY(array_ptr);
+               }
+               fe_ht = Z_ARRVAL_P(array_ptr);
+               while (1) {
+                       if (pos >= fe_ht->nNumUsed) {
 
+                               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
                        }
-                       if (!EG(exception)) {
-                               zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val);
+                       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;
                        }
-                       zend_throw_exception_internal(NULL);
-                       HANDLE_EXCEPTION();
+                       pos++;
                }
-       }
+               fe_ht->nInternalPointer = pos;
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
 
-       ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
+               CHECK_EXCEPTION();
+               ZEND_VM_NEXT_OPCODE();
+       } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
+               if (!Z_OBJCE_P(array_ptr)->get_iterator) {
+                       if (IS_CV == IS_VAR || IS_CV == 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) {
 
-       if (iter) {
-               iter->index = 0;
-               if (iter->funcs->rewind) {
-                       iter->funcs->rewind(iter);
-                       if (UNEXPECTED(EG(exception) != NULL)) {
-                               zval_ptr_dtor(array_ref);
-                               if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                                       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);
+
+                       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 (IS_CV == IS_VAR) {
+
+                               } else {
 
                                }
+                               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();
                        }
-               }
-               is_empty = iter->funcs->valid(iter) != SUCCESS;
-               if (UNEXPECTED(EG(exception) != NULL)) {
-                       zval_ptr_dtor(array_ref);
-                       if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
 
+                       iter->index = 0;
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       OBJ_RELEASE(&iter->std);
+                                       if (IS_CV == IS_VAR) {
+
+                                       } else {
+
+                                       }
+                                       HANDLE_EXCEPTION();
+                               }
                        }
-                       HANDLE_EXCEPTION();
-               }
-               iter->index = -1; /* will be set to 0 before using next handler */
-       } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
-               HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var);
-               HashPosition pos = 0;
-               Bucket *p;
 
-               while (1) {
-                       if (pos >= fe_ht->nNumUsed) {
-                               is_empty = 1;
-                               if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+                       is_empty = iter->funcs->valid(iter) != SUCCESS;
+
+                       if (UNEXPECTED(EG(exception) != NULL)) {
+                               OBJ_RELEASE(&iter->std);
+                               if (IS_CV == IS_VAR) {
+
+                               } else {
 
                                }
-                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                               HANDLE_EXCEPTION();
                        }
-                       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;
+                       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 (IS_CV == IS_VAR) {
+
+                       } else {
+
                        }
-                       if (!ce ||
-                           !p->key ||
-                           zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
-                               break;
+                       if (is_empty) {
+                               ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+                       } else {
+                               CHECK_EXCEPTION();
+                               ZEND_VM_NEXT_OPCODE();
                        }
-                       pos++;
                }
-               fe_ht->nInternalPointer = pos;
-               ptr->pos = pos;
-               ptr->ht = fe_ht;
-               ptr->h = fe_ht->arData[pos].h;
-               ptr->key = fe_ht->arData[pos].key;
-               is_empty = 0;
        } else {
                zend_error(E_WARNING, "Invalid argument supplied for foreach()");
-               is_empty = 1;
-       }
+               ZVAL_UNDEF(EX_VAR(opline->result.var));
+               Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
+               if (IS_CV == IS_VAR) {
 
-       if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) {
+               } else {
 
-       }
-       if (is_empty) {
+               }
                ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-       } else {
-               CHECK_EXCEPTION();
-               ZEND_VM_NEXT_OPCODE();
        }
 }
 
@@ -32986,6 +33422,21 @@ static int ZEND_FASTCALL  ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS
        ZEND_VM_NEXT_OPCODE();
 }
 
+static int ZEND_FASTCALL  ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       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();
+}
+
 static int ZEND_FASTCALL  ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -38306,31 +38757,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER,
-       ZEND_FE_RESET_SPEC_CONST_HANDLER,
-       ZEND_FE_RESET_SPEC_CONST_HANDLER,
-       ZEND_FE_RESET_SPEC_CONST_HANDLER,
-       ZEND_FE_RESET_SPEC_CONST_HANDLER,
-       ZEND_FE_RESET_SPEC_CONST_HANDLER,
-       ZEND_FE_RESET_SPEC_TMP_HANDLER,
-       ZEND_FE_RESET_SPEC_TMP_HANDLER,
-       ZEND_FE_RESET_SPEC_TMP_HANDLER,
-       ZEND_FE_RESET_SPEC_TMP_HANDLER,
-       ZEND_FE_RESET_SPEC_TMP_HANDLER,
-       ZEND_FE_RESET_SPEC_VAR_HANDLER,
-       ZEND_FE_RESET_SPEC_VAR_HANDLER,
-       ZEND_FE_RESET_SPEC_VAR_HANDLER,
-       ZEND_FE_RESET_SPEC_VAR_HANDLER,
-       ZEND_FE_RESET_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_FE_RESET_SPEC_CV_HANDLER,
-       ZEND_FE_RESET_SPEC_CV_HANDLER,
-       ZEND_FE_RESET_SPEC_CV_HANDLER,
-       ZEND_FE_RESET_SPEC_CV_HANDLER,
-       ZEND_FE_RESET_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_R_SPEC_CV_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
@@ -38341,11 +38792,11 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_FE_FETCH_SPEC_VAR_HANDLER,
-       ZEND_FE_FETCH_SPEC_VAR_HANDLER,
-       ZEND_FE_FETCH_SPEC_VAR_HANDLER,
-       ZEND_FE_FETCH_SPEC_VAR_HANDLER,
-       ZEND_FE_FETCH_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
@@ -39506,11 +39957,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNUSED_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
+       ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
@@ -39521,6 +39992,11 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
+       ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
@@ -39536,41 +40012,16 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+       ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
index c3006e60369380a80fd0977bdac37be3509f1f82..d7c5db696685ae97d20d5d71264bf5328d4d87d4 100644 (file)
@@ -99,8 +99,8 @@ const char *zend_vm_opcodes_map[170] = {
        "ZEND_UNSET_VAR",
        "ZEND_UNSET_DIM",
        "ZEND_UNSET_OBJ",
-       "ZEND_FE_RESET",
-       "ZEND_FE_FETCH",
+       "ZEND_FE_RESET_R",
+       "ZEND_FE_FETCH_R",
        "ZEND_EXIT",
        "ZEND_FETCH_R",
        "ZEND_FETCH_DIM_R",
@@ -147,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,
index f0fc7c7182ee89262e593e3cda2b616737e4620d..aa4afbd984bbacb6f7a618ebd9f5c29d838a526a 100644 (file)
@@ -101,8 +101,8 @@ END_EXTERN_C()
 #define ZEND_UNSET_VAR                        74
 #define ZEND_UNSET_DIM                        75
 #define ZEND_UNSET_OBJ                        76
-#define ZEND_FE_RESET                         77
-#define ZEND_FE_FETCH                         78
+#define ZEND_FE_RESET_R                       77
+#define ZEND_FE_FETCH_R                       78
 #define ZEND_EXIT                             79
 #define ZEND_FETCH_R                          80
 #define ZEND_FETCH_DIM_R                      81
@@ -149,6 +149,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
index 7948990f8130255eab21a93782235fcadd134eb9..4db48944e170fa6606f26dc4c21bfd6765fdbd9f 100644 (file)
@@ -166,14 +166,16 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
                        case ZEND_JMPNZ:
                        case ZEND_JMPZ_EX:
                        case ZEND_JMPNZ_EX:
-                       case ZEND_FE_RESET:
+                       case ZEND_FE_RESET_R:
+                       case ZEND_FE_RESET_RW:
                        case ZEND_NEW:
                        case ZEND_JMP_SET:
                        case ZEND_COALESCE:
                                START_BLOCK_OP(ZEND_OP2(opline).opline_num);
                                START_BLOCK_OP(opno + 1);
                                break;
-                       case ZEND_FE_FETCH:
+                       case ZEND_FE_FETCH_R:
+                       case ZEND_FE_FETCH_RW:
                                START_BLOCK_OP(ZEND_OP2(opline).opline_num);
                                START_BLOCK_OP(opno + 2);
                                break;
@@ -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);
index 0bb8cce17fbc96b8c688dcfcc4a0b7a0c7327fdf..dccd010ae44213908cf217d53478c17b68027223 100644 (file)
@@ -93,8 +93,10 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
                                case ZEND_JMPNZ:
                                case ZEND_JMPZ_EX:
                                case ZEND_JMPNZ_EX:
-                               case ZEND_FE_FETCH:
-                               case ZEND_FE_RESET:
+                               case ZEND_FE_FETCH_R:
+                               case ZEND_FE_FETCH_RW:
+                               case ZEND_FE_RESET_R:
+                               case ZEND_FE_RESET_RW:
                                case ZEND_NEW:
                                case ZEND_JMP_SET:
                                case ZEND_COALESCE:
index 27ba6bad8e5423461b0b4de6f22f7c71ce789744..dc93ce2f4c807112d91e0c21cd939050c0fb533b 100644 (file)
@@ -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 */
index 348fcb8efbe016a18303b01c28bf33f6853c23ab..341e80501b44fa46ee7c155c74c12d160030124f 100644 (file)
@@ -602,8 +602,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
                case ZEND_JMPNZ:
                case ZEND_JMPZ_EX:
                case ZEND_JMPNZ_EX:
-               case ZEND_FE_RESET:
-               case ZEND_FE_FETCH:
+               case ZEND_FE_RESET_R:
+               case ZEND_FE_RESET_RW:
+               case ZEND_FE_FETCH_R:
+               case ZEND_FE_FETCH_RW:
                case ZEND_NEW:
                case ZEND_JMP_SET:
                case ZEND_COALESCE:
index 5be1e30c9fc0666dd258dc83ef23dd34ed178ead..b5785924932f5c6d9240873e4c3a065b672f9ea7 100644 (file)
@@ -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;
                                                        }
index c3a55b319f194a110ac946cfd293b0c3841661d8..3019b274e953ab5901bd98d0eec29b166c8e59da 100644 (file)
@@ -328,7 +328,8 @@ continue_jmp_ex_optimization:
                                                           op->opcode == ZEND_RETURN ||
                                                           op->opcode == ZEND_RETURN_BY_REF ||
                                                           op->opcode == ZEND_FAST_RET ||
-                                                          op->opcode == ZEND_FE_FETCH ||
+                                                          op->opcode == ZEND_FE_FETCH_R ||
+                                                          op->opcode == ZEND_FE_FETCH_RW ||
                                                           op->opcode == ZEND_EXIT) {
                                                                break;
                                                        }
@@ -363,7 +364,8 @@ continue_jmp_ex_optimization:
                                                           op->opcode == ZEND_RETURN ||
                                                           op->opcode == ZEND_RETURN_BY_REF ||
                                                           op->opcode == ZEND_FAST_RET ||
-                                                          op->opcode == ZEND_FE_FETCH ||
+                                                          op->opcode == ZEND_FE_FETCH_R ||
+                                                          op->opcode == ZEND_FE_FETCH_RW ||
                                                           op->opcode == ZEND_EXIT) {
                                                                break;
                                                        }
index 54063d0ed3292d9124fd83023ab207fa60c1969c..a8506fb0789a893cc24e1f4dd419712bcd90a3e3 100644 (file)
@@ -450,8 +450,10 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                        case ZEND_JMP_SET:
                        case ZEND_COALESCE:
                        case ZEND_NEW:
-                       case ZEND_FE_RESET:
-                       case ZEND_FE_FETCH:
+                       case ZEND_FE_RESET_R:
+                       case ZEND_FE_RESET_RW:
+                       case ZEND_FE_FETCH_R:
+                       case ZEND_FE_FETCH_RW:
                                ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
                                break;
                }
@@ -488,8 +490,10 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                        case ZEND_JMP_SET:
                        case ZEND_COALESCE:
                        case ZEND_NEW:
-                       case ZEND_FE_RESET:
-                       case ZEND_FE_FETCH:
+                       case ZEND_FE_RESET_R:
+                       case ZEND_FE_RESET_RW:
+                       case ZEND_FE_FETCH_R:
+                       case ZEND_FE_FETCH_RW:
                                ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
                                break;
                }
index 3d63f96e346d9eb8ddbcded79bda63fe3d0e9d96..5b32baa64b5e4e50c27c451f40df65b6300bdb1b 100644 (file)
@@ -378,8 +378,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                                        case ZEND_JMP_SET:
                                        case ZEND_COALESCE:
                                        case ZEND_NEW:
-                                       case ZEND_FE_RESET:
-                                       case ZEND_FE_FETCH:
+                                       case ZEND_FE_RESET_R:
+                                       case ZEND_FE_RESET_RW:
+                                       case ZEND_FE_FETCH_R:
+                                       case ZEND_FE_FETCH_RW:
                                                ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
                                                break;
                                }
index 966ea0f1f69de13359b64b89639031c9b4868be1..f923ad9e7b565b3bdca3ef4417e5397236cec673 100644 (file)
@@ -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)));
index 4ddb82e8c688571678d88ad84a7e969954b2f263..f8778fffe380b3280310ee269b9a82ad5f100c38 100644 (file)
@@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null)
 ?>
 --EXPECT--
 string(3) "one"
-bool(false)
+string(3) "one"
index b24f14e81d6b45ebabf27253861948a317c8e540..c35b9b44e51f939a15210e70d26bd3ce42160073 100644 (file)
@@ -60,5 +60,5 @@ string(1) "f"
 int(2)\r
 string(1) "f"\r
 \r
-bool(false)\r
+string(1) "a"\r
 bool(false)
index 4586a35a10f865565ba9f4686b6f98e7a5a5549c..df51fd6be4f9552c7434c43cbe08e54adbe76df8 100644 (file)
@@ -55,6 +55,7 @@ foreach ($refedArray as $k=>&$v4) {
 Remove elements from a referenced array during loop\r
 key: 0; value: original.0\r
 key: 1; value: original.1\r
+key: 2; value: original.2\r
 \r
 Remove elements from a referenced array during loop, using &$value\r
 key: 0; value: original.0\r
@@ -64,11 +65,6 @@ Add elements to a referenced array during loop
 key: 0; value: original.0\r
 key: 1; value: original.1\r
 key: 2; value: original.2\r
-key: 3; value: new.0\r
-key: 4; value: new.1\r
-key: 5; value: new.2\r
-key: 6; value: new.3\r
-Loop detected, as expected.\r
 \r
 Add elements to a referenced array during loop, using &$value\r
 key: 0; value: original.0\r
index 671cfaf354edd2fa2fb5448f543005024ddcb64b..8527a8833e4a767089e7308f71c2abba886202d9 100644 (file)
@@ -25,10 +25,9 @@ foreach ($a as $v) {
 \r
 Change from array to non iterable:\r
 int(1)\r
-\r
-Warning: Invalid argument supplied for foreach() in %s on line 5\r
+int(2)\r
+int(3)\r
 \r
 Change from object to non iterable:\r
 int(1)\r
-\r
-Warning: Invalid argument supplied for foreach() in %s on line 15
+int(2)\r
index b0c5e8dcf51c195119a714bf47331cafade04520..ea728156f0f4ee2862314d41f487de6531e6687d 100644 (file)
@@ -1,7 +1,5 @@
 --TEST--\r
 Directly modifying an unreferenced array when foreach'ing over it while using &$value syntax.\r
---XFAIL--\r
-Needs major foreach changes to get sane behavior\r
 --FILE--\r
 <?php\r
 \r
@@ -70,7 +68,7 @@ withRefValue(3, $transform);
 withRefValue(4, $transform);\r
 \r
 ?>\r
---EXPECTF--\r
+--EXPECT--\r
 \r
 Popping elements off end of an unreferenced array, using &$value.\r
 ---( Array with 1 element(s): )---\r
@@ -95,9 +93,10 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.0\r
 --> State of array after loop:\r
-array(0) {\r
+array(1) {\r
+  [0]=>\r
+  &string(3) "v.0"\r
 }\r
 \r
 ---( Array with 3 element(s): )---\r
@@ -134,10 +133,12 @@ array(4) {
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
-     iteration 2:  $k=0; $v=v.0\r
-     iteration 3:  $k=0; $v=v.0\r
 --> State of array after loop:\r
-array(0) {\r
+array(2) {\r
+  [0]=>\r
+  string(3) "v.0"\r
+  [1]=>\r
+  &string(3) "v.1"\r
 }\r
 \r
 \r
@@ -289,12 +290,28 @@ array(1) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
+     iteration 1:  $k=1; $v=new.0\r
+     iteration 2:  $k=2; $v=new.1\r
+     iteration 3:  $k=3; $v=new.2\r
+     iteration 4:  $k=4; $v=new.3\r
+     iteration 5:  $k=5; $v=new.4\r
+  ** Stuck in a loop! **\r
 --> State of array after loop:\r
-array(2) {\r
+array(7) {\r
   [0]=>\r
-  &string(3) "v.0"\r
+  string(3) "v.0"\r
   [1]=>\r
   string(5) "new.0"\r
+  [2]=>\r
+  string(5) "new.1"\r
+  [3]=>\r
+  string(5) "new.2"\r
+  [4]=>\r
+  string(5) "new.3"\r
+  [5]=>\r
+  &string(5) "new.4"\r
+  [6]=>\r
+  string(5) "new.5"\r
 }\r
 \r
 ---( Array with 2 element(s): )---\r
@@ -446,30 +463,17 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=2; $v=v.1\r
 --> State of array after loop:\r
-array(8) {\r
+array(4) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
-  string(3) "v.1"\r
+  [3]=>\r
+  &string(3) "v.1"\r
 }\r
 \r
 ---( Array with 3 element(s): )---\r
@@ -484,32 +488,19 @@ array(3) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=3; $v=v.2\r
 --> State of array after loop:\r
-array(9) {\r
+array(5) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [3]=>\r
   string(3) "v.1"\r
-  [8]=>\r
-  string(3) "v.2"\r
+  [4]=>\r
+  &string(3) "v.2"\r
 }\r
 \r
 ---( Array with 4 element(s): )---\r
@@ -526,32 +517,19 @@ array(4) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=4; $v=v.3\r
 --> State of array after loop:\r
-array(10) {\r
+array(6) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [3]=>\r
   string(3) "v.1"\r
-  [8]=>\r
+  [4]=>\r
   string(3) "v.2"\r
-  [9]=>\r
-  string(3) "v.3"\r
+  [5]=>\r
+  &string(3) "v.3"\r
 }\r
index d32ea635fda7fc8cf5a10bcd0c19a26d7c2835cb..9b179ab22a7f9e0c6c4b50fc60d7e779bf4f4654 100644 (file)
@@ -1,7 +1,5 @@
 --TEST--\r
 Directly modifying a REFERENCED array when foreach'ing over it.\r
---XFAIL--\r
-Needs major foreach changes to get sane behavior\r
 --FILE--\r
 <?php\r
 \r
@@ -96,7 +94,7 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.0\r
+     iteration 1:  $k=1; $v=v.1\r
 --> State of array after loop:\r
 array(0) {\r
 }\r
@@ -114,10 +112,9 @@ array(3) {
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
+     iteration 2:  $k=2; $v=v.2\r
 --> State of array after loop:\r
-array(1) {\r
-  [0]=>\r
-  string(3) "v.0"\r
+array(0) {\r
 }\r
 \r
 ---( Array with 4 element(s): )---\r
@@ -135,8 +132,8 @@ array(4) {
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
-     iteration 2:  $k=0; $v=v.0\r
-     iteration 3:  $k=0; $v=v.0\r
+     iteration 2:  $k=2; $v=v.2\r
+     iteration 3:  $k=3; $v=v.3\r
 --> State of array after loop:\r
 array(0) {\r
 }\r
@@ -166,7 +163,7 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.1\r
+     iteration 1:  $k=1; $v=v.1\r
 --> State of array after loop:\r
 array(0) {\r
 }\r
@@ -183,8 +180,8 @@ array(3) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.1\r
-     iteration 2:  $k=0; $v=v.2\r
+     iteration 1:  $k=1; $v=v.1\r
+     iteration 2:  $k=2; $v=v.2\r
 --> State of array after loop:\r
 array(0) {\r
 }\r
@@ -203,9 +200,9 @@ array(4) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.1\r
-     iteration 2:  $k=0; $v=v.2\r
-     iteration 3:  $k=0; $v=v.3\r
+     iteration 1:  $k=1; $v=v.1\r
+     iteration 2:  $k=2; $v=v.2\r
+     iteration 3:  $k=3; $v=v.3\r
 --> State of array after loop:\r
 array(0) {\r
 }\r
@@ -309,13 +306,8 @@ array(2) {
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
-     iteration 2:  $k=2; $v=new.0\r
-     iteration 3:  $k=3; $v=new.1\r
-     iteration 4:  $k=4; $v=new.2\r
-     iteration 5:  $k=5; $v=new.3\r
-  ** Stuck in a loop! **\r
 --> State of array after loop:\r
-array(8) {\r
+array(4) {\r
   [0]=>\r
   string(3) "v.0"\r
   [1]=>\r
@@ -324,14 +316,6 @@ array(8) {
   string(5) "new.0"\r
   [3]=>\r
   string(5) "new.1"\r
-  [4]=>\r
-  string(5) "new.2"\r
-  [5]=>\r
-  string(5) "new.3"\r
-  [6]=>\r
-  string(5) "new.4"\r
-  [7]=>\r
-  string(5) "new.5"\r
 }\r
 \r
 ---( Array with 3 element(s): )---\r
@@ -348,12 +332,8 @@ array(3) {
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
      iteration 2:  $k=2; $v=v.2\r
-     iteration 3:  $k=3; $v=new.0\r
-     iteration 4:  $k=4; $v=new.1\r
-     iteration 5:  $k=5; $v=new.2\r
-  ** Stuck in a loop! **\r
 --> State of array after loop:\r
-array(9) {\r
+array(6) {\r
   [0]=>\r
   string(3) "v.0"\r
   [1]=>\r
@@ -366,12 +346,6 @@ array(9) {
   string(5) "new.1"\r
   [5]=>\r
   string(5) "new.2"\r
-  [6]=>\r
-  string(5) "new.3"\r
-  [7]=>\r
-  string(5) "new.4"\r
-  [8]=>\r
-  string(5) "new.5"\r
 }\r
 \r
 ---( Array with 4 element(s): )---\r
@@ -391,11 +365,8 @@ array(4) {
      iteration 1:  $k=1; $v=v.1\r
      iteration 2:  $k=2; $v=v.2\r
      iteration 3:  $k=3; $v=v.3\r
-     iteration 4:  $k=4; $v=new.0\r
-     iteration 5:  $k=5; $v=new.1\r
-  ** Stuck in a loop! **\r
 --> State of array after loop:\r
-array(10) {\r
+array(8) {\r
   [0]=>\r
   string(3) "v.0"\r
   [1]=>\r
@@ -412,10 +383,6 @@ array(10) {
   string(5) "new.2"\r
   [7]=>\r
   string(5) "new.3"\r
-  [8]=>\r
-  string(5) "new.4"\r
-  [9]=>\r
-  string(5) "new.5"\r
 }\r
 \r
 \r
@@ -447,29 +414,16 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=1; $v=v.1\r
 --> State of array after loop:\r
-array(8) {\r
+array(4) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [3]=>\r
   string(3) "v.1"\r
 }\r
 \r
@@ -485,31 +439,21 @@ array(3) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=1; $v=v.1\r
+     iteration 2:  $k=2; $v=v.2\r
 --> State of array after loop:\r
-array(9) {\r
+array(6) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
   string(5) "new.2"\r
-  [4]=>\r
+  [1]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [2]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [3]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [4]=>\r
   string(3) "v.1"\r
-  [8]=>\r
+  [5]=>\r
   string(3) "v.2"\r
 }\r
 \r
@@ -527,32 +471,25 @@ array(4) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=1; $v=v.1\r
+     iteration 2:  $k=2; $v=v.2\r
+     iteration 3:  $k=3; $v=v.3\r
 --> State of array after loop:\r
-array(10) {\r
+array(8) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  string(5) "new.4"\r
-  [2]=>\r
   string(5) "new.3"\r
-  [3]=>\r
+  [1]=>\r
   string(5) "new.2"\r
-  [4]=>\r
+  [2]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [3]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [4]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [5]=>\r
   string(3) "v.1"\r
-  [8]=>\r
+  [6]=>\r
   string(3) "v.2"\r
-  [9]=>\r
+  [7]=>\r
   string(3) "v.3"\r
 }\r
index 5b12a2b0c269f317c2c3805794ccf14db49a59e2..a4ee09d6a96900458349706675ff26284ce3d473 100644 (file)
@@ -1,7 +1,5 @@
 --TEST--\r
 Directly modifying a REFERENCED array when foreach'ing over it while using &$value syntax.\r
---XFAIL--\r
-Needs major foreach changes to get sane behavior\r
 --FILE--\r
 <?php\r
 \r
@@ -72,7 +70,7 @@ withRefValue(3, $transform);
 withRefValue(4, $transform);\r
 \r
 ?>\r
---EXPECTF--\r
+--EXPECT--\r
 \r
 Popping elements off end of a referenced array, using &$value\r
 ---( Array with 1 element(s): )---\r
@@ -97,9 +95,10 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=v.0\r
 --> State of array after loop:\r
-array(0) {\r
+array(1) {\r
+  [0]=>\r
+  &string(3) "v.0"\r
 }\r
 \r
 ---( Array with 3 element(s): )---\r
@@ -136,10 +135,12 @@ array(4) {
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
      iteration 1:  $k=1; $v=v.1\r
-     iteration 2:  $k=0; $v=v.0\r
-     iteration 3:  $k=0; $v=v.0\r
 --> State of array after loop:\r
-array(0) {\r
+array(2) {\r
+  [0]=>\r
+  string(3) "v.0"\r
+  [1]=>\r
+  &string(3) "v.1"\r
 }\r
 \r
 \r
@@ -291,12 +292,28 @@ array(1) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
+     iteration 1:  $k=1; $v=new.0\r
+     iteration 2:  $k=2; $v=new.1\r
+     iteration 3:  $k=3; $v=new.2\r
+     iteration 4:  $k=4; $v=new.3\r
+     iteration 5:  $k=5; $v=new.4\r
+  ** Stuck in a loop! **\r
 --> State of array after loop:\r
-array(2) {\r
+array(7) {\r
   [0]=>\r
-  &string(3) "v.0"\r
+  string(3) "v.0"\r
   [1]=>\r
   string(5) "new.0"\r
+  [2]=>\r
+  string(5) "new.1"\r
+  [3]=>\r
+  string(5) "new.2"\r
+  [4]=>\r
+  string(5) "new.3"\r
+  [5]=>\r
+  &string(5) "new.4"\r
+  [6]=>\r
+  string(5) "new.5"\r
 }\r
 \r
 ---( Array with 2 element(s): )---\r
@@ -448,30 +465,17 @@ array(2) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=2; $v=v.1\r
 --> State of array after loop:\r
-array(8) {\r
+array(4) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
-  string(3) "v.1"\r
+  [3]=>\r
+  &string(3) "v.1"\r
 }\r
 \r
 ---( Array with 3 element(s): )---\r
@@ -486,32 +490,19 @@ array(3) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=3; $v=v.2\r
 --> State of array after loop:\r
-array(9) {\r
+array(5) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [3]=>\r
   string(3) "v.1"\r
-  [8]=>\r
-  string(3) "v.2"\r
+  [4]=>\r
+  &string(3) "v.2"\r
 }\r
 \r
 ---( Array with 4 element(s): )---\r
@@ -528,32 +519,19 @@ array(4) {
 }\r
 --> Do loop:\r
      iteration 0:  $k=0; $v=v.0\r
-     iteration 1:  $k=0; $v=new.0\r
-     iteration 2:  $k=0; $v=new.1\r
-     iteration 3:  $k=0; $v=new.2\r
-     iteration 4:  $k=0; $v=new.3\r
-     iteration 5:  $k=0; $v=new.4\r
-  ** Stuck in a loop! **\r
+     iteration 1:  $k=4; $v=v.3\r
 --> State of array after loop:\r
-array(10) {\r
+array(6) {\r
   [0]=>\r
-  string(5) "new.5"\r
-  [1]=>\r
-  &string(5) "new.4"\r
-  [2]=>\r
-  string(5) "new.3"\r
-  [3]=>\r
-  string(5) "new.2"\r
-  [4]=>\r
   string(5) "new.1"\r
-  [5]=>\r
+  [1]=>\r
   string(5) "new.0"\r
-  [6]=>\r
+  [2]=>\r
   string(3) "v.0"\r
-  [7]=>\r
+  [3]=>\r
   string(3) "v.1"\r
-  [8]=>\r
+  [4]=>\r
   string(3) "v.2"\r
-  [9]=>\r
-  string(3) "v.3"\r
+  [5]=>\r
+  &string(3) "v.3"\r
 }\r
index 8218b44dab929f497016e2e1ae9f87e946c6eeef..5204aca9caf1e2e2bc8aed4e9c24a647c5453d35 100644 (file)
@@ -70,16 +70,12 @@ var_dump($obj);
 
 ?>
 --EXPECTF--
-
 Substituting the iterated object for a different object.
 string(10) "Original a"
 string(10) "Original b"
-string(5) "new a"
-string(5) "new b"
-string(5) "new c"
-string(5) "new d"
-string(5) "new e"
-string(5) "new f"
+string(10) "Original c"
+string(10) "Original d"
+string(10) "Original e"
 object(stdClass)#%d (6) {
   ["a"]=>
   string(5) "new a"
@@ -98,14 +94,9 @@ object(stdClass)#%d (6) {
 Substituting the iterated object for an array.
 string(10) "Original a"
 string(10) "Original b"
-int(1)
-int(2)
-int(3)
-int(4)
-int(5)
-int(6)
-int(7)
-int(8)
+string(10) "Original c"
+string(10) "Original d"
+string(10) "Original e"
 array(8) {
   [0]=>
   int(1)
@@ -128,11 +119,12 @@ array(8) {
 Substituting the iterated array for an object.
 int(1)
 int(2)
-string(10) "Original a"
-string(10) "Original b"
-string(10) "Original c"
-string(10) "Original d"
-string(10) "Original e"
+int(3)
+int(4)
+int(5)
+int(6)
+int(7)
+int(8)
 object(C)#%d (5) {
   ["a"]=>
   string(10) "Original a"