| 
        php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | 
  [2019-01-24 12:57 UTC] knyjoh at gmail dot com
 Description:
------------
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
2.
3. function generator() {
4.   if (false) {
5.     yield 1; // Force generator
6.   }
7. }
8.
9. // Example 1
10. $values = generator();
11. foreach ($values as $value) {} // This is ok
12.
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
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits             
             | 
    |||||||||||||||||||||||||||||||||||||
            
                 
                Copyright © 2001-2025 The PHP GroupAll rights reserved.  | 
        Last updated: Tue Nov 04 07:00:01 2025 UTC | 
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 $it->valid(); // No extra output (current value is 1, so it is still valid) $it->next(); // --> 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.