rfc:generators

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:generators [2012/09/01 15:56] – Close vote, RFC is accepted nikicrfc:generators [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 2: Line 2:
   * Date: 2012-06-05   * Date: 2012-06-05
   * Author: Nikita Popov <nikic@php.net>   * Author: Nikita Popov <nikic@php.net>
-  * Status: Accepted+  * Status: Implemented
  
 ===== Introduction ===== ===== Introduction =====
Line 189: Line 189:
          
     mixed send(mixed $value);     mixed send(mixed $value);
 +    mixed throw(Exception $exception);
 } }
 </code> </code>
Line 228: Line 229:
   * ''next'': Resumes the generator (unless the generator is already closed).   * ''next'': Resumes the generator (unless the generator is already closed).
   * ''send'': Sets the return value of the ''yield'' expression and resumes the generator (unless the generator is already closed). (More in the "Sending values" section.)   * ''send'': Sets the return value of the ''yield'' expression and resumes the generator (unless the generator is already closed). (More in the "Sending values" section.)
 +  * ''throw'': Throws an exception at the current suspension point in the generator. (More in the "Throwing into the generator" section.)
  
 ==== Yield syntax ==== ==== Yield syntax ====
Line 392: Line 394:
 $logger->send('Foo'); $logger->send('Foo');
 $logger->send('Bar'); $logger->send('Bar');
 +</code>
 +
 +==== Throwing into the generator ====
 +
 +Exceptions can be thrown into the generator using the ''Generator::throw()'' method. This will throw an exception in the generator's execution
 +context and then resume the generator. It is roughly equivalent to replacing the current ''yield'' expression with a ''throw'' statement and
 +resuming then. If the generator is already closed the exception will be thrown in the callers context instead (which is equivalent to replacing
 +the ''throw()'' call with a ''throw'' statement). The ''throw()'' method will return the next yielded value (if the exception is caught and no
 +other exception is thrown).
 +
 +An example of the functionality:
 +
 +<code php>
 +function gen() {
 +    echo "Foo\n";
 +    try {
 +        yield;
 +    } catch (Exception $e) {
 +        echo "Exception: {$e->getMessage()}\n";
 +    }
 +    echo "Bar\n";
 +}
 +
 +$gen = gen();
 +$gen->rewind();                     // echos "Foo"
 +$gen->throw(new Exception('Test')); // echos "Exception: Test"
 +                                    // and "Bar"
 </code> </code>
  
Line 430: Line 459:
 ==== Cloning a generator ==== ==== Cloning a generator ====
  
-Generators can be cloned, thus leaving two independent ''Generator'' objects with the same stateThis behavior can for example be +Generators cannot be cloned.
-used to create a ''rewindable'' function, which would make the passed generator rewindable: +
- +
-<code php> +
-class RewindableGenerator implements Iterator { +
-    protected $original; +
-    protected $current; +
-     +
-    public function __construct(Generator $generator) { +
-        $this->original = $generator; +
-        $this->current = null; +
-    } +
-     +
-    public function rewind() { +
-        if ($this->current) { $this->current->close();+
-        $this->current = clone $this->original; +
-        $this->current->rewind(); +
-    } +
-     +
-    public function valid() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->valid(); +
-    } +
-     +
-    public function current() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->current(); +
-    } +
-     +
-    public function key() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->key(); +
-    } +
-     +
-    public function next() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        $this->current->next(); +
-    } +
-     +
-    public function send($value) { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->send($value); +
-    } +
-     +
-    public function close() { +
-        $this->original->close(); +
-        if ($this->current) { +
-            $this->current->close(); +
-        } +
-    } +
-+
- +
-function rewindable(Generator $generator) { +
-    return new RewindableGenerator($generator); +
-+
-</code> +
- +
-It can be then used as follows: +
- +
-<code php> +
-function xrange($start, $end, $step = 1) { +
-    for ($i = $start; $i <= $end; $i += $step) { +
-        yield $i; +
-    } +
-+
- +
-$range = rewindable(xrange(0, 5)); +
-foreach ($range as $i) { +
-    echo $i, "\n"; +
-+
-foreach ($range as $i) { +
-    echo $i, "\n"; +
-+
-</code>+
  
-This will correctly output the 0..5 range twice.+Support for cloning was included in the initial version, but removed in PHP 5.5 Beta 3 due to implementational difficulties, unclear semantics and no particularly convincing use cases.
  
 ==== Closing a generator ==== ==== Closing a generator ====
rfc/generators.1346514961.txt.gz · Last modified: 2017/09/22 13:28 (external edit)