hyperf/docs/en/exception-handler.md

4.9 KiB

Excepiton Handler

In Hyperf, all the business code excute on Worker Process. In this case, once any request has an exception that has not been caught, the corresponding Worker Process will be interrupted and exited, which is unacceptable for the service. Catch exceptions and output reasonable error content is also more friendly to the client. We can define different ExceptionHandlers for each server, and once there are exceptions that are not caught in the process, they will be passed to the registered ExceptionHandler for processing.

Customize an Exception Handling

Register Exception Handler

Currently, it only supports the registration of ExceptionHandler in the form of a configuration file. The configuration file is located in config/autoload/exceptions.php. Configure your custom exception handler under the corresponding server:

<?php
// config/autoload/exceptions.php
return [
    'handler' => [
        // The http here corresponds to the name value corresponding to the server in config/autoload/server.php
        'http' => [
            // The registration of the exception handler has done by configuring the complete class namespace address here
            \App\Exception\Handler\FooExceptionHandler::class,
        ],    
    ],
];

The order of each exception handler configuration array determines the order in which exceptions are passed between handlers.

Define Exception Handler

We can define a class (Class) anywhere and inherit the abstract class Hyperf\ExceptionHandler\ExceptionHandler and implement the abstract methods in it. As shown below:

<?php
namespace App\Exception\Handler;

use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use App\Exception\FooException;
use Throwable;

class FooExceptionHandler extends  ExceptionHandler
{
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // Determine that the caught exception is the wanted exception
        if ($throwable instanceof FooException) {
            // Formatted output
            $data = json_encode([
                'code' => $throwable->getCode(),
                'message' => $throwable->getMessage(),
            ], JSON_UNESCAPED_UNICODE);

            // Prevent bubbling
            $this->stopPropagation();
            return $response->withStatus(500)->withBody(new SwooleStream($data));
        }

        // Hand over to the next exception handler
        return $response;

        // Or directly shield the exception without processing
    }

    /**
     * Determine whether the exception handler needs to handle the exception or not
     */
    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}

Define Exception Class

<?php
namespace App\Exception;

use App\Constants\ErrorCode;
use Hyperf\Server\Exception\ServerException;
use Throwable;

class FooException extends ServerException
{
}

Trigger Exception


namespace App\Controller;

use App\Exception\FooException;

class IndexController extends AbstractController
{
    public function index()
    {
        throw new FooException('Foo Exception...', 800);
    }
}

In the example above, we assume that FooException is a thrown exception, and exception handlers are configured. When an uncaught exception has been thrown, it will be passed through the handler registration order. Imagine the processing as a pipe, the exception will not be passed once there are some handler calls $this->stopPropagation(). The default handler of Hyperf will be the last one to catch exceptions if there is no other handler to catch such exceptions.

Integrated Whoops

The framework provides Whoops integration.

Install Whoops first

composer require --dev filp/whoops

Then configure the special exception handler for Whoops.

// config/autoload/exceptions.php
return [
    'handler' => [
        'http' => [
            \Hyperf\ExceptionHandler\Handler\WhoopsExceptionHandler::class,
        ],    
    ],
];

As shown in the image:

whoops

Error Listener

The framework provides the error_reporting() error level listener Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler.

Configuration

Add a listener in config/autoload/listeners.php

<?php
return [
    \Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler::class
];

When a code similar to the following appears, \ErrorException will be thrown

<?php
try {
    $a = [];
    var_dump($a[1]);
} catch (\Throwable $throwable) {
    var_dump(get_class($throwable), $throwable->getMessage());
}

// string(14) "ErrorException"
// string(19) "Undefined offset: 1"

If no listener is configured, no exception will be thrown.

PHP Notice:  Undefined offset: 1 in IndexController.php on line 24

Notice: Undefined offset: 1 in IndexController.php on line 24
NULL