Based on responses in the comments I was able to answer my own question
The code in the PHP core that throws this exception states:
/* php-src/Zend/zend_generators.c */
zend_throw_exception(
zend_ce_ClosedGeneratorException,
"Generator yielded from aborted, no return value available",
0
);
There is an article on airbrake.io that goes into the details of ClosedGeneratorException:
A ClosedGeneratorException occurs when attempting to perform a traversal on a generator that has already been closed or terminated.
In other words, when the generator has run out of values, requesting a new value will trigger this exception.(1)
Based on this test from the PHP core I've constructed two scenario's where an ClosedGeneratorException is thrown (and caught).
This behaviour can easily be emulated by throwing an exception from within an inner generator (using the yield from syntax). The exception is only thrown from inside a Generator. (Although it can be caught outside the generator).
The two scenario's are attached below. Both example can be seen running on ideone.com(2)(3)
The output of both examples is as follows:
Catching ClosedGeneratorException inside a Generator(2)
Generator: 0
1
Generator: 1
Caught ClosedGeneratorException
Catching ClosedGeneratorException outside a Generator(3)
Generator: 0
Caught Generic Exception
Generator: 1
Caught ClosedGeneratorException
Footnotes
- The article provides an example to trigger the exception but it looks like the example does not work (as a regular exception is thrown).
- Catching
ClosedGeneratorException inside a Generator: https://ideone.com/FxTLe9
- Catching
ClosedGeneratorException outside a Generator: https://ideone.com/ipEgKx
Code examples
Catch ClosedGeneratorException inside a generator:
<?php
class CustomException extends Exception {}
function from() {
yield 1;
throw new CustomException();
}
function gen($gen) {
try {
yield from $gen;
} catch (\ClosedGeneratorException $e) {
yield "Caught ClosedGeneratorException";
} catch (\Exception $e) {
yield "Caught Generic Exception";
}
}
$gen = from();
$gens[] = gen($gen);
$gens[] = gen($gen);
foreach ($gens as $g) {
$g->current(); // init.
}
foreach ($gens as $i => $g) {
print "Generator: $i\n";
print $g->current()."\n";
$g->next();
}
Catch ClosedGeneratorException outside a generator:
<?php
class CustomException extends Exception {}
function from() {
yield 1;
throw new CustomException();
}
function gen($gen) {
yield from $gen;
}
$gen = from();
$gens[] = gen($gen);
$gens[] = gen($gen);
foreach ($gens as $g) {
$g->current(); // init.
}
foreach ($gens as $i => $g) {
print "Generator: $i\n";
try {
$g->current();
$g->next();
} catch (\ClosedGeneratorException $e) {
print "Caught ClosedGeneratorException\n";
} catch (\Exception $e) {
print "Caught Generic Exception\n";
}
}