Обработка ошибок и исключений — ключевой аспект написания надёжного PHP-кода. Без правильной системы обработки ошибок приложение может падать, оставлять разработчика без информации о проблемах и создавать уязвимости.
Ошибки vs Исключения
В PHP есть два понятия: ошибки (errors) — фатальные проблемы, и исключения (exceptions) — контролируемые ситуации, которые можно перехватить и обработать.
Исключения: try-catch-finally
Базовый синтаксис
exec('SELECT * FROM nonexistent_table');
} catch (\PDOException e) {
error_log('Database error: ' . e->getMessage());
echo 'Произошла ошибка базы данных. Попробуйте позже.';
} catch (\Exception e) {
error_log('General error: ' . e->getMessage());
echo 'Произошла непредвиденная ошибка.';
} finally {
// Выполняется всегда, даже если было исключение
echo 'Завершение выполнения';
?>Пользовательские исключения
Создавайте свои классы исключений для лучшей организации кода:
errors=errors;
public function getErrors(): array {
return this->errors;
class DatabaseException extends \Exception {
public function __construct(string message, int
code=0,?
Throwableprevious = null) {
parent::__construct(
message,code, previous);
// Использование
class UserService {
public function createUser(array data): int {
if (empty(data['email'])) {
throw new ValidationException(['email' => 'Email is required']);
try {
stmt = this->db->prepare('INSERT INTO users (email) VALUES (?)');
stmt->execute([data['email']]);
return (int) this->db->lastInsertId();
} catch (\PDOException e) {
throw new DatabaseException('Failed to create user: ' . e->getMessage(), 500, e);
service = new UserService();
try {
id=service->createUser([]);
} catch (ValidationException e) {
http_response_code(422);
echo json_encode(['errors' => e->getErrors()]);
} catch (DatabaseException e) {
http_response_code(500);
error_log(e->getMessage());
echo json_encode(['error' => 'Internal server error']);
?>Глобальный обработчик исключений
Установите обработчик для всех неперехваченных исключений:
getMessage());
error_log('Stack trace: ' . e->getTraceAsString());
http_response_code(500);
echo json_encode([
'error' => 'Internal server error',
'message' => _ENV['APP_ENV'] === 'dev' ? e->getMessage() : null
]);
});
set_error_handler(function (int errno, string
errstr,stringerrfile, int errline) {
throw new \ErrorException(errstr, 0,
errno,errfile, errline);
});
register_shutdown_function(function () {
error = error_get_last();
if (error !== null && in_array(error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) {
error_log('Fatal error: ' .
error[
message
].
in
.error['file'] . ':' . error['line']);
});
?>Логирование ошибок
Настраиваем логирование с Monolog:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
logger = new Logger('app');
logger->pushHandler(new StreamHandler('/var/log/app.log', Logger::ERROR));
try {
// Код, который может вызвать ошибку
result = riskyOperation();
} catch (\Exception e) {
logger->error('Operation failed', [
'message' => e->getMessage(),
'code' => e->getCode(),
'file' => e->getFile(),
'line' => e->getLine(),
'trace' => e->getTraceAsString()
]);
throw e;
?>Стратегии обработки ошибок
| Тип ошибки | Стратегия | Пример |
|---|---|---|
| Валидация данных | Выбросить ValidationException | Пользователь ввёл некорректный email |
| БД недоступна | Повторная попытка или возврат 503 | Проблемы с подключением к БД |
| API стороннего сервиса | Fallback (кэш/заглушка) | Не отвечает внешний API |
| Фатальная ошибка | Логирование + корректный ответ | Отсутствует файл конфигурации |
Обработка ошибок в PDO
setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// Вместо проверки ошибок через if
stmt=pdo->prepare('SELECT * FROM users WHERE id = ?');
stmt−>execute([userId]);
if (stmt->rowCount() === 0) {
throw new \RuntimeException('User not found');
user = stmt->fetch(\PDO::FETCH_ASSOC);
?>Retry логика (повторные попытки)
Для временных сбоев полезно реализовать повторные попытки:
<?php
function withRetry(callable operation, int
maxAttempts=3,intdelay = 100): mixed {
attempt = 0;
lastException = null;
while (
attemptquery('SELECT COUNT(*) FROM users')->fetchColumn();
}, 5, 100);
} catch (\Exception e) {
error_log('Failed after 5 attempts: ' . e->getMessage());
result = 0; // Fallback значение
?> Итог
- Всегда используйте try-catch для контролируемых операций
- Создавайте пользовательские исключения для разных типов ошибок
- Настраивайте глобальные обработчики для неперехваченных ошибок
- Логируйте ошибки с достаточной информацией для отладки
- Используйте retry-механизмы для временных сбоев