Обработка исключений

Конфигурация

config/exception.php

return [
    // Здесь конфигурируется класс обработки исключений
    '' => support\exception\Handler::class,
];

В режиме нескольких приложений вы можете отдельно настроить класс обработки исключений для каждого приложения, см. Многоприложение

Класс по умолчанию для обработки исключений

В Webman исключения по умолчанию обрабатываются классом support\exception\Handler. Вы можете изменить класс по умолчанию, отредактировав файл конфигурации config/exception.php. Класс обработки исключений должен реализовывать интерфейс Webman\Exception\ExceptionHandlerInterface.

interface ExceptionHandlerInterface
{
    /**
     * Записывает лог
     * @param Throwable $e
     * @return mixed
     */
    public function report(Throwable $e);

    /**
     * Отображает ответ
     * @param Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render(Request $request, Throwable $e) : Response;
}

Отображение ответа

Метод render в классе обработки исключений используется для отображения ответа.

Если значение debug в файле конфигурации config/app.php равно true (в дальнейшем будет обозначаться как app.debug=true), будет возвращена подробная информация об исключении, в противном случае будет возвращена краткая информация об исключении.

Если запрос ожидает JSON-ответ, то информация об исключении будет возвращена в формате JSON, например:

{
    "code": "500",
    "msg": "Информация об исключении"
}

Если app.debug=true, в JSON-данные будет добавлено дополнительное поле trace, содержащие подробный стек вызовов.

Вы можете написать свой собственный класс обработки исключений, чтобы изменить логику обработки исключений по умолчанию.

Бизнес-исключение BusinessException

Иногда мы хотим прервать запрос в определенной вложенной функции и вернуть сообщение об ошибке клиенту, для этого можно выбросить BusinessException.
Например:

<?php
namespace app\controller;

use support\Request;
use support\exception\BusinessException;

class FooController
{
    public function index(Request $request)
    {
        $this->chackInpout($request->post());
        return response('hello index');
    }

    protected function chackInpout($input)
    {
        if (!isset($input['token'])) {
            throw new BusinessException('Ошибка параметра', 3000);
        }
    }
}

В приведенном примере будет возвращено:

{"code": 3000, "msg": "Ошибка параметра"}

Внимание
Бизнес-исключение BusinessException не требует обработки через try, так как фреймворк автоматически перехватывает его и возвращает соответствующий вывод в зависимости от типа запроса.

Пользовательское бизнес-исключение

Если приведенный выше ответ не соответствует вашим требованиям, например, если вы хотите изменить msg на message, вы можете создать пользовательское исключение MyBusinessException.

Создайте app/exception/MyBusinessException.php со следующим содержимым:

<?php

namespace app\exception;

use support\exception\BusinessException;
use Webman\Http\Request;
use Webman\Http\Response;

class MyBusinessException extends BusinessException
{
    public function render(Request $request): ?Response
    {
        // Если запрос в формате json, возвращаем json-данные
        if ($request->expectsJson()) {
            return json(['code' => $this->getCode() ?: 500, 'message' => $this->getMessage()]);
        }
        // Если запрос не в формате json, возвращаем страницу
        return new Response(200, [], $this->getMessage());
    }
}

Таким образом, когда бизнес-логика вызывает

use app\exception\MyBusinessException;

throw new MyBusinessException('Ошибка параметра', 3000);

Запрос в формате JSON получит аналогичный ответ в формате JSON:

{"code": 3000, "message": "Ошибка параметра"}

Подсказка
Поскольку исключение BusinessException относится к бизнес-исключениям (например, ошибка ввода параметров пользователем), оно предсказуемо, и фреймворк не считает его фатальной ошибкой и не записывает в лог.

Резюме

В любых случаях, когда вы хотите прервать текущий запрос и вернуть информацию клиенту, можно рассмотреть использование исключения BusinessException.