Обработка ошибок
Imhotep преобразует PHP-ошибки в исключения, передаёт необработанные исключения обработчику App\Exceptions\Handler, записывает нужные из них в журнал и формирует HTTP- или консольный ответ. Обычно приложение уже содержит свой класс App\Exceptions\Handler, расширяющий базовый Imhotep\Framework\Exceptions\Handler.
Режим отладки
Параметр debug в config/app.php обычно получает значение из APP_DEBUG файла .env:
APP_DEBUG=true
В локальной среде используйте true. В production установите false: подробности стека, пути к файлам и настройки не должны попадать пользователю.
Отчёты об исключениях
Когда в приложении происходит ошибка, её нужно не только показать, но и записать – чтобы вы могли её увидеть, проанализировать и исправить.
Запись ошибок (логирование) в Imhotep автоматически обрабатывается специальным классом App\Exceptions\Handler. Он сам решает, куда и как писать отчёт об ошибке. Но вы можете добавить свои действия — например, отправить уведомление в Telegram или Slack, сохранить ошибку в базу данных или передать в стороннюю систему мониторинга.
Обработчик reportable
Чтобы добавить свою логику при возникновении ошибки, используйте метод reportable() внутри метода register() вашего обработчика App\Exceptions\Handler:
namespace App\Exceptions;
use App\Exceptions\InvalidOrderException;
use Imhotep\Facades\Log;
use Imhotep\Framework\Exceptions\Handler as HandlerBase;
class Handler extends HandlerBase
{
protected array $dontReport = [
// ...
];
public function register(): void
{
// Когда возникает ошибка InvalidOrderException — отправляем данные в мониторинг
$this->reportable(function (InvalidOrderException $exception): void {
// Здесь вы можете, например, вызвать API вашей системы мониторинга
sendToMonitoring($exception->getMessage());
});
// Также можно явно указать класс
$this->reportable(InvalidOrderException::class, function (InvalidOrderException $exception): void {
// ...
});
}
public function context(): array
{
return array_merge(parent::context(), [
// your global data
]);
}
}
Колбэк выполняется и для наследников указанного класса. Несколько колбэков одного типа выполняются в порядке регистрации. После их выполнения исключение по-прежнему попадёт в стандартный журнал. Чтобы прекратить дальнейшую обработку отчёта, верните false или вызовите stop:
$this->reportable(function (InvalidOrderException $exception): bool {
// ...
return false;
});
// или
$this->reportable(function (InvalidOrderException $exception): void {
// ...
})->stop();
Метод report исключения
Логику отчёта можно поместить в сам класс исключения. Метод вызывается через контейнер, поэтому в него разрешено внедрять зависимости. Возврат false отменяет колбэки reportable и стандартное логирование:
namespace App\Exceptions;
use App\Services\ErrorTracker;
use Exception;
class InvalidOrderException extends Exception
{
public function report(ErrorTracker $tracker): bool
{
$tracker->capture($this);
return false;
}
}
Контекст журнала
Если пользователь авторизован, базовый обработчик добавляет его идентификатор в контекст записи под ключом userId. Чтобы добавить общий контекст, переопределите публичный метод context:
public function context(): array
{
return array_merge(parent::context(), [
'release' => config('app.version'),
]);
}
Конкретное исключение может вернуть дополнительный контекст методом context. Его значения объединяются с общим контекстом:
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
public function __construct(private int $orderId)
{
parent::__construct('Некорректный заказ.');
}
public function context(): array
{
return ['order_id' => $this->orderId];
}
}
Помощник report
Используйте report, когда нужно записать исключение, но продолжить выполнение кода:
use Throwable;
try {
// Проверка значения...
} catch (Throwable $exception) {
report($exception);
return false;
}
Исключения без отчёта
Добавьте исключение в $dontReport, если его не нужно записывать в журнал. Оно всё равно будет отображено обычным способом:
use App\Exceptions\InvalidOrderException;
protected array $dontReport = [
InvalidOrderException::class,
];
HTTP-исключения, ошибки валидации, аутентификации, авторизации и ответы, созданные через abort(Response|Responsable), уже не записываются в журнал.
Отображение исключений
При обработке HTTP-запроса Imhotep сначала проверяет колбэки renderable, затем метод render исключения, интерфейс Responsable и встроенные обработчики валидации и аутентификации. Если обработчик не вернул ответ, применяется стандартное отображение.
Обработчики renderable
Зарегистрируйте пользовательский ответ для типа исключения в методе register. Как и у reportable, тип можно вывести из первого параметра замыкания:
use App\Exceptions\InvalidOrderException;
use Imhotep\Contracts\Http\Request;
public function register(): void
{
$this->renderable(function (InvalidOrderException $exception, Request $request) {
return response()->view('errors.invalid-order', [], 500);
});
}
Для встроенного 404-исключения используйте класс Imhotep\Http\Exceptions\NotFoundHttpException:
use Imhotep\Http\Exceptions\NotFoundHttpException;
$this->renderable(function (NotFoundHttpException $exception, $request) {
if ($request->is('api/*')) {
return response()->json(['message' => 'Запись не найдена.'], 404);
}
return null; // Использовать стандартное отображение.
});
Методы render и Responsable
Исключение может самостоятельно сформировать ответ методом render. Если метод вернёт null или false, Imhotep продолжит стандартную обработку.
namespace App\Exceptions;
use Exception;
use Imhotep\Contracts\Http\Request;
class InvalidOrderException extends Exception
{
public function render(Request $request)
{
if ($request->expectsJson()) {
return response()->json(['message' => $this->getMessage()], 422);
}
return null;
}
}
Вместо метода render исключение может реализовать Imhotep\Contracts\Http\Responsable и вернуть ответ из toResponse(Request $request).
HTTP-исключения и abort
Помощник abort немедленно прекращает выполнение и формирует ответ. Передайте статус, при необходимости сообщение и заголовки:
abort(403, 'Доступ запрещён');
abort(429, 'Слишком много запросов', [
'Retry-After' => '60',
]);
Также можно передать готовый Response или объект Responsable; это полезно для раннего возврата из контроллера или middleware:
abort(response()->json(['message' => 'Недоступно'], 503));
Собственные страницы ошибок
Для HTTP-исключения Imhotep ищет шаблон в resources/views/errors. Сначала используется шаблон точного статуса, затем шаблон группы кодов:
-
404.moon.php— только для ответа 404; -
4xx.moon.php— для любого ответа 4xx, если точный шаблон не найден; -
5xx.moon.php— для любого ответа 5xx, если точный шаблон не найден.
В шаблон передаётся объект HTTP-исключения в переменной $exception:
<h2>{{ $exception->getMessage() }}</h2>
Если шаблон не найден, ответ содержит сообщение исключения, его статус и заголовки.