Skip to content

错误级别

概述

PHP 定义了多种错误级别(Error Levels),每个级别对应不同严重程度的错误。了解各级别的含义和用途是正确配置错误报告和处理策略的基础。

错误级别使用预定义常量表示,可以通过位运算进行组合。PHP 8.0+ 中许多原来低级别的错误被升级为更高级别的 Warning 或 Error。

版本说明

  • PHP 5.3 引入 E_DEPRECATEDE_USER_DEPRECATED
  • PHP 8.0 将许多 Notice 升级为 Warning,将部分 Warning 升级为 Error
  • PHP 8.4 引入了一些新的错误级别常量

基础概念

错误级别分类

PHP 错误级别大致分为三类:

  • Fatal 级别:致命错误,脚本终止执行(E_ERRORE_PARSEE_CORE_ERROR 等)
  • Warning 级别:警告,脚本继续执行但可能产生意外结果(E_WARNINGE_NOTICE 等)
  • Deprecated 级别:弃用提示,表示使用了未来版本可能移除的功能

错误级别完整表格

常量级别说明PHP 8 变化
E_ERROR1Fatal致命运行时错误无变化
E_WARNING2Warning运行时警告无变化
E_PARSE4Fatal编译时解析错误无变化
E_NOTICE8Notice运行时通知PHP 8.0 部分升级为 Warning
E_CORE_ERROR16FatalPHP 初始化致命错误无变化
E_CORE_WARNING32WarningPHP 初始化警告无变化
E_COMPILE_ERROR64Fatal编译致命错误无变化
E_COMPILE_WARNING128Warning编译警告无变化
E_USER_ERROR256Fatal用户自定义致命错误无变化
E_USER_WARNING512Warning用户自定义警告无变化
E_USER_NOTICE1024Notice用户自定义通知无变化
E_STRICT2048Notice严格模式建议PHP 8.4 弃用
E_RECOVERABLE_ERROR4096Fatal可捕获的致命错误PHP 7+ 转为 Error
E_DEPRECATED8192Deprecated弃用通知更严格
E_USER_DEPRECATED16384Deprecated用户自定义弃用通知无变化
E_ALLE_ALL全部所有错误和警告包含更多级别

语法与代码

各级别示例

php
<?php

declare(strict_types=1);

// E_ERROR — 致命错误(不可捕获)
// 内存不足、调用未定义的类方法等
// 脚本立即终止
// $obj->nonExistentMethod();  // Fatal error

// E_WARNING — 警告(继续执行)
// fopen 打开不存在的文件
$file = fopen('/nonexistent/path.txt', 'r');
// Warning: fopen(/nonexistent/path.txt): Failed to open stream

// E_NOTICE — 通知(继续执行)
// PHP 8.0: 访问未定义变量变为 Warning
$undefinedVar;  // PHP 8+: Warning: Undefined variable

// E_DEPRECATED — 弃用通知
// 使用已弃用的功能
$str = mysql_connect();  // Deprecated: mysql_connect()

// E_USER_ERROR / E_USER_WARNING / E_USER_NOTICE — 用户触发
trigger_error('这是一个用户通知', E_USER_NOTICE);
trigger_error('这是一个用户警告', E_USER_WARNING);
trigger_error('这是一个致命错误', E_USER_ERROR);

E_STRICT 级别

php
<?php

declare(strict_types=1);

// E_STRICT — PHP 代码严格模式建议
// PHP 8.0+:E_STRICT 已被弃用,大部分建议已融入 E_DEPRECATED 和 E_WARNING
// PHP 8.4 中 E_STRICT 正式弃用

// E_STRICT 常见场景(PHP 7.x):
// - 抽象类中不应定义构造函数
// - 静态方法中不应使用 $this
// - 子类签名应与父类兼容

// PHP 8.0+ 中这些场景变为 E_DEPRECATED 或 E_WARNING

E_RECOVERABLE_ERROR

php
<?php

declare(strict_types=1);

// E_RECOVERABLE_ERROR — 可恢复的致命错误
// PHP 7+ 中已被 Error 异常替代

// PHP 5.x 中:传递错误类型的参数给内置函数
function expectString(string $param): void
{
    echo $param;
}

// PHP 5.x: Catchable Fatal Error
// PHP 7+: TypeError
try {
    expectString(123);
} catch (TypeError $e) {
    echo "类型错误: " . $e->getMessage();
}

详细说明

错误级别的位运算组合

php
<?php

declare(strict_types=1);

// 错误级别使用位掩码(bitmask)
// 可以用位运算符组合

// 查看预定义常量的值
echo "E_ERROR: " . E_ERROR . "\n";           // 1
echo "E_WARNING: " . E_WARNING . "\n";       // 2
echo "E_NOTICE: " . E_NOTICE . "\n";         // 8
echo "E_ALL: " . E_ALL . "\n";               // PHP 8: 32767

// 组合示例
// 报告所有错误(E_ALL)
error_reporting(E_ALL);

// 报告所有错误,除了 Notice
error_reporting(E_ALL & ~E_NOTICE);

// 只报告 Warning 和 Error
error_reporting(E_ERROR | E_WARNING);

// 报告所有错误,除了 Deprecated 和 Notice
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);

// E_ALL 的实际值在不同 PHP 版本中不同
// PHP 7.x: E_ALL = 32767
// PHP 8.0+: E_ALL = 32767 (E_ALL 包含所有已定义级别)

E_USER_* 系列用户自定义错误

php
<?php

declare(strict_types=1);

// 用户可以通过 trigger_error() 触发自定义错误
// E_USER_ERROR    (256) — 致命,脚本终止
// E_USER_WARNING  (512) — 警告,继续执行
// E_USER_NOTICE   (1024) — 通知,继续执行
// E_USER_DEPRECATED (16384) — 弃用通知

class Validator
{
    public function validateAge(int $age): void
    {
        if ($age < 0) {
            trigger_error('年龄不能为负数', E_USER_WARNING);
            return;
        }

        if ($age > 150) {
            trigger_error('年龄超出合理范围', E_USER_NOTICE);
        }
    }
}

$validator = new Validator();
$validator->validateAge(-1);   // Warning: 年龄不能为负数
$validator->validateAge(200);  // Notice: 年龄超出合理范围

// 捕获用户自定义错误
set_error_handler(function (int $severity, string $message): bool {
    if ($severity === E_USER_ERROR) {
        throw new RuntimeException($message);
    }
    echo "[自定义错误 {$severity}]: {$message}\n";
    return true;
});

PHP 8 中的错误级别变化

php
<?php

declare(strict_types=1);

// PHP 8.0 中的主要变化

// 1. 未定义变量: Notice → Warning
$value = $undefined;  // Warning: Undefined variable $undefined

// 2. 未定义数组键: Notice → Warning(PHP 8.0)
$arr = [];
echo $arr['key'];  // Warning: Undefined array key "key"

// 3. 除以零: Warning → DivisionByZeroError(PHP 8.0)
// $result = 1 % 0;  // DivisionByZeroError

// 4. 字符串与数字比较: Notice(已修复)
// PHP 7: 0 == "foo" → true (Notice)
// PHP 8: 0 == "foo" → false (无 Notice,行为修正)

// 5. 内部函数参数类型不匹配
// PHP 7: Warning → TypeError (PHP 8.0)
// strlen([]);  // TypeError

// 6. abstract trait 方法(PHP 8.0)
trait ExampleTrait
{
    abstract public function doSomething(): void;
}
// 不再报 Strict 错误

// 7. 引用赋值和非引用赋值
// PHP 8.0: 不再产生 Indirect modification 错误

在错误处理器中判断级别

php
<?php

declare(strict_types=1);

function errorHandler(int $severity, string $message, string $file, int $line): bool
{
    // 检查是否在 error_reporting 范围内
    if (!(error_reporting() & $severity)) {
        return false;
    }

    // 根据级别采取不同措施
    switch ($severity) {
        case E_ERROR:
        case E_USER_ERROR:
        case E_RECOVERABLE_ERROR:
            // 致命错误:记录日志并终止
            error_log("[FATAL] {$message} in {$file}:{$line}");
            throw new ErrorException($message, 0, $severity, $file, $line);

        case E_WARNING:
        case E_USER_WARNING:
            // 警告:记录日志但继续
            error_log("[WARNING] {$message} in {$file}:{$line}");
            return true;

        case E_NOTICE:
        case E_USER_NOTICE:
        case E_STRICT:
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            // 轻量级:仅记录
            error_log("[{$severity}] {$message}");
            return true;

        default:
            return false;
    }
}

set_error_handler('errorHandler');

实战示例

按环境配置错误级别

php
<?php

declare(strict_types=1);

class ErrorConfig
{
    public static function configure(string $environment = 'production'): void
    {
        $config = match ($environment) {
            'development' => [
                'error_reporting'  => E_ALL,
                'display_errors'   => '1',
                'log_errors'       => '1',
                'error_log'        => '/tmp/php_dev_errors.log',
            ],
            'staging' => [
                'error_reporting'  => E_ALL & ~E_NOTICE & ~E_STRICT,
                'display_errors'  => '0',
                'log_errors'       => '1',
                'error_log'        => '/var/log/php_staging.log',
            ],
            'production' => [
                'error_reporting'  => E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT,
                'display_errors'  => '0',
                'log_errors'       => '1',
                'error_log'        => '/var/log/php_errors.log',
            ],
            default => [
                'error_reporting'  => E_ALL,
                'display_errors'  => '1',
                'log_errors'       => '0',
                'error_log'        => '',
            ],
        };

        foreach ($config as $key => $value) {
            ini_set($key, $value);
        }

        error_reporting((int) ini_get('error_reporting'));
    }
}

ErrorConfig::configure('development');

注意事项

E_ALL 的版本差异

php
<?php

declare(strict_types=1);

// E_ALL 的值在不同 PHP 版本中不同
// PHP 5.3: E_ALL = 30719 (不含 E_STRICT)
// PHP 5.4+: E_ALL = 32767 (含 E_STRICT)
// PHP 8.0+: E_ALL = 32767 (含所有已定义级别)

// 始终使用 E_ALL 常量,不要硬编码数字
error_reporting(E_ALL);  // 正确
// error_reporting(32767);  // 不推荐,版本间值可能不同

未处理错误的后果

php
<?php

declare(strict_types=1);

// E_ERROR / E_USER_ERROR:脚本终止,如果自定义处理器
// 没有抛出异常,后续代码不执行

// E_WARNING:脚本继续执行,但可能产生不正确的结果
$result = fopen('/nonexistent', 'r');
// $result 为 false
// 如果不检查就直接使用,可能导致更多错误
if ($result === false) {
    // 正确处理
}

// E_NOTICE:通常不影响执行,但暗示代码可能有问题
$arr = [];
echo $arr['missing'];  // Warning in PHP 8+
// 应先检查 array_key_exists('missing', $arr)

最佳实践

  1. 开发环境使用 E_ALL:显示所有错误、警告和通知
  2. 生产环境排除 Notice 和 Deprecated:减少日志量,关注 Warning 和 Error
  3. 使用常量而非数字E_ALLE_WARNING 比数字更可读
  4. 用户错误使用 E_USER_WARNING:trigger_error 适用于需要记录但不需要中断的验证错误
  5. 定期检查 Deprecated 警告:为 PHP 版本升级做准备
php
<?php

declare(strict_types=1);

// 推荐的错误报告配置模板
function setupErrorReporting(string $env): void
{
    match ($env) {
        'dev' => error_reporting(E_ALL | E_STRICT),
        'test' => error_reporting(E_ALL & ~E_DEPRECATED),
        'prod' => error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT),
    };

    ini_set('display_errors', $env === 'dev' ? '1' : '0');
    ini_set('log_errors', '1');
}

参考链接