I am working with a PHP class which needs to accept multiple types of iterators and encompass them inside a unified wrapper. One of the types of iterators I need to support (and can!) is an anonymous function containing the yield keyword -- an anonymous generator, essentially.
Is there a way in PHP to test whether or not an anonymous function is a generator? The following is a list of methods I have tried (designed to show the outputs, not how I used them):
$anon = function() { yield 1; yield 2; }; // build anonymous generator
gettype($anon); // "object"
$anon instanceof \Traversable; // 0
$anon instanceof \Iterable; // 0
$anon instanceof \IteratorAggregate; // 0
$anon instanceof \Generator; // 0
$anon instanceof \Closure; // 1 -- but doesn't tell me whether or not the closure is actually generator
$anon = $anon(); // invoke, then run the same checks
gettype($anon); // "object"
$anon instanceof \Traversable; // 1 (!!!)
$anon instanceof \Iterable; // 0
$anon instanceof \IteratorAggregate; // 0
$anon instanceof \Generator; // 1 (!!!)
$anon instanceof \Closure; // 0
As you can see above, I can invoke the anonymous function and then determine whether or not the function is a traversable type, but in order to implement this in a lazy-loading fashion (for example, an anonymous function wrapper around a SQL statement call followed by a yield of each record), I cannot invoke the anonymous function before the foreach iteration.
Are there any methods / types in PHP which I am missing that can be used to determine whether or not an anonymous method which has not yet been invoked is a generator?
$anon = function() { echo 'INVOKED', PHP_EOL; yield 1; yield 2; };
$r = new ReflectionFunction($anon);
var_dump($r->isGenerator());
shows
bool(true);
INVOKED
isn't displayed at all, proving that the closure isn't invoked at any time