ErrorHandling brick

Без цього brick несподіваний збій виглядає як сирий PHP-фатал або stack trace у браузері на проді. Два різні завдання: повідомити (report у лог) і відмалювати безпечну відповідь (Whoops локально / fallback HTML / JSON envelope). addErrorHandling() на ConceptStack підключає extension-error-handler-whoops і повертає ErrorHandlingBuilder. Рецепт — Обробка помилок.

Renderer і reporter задаються окремо навмисно: можна спочатку писати в error_log без Monolog, а пізніше лише змінити setLogReporting — Whoops і fallback не чіпаючи. withViewRenderer і withJsonRenderer — XOR (один renderer на capability): HTML-додаток vs чистий API.

Очікувані помилки форми (ValidationException, CSRF) — не зона цього brick: їх ловлять middleware у routes/*.php, інакше вони виглядали б як 500.

Методи ErrorHandlingBuilder:

Метод Параметри Опис Залежність
setDebug() bool $debug = true, HandlerInterface|Closure|null $handler = null Whoops PrettyPage (або інший handler) для non-JSON HTTP —
setLogReporting() bool $logger = true, bool $phpErrorLog = true Report recipe: Logger і/або PHP error_log addLogging() якщо $logger
setReporter() ExceptionReporterInterface|Closure Кастомний reporter —
withViewRenderer() string $fallbackPath = '' View + JSON Accept + PHP fallback files addView()
withJsonRenderer() — Лише JSON error envelope (API без View) addHttp()
setRenderer() HttpErrorRendererInterface|Closure Кастомний renderer —

setDebug()

$stack->addErrorHandling()
    ->setDebug(true, new PrettyPageHandler());

Той самий прапорець, що APP_DEBUG у Docker: локально розробнику потрібен Whoops (швидкий діагноз), на проді — жоден stack trace у браузері. При $debug = true для HTML-запитів показується Whoops (або переданий handler); при false — обраний renderer (fallback / JSON). JSON Accept завжди йде в безпечний JSON renderer, навіть у debug — API-клієнту Whoops HTML не потрібен.

setLogReporting()

$stack->addErrorHandling()
    ->setLogReporting();           // logger + php error_log
// ->setLogReporting(true, false); // лише LoggerInterface
// ->setLogReporting(false, true); // лише error_log

Помилку все одно треба кудись записати, навіть коли сторінка «спокійна». Хоча б один канал має бути true. На кроці рецепта без Monolog ставлять logger: false, phpErrorLog: true — свідомий компроміс; коли з’явиться Logging, увімкнете logger: true без переробки Whoops і fallback. З $logger = true Stack вимагає addLogging().

setReporter()

$stack->addErrorHandling()
    ->setReporter(new MyExceptionReporter());

Коли стандартного «Logger і/або error_log» мало (Sentry, власний bus): escape hatch замість setLogReporting() — власний ExceptionReporterInterface або factory-closure з контейнера.

withViewRenderer()

$stack->addErrorHandling()
    ->withViewRenderer($rootPath . 'resources/views/errors/fallback');

Для HTML-додатка з View: спочатку пробує Twig @frontend/errors/{code} (або namespace з route). Якщо шаблону немає — PHP-файл {fallbackPath}/{code}.php, інакше мінімальний HTML. Fallback навмисно простий PHP, не Twig: production-відповідь не залежить від того, чи зібрався Twig-кеш. JSON Accept → jsonError. XOR із withJsonRenderer(). Потребує addView().

withJsonRenderer()

$stack->addErrorHandling()
    ->withJsonRenderer();

Коли View немає і клієнт завжди чекає JSON envelope — не тягніть Twig лише заради 500-сторінки. Потребує addHttp(). Не сумісний із withViewRenderer() на тому ж builder.

setRenderer()

$stack->addErrorHandling()
    ->setRenderer(new MyHttpErrorRenderer());

Повний контроль над HTTP-відповіддю на помилку (HttpErrorRendererInterface або factory), коли ні view-, ні json-рецепт не підходять (наприклад, branded error pages з власним pipeline).

Що реєструє brick

  • Whoops / error handler wiring у kernel
  • ExceptionReporterInterface
  • HttpErrorRendererInterface

Middleware на ValidationException / CSRF — у routes/*.php, не в цьому brick: вони перетворюють «очікувану» помилку на UX, а не на Whoops.

Рецепт — 7. Обробка помилок. Logging — Logging brick (окремий крок: спочатку render/report, потім Monolog).