Home Development of Websites Exception handling in Yii2

Exception handling in Yii2

by admin

In Yii2, by default an Exception Handler is responsible for handling exceptions. If an awkward situation occurs while processing a request (for example, an invalid data is received from a client), an exception can be thrown. The handler will generate a human-readable response.
The interesting thing is that in this case the "Warning: Uncaught exception" error is not printed to the error log. One might get the impression that all exceptions are captured by the framework. But it is not so. Some time ago our project was affected by a monitoring tool (New Relic in our case) which displays information about all exceptions thrown in errors (exactly as "Warning: Uncaught exception") and considers these exceptions as unhandled. Something had to be done about it.
Below I will tell you about the exception handling scheme that I finally chose. It’s quite possible that it will be useful for someone else.

Why processed exceptions are considered uncaught

In Yii2, the error handler is defined by the function set_exception_handler() This function defines a handler for uncaught exceptions. Exceptions are handled, but they are not caught. For exceptions to be considered caught, they still need to be caught explicitly, by wrapping calls to try-catch. I didn’t really want to do this in each action of each controller. I find it convenient to have a single intercept point.
In Yii2, as it turns out, there is a ready-made solution for this – if you throw out the exception yiibaseExitException (or its descendant), then such an exception is handled by the framework tools. For illustration, here is how it is done in Application::run():

public function run(){try {$this-> state = self::STATE_BEFORE_REQUEST;$this-> trigger(self::EVENT_BEFORE_REQUEST);$this-> state = self::STATE_HANDLING_REQUEST;$response = $this-> handleRequest($this-> getRequest());$this-> state = self::STATE_AFTER_REQUEST;$this-> trigger(self::EVENT_AFTER_REQUEST);$this-> state = self::STATE_SENDING_RESPONSE;$response-> send();$this-> state = self::STATE_END;return $response-> exitStatus;} catch (ExitException $e) {$this-> end($e-> statusCode, isset($response) ? $response : null);return $e-> statusCode;}}

"Good" and "bad" exceptions

I’m comfortable throwing exceptions to end query processing in two cases.

  1. If nothing is broken, it’s just a small misunderstanding – a crooked web request to the client was received or some not particularly critical requested data was not found.
  2. If something is broken.

In the first case, you don’t need to log the event as an error and you don’t need to deal with it.
In the second case, you have to log the problem to know what happened and deal with it.
For the first case, I created such a class inherited from yiibaseExitException. To make sure the script doesn’t result in a blank page, it generates an answer right in the exception.

<?phpnamespace appcomponents;use yii;use yiibaseExitException;/*** Exception that will be automatically handled at the yiibaseApplication level*/class GoodException Extends ExitException{/*** Constructor* @param string $name Name (will be printed as page name)* @param string $message Detailed error message* @param int $code Error code* @param int $status Response status* @param Exception $previous The previous exception*/public function __construct($name, $message = null, $code = 0, $status = 500, Exception $previous = null){# Generate response$view = yii::$app-> getView();$response = yii::$app-> getResponse();$response-> data = $view-> renderFile('@app/views/exception.php', ['name' => $name, 'message' => $message, ]);# return the desired status (by default, give it 500)$response-> setStatusCode($status);parent::__construct($status, $message, $code, $previous);}}

And another introduction is also created.

<?php/* @var $this yiiwebView *//* @var $name string *//* @var $message string *//* @var $exception Exception */use yiihelpersHtml;$this-> title = $name;?><?php $this-> beginContent('@app/views/layouts/main.php'); ?><div class="site-error"><h1> <?= Html::encode($this-> title) ?></h1><div class="alert alert-danger"><?= nl2br(Html::encode($message)) ?> </div><p>The above error occurred while the Web server was processing your request.</p><p>Please contact us if you think this is a server error. Thank you.</p></div><?php $this-> endContent(); ?>


Thus, to throw out the "cultural" exception, we write :

# Throw an exception that will be caughtthrow new GoodException('Problem', 'This problem is neatly handled');

Such exceptions will be caught and a tidy response will be returned to the client. The error log will not contain such events.
All other exceptions, unless you explicitly catch them, will not be caught. And they will be caught in errors. That is, for the second case you can write

throw new yiibaseErrorException('This problem is critical');

You may also like