summaryrefslogtreecommitdiff
path: root/Zend/tests/generators
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/tests/generators')
-rw-r--r--Zend/tests/generators/auto_incrementing_keys.phpt22
-rw-r--r--Zend/tests/generators/backtrace.phpt27
-rw-r--r--Zend/tests/generators/bug63066.phpt16
-rw-r--r--Zend/tests/generators/bug65035.phpt20
-rw-r--r--Zend/tests/generators/bug65161.phpt20
-rw-r--r--Zend/tests/generators/bug66041.phpt17
-rw-r--r--Zend/tests/generators/clone.phpt15
-rw-r--r--Zend/tests/generators/dynamic_call.phpt19
-rw-r--r--Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt13
-rw-r--r--Zend/tests/generators/errors/generator_cannot_return_error.phpt13
-rw-r--r--Zend/tests/generators/errors/generator_extend_error.phpt10
-rw-r--r--Zend/tests/generators/errors/generator_instantiate_error.phpt10
-rw-r--r--Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt18
-rw-r--r--Zend/tests/generators/errors/resume_running_generator_error.phpt17
-rw-r--r--Zend/tests/generators/errors/serialize_unserialize_error.phpt44
-rw-r--r--Zend/tests/generators/errors/yield_const_by_ref_error.phpt16
-rw-r--r--Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt29
-rw-r--r--Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt20
-rw-r--r--Zend/tests/generators/errors/yield_outside_function_error.phpt10
-rw-r--r--Zend/tests/generators/fibonacci.phpt36
-rw-r--r--Zend/tests/generators/finally/finally_ran_on_close.phpt30
-rw-r--r--Zend/tests/generators/finally/return_return.phpt33
-rw-r--r--Zend/tests/generators/finally/return_yield.phpt18
-rw-r--r--Zend/tests/generators/finally/run_on_dtor.phpt22
-rw-r--r--Zend/tests/generators/finally/throw_yield.phpt24
-rw-r--r--Zend/tests/generators/finally/yield_return.phpt18
-rw-r--r--Zend/tests/generators/finally/yield_throw.phpt24
-rw-r--r--Zend/tests/generators/finally/yield_yield.phpt22
-rw-r--r--Zend/tests/generators/func_get_args.phpt21
-rw-r--r--Zend/tests/generators/generator_closure.phpt20
-rw-r--r--Zend/tests/generators/generator_closure_with_this.phpt20
-rw-r--r--Zend/tests/generators/generator_in_multipleiterator.phpt37
-rw-r--r--Zend/tests/generators/generator_method.phpt29
-rw-r--r--Zend/tests/generators/generator_method_by_ref.phpt44
-rw-r--r--Zend/tests/generators/generator_returns_generator.phpt18
-rw-r--r--Zend/tests/generators/generator_rewind.phpt62
-rw-r--r--Zend/tests/generators/generator_send.phpt22
-rw-r--r--Zend/tests/generators/generator_static_method.phpt29
-rw-r--r--Zend/tests/generators/generator_throwing_during_function_call.phpt32
-rw-r--r--Zend/tests/generators/generator_throwing_exception.phpt28
-rw-r--r--Zend/tests/generators/generator_throwing_in_foreach.phpt20
-rw-r--r--Zend/tests/generators/generator_with_keys.phpt26
-rw-r--r--Zend/tests/generators/generator_with_nonscalar_keys.phpt52
-rw-r--r--Zend/tests/generators/ignored_send_leak.phpt17
-rw-r--r--Zend/tests/generators/nested_calls_with_die.phpt30
-rw-r--r--Zend/tests/generators/nested_method_calls.phpt36
-rw-r--r--Zend/tests/generators/no_foreach_var_leaks.phpt19
-rw-r--r--Zend/tests/generators/send_after_close.phpt14
-rw-r--r--Zend/tests/generators/send_returns_current.phpt20
-rw-r--r--Zend/tests/generators/throw_already_closed.phpt23
-rw-r--r--Zend/tests/generators/throw_caught.phpt27
-rw-r--r--Zend/tests/generators/throw_not_an_exception.phpt15
-rw-r--r--Zend/tests/generators/throw_rethrow.phpt34
-rw-r--r--Zend/tests/generators/throw_uncaught.phpt19
-rw-r--r--Zend/tests/generators/unused_return_value.phpt13
-rw-r--r--Zend/tests/generators/xrange.phpt23
-rw-r--r--Zend/tests/generators/yield_array_key.phpt18
-rw-r--r--Zend/tests/generators/yield_array_offset_by_ref.phpt26
-rw-r--r--Zend/tests/generators/yield_by_reference.phpt42
-rw-r--r--Zend/tests/generators/yield_closure.phpt17
-rw-r--r--Zend/tests/generators/yield_during_function_call.phpt15
-rw-r--r--Zend/tests/generators/yield_during_method_call.phpt27
-rw-r--r--Zend/tests/generators/yield_in_finally.phpt29
-rw-r--r--Zend/tests/generators/yield_in_parenthesis.phpt23
-rw-r--r--Zend/tests/generators/yield_ref_function_call_by_reference.phpt24
-rw-r--r--Zend/tests/generators/yield_without_value.phpt27
66 files changed, 1581 insertions, 0 deletions
diff --git a/Zend/tests/generators/auto_incrementing_keys.phpt b/Zend/tests/generators/auto_incrementing_keys.phpt
new file mode 100644
index 0000000000..acfb2f2ce0
--- /dev/null
+++ b/Zend/tests/generators/auto_incrementing_keys.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Generator keys are auto-incrementing by default
+--FILE--
+<?php
+
+function gen() {
+ yield 'foo';
+ yield 'bar';
+ yield 5 => 'rab';
+ yield 'oof';
+}
+
+foreach (gen() as $k => $v) {
+ echo $k, ' => ', $v, "\n";
+}
+
+?>
+--EXPECT--
+0 => foo
+1 => bar
+5 => rab
+6 => oof
diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt
new file mode 100644
index 0000000000..5fed1d467e
--- /dev/null
+++ b/Zend/tests/generators/backtrace.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Printing the stack trace in a generator
+--FILE--
+<?php
+
+function f1() {
+ debug_print_backtrace();
+}
+
+function f2($arg1, $arg2) {
+ f1();
+ yield; // force generator
+}
+
+function f3($gen) {
+ $gen->rewind(); // trigger run
+}
+
+$gen = f2('foo', 'bar');
+f3($gen);
+
+?>
+--EXPECTF--
+#0 f1() called at [%s:%d]
+#1 f2(foo, bar)
+#2 Generator->rewind() called at [%s:%d]
+#3 f3(Generator Object ()) called at [%s:%d]
diff --git a/Zend/tests/generators/bug63066.phpt b/Zend/tests/generators/bug63066.phpt
new file mode 100644
index 0000000000..8c4c8b4a84
--- /dev/null
+++ b/Zend/tests/generators/bug63066.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Bug #63066 (Calling an undefined method in a generator results in a seg fault)
+--FILE--
+<?php
+function gen($o)
+{
+ yield 'foo';
+ $o->fatalError();
+}
+
+foreach(gen(new stdClass()) as $value)
+ echo $value, "\n";
+--EXPECTF--
+foo
+
+Fatal error: Call to undefined method stdClass::fatalError() in %sbug63066.php on line 5
diff --git a/Zend/tests/generators/bug65035.phpt b/Zend/tests/generators/bug65035.phpt
new file mode 100644
index 0000000000..18276cc23a
--- /dev/null
+++ b/Zend/tests/generators/bug65035.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #65035: yield / exit segfault
+--FILE--
+<?php
+
+function gen() {
+ fn();
+ yield;
+}
+
+function fn() {
+ exit('Done');
+}
+
+$gen = gen();
+$gen->current();
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/generators/bug65161.phpt b/Zend/tests/generators/bug65161.phpt
new file mode 100644
index 0000000000..215c1880e3
--- /dev/null
+++ b/Zend/tests/generators/bug65161.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #65161: Generator + autoload + syntax error = segfault
+--FILE--
+<?php
+
+function autoload() {
+ foo();
+}
+spl_autoload_register('autoload');
+
+function testGenerator() {
+ new SyntaxError('param');
+ yield;
+}
+
+foreach (testGenerator() as $i);
+
+?>
+--EXPECTF--
+Fatal error: Call to undefined function foo() in %s on line %d
diff --git a/Zend/tests/generators/bug66041.phpt b/Zend/tests/generators/bug66041.phpt
new file mode 100644
index 0000000000..d944224134
--- /dev/null
+++ b/Zend/tests/generators/bug66041.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Bug #66041: list() fails to unpack yielded ArrayAccess object
+--FILE--
+<?php
+function dumpElement() {
+ list($value) = yield;
+ var_dump($value);
+};
+
+$fixedArray = new SplFixedArray(1);
+$fixedArray[0] = 'the element';
+
+$generator = dumpElement();
+$generator->send($fixedArray);
+?>
+--EXPECT--
+string(11) "the element"
diff --git a/Zend/tests/generators/clone.phpt b/Zend/tests/generators/clone.phpt
new file mode 100644
index 0000000000..22f4428121
--- /dev/null
+++ b/Zend/tests/generators/clone.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Generators cannot be cloned
+--FILE--
+<?php
+
+function gen() {
+ yield;
+}
+
+$gen = gen();
+clone $gen;
+
+?>
+--EXPECTF--
+Fatal error: Trying to clone an uncloneable object of class Generator in %s on line %d
diff --git a/Zend/tests/generators/dynamic_call.phpt b/Zend/tests/generators/dynamic_call.phpt
new file mode 100644
index 0000000000..d564540952
--- /dev/null
+++ b/Zend/tests/generators/dynamic_call.phpt
@@ -0,0 +1,19 @@
+--TEST--
+It's possible to invoke a generator dynamically
+--FILE--
+<?php
+
+function gen($foo, $bar) {
+ yield $foo;
+ yield $bar;
+}
+
+$gen = call_user_func('gen', 'bar', 'foo');
+foreach ($gen as $value) {
+ var_dump($value);
+}
+
+?>
+--EXPECT--
+string(3) "bar"
+string(3) "foo"
diff --git a/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt
new file mode 100644
index 0000000000..ad618d20ba
--- /dev/null
+++ b/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Generators cannot return values (even before yield)
+--FILE--
+<?php
+
+function gen() {
+ return $foo;
+ yield;
+}
+
+?>
+--EXPECTF--
+Fatal error: Generators cannot return values using "return" in %s on line 4
diff --git a/Zend/tests/generators/errors/generator_cannot_return_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_error.phpt
new file mode 100644
index 0000000000..51149062a7
--- /dev/null
+++ b/Zend/tests/generators/errors/generator_cannot_return_error.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Generators cannot return values
+--FILE--
+<?php
+
+function gen() {
+ yield;
+ return $abc;
+}
+
+?>
+--EXPECTF--
+Fatal error: Generators cannot return values using "return" in %s on line 5
diff --git a/Zend/tests/generators/errors/generator_extend_error.phpt b/Zend/tests/generators/errors/generator_extend_error.phpt
new file mode 100644
index 0000000000..550f16ae03
--- /dev/null
+++ b/Zend/tests/generators/errors/generator_extend_error.phpt
@@ -0,0 +1,10 @@
+--TEST--
+The Generator class cannot be extended
+--FILE--
+<?php
+
+class ExtendedGenerator extends Generator { }
+
+?>
+--EXPECTF--
+Fatal error: Class ExtendedGenerator may not inherit from final class (Generator) in %s on line %d
diff --git a/Zend/tests/generators/errors/generator_instantiate_error.phpt b/Zend/tests/generators/errors/generator_instantiate_error.phpt
new file mode 100644
index 0000000000..f8941c087a
--- /dev/null
+++ b/Zend/tests/generators/errors/generator_instantiate_error.phpt
@@ -0,0 +1,10 @@
+--TEST--
+It's not possible to directly instantiate the Generator class
+--FILE--
+<?php
+
+new Generator;
+
+?>
+--EXPECTF--
+Catchable fatal error: The "Generator" class is reserved for internal use and cannot be manually instantiated in %s on line %d
diff --git a/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt
new file mode 100644
index 0000000000..de5b22f6ba
--- /dev/null
+++ b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Non-ref generators cannot be iterated by-ref
+--FILE--
+<?php
+
+function gen() { yield; }
+
+$gen = gen();
+foreach ($gen as &$value) { }
+
+?>
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'You can only iterate a generator by-reference if it declared that it yields by-reference' in %s:%d
+Stack trace:
+#0 %s(%d): unknown()
+#1 {main}
+ thrown in %s on line %d
+
diff --git a/Zend/tests/generators/errors/resume_running_generator_error.phpt b/Zend/tests/generators/errors/resume_running_generator_error.phpt
new file mode 100644
index 0000000000..567d72f3f9
--- /dev/null
+++ b/Zend/tests/generators/errors/resume_running_generator_error.phpt
@@ -0,0 +1,17 @@
+--TEST--
+It is not possible to resume an already running generator
+--FILE--
+<?php
+
+function gen() {
+ $gen = yield;
+ $gen->next();
+}
+
+$gen = gen();
+$gen->send($gen);
+$gen->next();
+
+?>
+--EXPECTF--
+Fatal error: Cannot resume an already running generator in %s on line %d
diff --git a/Zend/tests/generators/errors/serialize_unserialize_error.phpt b/Zend/tests/generators/errors/serialize_unserialize_error.phpt
new file mode 100644
index 0000000000..aa2d4693f7
--- /dev/null
+++ b/Zend/tests/generators/errors/serialize_unserialize_error.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Generators can't be serialized or unserialized
+--FILE--
+<?php
+
+function gen() { yield; }
+
+$gen = gen();
+
+try {
+ serialize($gen);
+} catch (Exception $e) {
+ echo $e, "\n\n";
+}
+
+try {
+ var_dump(unserialize('O:9:"Generator":0:{}'));
+} catch (Exception $e) {
+ echo $e, "\n\n";
+}
+
+try {
+ var_dump(unserialize('C:9:"Generator":0:{}'));
+} catch (Exception $e) {
+ echo $e;
+}
+
+?>
+--EXPECTF--
+exception 'Exception' with message 'Serialization of 'Generator' is not allowed' in %s:%d
+Stack trace:
+#0 %s(%d): serialize(Object(Generator))
+#1 {main}
+
+exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d
+Stack trace:
+#0 [internal function]: Generator->__wakeup()
+#1 %s(%d): unserialize('O:9:"Generator"...')
+#2 {main}
+
+exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d
+Stack trace:
+#0 %s(%d): unserialize('C:9:"Generator"...')
+#1 {main}
diff --git a/Zend/tests/generators/errors/yield_const_by_ref_error.phpt b/Zend/tests/generators/errors/yield_const_by_ref_error.phpt
new file mode 100644
index 0000000000..e79f83e24a
--- /dev/null
+++ b/Zend/tests/generators/errors/yield_const_by_ref_error.phpt
@@ -0,0 +1,16 @@
+--TEST--
+A notice is thrown when yielding a constant value by reference
+--FILE--
+<?php
+
+function &gen() {
+ yield "foo";
+}
+
+$gen = gen();
+var_dump($gen->current());
+
+?>
+--EXPECTF--
+Notice: Only variable references should be yielded by reference in %s on line %d
+string(3) "foo"
diff --git a/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt
new file mode 100644
index 0000000000..aada676a68
--- /dev/null
+++ b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt
@@ -0,0 +1,29 @@
+--TEST--
+yield cannot be used in a finally block when the generator is force-closed
+--FILE--
+<?php
+
+function gen() {
+ try {
+ echo "before yield\n";
+ yield;
+ echo "after yield\n";
+ } finally {
+ echo "before yield in finally\n";
+ yield;
+ echo "after yield in finally\n";
+ }
+
+ echo "after finally\n";
+}
+
+$gen = gen();
+$gen->rewind();
+unset($gen);
+
+?>
+--EXPECTF--
+before yield
+before yield in finally
+
+Fatal error: Cannot yield from finally in a force-closed generator in %s on line %d
diff --git a/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt
new file mode 100644
index 0000000000..4b8563331c
--- /dev/null
+++ b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Yielding the result of a non-ref function call throw a notice
+--FILE--
+<?php
+
+function foo() {
+ return "bar";
+}
+
+function &gen() {
+ yield foo();
+}
+
+$gen = gen();
+var_dump($gen->current());
+
+?>
+--EXPECTF--
+Notice: Only variable references should be yielded by reference in %s on line %d
+string(3) "bar"
diff --git a/Zend/tests/generators/errors/yield_outside_function_error.phpt b/Zend/tests/generators/errors/yield_outside_function_error.phpt
new file mode 100644
index 0000000000..f999c1c03b
--- /dev/null
+++ b/Zend/tests/generators/errors/yield_outside_function_error.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Yield cannot be used outside of functions
+--FILE--
+<?php
+
+yield "Test";
+
+?>
+--EXPECTF--
+Fatal error: The "yield" expression can only be used inside a function in %s on line %d
diff --git a/Zend/tests/generators/fibonacci.phpt b/Zend/tests/generators/fibonacci.phpt
new file mode 100644
index 0000000000..35b31352ff
--- /dev/null
+++ b/Zend/tests/generators/fibonacci.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Creating an infinite fibonacci list using a generator
+--FILE--
+<?php
+
+function fib() {
+ list($a, $b) = [1, 1];
+ while (true) {
+ yield $b;
+ list($a, $b) = [$b, $a + $b];
+ }
+}
+
+foreach (fib() as $n) {
+ if ($n > 1000) break;
+
+ var_dump($n);
+}
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(3)
+int(5)
+int(8)
+int(13)
+int(21)
+int(34)
+int(55)
+int(89)
+int(144)
+int(233)
+int(377)
+int(610)
+int(987)
diff --git a/Zend/tests/generators/finally/finally_ran_on_close.phpt b/Zend/tests/generators/finally/finally_ran_on_close.phpt
new file mode 100644
index 0000000000..04a0561c87
--- /dev/null
+++ b/Zend/tests/generators/finally/finally_ran_on_close.phpt
@@ -0,0 +1,30 @@
+--TEST--
+finally is run even if a generator is closed mid-execution
+--FILE--
+<?php
+
+function gen() {
+ try {
+ try {
+ echo "before yield\n";
+ yield;
+ echo "after yield\n";
+ } finally {
+ echo "finally run\n";
+ }
+ echo "code after finally\n";
+ } finally {
+ echo "second finally run\n";
+ }
+ echo "code after second finally\n";
+}
+
+$gen = gen();
+$gen->rewind();
+unset($gen);
+
+?>
+--EXPECT--
+before yield
+finally run
+second finally run
diff --git a/Zend/tests/generators/finally/return_return.phpt b/Zend/tests/generators/finally/return_return.phpt
new file mode 100644
index 0000000000..02e2739de7
--- /dev/null
+++ b/Zend/tests/generators/finally/return_return.phpt
@@ -0,0 +1,33 @@
+--TEST--
+try { return } finally { return } in generator
+--FILE--
+<?php
+
+function gen() {
+ try {
+ try {
+ echo "before return\n";
+ return;
+ echo "after return\n";
+ } finally {
+ echo "before return in inner finally\n";
+ return;
+ echo "after return in inner finally\n";
+ }
+ } finally {
+ echo "outer finally run\n";
+ }
+
+ echo "code after finally\n";
+
+ yield; // force generator
+}
+
+$gen = gen();
+$gen->rewind(); // force run
+
+?>
+--EXPECTF--
+before return
+before return in inner finally
+outer finally run
diff --git a/Zend/tests/generators/finally/return_yield.phpt b/Zend/tests/generators/finally/return_yield.phpt
new file mode 100644
index 0000000000..c4f3485987
--- /dev/null
+++ b/Zend/tests/generators/finally/return_yield.phpt
@@ -0,0 +1,18 @@
+--TEST--
+try { return } finally { yield }
+--FILE--
+<?php
+function foo($f, $t) {
+ for ($i = $f; $i <= $t; $i++) {
+ try {
+ return;
+ } finally {
+ yield $i;
+ }
+ }
+}
+foreach (foo(1, 5) as $x) {
+ echo $x, "\n";
+}
+--EXPECT--
+1
diff --git a/Zend/tests/generators/finally/run_on_dtor.phpt b/Zend/tests/generators/finally/run_on_dtor.phpt
new file mode 100644
index 0000000000..35f8f4e0de
--- /dev/null
+++ b/Zend/tests/generators/finally/run_on_dtor.phpt
@@ -0,0 +1,22 @@
+--TEST--
+finally is run on object dtor, not free
+--FILE--
+<?php
+
+function gen() {
+ try {
+ yield;
+ } finally {
+ var_dump($_GET);
+ }
+}
+
+$gen = gen();
+$gen->rewind();
+
+set_error_handler(function() use($gen) {});
+
+?>
+--EXPECT--
+array(0) {
+}
diff --git a/Zend/tests/generators/finally/throw_yield.phpt b/Zend/tests/generators/finally/throw_yield.phpt
new file mode 100644
index 0000000000..2013c3ee14
--- /dev/null
+++ b/Zend/tests/generators/finally/throw_yield.phpt
@@ -0,0 +1,24 @@
+--TEST--
+try { throw } finally { yield }
+--FILE--
+<?php
+function foo($f, $t) {
+ for ($i = $f; $i <= $t; $i++) {
+ try {
+ throw new Exception;
+ } finally {
+ yield $i;
+ }
+ }
+}
+foreach (foo(1, 5) as $x) {
+ echo $x, "\n";
+}
+--EXPECTF--
+1
+
+Fatal error: Uncaught exception 'Exception' in %s:%d
+Stack trace:
+#0 %s(%d): foo(1, 5)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/generators/finally/yield_return.phpt b/Zend/tests/generators/finally/yield_return.phpt
new file mode 100644
index 0000000000..e3e1bec357
--- /dev/null
+++ b/Zend/tests/generators/finally/yield_return.phpt
@@ -0,0 +1,18 @@
+--TEST--
+try { yield } finally { return }
+--FILE--
+<?php
+function foo($f, $t) {
+ for ($i = $f; $i <= $t; $i++) {
+ try {
+ yield $i;
+ } finally {
+ return;
+ }
+ }
+}
+foreach (foo(1, 5) as $x) {
+ echo $x, "\n";
+}
+--EXPECT--
+1
diff --git a/Zend/tests/generators/finally/yield_throw.phpt b/Zend/tests/generators/finally/yield_throw.phpt
new file mode 100644
index 0000000000..0ead4501af
--- /dev/null
+++ b/Zend/tests/generators/finally/yield_throw.phpt
@@ -0,0 +1,24 @@
+--TEST--
+try { yield } finally { throw }
+--FILE--
+<?php
+function foo($f, $t) {
+ for ($i = $f; $i <= $t; $i++) {
+ try {
+ yield $i;
+ } finally {
+ throw new Exception;
+ }
+ }
+}
+foreach (foo(1, 5) as $x) {
+ echo $x, "\n";
+}
+--EXPECTF--
+1
+
+Fatal error: Uncaught exception 'Exception' in %s:%d
+Stack trace:
+#0 %s(%d): foo(1, 5)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/generators/finally/yield_yield.phpt b/Zend/tests/generators/finally/yield_yield.phpt
new file mode 100644
index 0000000000..76610ef1a4
--- /dev/null
+++ b/Zend/tests/generators/finally/yield_yield.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Try { yield } finally { yield }
+--FILE--
+<?php
+
+function foo() {
+ try {
+ echo "1";
+ yield "2";
+ echo "3";
+ } finally {
+ echo "4";
+ yield "5";
+ echo "6";
+ }
+ echo "7";
+}
+foreach (foo() as $x) {
+ echo $x;
+}
+--EXPECT--
+1234567
diff --git a/Zend/tests/generators/func_get_args.phpt b/Zend/tests/generators/func_get_args.phpt
new file mode 100644
index 0000000000..f8d3fa7c14
--- /dev/null
+++ b/Zend/tests/generators/func_get_args.phpt
@@ -0,0 +1,21 @@
+--TEST--
+func_get_args() can be used inside generator functions
+--FILE--
+<?php
+
+function gen() {
+ var_dump(func_get_args());
+ yield; // trigger generator
+}
+
+$gen = gen("foo", "bar");
+$gen->rewind();
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ string(3) "bar"
+}
diff --git a/Zend/tests/generators/generator_closure.phpt b/Zend/tests/generators/generator_closure.phpt
new file mode 100644
index 0000000000..bf80066015
--- /dev/null
+++ b/Zend/tests/generators/generator_closure.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Closures can be generators
+--FILE--
+<?php
+
+$genFactory = function() {
+ yield 1;
+ yield 2;
+ yield 3;
+};
+
+foreach ($genFactory() as $value) {
+ var_dump($value);
+}
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(3)
diff --git a/Zend/tests/generators/generator_closure_with_this.phpt b/Zend/tests/generators/generator_closure_with_this.phpt
new file mode 100644
index 0000000000..d5a4861e80
--- /dev/null
+++ b/Zend/tests/generators/generator_closure_with_this.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Non-static closures can be generators
+--FILE--
+<?php
+
+class Test {
+ public function getGenFactory() {
+ return function() {
+ yield $this;
+ };
+ }
+}
+
+$genFactory = (new Test)->getGenFactory();
+var_dump($genFactory()->current());
+
+?>
+--EXPECT--
+object(Test)#1 (0) {
+}
diff --git a/Zend/tests/generators/generator_in_multipleiterator.phpt b/Zend/tests/generators/generator_in_multipleiterator.phpt
new file mode 100644
index 0000000000..611dbc9652
--- /dev/null
+++ b/Zend/tests/generators/generator_in_multipleiterator.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Generators work properly in MultipleIterator
+--FILE--
+<?php
+
+function gen1() {
+ yield 'a';
+ yield 'aa';
+}
+
+function gen2() {
+ yield 'b';
+ yield 'bb';
+}
+
+$it = new MultipleIterator;
+$it->attachIterator(gen1());
+$it->attachIterator(gen2());
+
+foreach ($it as $values) {
+ var_dump($values);
+}
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(1) "a"
+ [1]=>
+ string(1) "b"
+}
+array(2) {
+ [0]=>
+ string(2) "aa"
+ [1]=>
+ string(2) "bb"
+}
diff --git a/Zend/tests/generators/generator_method.phpt b/Zend/tests/generators/generator_method.phpt
new file mode 100644
index 0000000000..b8196c171e
--- /dev/null
+++ b/Zend/tests/generators/generator_method.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Methods can be generators
+--FILE--
+<?php
+
+class Test implements IteratorAggregate {
+ protected $data;
+
+ public function __construct(array $data) {
+ $this->data = $data;
+ }
+
+ public function getIterator() {
+ foreach ($this->data as $value) {
+ yield $value;
+ }
+ }
+}
+
+$test = new Test(['foo', 'bar', 'baz']);
+foreach ($test as $value) {
+ var_dump($value);
+}
+
+?>
+--EXPECT--
+string(3) "foo"
+string(3) "bar"
+string(3) "baz"
diff --git a/Zend/tests/generators/generator_method_by_ref.phpt b/Zend/tests/generators/generator_method_by_ref.phpt
new file mode 100644
index 0000000000..cfe52fe67f
--- /dev/null
+++ b/Zend/tests/generators/generator_method_by_ref.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Generator methods can yield by reference
+--FILE--
+<?php
+
+class Test implements IteratorAggregate {
+ protected $data;
+
+ public function __construct(array $data) {
+ $this->data = $data;
+ }
+
+ public function getData() {
+ return $this->data;
+ }
+
+ public function &getIterator() {
+ foreach ($this->data as $key => &$value) {
+ yield $key => $value;
+ }
+ }
+}
+
+$test = new Test([1, 2, 3, 4, 5]);
+foreach ($test as &$value) {
+ $value *= -1;
+}
+
+var_dump($test->getData());
+
+?>
+--EXPECT--
+array(5) {
+ [0]=>
+ int(-1)
+ [1]=>
+ int(-2)
+ [2]=>
+ int(-3)
+ [3]=>
+ int(-4)
+ [4]=>
+ &int(-5)
+}
diff --git a/Zend/tests/generators/generator_returns_generator.phpt b/Zend/tests/generators/generator_returns_generator.phpt
new file mode 100644
index 0000000000..ad332a3be9
--- /dev/null
+++ b/Zend/tests/generators/generator_returns_generator.phpt
@@ -0,0 +1,18 @@
+--TEST--
+A generator function returns a Generator object
+--FILE--
+<?php
+
+function gen() {
+ // execution is suspended here, so the following never gets run:
+ echo "Foo";
+ // trigger a generator
+ yield;
+}
+
+$generator = gen();
+var_dump($generator instanceof Generator);
+
+?>
+--EXPECT--
+bool(true)
diff --git a/Zend/tests/generators/generator_rewind.phpt b/Zend/tests/generators/generator_rewind.phpt
new file mode 100644
index 0000000000..c4b5bbbdf4
--- /dev/null
+++ b/Zend/tests/generators/generator_rewind.phpt
@@ -0,0 +1,62 @@
+--TEST--
+A generator can only be rewinded before or at the first yield
+--FILE--
+<?php
+
+function gen() {
+ echo "before yield\n";
+ yield;
+ echo "after yield\n";
+ yield;
+}
+
+$gen = gen();
+$gen->rewind();
+$gen->rewind();
+$gen->next();
+
+try {
+ $gen->rewind();
+} catch (Exception $e) {
+ echo "\n", $e, "\n\n";
+}
+
+function &gen2() {
+ $foo = 'bar';
+ yield $foo;
+ yield $foo;
+}
+
+$gen = gen2();
+foreach ($gen as $v) { }
+try {
+ foreach ($gen as $v) { }
+} catch (Exception $e) {
+ echo $e, "\n\n";
+}
+
+function gen3() {
+ echo "in generator\n";
+
+ if (false) yield;
+}
+
+$gen = gen3();
+$gen->rewind();
+
+?>
+--EXPECTF--
+before yield
+after yield
+
+exception 'Exception' with message 'Cannot rewind a generator that was already run' in %s:%d
+Stack trace:
+#0 %s(%d): Generator->rewind()
+#1 {main}
+
+exception 'Exception' with message 'Cannot traverse an already closed generator' in %s:%d
+Stack trace:
+#0 %s(%d): unknown()
+#1 {main}
+
+in generator
diff --git a/Zend/tests/generators/generator_send.phpt b/Zend/tests/generators/generator_send.phpt
new file mode 100644
index 0000000000..074d815389
--- /dev/null
+++ b/Zend/tests/generators/generator_send.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Values can be sent back to the generator
+--FILE--
+<?php
+
+function gen() {
+ var_dump(yield "yield foo");
+ var_dump(yield "yield bar");
+}
+
+$gen = gen();
+var_dump($gen->current());
+$gen->send("send bar");
+var_dump($gen->current());
+$gen->send("send foo");
+
+?>
+--EXPECT--
+string(9) "yield foo"
+string(8) "send bar"
+string(9) "yield bar"
+string(8) "send foo"
diff --git a/Zend/tests/generators/generator_static_method.phpt b/Zend/tests/generators/generator_static_method.phpt
new file mode 100644
index 0000000000..cd9b450a76
--- /dev/null
+++ b/Zend/tests/generators/generator_static_method.phpt
@@ -0,0 +1,29 @@
+--TEST--
+A static method can be a generator
+--FILE--
+<?php
+
+class Test {
+ public static function gen() {
+ var_dump(get_class());
+ var_dump(get_called_class());
+ yield 1;
+ yield 2;
+ yield 3;
+ }
+}
+
+class ExtendedTest extends Test {
+}
+
+foreach (ExtendedTest::gen() as $i) {
+ var_dump($i);
+}
+
+?>
+--EXPECT--
+string(4) "Test"
+string(12) "ExtendedTest"
+int(1)
+int(2)
+int(3)
diff --git a/Zend/tests/generators/generator_throwing_during_function_call.phpt b/Zend/tests/generators/generator_throwing_during_function_call.phpt
new file mode 100644
index 0000000000..bd0d2448b7
--- /dev/null
+++ b/Zend/tests/generators/generator_throwing_during_function_call.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Stack is cleaned up properly when an exception is thrown during a function call
+--FILE--
+<?php
+
+function throwException() {
+ throw new Exception('test');
+}
+
+function gen() {
+ yield 'foo';
+ strlen("foo", "bar", throwException());
+ yield 'bar';
+}
+
+$gen = gen();
+
+var_dump($gen->current());
+
+try {
+ $gen->next();
+} catch (Exception $e) {
+ echo 'Caught exception with message "', $e->getMessage(), '"', "\n";
+}
+
+var_dump($gen->current());
+
+?>
+--EXPECT--
+string(3) "foo"
+Caught exception with message "test"
+NULL
diff --git a/Zend/tests/generators/generator_throwing_exception.phpt b/Zend/tests/generators/generator_throwing_exception.phpt
new file mode 100644
index 0000000000..f537c3fc77
--- /dev/null
+++ b/Zend/tests/generators/generator_throwing_exception.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Generators can throw exceptions
+--FILE--
+<?php
+
+function gen() {
+ yield 'foo';
+ throw new Exception('test');
+ yield 'bar';
+}
+
+$gen = gen();
+
+var_dump($gen->current());
+
+try {
+ $gen->next();
+} catch (Exception $e) {
+ echo 'Caught exception with message "', $e->getMessage(), '"', "\n";
+}
+
+var_dump($gen->current());
+
+?>
+--EXPECT--
+string(3) "foo"
+Caught exception with message "test"
+NULL
diff --git a/Zend/tests/generators/generator_throwing_in_foreach.phpt b/Zend/tests/generators/generator_throwing_in_foreach.phpt
new file mode 100644
index 0000000000..dbf20c2ca1
--- /dev/null
+++ b/Zend/tests/generators/generator_throwing_in_foreach.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Exceptions throwing by generators during foreach iteration are properly handled
+--FILE--
+<?php
+
+function gen() {
+ throw new Exception("foo");
+ yield; // force generator
+}
+
+foreach (gen() as $value) { }
+
+?>
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'foo' in %s:%d
+Stack trace:
+#0 %s(%d): gen()
+#1 {main}
+ thrown in %s on line %d
+
diff --git a/Zend/tests/generators/generator_with_keys.phpt b/Zend/tests/generators/generator_with_keys.phpt
new file mode 100644
index 0000000000..efb377679e
--- /dev/null
+++ b/Zend/tests/generators/generator_with_keys.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Generators can also yield keys
+--FILE--
+<?php
+
+function reverse(array $array) {
+ end($array);
+ while (null !== $key = key($array)) {
+ yield $key => current($array);
+ prev($array);
+ }
+}
+
+$array = [
+ 'foo' => 'bar',
+ 'bar' => 'foo',
+];
+
+foreach (reverse($array) as $key => $value) {
+ echo $key, ' => ', $value, "\n";
+}
+
+?>
+--EXPECT--
+bar => foo
+foo => bar
diff --git a/Zend/tests/generators/generator_with_nonscalar_keys.phpt b/Zend/tests/generators/generator_with_nonscalar_keys.phpt
new file mode 100644
index 0000000000..5ae55a1be0
--- /dev/null
+++ b/Zend/tests/generators/generator_with_nonscalar_keys.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Generators can return non-scalar keys
+--FILE--
+<?php
+
+function gen() {
+ yield [1, 2, 3] => [4, 5, 6];
+ yield (object) ['a' => 'b'] => (object) ['b' => 'a'];
+ yield 3.14 => 2.73;
+ yield false => true;
+ yield true => false;
+ yield null => null;
+}
+
+foreach (gen() as $k => $v) {
+ var_dump($k, $v);
+}
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+}
+array(3) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(5)
+ [2]=>
+ int(6)
+}
+object(stdClass)#3 (1) {
+ ["a"]=>
+ string(1) "b"
+}
+object(stdClass)#4 (1) {
+ ["b"]=>
+ string(1) "a"
+}
+float(3.14)
+float(2.73)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+NULL
+NULL
diff --git a/Zend/tests/generators/ignored_send_leak.phpt b/Zend/tests/generators/ignored_send_leak.phpt
new file mode 100644
index 0000000000..352ba406ba
--- /dev/null
+++ b/Zend/tests/generators/ignored_send_leak.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Ignoring a sent value shouldn't leak memory
+--FILE--
+<?php
+
+function gen() {
+ yield;
+}
+
+$gen = gen();
+$gen->send(NULL);
+
+echo "DONE";
+
+?>
+--EXPECT--
+DONE
diff --git a/Zend/tests/generators/nested_calls_with_die.phpt b/Zend/tests/generators/nested_calls_with_die.phpt
new file mode 100644
index 0000000000..f43d89ba21
--- /dev/null
+++ b/Zend/tests/generators/nested_calls_with_die.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Test nested calls with die() in a generator
+--FILE--
+<?php
+
+function gen() {
+ die('Test');
+ yield; // force generator
+}
+
+function function_with_3_args() {
+ $gen = gen();
+ $gen->rewind();
+}
+
+function function_with_4_args() {
+ function_with_3_args(4, 5, 6);
+}
+
+function outerGen() {
+ function_with_4_args(0, 1, 2, 3);
+ yield; // force generator
+}
+
+$outerGen = outerGen();
+$outerGen->rewind();
+
+?>
+--EXPECT--
+Test
diff --git a/Zend/tests/generators/nested_method_calls.phpt b/Zend/tests/generators/nested_method_calls.phpt
new file mode 100644
index 0000000000..0d640b31c6
--- /dev/null
+++ b/Zend/tests/generators/nested_method_calls.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Yield can be used in nested method calls
+--FILE--
+<?php
+
+class A {
+ function foo() {
+ echo "Called A::foo\n";
+ }
+}
+
+class B {
+ function foo() {
+ echo "Called B::foo\n";
+ }
+}
+
+function gen($obj) {
+ $obj->foo($obj->foo(yield));
+}
+
+$g1 = gen(new A);
+$g1->current();
+
+$g2 = gen(new B);
+$g2->current();
+
+$g1->next();
+$g2->next();
+
+?>
+--EXPECT--
+Called A::foo
+Called A::foo
+Called B::foo
+Called B::foo
diff --git a/Zend/tests/generators/no_foreach_var_leaks.phpt b/Zend/tests/generators/no_foreach_var_leaks.phpt
new file mode 100644
index 0000000000..62743895eb
--- /dev/null
+++ b/Zend/tests/generators/no_foreach_var_leaks.phpt
@@ -0,0 +1,19 @@
+--TEST--
+foreach() (and other) variables aren't leaked on premature close
+--FILE--
+<?php
+
+function gen(array $array) {
+ foreach ($array as $value) {
+ yield $value;
+ }
+}
+
+$gen = gen(['Foo', 'Bar']);
+var_dump($gen->current());
+
+// generator is closed here, without running SWITCH_FREE
+
+?>
+--EXPECT--
+string(3) "Foo"
diff --git a/Zend/tests/generators/send_after_close.phpt b/Zend/tests/generators/send_after_close.phpt
new file mode 100644
index 0000000000..806baf8cee
--- /dev/null
+++ b/Zend/tests/generators/send_after_close.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Calls to send() after close should do nothing
+--FILE--
+<?php
+
+function gen() { var_dump(yield); }
+
+$gen = gen();
+$gen->send('foo');
+$gen->send('bar');
+
+?>
+--EXPECT--
+string(3) "foo"
diff --git a/Zend/tests/generators/send_returns_current.phpt b/Zend/tests/generators/send_returns_current.phpt
new file mode 100644
index 0000000000..27ba74bc1b
--- /dev/null
+++ b/Zend/tests/generators/send_returns_current.phpt
@@ -0,0 +1,20 @@
+--TEST--
+$generator->send() returns the yielded value
+--FILE--
+<?php
+
+function reverseEchoGenerator() {
+ $data = yield;
+ while (true) {
+ $data = (yield strrev($data));
+ }
+}
+
+$gen = reverseEchoGenerator();
+var_dump($gen->send('foo'));
+var_dump($gen->send('bar'));
+
+?>
+--EXPECT--
+string(3) "oof"
+string(3) "rab"
diff --git a/Zend/tests/generators/throw_already_closed.phpt b/Zend/tests/generators/throw_already_closed.phpt
new file mode 100644
index 0000000000..e918e540ab
--- /dev/null
+++ b/Zend/tests/generators/throw_already_closed.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Generator::throw() on an already closed generator
+--FILE--
+<?php
+
+function gen() {
+ yield;
+}
+
+$gen = gen();
+$gen->next();
+$gen->next();
+var_dump($gen->valid());
+$gen->throw(new Exception('test'));
+
+?>
+--EXPECTF--
+bool(false)
+
+Fatal error: Uncaught exception 'Exception' with message 'test' in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/generators/throw_caught.phpt b/Zend/tests/generators/throw_caught.phpt
new file mode 100644
index 0000000000..c5e9d81ebc
--- /dev/null
+++ b/Zend/tests/generators/throw_caught.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Generator::throw() where the exception is caught in the generator
+--FILE--
+<?php
+
+function gen() {
+ echo "before yield\n";
+ try {
+ yield;
+ } catch (RuntimeException $e) {
+ echo $e, "\n\n";
+ }
+
+ yield 'result';
+}
+
+$gen = gen();
+var_dump($gen->throw(new RuntimeException('Test')));
+
+?>
+--EXPECTF--
+before yield
+exception 'RuntimeException' with message 'Test' in %s:%d
+Stack trace:
+#0 {main}
+
+string(6) "result"
diff --git a/Zend/tests/generators/throw_not_an_exception.phpt b/Zend/tests/generators/throw_not_an_exception.phpt
new file mode 100644
index 0000000000..d93903e215
--- /dev/null
+++ b/Zend/tests/generators/throw_not_an_exception.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Generator::throw() with something that's not an exception
+--FILE--
+<?php
+
+function gen() {
+ yield;
+}
+
+$gen = gen();
+$gen->throw(new stdClass);
+
+?>
+--EXPECTF--
+Fatal error: Exceptions must be valid objects derived from the Exception base class in %s on line %d
diff --git a/Zend/tests/generators/throw_rethrow.phpt b/Zend/tests/generators/throw_rethrow.phpt
new file mode 100644
index 0000000000..65044ee3f3
--- /dev/null
+++ b/Zend/tests/generators/throw_rethrow.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Generator::throw() where the generator throws a different exception
+--FILE--
+<?php
+
+function gen() {
+ echo "before yield\n";
+ try {
+ yield;
+ } catch (RuntimeException $e) {
+ echo 'Caught: ', $e, "\n\n";
+
+ throw new LogicException('new throw');
+ }
+}
+
+$gen = gen();
+var_dump($gen->throw(new RuntimeException('throw')));
+
+?>
+--EXPECTF--
+before yield
+Caught: exception 'RuntimeException' with message 'throw' in %s:%d
+Stack trace:
+#0 {main}
+
+
+Fatal error: Uncaught exception 'LogicException' with message 'new throw' in %s:%d
+Stack trace:
+#0 [internal function]: gen()
+#1 %s(%d): Generator->throw(Object(RuntimeException))
+#2 {main}
+ thrown in %s on line %d
+
diff --git a/Zend/tests/generators/throw_uncaught.phpt b/Zend/tests/generators/throw_uncaught.phpt
new file mode 100644
index 0000000000..f06cff1b8e
--- /dev/null
+++ b/Zend/tests/generators/throw_uncaught.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Generator::throw() where the exception is not caught in the generator
+--FILE--
+<?php
+
+function gen() {
+ yield 'thisThrows';
+ yield 'notReached';
+}
+
+$gen = gen();
+var_dump($gen->throw(new RuntimeException('test')));
+
+?>
+--EXPECTF--
+Fatal error: Uncaught exception 'RuntimeException' with message 'test' in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/generators/unused_return_value.phpt b/Zend/tests/generators/unused_return_value.phpt
new file mode 100644
index 0000000000..ddce64542c
--- /dev/null
+++ b/Zend/tests/generators/unused_return_value.phpt
@@ -0,0 +1,13 @@
+--TEST--
+There shouldn't be any leaks when the genertor's return value isn't used
+--FILE--
+<?php
+
+function gen($foo) { yield; }
+
+gen('foo'); // return value not used
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/generators/xrange.phpt b/Zend/tests/generators/xrange.phpt
new file mode 100644
index 0000000000..4d8b60fa90
--- /dev/null
+++ b/Zend/tests/generators/xrange.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Simple generator xrange() test
+--FILE--
+<?php
+
+function xrange($start, $end, $step = 1) {
+ for ($i = $start; $i <= $end; $i += $step) {
+ yield $i;
+ }
+}
+
+foreach (xrange(10, 20, 2) as $i) {
+ var_dump($i);
+}
+
+?>
+--EXPECT--
+int(10)
+int(12)
+int(14)
+int(16)
+int(18)
+int(20)
diff --git a/Zend/tests/generators/yield_array_key.phpt b/Zend/tests/generators/yield_array_key.phpt
new file mode 100644
index 0000000000..5afba00de8
--- /dev/null
+++ b/Zend/tests/generators/yield_array_key.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Array keys can be yielded from generators
+--FILE--
+<?php
+
+function gen() {
+ yield [] => 1;
+}
+
+$gen = gen();
+var_dump($gen->key());
+var_dump($gen->current());
+
+?>
+--EXPECT--
+array(0) {
+}
+int(1)
diff --git a/Zend/tests/generators/yield_array_offset_by_ref.phpt b/Zend/tests/generators/yield_array_offset_by_ref.phpt
new file mode 100644
index 0000000000..544108e64d
--- /dev/null
+++ b/Zend/tests/generators/yield_array_offset_by_ref.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Array offsets can be yielded by reference
+--FILE--
+<?php
+
+function &gen(array &$array) {
+ yield $array[0];
+}
+
+$array = [1, 2, 3];
+$gen = gen($array);
+foreach ($gen as &$val) {
+ $val *= -1;
+}
+var_dump($array);
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ &int(-1)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+}
diff --git a/Zend/tests/generators/yield_by_reference.phpt b/Zend/tests/generators/yield_by_reference.phpt
new file mode 100644
index 0000000000..dba0791c0d
--- /dev/null
+++ b/Zend/tests/generators/yield_by_reference.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Generators can yield by-reference
+--FILE--
+<?php
+
+function &iter(array &$array) {
+ foreach ($array as $key => &$value) {
+ yield $key => $value;
+ }
+}
+
+$array = [1, 2, 3];
+$iter = iter($array);
+foreach ($iter as &$value) {
+ $value *= -1;
+}
+var_dump($array);
+
+$array = [1, 2, 3];
+foreach (iter($array) as &$value) {
+ $value *= -1;
+}
+var_dump($array);
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ int(-1)
+ [1]=>
+ int(-2)
+ [2]=>
+ &int(-3)
+}
+array(3) {
+ [0]=>
+ int(-1)
+ [1]=>
+ int(-2)
+ [2]=>
+ &int(-3)
+}
diff --git a/Zend/tests/generators/yield_closure.phpt b/Zend/tests/generators/yield_closure.phpt
new file mode 100644
index 0000000000..e380b29946
--- /dev/null
+++ b/Zend/tests/generators/yield_closure.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Generator shouldn't crash if last yielded value is a closure
+--FILE--
+<?php
+
+function gen() {
+ yield function() {};
+}
+
+$gen = gen();
+$gen->next();
+
+echo "Done!";
+
+?>
+--EXPECT--
+Done!
diff --git a/Zend/tests/generators/yield_during_function_call.phpt b/Zend/tests/generators/yield_during_function_call.phpt
new file mode 100644
index 0000000000..21071f9fb4
--- /dev/null
+++ b/Zend/tests/generators/yield_during_function_call.phpt
@@ -0,0 +1,15 @@
+--TEST--
+"yield" can occur during a function call
+--FILE--
+<?php
+
+function gen() {
+ var_dump(str_repeat("x", yield));
+}
+
+$gen = gen();
+$gen->send(10);
+
+?>
+--EXPECT--
+string(10) "xxxxxxxxxx"
diff --git a/Zend/tests/generators/yield_during_method_call.phpt b/Zend/tests/generators/yield_during_method_call.phpt
new file mode 100644
index 0000000000..3a9914d58b
--- /dev/null
+++ b/Zend/tests/generators/yield_during_method_call.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Yield can be used during a method call
+--FILE--
+<?php
+
+class A {
+ public function b($c) {
+ echo $c, "\n";
+ }
+}
+
+function gen() {
+ $a = new A;
+ $a->b(yield);
+}
+
+$gen = gen();
+$gen->send('foo');
+
+// test resource cleanup
+$gen = gen();
+$gen->rewind();
+unset($gen);
+
+?>
+--EXPECT--
+foo
diff --git a/Zend/tests/generators/yield_in_finally.phpt b/Zend/tests/generators/yield_in_finally.phpt
new file mode 100644
index 0000000000..805484ad1d
--- /dev/null
+++ b/Zend/tests/generators/yield_in_finally.phpt
@@ -0,0 +1,29 @@
+--TEST--
+yield can be used in finally (apart from forced closes)
+--FILE--
+<?php
+
+function gen() {
+ try {
+ echo "before return\n";
+ return;
+ echo "after return\n";
+ } finally {
+ echo "before yield\n";
+ yield "yielded value";
+ echo "after yield\n";
+ }
+
+ echo "after finally\n";
+}
+
+$gen = gen();
+var_dump($gen->current());
+$gen->next();
+
+?>
+--EXPECTF--
+before return
+before yield
+string(%d) "yielded value"
+after yield
diff --git a/Zend/tests/generators/yield_in_parenthesis.phpt b/Zend/tests/generators/yield_in_parenthesis.phpt
new file mode 100644
index 0000000000..4a603f4cc1
--- /dev/null
+++ b/Zend/tests/generators/yield_in_parenthesis.phpt
@@ -0,0 +1,23 @@
+--TEST--
+No additional parenthesis are required around yield if they are already present
+--FILE--
+<?php
+
+function gen() {
+ if (yield $foo); elseif (yield $foo);
+ if (yield $foo): elseif (yield $foo): endif;
+ while (yield $foo);
+ do {} while (yield $foo);
+ switch (yield $foo) {}
+ (yield $foo);
+ die(yield $foo);
+ func(yield $foo);
+ $foo->func(yield $foo);
+ new Foo(yield $foo);
+}
+
+echo "Done";
+
+?>
+--EXPECT--
+Done
diff --git a/Zend/tests/generators/yield_ref_function_call_by_reference.phpt b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt
new file mode 100644
index 0000000000..e371affd92
--- /dev/null
+++ b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt
@@ -0,0 +1,24 @@
+--TEST--
+The result of a by-ref function call can be yielded just fine
+--FILE--
+<?php
+
+function &nop(&$var) {
+ return $var;
+}
+
+function &gen(&$var) {
+ yield nop($var);
+}
+
+$var = "foo";
+$gen = gen($var);
+foreach ($gen as &$varRef) {
+ $varRef = "bar";
+}
+
+var_dump($var);
+
+?>
+--EXPECT--
+string(3) "bar"
diff --git a/Zend/tests/generators/yield_without_value.phpt b/Zend/tests/generators/yield_without_value.phpt
new file mode 100644
index 0000000000..52292b737c
--- /dev/null
+++ b/Zend/tests/generators/yield_without_value.phpt
@@ -0,0 +1,27 @@
+--TEST--
+yield can be used without a value
+--FILE--
+<?php
+
+function recv() {
+ while (true) {
+ var_dump(yield);
+ }
+}
+
+$receiver = recv();
+var_dump($receiver->current());
+$receiver->send(1);
+var_dump($receiver->current());
+$receiver->send(2);
+var_dump($receiver->current());
+$receiver->send(3);
+
+?>
+--EXPECT--
+NULL
+int(1)
+NULL
+int(2)
+NULL
+int(3)