-->

How can I catch errors and exceptions in Symfony2/

2019-02-25 21:16发布

问题:

I'd like to catch both errors and exceptions in my Silex app to wrap them in a custom JSON response that will always be returned to clients. I've found three basic methods:

$app->error()
Symfony\Component\Debug\ErrorHandler::register();
Symfony\Component\Debug\ExceptionHandler::register();

While I'm able to catch controller exceptions using error() I'm failing with php errors- they always end up in xdebug. I'm also failing to understand how error() and ExceptionHandler::register() interact with each other- do I need both? How can I make sure my error() response is JSON?

I've got the following example code right now:

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class Router extends Silex\Application
{
    function __construct() {
        parent::__construct();

        // routes
        $this->match('/{context}', array($this, 'handler'));

        // error handler
        $this->error(function(\Exception $e, $code) {
            return $this->json(array("error" => $e->getMessage()), $code);
        });
    }

    function handler(Request $request, $context) {
        // throw new \Exception('test'); // exception- this is caught
        $t = new Test(); // error- this is not caught

        return 'DONE';
    }
}

Symfony\Component\Debug\ErrorHandler::register();

$app = new Router();
$app->run();

回答1:

With ErrorHandler::register(); you can catch your errors like exceptions

Example

use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\ErrorHandler;

///bla bla bla some code

//catch all errors and convert them to exceptions
ErrorHandler::register();

try {
    //for example error happens here
    trigger_error( 'OH MY GOD, I AM ON FIRE' );
} catch ( \Exception $e ) {

    //for debugging you can do like this
    $handler = new ExceptionHandler();
    $handler->handle( $e );

    /*
    * ExceptionHendler class comments
    * It is mostly useful in debug mode to replace the default PHP/XDebug
    * output with something prettier and more useful.
    * so i suggest to create json response
    * and replace this code $handler = new ExceptionHandler();
    * $handler->handle( $e );
    */
    return new JsonResponse(
        array(
            'status' => 'error', 
            'message' => $e->getMessage()
        )
    );
}

with silex you can do next thing

ErrorHandler::register();
//register an error handler
$app->error(function ( \Exception $e, $code ) use ($app) {

    //return your json response here
    $error = array( 'message' => $e->getMessage() );

    return $app->json( $error, 200 );
});


回答2:

I encounter this case today, and find a way to bind fatal error with Silex.

First, you need to bind an exception handler with Silex :

$app->error(function (\Exception $exception, $code) {
    // Something that build a nice \Symfony\Component\HttpFoundation\Response. This part is up to you.
    $response = MyExceptionFormatter::format($exception, $code);

    // A Silex exception handler must return a Response.
    return $response;
});

Now, we use the Symfony Debug Component to convert our errors.

// Convert simple errors into nice Exception, automaticaly handled by Silex.
Symfony\Component\Debug\ErrorHandler::register();

// Now, the hard part, handle fatal error.
$handler = Symfony\Component\Debug\ExceptionHandler::register($app['debug']);
$handler->setHandler(function ($exception) use ($app) {

    // Create an ExceptionEvent with all the informations needed.
    $event = new Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent(
        $app,
        $app['request'],
        Symfony\Component\HttpKernel\HttpKernelInterface::MASTER_REQUEST,
        $exception
    );

    // Hey Silex ! We have something for you, can you handle it with your exception handler ?
    $app['dispatcher']->dispatch(Symfony\Component\HttpKernel\KernelEvents::EXCEPTION, $event);

    // And now, just display the response ;)
    $response = $event->getResponse();
    $response->sendHeaders();
    $response->sendContent();
    //$response->send(); We can't do that, something happened with the buffer, and Symfony still return its HTML.
});