Mark Baker Mark Baker - 1 month ago 7
PHP Question

Generator cannot be in a Closure

I'm creating a class that uses a generator to return values when a particular method is called, something like:

class test {
protected $generator;

private function getValueGenerator() {
yield from [1,1,2,3,5,8,13,21];
}

public function __construct() {
$this->generator = $this->getValueGenerator();
}

public function getValue() {
while($this->generator->valid()) {
$latitude = $this->generator->current();
$this->generator->next();
return $latitude;
}
throw new RangeException('End of line');
}
}

$line = new test();

try {
for($i = 0; $i < 10; ++$i) {
echo $line->getValue();
echo PHP_EOL;
}
} catch (Exception $e) {
echo $e->getMessage();
}


Which works perfectly well when the generator is defined as a method within the class itself.... but I want to make this more dynamic, and use a closure as the generator, something like:

class test {
public function __construct() {
$this->generator = function() {
yield from [1,1,2,3,5,8,13,21];
};
}
}


Unfortunately, when I try to run this, I get


Fatal error: Uncaught Error: Call to undefined method Closure::valid()


in the call to
getValue()


Can anybody explain the actual logic of why I can't call the generator this way? And how I might be able to use a closure rather than a hard-coded generator function?

Answer Source

In the first example you invoke the method, creating the generator:

$this->generator = $this->getValueGenerator();

In the second you do not invoke it, so it's merely a closure:

$this->generator = function() {
    yield from [1,1,2,3,5,8,13,21];
};

Invoking that closure should create the generator (PHP 7 if you don't want to assign an intermediate variable):

$this->generator = function() {
    yield from [1,1,2,3,5,8,13,21];
}();