©
This document uses PHP Chinese website manual Release
The primary advantage of generators is their simplicity. Much less boilerplate code has to be written compared to implementing an Iterator class, and the code is generally much more readable. For example, the following function and class are equivalent:
<?php
function getLinesFromFile ( $fileName ) {
if (! $fileHandle = fopen ( $fileName , 'r' )) {
return;
}
while ( false !== $line = fgets ( $fileHandle )) {
yield $line ;
}
fclose ( $fileHandle );
}
// versus...
class LineIterator implements Iterator {
protected $fileHandle ;
protected $line ;
protected $i ;
public function __construct ( $fileName ) {
if (! $this -> fileHandle = fopen ( $fileName , 'r' )) {
throw new RuntimeException ( 'Couldn\'t open file "' . $fileName . '"' );
}
}
public function rewind () {
fseek ( $this -> fileHandle , 0 );
$this -> line = fgets ( $this -> fileHandle );
$this -> i = 0 ;
}
public function valid () {
return false !== $this -> line ;
}
public function current () {
return $this -> line ;
}
public function key () {
return $this -> i ;
}
public function next () {
if ( false !== $this -> line ) {
$this -> line = fgets ( $this -> fileHandle );
$this -> i ++;
}
}
public function __destruct () {
fclose ( $this -> fileHandle );
}
}
?>
This flexibility does come at a cost, however: generators are forward-only iterators, and cannot be rewound once iteration has started. This also means that the same generator can't be iterated over multiple times: the generator will need to either be rebuilt by calling the generator function again, or cloned via the clone keyword.
[#1] sergeyzsg at yandex dot ru [2014-06-08 12:32:03]
I think that this is bad generator example.
If user will not consume all lines then file will not be closed.
<?php
function getLinesFromFile($fileHandle) {
while (false !== $line = fgets($fileHandle)) {
yield $line;
}
}
if ($fileHandle = fopen($fileName, 'r')) {
fclose($fileHandle);
}
?>
[#2] sou at oand dot re [2013-07-04 13:34:19]
I think to be more similar the samples in the function, throw a new exception is better. But looking into "Generator syntax" session, you can see there this: "An empty return statement is valid syntax within a generator and it will terminate the generator.". By this point of view, we can imagine that this is just to exemplify an usage of the empty return.
[#3] mNOSPAMsenghaa at nospam dot gmail dot com [2013-06-20 19:45:42]
This hardly seems a fair comparison between the two examples, size-for-size. As noted, generators are forward-only, meaning that it should be compared to an iterator with a dummy rewind function defined. Also, to be fair, since the iterator throws an exception, shouldn't the generator example also throw the same exception? The code comparison would become more like this:
<?php
function getLinesFromFile($fileName) {
if (!$fileHandle = fopen($fileName, 'r')) {
throw new RuntimeException('Couldn\'t open file "' . $fileName . '"');
}
while (false !== $line = fgets($fileHandle)) {
yield $line;
}
fclose($fileHandle);
}
// versus...
class LineIterator implements Iterator {
protected $fileHandle;
protected $line;
protected $i;
public function __construct($fileName) {
if (!$this->fileHandle = fopen($fileName, 'r')) {
throw new RuntimeException('Couldn\'t open file "' . $fileName . '"');
}
}
public function rewind() { }
public function valid() {
return false !== $this->line;
}
public function current() {
return $this->line;
}
public function key() {
return $this->i;
}
public function next() {
if (false !== $this->line) {
$this->line = fgets($this->fileHandle);
$this->i++;
}
}
public function __destruct() {
fclose($this->fileHandle);
}
}
?>
The generator is still obviously much shorter, but this seems a more reasonable comparison.