Ошибки PHP: полная классификация и подходы к обработке

Раздел: Основы PHP -> Обработка исключений и типов ошибок

Основные типы ошибок PHP

В PHP предусмотрено несколько уровней ошибок, обозначаемых константами. Фатальные ошибки (E_ERROR) прерывают выполнение скрипта. Предупреждения (E_WARNING) не останавливают работу, но сигнализируют о проблемах. Синтаксические ошибки (E_PARSE) возникают на этапе компиляции. Замечания (E_NOTICE) указывают на возможные недочеты. E_STRICT и E_DEPRECATED содержат рекомендации и информацию об устаревших функциях. Пользовательские ошибки (E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE) генерируются разработчиком.

Единая обработка через преобразование в исключения

Как добиться того, чтобы все ошибки PHP обрабатывались единообразно через механизм исключений?

Наиболее эффективный способ - установить пользовательский обработчик ошибок, который превращает каждую ошибку (кроме фатальных уровня E_ERROR и выше) в объект ErrorException. Затем фатальные ошибки (включая E_ERROR) перехватываются через блок try-catch с типом Throwable или Error.


set_error_handler(function ($level, $message, $file, $line) {
    if (error_reporting() & $level) {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
});

try {
    // Пример: вызов несуществующей функции
    undefinedFunction();
} catch (ErrorException $e) {
    echo 'Исключение: ' . $e->getMessage();
} catch (Throwable $e) {
    echo 'Неожиданная ошибка: ' . $e->getMessage();
}

типы ошибок php (типы ошибок в php)

Исключение: Call to undefined function undefinedFunction()

Такой подход позволяет централизованно обрабатывать ошибки любого уровня, используя знакомый механизм try-catch.

Типичные проблемы

  • Ошибки компиляции (E_PARSE) не могут быть перехвачены таким образом, так как они происходят до выполнения кода. Для их обработки применяется проверка синтаксиса перед запуском (например, через php -l).
  • Некоторые фатальные ошибки (например, нехватка памяти) могут обойти обработчик. В таких случаях используют register_shutdown_function в сочетании с error_get_last.
  • Если обработчик не проверяет error_reporting(), то он будет выбрасывать исключения даже для ошибок, отключенных в конфигурации.

Вариант: Настройка уровней ошибок и их отображения

Как контролировать, какие ошибки видны на экране, а какие сохраняются в лог?

Самый простой способ - манипулировать директивами error_reporting, display_errors и log_errors через ini_set или в php.ini.


error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); // Игнорируем уведомления и устаревшие предупреждения
ini_set('display_errors', '1'); // Показывать на экране (только для разработки)
ini_set('log_errors', '1');
ini_set('error_log', '/tmp/php_errors.log');

В продакшене display_errors обычно выключают.

Проблема: Фатальные ошибки (E_ERROR) всё равно завершают скрипт, и сообщение может быть выведено до того, как сработают настройки буферизации. Для полного контроля нужны дополнительные средства.

Вариант: Перехват фатальных ошибок с помощью Error (PHP 7+)

Как предотвратить остановку скрипта при фатальной ошибке, например, вызове несуществующего метода?

Начиная с PHP 7, фатальные ошибки (кроме некоторых) можно перехватывать через catch (Error $e).


try {
    $obj = new stdClass();
    $obj->method(); // Call to undefined method stdClass::method()
} catch (Error $e) {
    echo 'Фатальная ошибка перехвачена: ' . $e->getMessage();
}
Фатальная ошибка перехвачена: Call to undefined method stdClass::method()

Не все фатальные ошибки наследуются от Error. Например, ошибки парсинга (E_PARSE) происходят до выполнения try-catch. Также некоторые внутренние ошибки (например, превышение времени выполнения) могут не перехватываться.

Вариант: Собственный обработчик без исключений

Как обработать ошибку и продолжить выполнение скрипта?

Функция set_error_handler может вернуть true, чтобы указать, что ошибка обработана, и PHP не должен запускать стандартный обработчик. Это позволяет логировать ошибку, но не прерывать выполнение.


function myErrorHandler($level, $message, $file, $line) {
    if (error_reporting() & $level) {
        error_log('Ошибка [' . $level . ']: ' . $message . ' в файле ' . $file . ' на строке ' . $line);
        // Не выбрасываем исключение
    }
    return true; // Ошибка обработана
}
set_error_handler('myErrorHandler');

echo $undefinedVariable; // Notice
echo 'Скрипт продолжает работу';
Скрипт продолжает работу

В лог будет записано уведомление.

Возврат true не отменяет фатальные ошибки - они всё равно приведут к остановке. Кроме того, такой подход не позволяет использовать исключения для управления потоком.

Вариант: Обработка фатальных ошибок через shutdown и error_get_last

Как узнать о фатальной ошибке после её возникновения и выполнить завершающие действия?

Функция register_shutdown_function регистрирует callback, который вызывается после завершения скрипта. Внутри можно проверить error_get_last() на наличие фатальной ошибки.


register_shutdown_function(function() {
    $error = error_get_last();
    if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        echo 'Фатальная ошибка: ' . $error['message'];
        // Дополнительные действия: логирование, очистка
    }
});

// Вызов несуществующей функции
nonexistent();
Фатальная ошибка: Call to undefined function nonexistent()

Этот метод не позволяет предотвратить остановку скрипта, так как callback вызывается уже после завершения. Он подходит для логирования и очистки ресурсов.

Расширенные примеры обработки ошибок PHP

Комбинированный обработчик для всех типов ошибок

Сочетание set_error_handler для нефатальных ошибок и register_shutdown_function для фатальных позволяет охватить практически все ситуации.

Пример

set_error_handler(function($level, $message, $file, $line) {
    throw new ErrorException($message, 0, $level, $file, $line);
});

register_shutdown_function(function() {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        echo 'Неисправимая ошибка: ' . $error['message'] . PHP_EOL;
        // Здесь можно отправить уведомление разработчику
    }
});

try {
    // Фатальная ошибка - вызов undefined
    undefined();
} catch (Error $e) {
    echo 'Перехвачено через Error: ' . $e->getMessage() . PHP_EOL;
}
Неисправимая ошибка: Call to undefined function undefined()

Подавление ошибок оператором @

Оператор @ подавляет вывод ошибок для конкретного выражения. Но он не отключает обработчик ошибок, и ошибки всё равно могут логироваться.

Пример

error_reporting(E_ALL);
ini_set('display_errors', 1);
$result = @file_get_contents('/nonexistent/file.txt');
if ($result === false) {
    echo 'Файл не найден, но ошибка подавлена.';
}
// Проверим последнюю ошибку
$lastError = error_get_last();
if ($lastError) {
    echo 'Тип: ' . $lastError['type'] . ', сообщение: ' . $lastError['message'];
}
Файл не найден, но ошибка подавлена.
Тип: 2, сообщение: file_get_contents(/nonexistent/file.txt): Failed to open stream: No such file or directory

Использование @ не рекомендуется, так как затрудняет отладку и маскирует проблемы.

Проверка синтаксиса PHP перед выполнением

Ошибки парсинга (E_PARSE) невозможно перехватить внутри исполняемого кода. Чтобы избежать их, можно проверять синтаксис файла перед его выполнением.

Пример

$filename = 'test.php';
$output = [];
$returnVar = 0;
exec('php -l ' . $filename . ' 2>&1', $output, $returnVar);
if ($returnVar !== 0) {
    echo 'Синтаксическая ошибка в ' . $filename . ':' . PHP_EOL;
    echo implode(PHP_EOL, $output);
} else {
    echo 'Файл ' . $filename . ' синтаксически корректен.';
}
Синтаксическая ошибка в test.php:
Parse error: syntax error, unexpected '}' in test.php on line 5

Класс для централизованного управления ошибками

Можно создать класс, который инкапсулирует логику обработки ошибок.

Пример

class ErrorHandler {
    public static function register() {
        set_error_handler([__CLASS__, 'handleError']);
        register_shutdown_function([__CLASS__, 'handleShutdown']);
    }

    public static function handleError($level, $message, $file, $line) {
        $logMessage = '[Error] Level ' . $level . ': ' . $message . ' in ' . $file . ':' . $line;
        error_log($logMessage);
        if (error_reporting() & $level) {
            throw new ErrorException($message, 0, $level, $file, $line);
        }
        return true;
    }

    public static function handleShutdown() {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $logMessage = '[Fatal] ' . $error['message'] . ' in ' . $error['file'] . ':' . $error['line'];
            error_log($logMessage);
            // Отправить уведомление
        }
    }
}

ErrorHandler::register();

try {
    strpos(); // Warning: expects at least 2 parameters
} catch (ErrorException $e) {
    echo 'Исключение: ' . $e->getMessage();
}
Исключение: strpos() expects at least 2 parameters, 0 given

Пользовательские ошибки с разными уровнями

Функция trigger_error позволяет создавать собственные ошибки с нужным уровнем.

Пример

function validateAge($age) {
    if ($age < 0) {
        trigger_error('Возраст не может быть отрицательным', E_USER_WARNING);
    } elseif ($age > 150) {
        trigger_error('Неправдоподобный возраст', E_USER_ERROR);
    }
    return true;
}

set_error_handler(function($level, $message) {
    if ($level === E_USER_WARNING) {
        echo 'Предупреждение: ' . $message . PHP_EOL;
    }
    if ($level === E_USER_ERROR) {
        throw new Exception('Фатальная ошибка: ' . $message);
    }
});

validateAge(-5);
try {
    validateAge(200);
} catch (Exception $e) {
    echo $e->getMessage();
}
Предупреждение: Возраст не может быть отрицательным
Фатальная ошибка: Неправдоподобный возраст

Битовые маски error_reporting

Уровни ошибок комбинируются с помощью побитовых операторов. Полезно для гибкой настройки.

Пример

// Включить все ошибки, кроме уведомлений (E_NOTICE) и строгих стандартов (E_STRICT)
$level = E_ALL & ~E_NOTICE & ~E_STRICT;
error_reporting($level);

// Проверить, входит ли данный уровень в текущую маску
if (error_reporting() & E_WARNING) {
    echo 'E_WARNING сейчас отображается.';
}
E_WARNING сейчас отображается.

Восстановление после ошибок в цикле

Использование try-catch внутри цикла позволяет продолжить выполнение, даже если одна итерация вызывает ошибку.

Пример

$array = ['a', 'b', null, 'd'];
foreach ($array as $key => $value) {
    try {
        if ($value === null) {
            trigger_error('Пустое значение на позиции ' . $key, E_USER_WARNING);
        }
        echo 'Обработка элемента ' . $key . ': ' . strtoupper($value) . PHP_EOL;
    } catch (ErrorException $e) {
        echo 'Ошибка: ' . $e->getMessage() . PHP_EOL;
    }
}
Обработка элемента 0: A
Обработка элемента 1: B
Ошибка: Пустое значение на позиции 2
Обработка элемента 3: D

Типы ошибок в PHP - comments

En
типы ошибок php (php)