Bug #77515 [Opn->Dup]: A generator is closed by making a call to Generator::valid

From: Date: Fri, 16 Oct 2020 14:11:51 +0000
Subject: Bug #77515 [Opn->Dup]: A generator is closed by making a call to Generator::valid
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-229666@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=77515&edit=1

 ID:                 77515
 Updated by:         nikic@php.net
 Reported by:        knyjoh at gmail dot com
 Summary:            A generator is closed by making a call to
                     Generator::valid
-Status:             Open
+Status:             Duplicate
 Type:               Bug
 Package:            *General Issues
 PHP Version:        7.3.1
 Block user comment: N
 Private report:     N

 New Comment:

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.


Previous Comments:
------------------------------------------------------------------------
[2020-03-08 20:50:08] 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.

------------------------------------------------------------------------
[2019-01-24 12:57:40] 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


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=77515&edit=1


Thread (3 messages)

« previous php.bugs (#229666) next »