Использование класса Error: создание экземпляров в PHP
Создание объекта Error в PHP: синтаксис и варианты
Основной способ: new Error(string $message, int $code, ?Throwable $previous)
Класс Error появился в PHP 7 для представления фатальных ошибок, которые можно перехватить. Стандартный конструктор принимает три необязательных параметра:
- $message - текст ошибки (по умолчанию пустая строка);
- $code - числовой код ошибки (по умолчанию 0);
- $previous - предыдущее исключение (объект, реализующий Throwable).
$error = new Error('Фатальная ошибка', 500);
echo $error->getMessage(); // Фатальная ошибка
echo $error->getCode(); // 500
Объект обычно выбрасывается через throw, а затем ловится в блоке catch (Error $e). Типичная ошибка - пропуск обязательных аргументов при переопределении конструктора в наследниках (см. проблему ниже).
Проблема: если в пользовательском классе, наследующем Error, переопределён конструктор без вызова родительского, то свойства message и code не инициализируются.
Решение: всегда вызывать parent::__construct($message, $code, $previous) в начале конструктора наследника.
Как создать объект Error без сообщения?
Все параметры конструктора необязательны. Можно написать new Error() - будет создан объект с пустым сообщением и кодом 0. Однако это затрудняет отладку, поэтому рекомендуется указывать хотя бы строку.
$e = new Error();
var_dump($e->getMessage()); // string(0) ""
Проблема: без сообщения узнать причину ошибки невозможно.
Решение: всегда передавать осмысленное сообщение.
Как сохранить цепочку исключений при создании Error?
Третий параметр $previous позволяет привязать предыдущее исключение. Это полезно для логирования и отслеживания последовательности ошибок.
$original = new Exception('База данных недоступна');
$error = new Error('Невозможно выполнить запрос', 0, $original);
echo $error->getPrevious()->getMessage(); // База данных недоступна
Проблема: передача в $previous объекта, который уже ссылается на новый Error, создаёт циклическую ссылку.
Решение: не передавать себя или исключения, которые уже содержат создаваемый объект.
Как создать собственный тип ошибки на основе Error?
Наследование от Error позволяет определить собственные классы для разных категорий ошибок. В наследнике можно добавлять свои методы и свойства.
class ValidationError extends Error {
private array $errors;
public function __construct(string $message, array $errors, int $code = 0, ?Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->errors = $errors;
}
public function getErrors(): array {
return $this->errors;
}
}
$validationError = new ValidationError('Некорректные данные', ['email' => 'Неверный формат']);
echo $validationError->getErrors()['email']; // Неверный формат
Проблема: если не вызвать parent::__construct, поля message и code останутся пустыми.
Решение: всегда вызывать родительский конструктор с соответствующими аргументами.
Когда использовать Error вместо Exception?
Класс Error предназначен для фатальных ошибок, которые обычно не должны восстанавливаться (например, ошибки компиляции, нехватка памяти). Exception - для проверяемых исключений, которые можно обработать. Выбор зависит от семантики: если ошибка критическая и логика приложения не может быть продолжена, используйте Error.
// Пример выбора
if (!extension_loaded('pdo')) {
throw new Error('Расширение PDO не установлено');
}
Расширенные примеры создания и обработки Error
Пример 1. Базовое создание и перехват Error
try {
throw new Error('Критическая ошибка', 100);
} catch (Error $e) {
echo 'Поймана ошибка: ' . $e->getMessage() . ' (код: ' . $e->getCode() . ')';
}
Поймана ошибка: Критическая ошибка (код: 100)
Пример 2. Цепочка исключений с Error
$prevException = new RuntimeException('Предшествующая ошибка');
$mainError = new Error('Главная ошибка', 500, $prevException);
echo $mainError->getPrevious()->getMessage();
Предшествующая ошибка
Пример 3. Пользовательский класс Error с дополнительной логикой
class DatabaseError extends Error {
private $query;
public function __construct(string $message, string $query, int $code = 0, ?Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->query = $query;
}
public function getQuery(): string {
return $this->query;
}
}
try {
$dbError = new DatabaseError('Запрос не выполнен', 'SELECT * FROM users');
throw $dbError;
} catch (DatabaseError $e) {
echo 'Ошибка БД: ' . $e->getMessage() . ' | Запрос: ' . $e->getQuery();
}
Ошибка БД: Запрос не выполнен | Запрос: SELECT * FROM users
Пример 4. Логирование Error в файл
function logError(Error $error): void {
$log = date('Y-m-d H:i:s') . ' [' . $error->getCode() . '] ' . $error->getMessage() . PHP_EOL;
file_put_contents('errors.log', $log, FILE_APPEND);
}
try {
throw new Error('Не удалось открыть файл', 404);
} catch (Error $e) {
logError($e);
echo 'Ошибка залогирована.';
}
// Содержимое errors.log:
// 2025-04-10 12:30:00 [404] Не удалось открыть файл
Ошибка залогирована.
Пример 5. Множественные блоки catch для Error и Exception
try {
if (rand(0, 1)) {
throw new Error('Фатальная ошибка');
} else {
throw new InvalidArgumentException('Неверный аргумент');
}
} catch (Error $e) {
echo 'Перехвачен Error: ' . $e->getMessage();
} catch (Exception $e) {
echo 'Перехвачен Exception: ' . $e->getMessage();
}
Перехвачен Error: Фатальная ошибка
Пример 6. Использование Throwable для перехвата любых исключений и ошибок
try {
throw new Error('Любая ошибка');
} catch (Throwable $t) {
echo 'Тип: ' . get_class($t) . ', сообщение: ' . $t->getMessage();
}
Тип: Error, сообщение: Любая ошибка