php.net |  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
Votes:2
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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: knyjoh at gmail dot com
New email:
PHP Version: OS:

 

 [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

Patches

Pull Requests

History

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
$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.
 [2020-10-16 14:11 UTC] nikic@php.net
-Status: Open +Status: Duplicate
 [2020-10-16 14:11 UTC] nikic@php.net
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: Sun Dec 22 01:01:30 2024 UTC