首页 / PHP 入门教程 / PHP 错误与异常处理

PHP 入门教程

PHP 错误与异常处理

本教程共 65 篇 · 第 31 篇 · 更新于 2026-07-24 · 约 5 分钟阅读

PHPPHP8错误处理异常try-catchErrorException

31. PHP 错误与异常处理

本节目标:理解 PHP 的错误级别,学会配置错误报告,掌握 try-catch-finally 和自定义异常。

程序难免出错。良好的错误处理机制能让问题暴露得更清晰,也能避免把敏感信息泄露给用户。

31.1 PHP 错误级别

PHP 的错误分为多个级别:

级别常量说明
致命错误E_ERROR脚本终止,如调用未定义函数
警告E_WARNING不终止,如包含不存在的文件
通知E_NOTICE不终止,如访问未定义变量
解析错误E_PARSE语法错误,脚本无法运行
严格标准E_STRICT建议性提示,如弃用的用法
弃用警告E_DEPRECATEDPHP 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");
Note

PHP 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 的情况变成了 TypeErrorValueError(都是 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 异常处理」等,改写后所得。