diff options
Diffstat (limited to 'Zend/tests/generators')
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) |
