PHP 错误与异常处理
本教程共 65 篇 · 第 31 篇 · 更新于 2026-07-24 · 约 5 分钟阅读
31. PHP 错误与异常处理
本节目标:理解 PHP 的错误级别,学会配置错误报告,掌握
try-catch-finally和自定义异常。
程序难免出错。良好的错误处理机制能让问题暴露得更清晰,也能避免把敏感信息泄露给用户。
31.1 PHP 错误级别
PHP 的错误分为多个级别:
| 级别 | 常量 | 说明 |
|---|---|---|
| 致命错误 | E_ERROR | 脚本终止,如调用未定义函数 |
| 警告 | E_WARNING | 不终止,如包含不存在的文件 |
| 通知 | E_NOTICE | 不终止,如访问未定义变量 |
| 解析错误 | E_PARSE | 语法错误,脚本无法运行 |
| 严格标准 | E_STRICT | 建议性提示,如弃用的用法 |
| 弃用警告 | E_DEPRECATED | PHP 8.x 常见,提示未来版本将移除的功能 |
31.2 配置错误报告
开发环境和生产环境的错误显示策略应该不同:
<?php
// 开发环境:显示所有错误
error_reporting(E_ALL);
ini_set("display_errors", "1");
ini_set("display_startup_errors", "1");
// 生产环境:记录到日志,不显示给用户
error_reporting(E_ALL);
ini_set("display_errors", "0");
ini_set("log_errors", "1");
ini_set("error_log", "/var/log/php_errors.log");
NotePHP 8.0 起默认
error_reporting已包含E_ALL,显式设置可确保一致性。
31.3 自定义错误处理器
用 set_error_handler() 接管默认错误处理:
<?php
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
// 把错误转为异常抛出
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
// 现在 notice 和 warning 也会被 catch 捕获
try {
echo $undefinedVar; // 触发 E_NOTICE
} catch (Throwable $e) {
echo "捕获到:" . $e->getMessage();
}
31.4 异常基础
异常用 throw 抛出,用 try-catch 捕获:
<?php
function divide(float $a, float $b): float {
if ($b === 0.0) {
throw new Exception("除数不能为零");
}
return $a / $b;
}
try {
$result = divide(10, 0);
echo "结果:{$result}";
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
echo "<br>文件:" . $e->getFile();
echo "<br>行号:" . $e->getLine();
}
捕获多个异常类型
<?php
try {
// 某些可能抛出不同异常的操作
riskyOperation();
} catch (InvalidArgumentException $e) {
echo "参数错误:" . $e->getMessage();
} catch (RuntimeException $e) {
echo "运行时错误:" . $e->getMessage();
} catch (Exception $e) {
echo "其他错误:" . $e->getMessage();
}
Tip捕获顺序要从子类到父类。若
Exception放在最前,后面的特定异常永远不会被触发。
31.5 finally 块
finally 中的代码无论是否发生异常都会执行,常用于释放资源:
<?php
$file = fopen("data.txt", "r");
try {
$content = fread($file, 1024);
// 可能抛出异常的处理...
} catch (Exception $e) {
echo "读取失败:" . $e->getMessage();
} finally {
fclose($file); // 保证文件一定被关闭
echo "文件已关闭";
}
31.6 自定义异常类
通过继承 Exception 创建语义更明确的异常:
<?php
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
function login(string $user, string $pass): void {
if (empty($user) || empty($pass)) {
throw new ValidationException("用户名和密码不能为空");
}
if (!databaseConnect()) {
throw new DatabaseException("数据库连接失败");
}
}
try {
login("", "123");
} catch (ValidationException $e) {
echo "表单验证失败:" . $e->getMessage();
} catch (DatabaseException $e) {
echo "系统错误,请稍后重试";
error_log($e->getMessage()); // 记录详细日志
}
31.7 PHP 8 异常新特性
throw 表达式
PHP 8.0 起 throw 可以用在表达式位置:
<?php
// PHP 8.0 之前
if ($value === null) {
throw new Exception("不能为空");
}
$x = $value;
// PHP 8.0 起
$x = $value ?? throw new Exception("不能为空");
Stringable 接口
PHP 8.0 引入 Stringable 接口,Exception 已实现它,可直接用于字符串上下文。
更严格的类型错误
PHP 8.0 起很多过去产生 Warning 的情况变成了 TypeError 或 ValueError(都是 Throwable):
<?php
// PHP 8 之前:传入 null 产生 Warning,返回 false
// PHP 8 起:传入 null 抛出 TypeError
$result = str_contains($userInput, 'php'); // 如果 $userInput 是 null,抛出 TypeError
// 另一个例子:preg_match 在 PHP 8 起不允许 null 作为 subject
// preg_match('/\d+/', null); // PHP 8: TypeError
Tip
str_contains()是 PHP 8.0 引入的新函数,PHP 7.x 中不存在。空字符串""是合法的 string 类型参数,不会抛出异常。
31.8 全局异常捕获
用 set_exception_handler() 捕获未被 try-catch 处理的异常:
<?php
set_exception_handler(function (Throwable $e): void {
http_response_code(500);
echo "抱歉,系统出了点问题。";
// 记录到日志
$log = sprintf(
"[%s] %s in %s:%d\n",
date("Y-m-d H:i:s"),
$e->getMessage(),
$e->getFile(),
$e->getLine()
);
file_put_contents("exceptions.log", $log, FILE_APPEND);
});
// 未捕获的异常会进入这里
throw new Exception("出错了!");
来源:参考了 runoob「PHP Error」「PHP Exception」、w3cschool「PHP 错误处理」「PHP 异常处理」等,改写后所得。