PHP Error
Error handling functions and constants reference.
Key Points`set_error_handler` cannot catch `E_ERROR` — use a shutdown function.
Functions
| Function | Purpose |
|---|---|
| error_reporting | 设置/读取报告级别 |
| ini_set | 改 display_errors、log_errors |
| set_error_handler | 自定义错误处理器 |
| restore_error_handler | 恢复默认处理器 |
| trigger_error | 主动抛一个错误 |
| error_log | 写错误日志 |
| error_get_last | 取最后一个错误 |
| register_shutdown_function | 脚本结束/致命错误时执行 |
Level constants
| Constant | Value | Meaning |
|---|---|---|
| E_ERROR | 1 | 致命错误,脚本终止 |
| E_WARNING | 2 | 警告,继续执行 |
| E_PARSE | 4 | 语法错误 |
| E_NOTICE | 8 | 提示 |
| E_DEPRECATED | 8192 | 过时特性 |
| E_USER_ERROR | 256 | trigger_error 的用户级错误 |
| E_ALL | 32767 | 所有级别 |
PHP
<?php
echo E_ERROR, " ", E_WARNING, " ", E_PARSE, " ", E_NOTICE, "\n";
echo E_DEPRECATED, " ", E_USER_ERROR, " ", E_ALL, "\n";
echo (E_ALL & E_WARNING) ? "包含 WARNING" : "不包含", "\n";
echo (E_ALL & ~E_NOTICE) === E_ALL - E_NOTICE ? "位运算可用" : "不同", "\n";Output
1 2 4 8
8192 256 32767
包含 WARNING
位运算可用Levels are a bitmask, so E_ALL & ~E_DEPRECATED means "everything except deprecations". Combine with |.
Custom handler
PHP
<?php
set_error_handler(function (int $no, string $msg, string $file, int $line): bool {
$map = [
E_WARNING => "WARN",
E_NOTICE => "NOTE",
E_USER_WARNING => "WARN",
E_USER_NOTICE => "NOTE",
E_DEPRECATED => "DEPR",
];
$level = $map[$no] ?? "ERR";
printf("[%s] %s (%s:%d)\n", $level, $msg, basename($file), $line);
return true;
});
trigger_error("自定义警告", E_USER_WARNING);
trigger_error("自定义提示", E_USER_NOTICE);
restore_error_handler();
echo "处理器已恢复\n";Output
[WARN] 自定义警告 (error.php:15)
[NOTE] 自定义提示 (error.php:16)
处理器已恢复Logging
PHP
<?php
ini_set("log_errors", "1");
ini_set("error_log", sys_get_temp_dir() . "/php_err.log");
error_log("这是一条普通日志");
error_log("这是一条带级别的日志", 0);
$f = ini_get("error_log");
echo basename($f), "\n";
echo file_exists($f) ? "日志已写入" : "未写入", "\n";
@unlink($f);Output
php_err.log
日志已写入The second argument: 0 for the system log, 1 to email, 3 to the file given as the third argument. Use 0 or 3 in production.
Never log full SQL, passwords or ID numbers. Logs are read by ops, monitoring and backup systems — mask sensitive fields.
Fatal errors
PHP
<?php
register_shutdown_function(function (): void {
$e = error_get_last();
if ($e === null) {
return;
}
if (in_array($e["type"], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
error_log(sprintf("致命: %s in %s:%d", $e["message"], $e["file"], $e["line"]));
if (!headers_sent()) {
http_response_code(500);
}
echo "服务异常,请稍后再试";
}
});
// undefined_function(); // 取消注释可触发 E_ERROR
echo "正常结束\n";Output
正常结束error_get_last() returns only the last error, which may be a warning rather than the fatal one — hence the type check.函数
| 函数 | 作用 |
|---|---|
| error_reporting | 设置/读取报告级别 |
| ini_set | 改 display_errors、log_errors |
| set_error_handler | 自定义错误处理器 |
| restore_error_handler | 恢复默认处理器 |
| trigger_error | 主动抛一个错误 |
| error_log | 写错误日志 |
| error_get_last | 取最后一个错误 |
| register_shutdown_function | 脚本结束/致命错误时执行 |
错误级别常量
| 常量 | 值 | 含义 |
|---|---|---|
| E_ERROR | 1 | 致命错误,脚本终止 |
| E_WARNING | 2 | 警告,继续执行 |
| E_PARSE | 4 | 语法错误 |
| E_NOTICE | 8 | 提示 |
| E_DEPRECATED | 8192 | 过时特性 |
| E_USER_ERROR | 256 | trigger_error 的用户级错误 |
| E_ALL | 32767 | 所有级别 |
PHP
<?php
echo E_ERROR, " ", E_WARNING, " ", E_PARSE, " ", E_NOTICE, "\n";
echo E_DEPRECATED, " ", E_USER_ERROR, " ", E_ALL, "\n";
echo (E_ALL & E_WARNING) ? "包含 WARNING" : "不包含", "\n";
echo (E_ALL & ~E_NOTICE) === E_ALL - E_NOTICE ? "位运算可用" : "不同", "\n";Output
1 2 4 8
8192 256 32767
包含 WARNING
位运算可用错误级别是位掩码,所以 E_ALL & ~E_DEPRECATED 表示「除了弃用提示」。组合多个级别用 |。
自定义处理器
PHP
<?php
set_error_handler(function (int $no, string $msg, string $file, int $line): bool {
$map = [
E_WARNING => "WARN",
E_NOTICE => "NOTE",
E_USER_WARNING => "WARN",
E_USER_NOTICE => "NOTE",
E_DEPRECATED => "DEPR",
];
$level = $map[$no] ?? "ERR";
printf("[%s] %s (%s:%d)\n", $level, $msg, basename($file), $line);
return true;
});
trigger_error("自定义警告", E_USER_WARNING);
trigger_error("自定义提示", E_USER_NOTICE);
restore_error_handler();
echo "处理器已恢复\n";Output
[WARN] 自定义警告 (error.php:15)
[NOTE] 自定义提示 (error.php:16)
处理器已恢复日志
PHP
<?php
ini_set("log_errors", "1");
ini_set("error_log", sys_get_temp_dir() . "/php_err.log");
error_log("这是一条普通日志");
error_log("这是一条带级别的日志", 0);
$f = ini_get("error_log");
echo basename($f), "\n";
echo file_exists($f) ? "日志已写入" : "未写入", "\n";
@unlink($f);Output
php_err.log
日志已写入error_log() 的第二个参数:0 写系统日志,1 发邮件(第四个参数是收件人),3 写到第三个参数指定的文件。**生产环境用 0 或 3。**
日志里不要打完整 SQL、密码、身份证号、银行卡。日志文件经常被运维、监控系统、甚至备份卷看到。敏感字段要脱敏。
致命错误
PHP
<?php
register_shutdown_function(function (): void {
$e = error_get_last();
if ($e === null) {
return;
}
if (in_array($e["type"], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
error_log(sprintf("致命: %s in %s:%d", $e["message"], $e["file"], $e["line"]));
if (!headers_sent()) {
http_response_code(500);
}
echo "服务异常,请稍后再试";
}
});
// undefined_function(); // 取消注释可触发 E_ERROR
echo "正常结束\n";Output
正常结束error_get_last() 只返回最后一个错误。如果中间有 warning,可能拿到的是 warning 而不是致命错误,所以要检查 type。