PHP 异常处理
本章将介绍 PHP 的错误和异常处理机制。
错误类型
PHP 有多种错误类型:
<?php
// E_ERROR - 致命错误,脚本终止
// E_WARNING - 警告,脚本继续
// E_NOTICE - 通知,脚本继续
// E_PARSE - 语法错误
// E_STRICT - 建议修改
// E_DEPRECATED - 弃用警告
// E_ALL - 所有错误
// 错误报告级别
error_reporting(E_ALL); // 报告所有错误
error_reporting(E_ALL & ~E_NOTICE); // 报告除 NOTICE 外的错误
error_reporting(0); // 不报告错误
// php.ini 配置
// display_errors = On ; 显示错误
// log_errors = On ; 记录错误日志
// error_log = /var/log/php_errors.log
?>
异常基础
抛出异常
<?php
function divide($a, $b) {
if ($b == 0) {
throw new Exception("除数不能为零");
}
return $a / $b;
}
// 抛出异常后,代码不再继续执行
divide(10, 0); // 抛出异常
echo "这行不会执行";
?>
捕获异常
<?php
try {
$result = divide(10, 0);
echo "结果:$result";
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
} finally {
echo "无论是否异常都会执行";
}
?>
多个异常
<?php
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
function processUser($data) {
if (empty($data['name'])) {
throw new ValidationException("用户名不能为空");
}
if (!$data['email']) {
throw new ValidationException("邮箱无效");
}
// 数据库操作
if (!saveToDatabase($data)) {
throw new DatabaseException("保存失败");
}
}
try {
processUser($data);
} catch (ValidationException $e) {
echo "验证错误:" . $e->getMessage();
} catch (DatabaseException $e) {
echo "数据库错误:" . $e->getMessage();
} catch (Exception $e) {
echo "其他错误:" . $e->getMessage();
}
?>
自定义异常
<?php
// 自定义异常类
class AppException extends Exception {
protected $code = 500;
protected $details;
public function __construct($message, $code = 0, $details = []) {
parent::__construct($message, $code);
$this->details = $details;
}
public function getDetails() {
return $this->details;
}
public function toArray() {
return [
'error' => $this->getMessage(),
'code' => $this->code,
'details' => $this->details
];
}
}
class NotFoundException extends AppException {
protected $code = 404;
}
class UnauthorizedException extends AppException {
protected $code = 401;
}
class ValidationException extends AppException {
protected $code = 422;
}
// 使用
try {
throw new ValidationException("验证失败", 422, [
'name' => '用户名不能为空',
'email' => '邮箱格式无效'
]);
} catch (ValidationException $e) {
http_response_code($e->getCode());
echo json_encode($e->toArray());
}
?>
异常链
<?php
class DatabaseException extends Exception {}
function connectDatabase() {
try {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
} catch (PDOException $e) {
throw new DatabaseException("数据库连接失败", 0, $e);
}
}
try {
connectDatabase();
} catch (DatabaseException $e) {
echo $e->getMessage(); // 数据库连接失败
echo $e->getPrevious()->getMessage(); // 原始异常信息
}
?>
全局异常处理
<?php
// 设置全局异常处理器
set_exception_handler(function (Throwable $e) {
error_log($e->getMessage());
if (php_sapi_name() === 'cli') {
echo "错误:" . $e->getMessage() . "\n";
} else {
http_response_code(500);
echo json_encode([
'error' => '服务器内部错误',
'message' => $e->getMessage()
]);
}
});
// 未捕获的异常会由全局处理器处理
throw new Exception("未捕获的异常");
?>
错误转异常
<?php
// 将错误转换为异常
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
try {
// 这会产生一个警告,但会被转换为异常
$content = file_get_contents('nonexistent.txt');
} catch (ErrorException $e) {
echo "错误:" . $e->getMessage();
}
?>
Throwable 接口
PHP 7+ 引入了 Throwable 接口,Exception 和 Error 都实现了它:
<?php
try {
// 可能抛出 Exception 或 Error
$result = someFunction();
} catch (InvalidArgumentException $e) {
// 特定异常
} catch (RuntimeException $e) {
// 运行时异常
} catch (Exception $e) {
// 其他异常
} catch (Error $e) {
// 错误(如 TypeError)
} catch (Throwable $e) {
// 捕获所有
}
?>
常见内置异常
<?php
// LogicException - 逻辑错误
// RuntimeException - 运行时错误
// InvalidArgumentException - 参数无效
// OutOfBoundsException - 索引越界
//OutOfRangeException - 值越界
// LengthException - 长度无效
// DomainException - 值不在有效范围内
// UnexpectedValueException - 意外的值
// BadMethodCallException - 方法不存在
// BadFunctionCallException - 函数不存在
function getAge($age) {
if (!is_int($age)) {
throw new InvalidArgumentException("年龄必须是整数");
}
if ($age < 0 || $age > 150) {
throw new OutOfRangeException("年龄必须在 0-150 之间");
}
return $age;
}
?>
小结
本章我们学习了:
- PHP 错误类型和报告级别
- 异常的抛出和捕获
- 自定义异常类
- 异常链
- 全局异常处理
- Throwable 接口
下一章我们将学习数据库操作。