Generator exception handling

Posted by Joseph07 on Sun, 02 Jun 2019 22:41:45 +0200

This article is a summary of my research on PHP asynchronous programming. For a considerable number of PHPer, you may not know Generator, or you may not be familiar with the Generaotr process. Because Generator makes programs out of order. In view of my limited level, if you have different opinions, I would like to give you some advice. Thank you very much.

Exception handling in PHP

Starting with PHP 5, PHP provides try catch for exception handling. When we use catch to catch exceptions, a subsequent code execution occurs. Let's look at the following examples.

try {
    throw new Exception('e');
} catch (Exception $e) {
    echo $e->getMessage(); // output: e
}

echo 2; // output: 2

If we don't catch exceptions, the later code won't execute.

throw new Exception('e'); // throw an exception

echo 2; // not execute

throw method of Generator

In PHP, Generator provides throw methods to throw exceptions. The use is the same as normal exceptions, except that the throw keyword is changed to a method call.

function gen()
{
    yield 0;
    yield 1;
    yield 2;
    yield 3;
}

$gen = gen();

$gen->throw(new Exception('e')); // throw an exception

var_dump($gen->valid()); // output: false

echo 2; // not execute

Similarly, we can capture this exception by try catch.

try {
    $gen->throw(new Exception('e'));
} catch (Exception $e) {
    echo $e->getMessage(); // output: e
}

var_dump($gen->valid()); // output: false

echo 2; // output: 2

We can see that when we use throw to throw an exception, the valid of the current generator becomes false. But considering the following situation, what happens when we catch exceptions in generator functions after calling throw methods outside? Let's look at the following examples.

function gen()
{
    yield 0;
    try {
        yield;
    } catch (Exception $e) {
        echo $e->getMessage(); // output: e
    }
    yield 2;
    yield 3;
}

$gen = gen();
$gen->next(); // reach the point of catching exception
$gen->throw(new Exception('e'));

var_dump($gen->valid()); // output: true

echo 2; // output: 2

When we capture exceptions thrown from throw methods by generator functions, the generator is still valid. But if you just call the throw method as before, the generator is over.

Throw an exception in the generator function

function gen()
{
    yield 0;
    throw new Exception('e');
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();

$gen->current(); // throw an exception

var_dump($gen->valid()); // output: false

echo 2; // not execute

What we've seen before is that we call the throw method to throw an exception. Then in the generator function, an exception is thrown but not captured in the generator function, and the result is the same. Similarly, if an exception is caught in the generator function, it is the same as the previous example.

After understanding the above examples, we need to consider what would happen if there were nested generators.

Nested Generator

When we have yield ed another generator function in one generator function, it becomes a nested generator. Let's look at the following examples.

function subGen()
{
    yield 1;
    throw new Exception('e');
    yield 4;
}

function gen()
{
    yield 0;
    yield subGen();
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();
$gen->current()->next(); // throw an exception

echo 2; // not execute

For nested generators, if an exception is thrown in the sub-generator, it will be thrown up level by level until the end without catching the exception.

Just now we tried to change the return value of valid to false after throwing an exception. So in nested generators, is that the same? Let's capture exceptions so that the program can continue to execute. Let's look at the following example.

function subGen()
{
    yield 1;
    throw new Exception('e');
    yield 4;
}

function gen()
{
    yield 0;
    yield subGen();
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();
try {
    $gen->current()->next();
} catch (Exceprion $e) {
    echo $e->getMessage(); //output: e
}

var_dump($gen->valid()); // output: true

echo 2; // output: 2

Therefore, when the child generator throws an exception and is captured normally during the iteration, the parent generator will not be affected, and the return value of valid is still true.

summary

Here's a summary of generator exception handling.

  • If an exception is thrown in the generator or an exception is thrown in the throw method, the iteration of the generator will end and the valid will become false.
  • An exception is thrown in the generator, and the exception is captured in the iteration process. The iteration of the generator will still end and the valid will still become false.
  • An exception is thrown in the generator and captured in the generator. The iteration of the generator will not end and the valid will return true.
  • In a nested generator, if an exception is thrown by a child generator, it will only affect the child generator, not the parent generator.

Epilogue

Yield provides us with tools to implement semi-collaboration using PHP. Recently, the use of yield to implement semi-cooperative processes has been studied, and in this process, exception handling is very important. However, the operation of yield determines that exception handling is difficult to understand. So I spent a few days trying out all kinds of possibilities and coming to these conclusions. Of course, due to my limited level, if there are any mistakes, I would like to give you some advice.

Topics: PHP Programming