4

I know that this can be super easily archieved without generators, however I want to understand generators better. Therefore please don't suggest using something else.


I've got a class that generates filenames for screenshots (selenium):

class ScreenshotName
{

    private $counter = 0;

    public function screenshotNameIterator()
    {
         while(true) {
            yield sprintf("screenshot-%s-%s.png", date("Y-m-d\\TH:i:s"), ++$this->counter);
        }
    }
}

Now my question is: can I use such a generator in any other context than a foreach loop? e.g.

(new ScreenshotName())->screenshotNameIterator()->next()

for me this always returns null, and if I debug, it never enters the generator method. Also the PHP docs don't really mention this.

So my question is: is there a documented way to use a generator in a different context than a for-loop?

NikiC
  • 100,734
  • 37
  • 191
  • 225
Matthias
  • 2,622
  • 1
  • 18
  • 29
  • This post explains it in very good detail: http://stackoverflow.com/questions/17483806/what-does-yield-mean-in-php – cyber_rookie Apr 15 '15 at 07:05
  • 2
    you need to use `iterator()->current()` to get the value, and `iterator()->next()` to increment it. two step process. – pala_ Apr 15 '15 at 07:38

2 Answers2

2

There is a documented way to do this. In fact Generator does implement the iterator interface as you can see it on this page.

In fact the foreach keyword only work on iterators. So if you can use foreach on a generator you must be able to call next

Here is a sample code using next instead of foreach :

<?php

function evenNumbers() {
    for ($i = 0;; $i++) {
        yield 2*$i;
    }
}

$gen = evenNumbers();
$gen->next();
echo $gen->current();

?>
DARK_DUCK
  • 1,727
  • 2
  • 12
  • 22
  • A code sample to illustrate would be really apropos here, instead of vague gesturing towards the documentation. – deceze Apr 15 '15 at 07:11
  • 1
    I added it as you suggested it – DARK_DUCK Apr 15 '15 at 07:36
  • @DARK_DUCK: "In fact the foreach keyword only work on iterators". Being pedantic, I think it's incorrect. What about native arrays? I made some experiments and now I believe that even generators are handled inside `foreach` in some native way, not as iterators. – 3DFace Oct 13 '15 at 11:52
0

Here is a complete code snippet to go with Dark Duck's answer. That is, it demonstrates how to use Iterator interface to iterate over all values. (Dark Duck's code is not complete.) Based on a comment by robert_e_lee on Iterator doc:

$gen = evenNumbers();
// If in a method that re-uses an iterator, may want the next line.
//OPTIONAL $gen->rewind();
while ($gen->valid()) {
    // This is the value:
    $value = $it->current();
    // You might or might not care about the key:
    //OPTIONAL $key = $it->key();

    // ... use $value and/or $key ...

    $it->next();
}

The above is roughly equivalent to:

foreach (evenNumbers() as $value) {
    // ... use $value ...
}

or

foreach (evenNumbers() as $key => $value) {
    // ... use $value and/or $key ...
}
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196