Лучшая практика регистрации пользовательских исключений в PHP
У меня есть пользовательское исключение (которое может быть расширено в других пользовательских исключениях). Мой проект требует журнала всех пользовательских исключений (и всех его потомков), что происходит. У меня есть регистратор, который может регистрировать customException (и все остальное). Один из способов сделать это - явно регистрировать исключение, когда оно обрабатывается следующим образом.
try{
//some exception occur
}
catch(customeException $e)
{
$log->logException($e);
$e->showMessage(); // or do anything that we have to do with the error.
}
Так как мы регистрируем все customExceptions, я могу думать иначе, обновляя конструктор customException и регистрируя исключение прямо в конструкторе. Таким образом, он гарантирует, что все customException регистрируются. Однако, если мы пойдем по этому пути, мои вопросы:
- Как внедрить регистратор в customException?
- Будет ли это против принципа SRP?
- Будет ли это считаться плохой практикой в смысле ООП или какова лучшая практика в этом отношении?
1 ответ
Я думаю, что вставлять регистратор в CustomException не правильно, потому что (как вы указали) он нарушает SRP и увеличивает сложность ваших классов исключений.
Я предлагаю вам отделить Exception от ExceptionHandler. Класс исключения должен содержать только информацию о том, "что (и где) пошло не так". ExceptionHandler отвечает за регистрацию исключений (и выполнение какой-либо другой работы с исключением при необходимости).
Таким образом, вы можете настроить один глобальный ExceptionHandler
(используя set_exception_handler и set_error_handler или некоторый основанный на фреймворке механизм обработки исключений, такой как ExceptionListener symfony), который будет перехватывать все необработанные исключения.
<?php
class ExceptionHandler {
/**
* @var Logger
*/
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function handle(Throwable $e)
{
$this->logger->logException($e);
}
}
В коде приложения вы все еще можете генерировать и ловить исключения. Я думаю, что есть 4 общие ситуации.
Полностью восстанавливаемые исключения
Это общий способ обработки восстанавливаемых исключений - таких ситуаций, когда вы вообще не хотите терпеть неудачу, но вам нужно что-то делать, когда такое исключение происходит.
<?php
try {
$methodThatThrowsException();
}
catch (DoesNotMatterException $e) {
// do some stuff and continue the execution
// note, that this exception won't be logged
}
Восстанавливаемое зарегистрированное исключение
То же, что и предыдущий, но вы хотите записать это исключение.
<?php
try {
$methodThatThrowsException();
}
catch (NonCriticalExceptionThatShouldBeLogged $e) {
$this->exceptionHandler->handle($e); // log exception
// do some stuff and continue the execution
}
Невосстановимые исключения с "финализатором"
Вы хотите выполнить определенную бизнес-логику, а затем потерпеть неудачу. Вы можете поймать исключение, обработать его и затем выбросить снова. Глобальный обработчик исключений будет обрабатывать это исключение и регистрировать его.
<?php
try {
$methodThatThrowsException();
}
catch (CriticalException $e) {
// do some stuff like cleanup/transaction rollback
throw $e;
}
Невосстановимые исключения
Если вы хотите просто зарегистрировать исключение и потерпеть неудачу, вы можете просто выбросить это исключение, и глобальный обработчик исключений поймает и зарегистрирует его.
<?php
$methodThatThrowsException();
// ExceptionHandler::handle will be executed
Сервисы могут быть внедрены в другие сервисы, если они актуальны и поддерживают SRP или делают что-то системное, например, ведение журнала ошибок. Однако исключение на самом деле не является службой, и оно должно быть связано только с управлением данными исключения, основанными на том, что они могут быть выброшены.
Я думаю, что регистрация в классе исключений также является проблемой из-за:
- Нет контроля над тем, какой тип журнала использовать, предупреждение, ошибка, критический и т. Д. Если вы не сделаете его запутанным / сложным
- Нет контроля над тем, регистрироваться или нет. Существуют сценарии, в которых может произойти исключение, но это известный сценарий, который не требует обработки ошибок и т. Д.
- Вам нужно будет передать различные другие данные журнала в исключение, что, возможно, немного пахнет в зависимости от данных, которые вы хотите зарегистрировать.
В момент перехвата исключения зарегистрируйте его, а затем создайте новое, чтобы вызывающий также мог поймать, зарегистрировать, выбросить. Повторите это во всем приложении, чтобы каждый вызов метода thinga, который должен перехватывать потенциальное исключение, регистрировал и генерировал собственное исключение с именем, соответствующим этому классу.
Когда вы доберетесь до вершины, например, контроллера, не бросайте, а визуализируйте представление с красивым сообщением.