Обработка ошибок в PHP: try/catch и не только
Обработка ошибок — одна из ключевых задач любого веб-разработчика. Грамотно написанный код на PHP не только выполняет бизнес-логику, но и корректно реагирует на непредвиденные ситуации: отсутствие файла, проблемы с базой данных, неверный ввод данных пользователем. В этой статье мы подробно разберём, как работает обработка ошибок в PHP, начиная с базового try/catch и заканчивая продвинутыми техниками — пользовательскими исключениями и глобальными обработчиками.
1. Основы: конструкция try/catch
Самый распространённый способ обработки ошибок в современном PHP — это конструкция try/catch. Она позволяет «поймать» исключение (объект класса Throwable или его наследников) и выполнить альтернативный код вместо аварийного завершения скрипта.
Базовый синтаксис выглядит так:
<?php
try {
// Код, который может выбросить исключение
$file = fopen('data.txt', 'r');
if (!$file) {
throw new Exception('Не удалось открыть файл');
}
// Работа с файлом...
} catch (Exception $e) {
// Обработка ошибки
echo 'Произошла ошибка: ' . $e->getMessage();
}
?>
В блоке try размещается потенциально опасный код. Если внутри него возникает исключение (через throw или из-за внутренней ошибки PHP), выполнение немедленно переходит в блок catch. Переменная $e содержит объект исключения, у которого можно вызвать методы:
getMessage()— текст ошибки;getCode()— числовой код ошибки;getFile()— файл, где произошла ошибка;getLine()— строка, вызвавшая ошибку.
2. Блок finally: код, который выполняется всегда
Иногда требуется выполнить определённые действия независимо от того, возникла ошибка или нет — например, закрыть соединение с базой данных или освободить ресурсы. Для этого используется блок finally:
<?php
function processData($data) {
try {
// Опасная операция
if (empty($data)) {
throw new InvalidArgumentException('Данные не могут быть пустыми');
}
echo "Обработка данных...\n";
return true;
} catch (InvalidArgumentException $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
return false;
} finally {
echo "Блок finally выполняется всегда.\n";
}
}
processData([]); // Выведет: Ошибка: Данные не могут быть пустыми, затем блок finally
processData('test'); // Выведет: Обработка данных..., затем блок finally
?>
Блок finally выполняется даже если в try или catch есть оператор return. Это гарантирует очистку ресурсов.
3. Пользовательские исключения: расширяем класс Exception
В реальных проектах редко обходятся стандартным классом Exception. Гораздо удобнее создавать собственные классы исключений для разных типов ошибок. Это делает код более читаемым и упрощает отлов специфических ситуаций.
Пример: создадим исключение для ошибок валидации и для ошибок базы данных.
<?php
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
function validateUser($age) {
if ($age < 0 || $age > 150) {
throw new ValidationException('Некорректный возраст');
}
return true;
}
function connectToDatabase($dsn) {
if (empty($dsn)) {
throw new DatabaseException('DSN не может быть пустым');
}
// Симуляция подключения...
}
try {
validateUser(200);
} catch (ValidationException $e) {
echo 'Ошибка валидации: ' . $e->getMessage();
}
try {
connectToDatabase('');
} catch (DatabaseException $e) {
echo 'Ошибка БД: ' . $e->getMessage();
}
?>
Теперь можно ловить разные исключения отдельно:
<?php
try {
// Выполняем операции
validateUser(200);
connectToDatabase('mysql:host=localhost');
} catch (ValidationException $e) {
// Логика для ошибок валидации
echo "Ошибка ввода: " . $e->getMessage();
} catch (DatabaseException $e) {
// Логика для ошибок БД
echo "Проблема с БД: " . $e->getMessage();
} catch (Exception $e) {
// Общий обработчик для всех остальных исключений
echo "Неизвестная ошибка: " . $e->getMessage();
}
?>
Порядок блоков catch важен: сначала идут более специфичные исключения (наследники), затем — более общие (родители). Если поставить catch (Exception $e) первым, все остальные блоки никогда не сработают.
4. Глобальная обработка ошибок: set_error_handler и set_exception_handler
Не все ошибки в PHP являются исключениями. Старые функции (например, mysql_* или file_get_contents) могут генерировать предупреждения и уведомления, которые не ловятся через try/catch. Для их перехвата существует механизм пользовательских обработчиков.
4.1. set_error_handler — перехват «старых» ошибок
Эта функция позволяет задать собственный обработчик для ошибок уровня E_WARNING, E_NOTICE и других (кроме E_ERROR, E_PARSE).
<?php
function customErrorHandler($severity, $message, $file, $line) {
// Превращаем предупреждение в исключение
throw new ErrorException($message, 0, $severity, $file, $line);
}
// Устанавливаем обработчик
set_error_handler('customErrorHandler');
try {
// Вызов несуществующей переменной вызовет E_NOTICE
echo $undefinedVariable;
} catch (ErrorException $e) {
echo 'Поймана ошибка: ' . $e->getMessage();
}
// Восстанавливаем стандартный обработчик (опционально)
restore_error_handler();
?>
Этот подход позволяет унифицировать обработку: все ошибки (и старые, и новые) превраща