Не кожна помилка має зупиняти запит. Якщо не вдалося оновити рекомендації чи відправити аналітику, користувач усе одно має отримати сторінку - але розробник має про це дізнатися.
report() - передати виняток обробнику для звітування (лог, Sentry) і продовжити виконання:
public function isValid(string $value): bool
{
try {
// перевірка, що може кинути виняток
} catch (Throwable $e) {
report($e);
return false;
}
}
Без report() виняток у catch просто зникає - це «проковтування» помилки, яке роками ховає баги.
rescue() - виконати замикання, а при винятку звітувати й повернути значення за замовчуванням:
$recommendations = rescue(
fn () => $this->recommender->for($user),
[], // значення при помилці
);
// без звітування - третій аргумент
$preview = rescue(fn () => $this->renderPreview($post), null, report: false);
Значенням за замовчуванням може бути й замикання - воно виконається лише при помилці.
report_if() / report_unless():
report_if($response->failed(), new PaymentGatewayException($response->body()));
Дублікати: якщо той самий виняток передали в report() кілька разів (у сервісі й ще раз у контролері), у лозі з'являться дублікати. $exceptions->dontReportDuplicates() у bootstrap/app.php звітує кожен екземпляр лише раз.
Коли rescue - погана ідея:
- помилки, від яких залежить коректність: оплата, збереження замовлення, перевірка прав. Тиха заміна на значення за замовчуванням тут перетворює помилку на неправильні дані;
- широкий блок коду:
rescue()навколо половини методу ховає і очікувані, і зовсім несподівані збої. Загортайте лише конкретну необов'язкову операцію; - без звітування (
report: false) - лише коли помилка справді очікувана і не цікава.
Правило: якщо помилку ловите, то або обробляєте осмислено, або звітуєте. Порожній catch - майже завжди баг.