|  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77515 A generator is closed by making a call to Generator::valid
Submitted: 2019-01-24 12:57 UTC Modified: 2020-10-16 14:11 UTC
Avg. Score:4.0 ± 1.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: knyjoh at gmail dot com Assigned:
Status: Duplicate Package: *General Issues
PHP Version: 7.3.1 OS:
Private report: No CVE-ID: None
 [2019-01-24 12:57 UTC] knyjoh at gmail dot com
A generator is closed by making a call to Generator::valid

It is not the expected behavior that a call to valid() will actually change a state. If it is considered a correct behavior it should be documented.
Note that the same problem exists with the current() function.

In example 1 it is clear that when reaching the loop the generator is not closed, because the loop does not fail. Traversing a closed generator is not allowed and would throw an exception, which means that the generator must be open at this stage.

In example 2 however, when reaching the loop an exception is thrown "Cannot traverse an already closed generator". The generator has therefore been closed by the call to valid().

Test script:
1. <?php
3. function generator() {
4.   if (false) {
5.     yield 1; // Force generator
6.   }
7. }
9. // Example 1
10. $values = generator();
11. foreach ($values as $value) {} // This is ok
13. // Example 2
14. $values = generator();
15. $values->valid();
16. foreach ($values as $value) {} // This is not ok

Actual result:
Fatal error: Uncaught Exception: Cannot traverse an already closed generator in /in/ab0KK:16
Stack trace:
#0 {main}
  thrown in /in/ab0KK on line 16


Add a Patch

Pull Requests

Add a Pull Request


AllCommentsChangesGit/SVN commitsRelated reports
 [2020-03-08 20:50 UTC] nawarian at gmail dot com
That's a tricky one. I think this is actually the intended behaviour.

The Generator::valid() will check if the *current* value is not null. In order to fetch Generator::current() it will have to init the generator function: this means, it will execute the Generator function until it finds the first yielded value.

The following snippet might get us some more clue:
function gen() {
  echo 1;
  yield 2;
  echo 3;
  if (false) {
    yield 4;

$it = gen();
$it->valid(); // true
// --> Output: 1
$it->current(); // 2 (yielded value)
// No extra output
// No extra output (current value is 1, so it is still valid)
// --> Output: 3
// No further yield statements found
$it->valid(); // false

In your particular example, the yield statement is never accessible, so calling $it->valid() will run through the whole function, making this generator invalid right away.

I don't see a better solution for this, though. I think the behaviour is correct.
 [2020-10-16 14:11 UTC]
-Status: Open +Status: Duplicate
 [2020-10-16 14:11 UTC]
Marking this as a duplicate of bug #75819, which is about the empty case specifically, where we arguably shouldn't throw due to the "at first yield" exception.
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Jun 18 22:01:29 2024 UTC