Как поймать фатальную ошибку PHP (E_ERROR) и не сойти с ума
Вы когда-нибудь видели это? Ваш сайт на PHP выдаёт пустую белую страницу без единой ошибки. Или, что ещё хуже, — страшное сообщение Fatal error: Uncaught Error.... Если вы разработчик, который работает с PHP, вы сталкивались с E_ERROR — самой неприятной разновидностью ошибок. В отличие от предупреждений (Warnings) или уведомлений (Notices), фатальная ошибка полностью останавливает выполнение скрипта. И самое обидное: стандартный блок try...catch её не ловит.
В этой статье мы разберём, как правильно перехватывать фатальные ошибки PHP, почему обычные методы не работают, и как сделать ваш код более устойчивым. Это не просто теория — мы напишем несколько реальных примеров, которые вы сможете сразу применить на своих проектах.
Почему try...catch не спасает от E_ERROR?
Давайте сразу развеем иллюзию: try...catch в PHP предназначен для обработки исключений (Exceptions). А E_ERROR — это ошибка (Error). Это разные сущности. PHP до версии 7.0 не позволял ловить фатальные ошибки вообще. Начиная с PHP 7, появился класс Error, который, как и Exception, реализует интерфейс Throwable. Но это не значит, что любой E_ERROR автоматически становится исключением. Например, ошибка нехватки памяти (Out of memory) или вызов несуществующего метода — это всё ещё фатальные ошибки, которые не всегда можно перехватить через try...catch.
Вот простой пример:
try {
$obj = new NonExistentClass(); // Это вызовет E_ERROR
} catch (Throwable $e) {
echo 'Поймали: ' . $e->getMessage();
}
В PHP 7+ этот код сработает, потому что Throwable включает и Error. Но если ошибка происходит на этапе компиляции (например, синтаксическая ошибка) или в shutdown-фазе — try...catch бесполезен.
Топ-3 фатальных ошибки PHP и как их избежать
1. Вызов несуществующего метода или класса
Самая частая фатальная ошибка — Call to undefined method или Class not found. Она возникает, когда вы пытаетесь вызвать метод, которого нет, или использовать класс, который не подключён.
Как избежать:
- Всегда используйте автозагрузку классов (spl_autoload_register или Composer).
- Проверяйте существование метода через
method_exists().
if (method_exists($object, 'someMethod')) {
$object->someMethod();
} else {
// обработка
}
2. Переполнение памяти (Allowed memory size exhausted)
Ошибка Allowed memory size of X bytes exhausted — это фатальная ошибка, которая возникает, когда скрипт потребляет больше памяти, чем разрешено. Её нельзя перехватить обычным способом.
Как избежать:
- Оптимизируйте запросы к базе данных.
- Используйте генераторы (
yield) для обработки больших массивов. - Увеличьте лимит памяти в
php.iniили черезini_set('memory_limit', '256M').
3. Таймаут выполнения (Maximum execution time exceeded)
Хотя эта ошибка не всегда фатальная, в некоторых конфигурациях она останавливает выполнение скрипта полностью.
Как избежать:
- Используйте
set_time_limit(0)для длительных скриптов. - Разбивайте тяжёлые операции на части.
Как поймать фатальную ошибку PHP: 3 проверенных способа
Теперь перейдём к главному. Есть несколько техник, которые позволяют перехватить E_ERROR и выполнить код до того, как скрипт умрёт.
Способ 1. Использование register_shutdown_function()
Это самый надёжный метод. Функция register_shutdown_function() регистрирует callback, который будет выполнен после завершения скрипта — даже если произошла фатальная ошибка. Внутри неё можно проверить, была ли ошибка, с помощью error_get_last().
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
// Логируем ошибку
error_log("Фатальная ошибка: " . $error['message']);
// Показываем пользователю понятное сообщение
echo "Что-то пошло не так. Мы уже работаем над этим.";
// Можно отправить email администратору
mail('admin@example.com', 'PHP Fatal Error', print_r($error, true));
}
});
// Пример, который вызовет E_ERROR
$data = file_get_contents('http://несуществующий-сайт.xyz');
Важно: shutdown-функция не может предотвратить остановку скрипта, но она даёт вам шанс корректно завершить работу — записать лог, отправить уведомление, показать пользователю дружественную страницу.
Способ 2. Комбинация с set_error_handler()
Стандартный обработчик ошибок set_error_handler() не ловит E_ERROR. Но вы можете настроить его так, чтобы он преобразовывал некоторые ошибки в исключения. А E_ERROR обрабатывать через shutdown.
// Превращаем предупреждения в исключения
set_error_handler(function($severity, $message, $file, $line) {
if (error_reporting() & $severity) {
throw new ErrorException($message, 0, $severity, $file, $line);
}
});
// Регистрируем shutdown для фатальных ошибок
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
// Ваша логика
}
});
Способ 3. Глобальный обработчик исключений set_exception_handler()
Этот метод работает для непойманных исключений (включая Error в PHP 7+). Если исключение не было перехвачено в try...catch, оно попадает в этот обработчик.
set_exception_handler(function(Throwable $e) {
echo "Непойманное исключение: " . $e->getMessage();
error_log("Exception: " . $e->getMessage());
});
Но помните: set_exception_handler не ловит E_ERROR, который не является исключением (например, ошибка памяти). Поэтому лучше комбинировать его с register_shutdown_function.
Реальный пример: как защитить сайт от фатальных ошибок
Давайте соберём всё вместе и напишем класс, который будет перех