错误级别
概述
PHP 定义了多种错误级别(Error Levels),每个级别对应不同严重程度的错误。了解各级别的含义和用途是正确配置错误报告和处理策略的基础。
错误级别使用预定义常量表示,可以通过位运算进行组合。PHP 8.0+ 中许多原来低级别的错误被升级为更高级别的 Warning 或 Error。
版本说明
- PHP 5.3 引入
E_DEPRECATED和E_USER_DEPRECATED - PHP 8.0 将许多 Notice 升级为 Warning,将部分 Warning 升级为 Error
- PHP 8.4 引入了一些新的错误级别常量
基础概念
错误级别分类
PHP 错误级别大致分为三类:
- Fatal 级别:致命错误,脚本终止执行(
E_ERROR、E_PARSE、E_CORE_ERROR等) - Warning 级别:警告,脚本继续执行但可能产生意外结果(
E_WARNING、E_NOTICE等) - Deprecated 级别:弃用提示,表示使用了未来版本可能移除的功能
错误级别完整表格
| 常量 | 值 | 级别 | 说明 | PHP 8 变化 |
|---|---|---|---|---|
E_ERROR | 1 | Fatal | 致命运行时错误 | 无变化 |
E_WARNING | 2 | Warning | 运行时警告 | 无变化 |
E_PARSE | 4 | Fatal | 编译时解析错误 | 无变化 |
E_NOTICE | 8 | Notice | 运行时通知 | PHP 8.0 部分升级为 Warning |
E_CORE_ERROR | 16 | Fatal | PHP 初始化致命错误 | 无变化 |
E_CORE_WARNING | 32 | Warning | PHP 初始化警告 | 无变化 |
E_COMPILE_ERROR | 64 | Fatal | 编译致命错误 | 无变化 |
E_COMPILE_WARNING | 128 | Warning | 编译警告 | 无变化 |
E_USER_ERROR | 256 | Fatal | 用户自定义致命错误 | 无变化 |
E_USER_WARNING | 512 | Warning | 用户自定义警告 | 无变化 |
E_USER_NOTICE | 1024 | Notice | 用户自定义通知 | 无变化 |
E_STRICT | 2048 | Notice | 严格模式建议 | PHP 8.4 弃用 |
E_RECOVERABLE_ERROR | 4096 | Fatal | 可捕获的致命错误 | PHP 7+ 转为 Error |
E_DEPRECATED | 8192 | Deprecated | 弃用通知 | 更严格 |
E_USER_DEPRECATED | 16384 | Deprecated | 用户自定义弃用通知 | 无变化 |
E_ALL | E_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_WARNINGE_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)最佳实践
- 开发环境使用 E_ALL:显示所有错误、警告和通知
- 生产环境排除 Notice 和 Deprecated:减少日志量,关注 Warning 和 Error
- 使用常量而非数字:
E_ALL、E_WARNING比数字更可读 - 用户错误使用 E_USER_WARNING:trigger_error 适用于需要记录但不需要中断的验证错误
- 定期检查 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');
}