Question: In PHP, what is the `ClosedGeneratorException`?

Question

In PHP, what is the `ClosedGeneratorException`?

Answers 1
Added at 2017-11-08 16:11
Tags
Question

When looking through the output of get_declared_classes() in PHP 7.0 and 7.1, I noticed a ClosedGeneratorException.

The manual does not make much mention of it and it does not seem to be listed on either the Predefined Exceptions or SPL exceptions entries in the manual.

Even the source-code does not hold much information about it.

So, what is the ClosedGeneratorException?

(What is it for? When does it occur? When should it be used?)

Answers to

In PHP, what is the `ClosedGeneratorException`?

nr: #1 dodano: 2017-11-09 13:11

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

  1. The article provides an example to trigger the exception but it looks like the example does not work (as a regular exception is thrown).
  2. Catching ClosedGeneratorException inside a Generator: https://ideone.com/FxTLe9
  3. 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";
    }
}
Source Show
◀ Wstecz