跳到主要内容

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 接口,ExceptionError 都实现了它:

<?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;
}
?>

小结

本章我们学习了:

  1. PHP 错误类型和报告级别
  2. 异常的抛出和捕获
  3. 自定义异常类
  4. 异常链
  5. 全局异常处理
  6. Throwable 接口

下一章我们将学习数据库操作。