Выстрой полноценную стратегию обработки ошибок: связанные бизнес-исключения, контролируемый finally и глобальные обработчики как последний рубеж.
Открыть этот урок в KodokonНачиная с PHP 7 всё, что выбрасывается, реализует Throwable, и веток здесь две: Error (сбои движка и типизации, вроде TypeError) и Exception (сбои приложения). Error ты ловишь только на границах программы. Со стороны приложения SPL даёт готовую иерархию: LogicException сигнализирует об ошибке разработчика, которую надо исправить; RuntimeException - о непредвиденной ситуации во время выполнения, которую надо обработать. Профессиональный рефлекс: выводи свои бизнес-исключения из этих базовых классов, по одному на каждую опознаваемую ситуацию.
<?php
declare(strict_types=1);
abstract class AppException extends RuntimeException
{
}
final class UserNotFound extends AppException
{
public static function withEmail(
string $email
): self {
return new self("No user for $email");
}
}
try {
throw UserNotFound::withEmail('lea@example.com');
} catch (UserNotFound $e) {
echo $e->getMessage();
}try/catch оправдан, только если ты знаешь, что делать с ошибкой - иначе дай ей всплыть. Контроль уточняют три инструмента: множественный catch (JsonException | ValueError $e) объединяет одинаковую обработку; параметр previous связывает исходную причину, когда ты переводишь техническое исключение в бизнесовое; finally выполняется в любом случае - при успехе, при исключении, даже при досрочном return - и идеально подходит для освобождения ресурса.
<?php
declare(strict_types=1);
final class PayloadInvalid extends RuntimeException
{
}
function decode(string $json): mixed
{
try {
return json_decode(
$json,
true,
flags: JSON_THROW_ON_ERROR,
);
} catch (JsonException $e) {
throw new PayloadInvalid(
'Invalid JSON payload',
previous: $e,
);
} finally {
echo "decode() finished\n";
}
}
try {
decode('{broken');
} catch (PayloadInvalid $e) {
echo $e->getPrevious()?->getMessage();
}В конце цепочки конструкцию замыкают два глобальных обработчика. set_exception_handler перехватывает всё, что не было обработано: структурированное логирование, обобщённый ответ 500 - и при этом ни в коем случае не раскрывает пользователю сообщение или трассировку. set_error_handler превращает устаревшие ошибки (warnings, notices) в ErrorException, объединяя два мира: всё становится исключением, всё идёт одним и тем же путём обработки.
<?php
declare(strict_types=1);
set_error_handler(function (
int $severity,
string $message,
string $file,
int $line,
): bool {
throw new ErrorException(
$message,
0,
$severity,
$file,
$line,
);
});
set_exception_handler(function (Throwable $e): void {
error_log($e->getMessage());
http_response_code(500);
echo 'A technical error occurred.';
});
trigger_error('Legacy warning', E_USER_WARNING);
echo 'Never reached';Error и Exception в современном PHP?Error наследуется от ExceptionThrowablefinally?previous у исключения?