PHP `Error Handling` (`set_error_handler`, `set_exception_handler`) 与 `E_ALL` 行为

好的,各位观众老爷们,欢迎来到今天的PHP错误处理专场!今天咱们就来聊聊set_error_handlerset_exception_handler,以及它们和E_ALL之间那些剪不断理还乱的恩怨情仇。准备好了吗?Let’s go!

第一幕:错误的世界观

在开始之前,咱们先得达成一个共识:PHP的世界里,错误分为两种主要类型:

  1. Error (错误):这种错误通常是代码层面的问题,比如语法错误、运行时错误、逻辑错误等等。
  2. Exception (异常):这种错误通常代表着程序执行过程中出现了不符合预期的情况,比如文件找不到、数据库连接失败等等。

PHP对这两种类型的错误处理方式是不一样的。Error主要通过PHP的内置错误处理机制来报告,而Exception则需要我们自己去try...catch或者使用set_exception_handler来捕获。

第二幕:set_error_handler登场

set_error_handler函数,顾名思义,就是用来设置自定义的错误处理函数的。它可以让你接管PHP的默认错误处理机制,自定义错误的处理逻辑。

语法:

set_error_handler(callable $error_handler, int $error_types = E_ALL): ?callable
  • $error_handler:这是一个可调用函数(callable),也就是你自定义的错误处理函数。
  • $error_types:这是一个可选参数,用来指定要处理的错误类型。默认值是E_ALL,表示处理所有类型的错误。
  • 返回值:如果成功,返回之前的错误处理函数。如果失败,返回null

错误处理函数($error_handler)的格式:

void error_handler ( int $errno , string $errstr , string $errfile , int $errline , array $errcontext = [] )
  • $errno:错误级别(整数)。
  • $errstr:错误消息(字符串)。
  • $errfile:发生错误的文件名(字符串)。
  • $errline:发生错误的行号(整数)。
  • $errcontext:一个包含错误发生时活动符号表的数组。

示例:

<?php

function myErrorHandler($errno, $errstr, $errfile, $errline) {
    echo "<b>自定义错误:</b> [$errno] $errstr<br>";
    echo "  错误发生在:$errfile 第 $errline 行<br>";
    // 可以选择终止脚本执行
    // die();
    return true; // 返回true阻止PHP内置错误处理
}

// 设置自定义错误处理函数
set_error_handler("myErrorHandler");

// 触发一个错误
$test = 1/0; // 产生一个 E_WARNING 错误
?>

在这个例子中,我们定义了一个名为myErrorHandler的错误处理函数,并使用set_error_handler将其设置为PHP的错误处理函数。当发生除以零的错误时,myErrorHandler会被调用,并输出错误信息。

重要提示:

  • set_error_handler 只能处理 运行时错误。这意味着它无法处理 语法错误,因为语法错误会导致脚本无法解析,根本不会运行到set_error_handler
  • $error_types 参数允许你选择要处理的错误类型。例如,如果你只想处理警告,可以设置 $error_types = E_WARNING;
  • 错误处理函数应该返回 true 来阻止 PHP 的内置错误处理机制继续执行。如果返回 false 或者不返回任何值(相当于返回 null),PHP 的内置错误处理机制仍然会执行。

第三幕:set_exception_handler登场

set_exception_handler函数,顾名思义,就是用来设置自定义的异常处理函数的。它可以让你接管PHP的默认异常处理机制,自定义异常的处理逻辑。

语法:

set_exception_handler(callable $exception_handler): ?callable
  • $exception_handler:这是一个可调用函数(callable),也就是你自定义的异常处理函数。
  • 返回值:如果成功,返回之前的异常处理函数。如果失败,返回null

异常处理函数($exception_handler)的格式:

void exception_handler ( Throwable $exception )
  • $exception:一个Throwable对象,包含了异常的信息。

示例:

<?php

function myExceptionHandler(Throwable $exception) {
    echo "<b>未捕获的异常:</b> " . $exception->getMessage() . "<br>";
    echo "  异常发生在: " . $exception->getFile() . " 第 " . $exception->getLine() . " 行<br>";
    echo "  堆栈跟踪:<pre>" . $exception->getTraceAsString() . "</pre><br>";
}

// 设置自定义异常处理函数
set_exception_handler("myExceptionHandler");

// 抛出一个异常
throw new Exception("这是一个测试异常");

?>

在这个例子中,我们定义了一个名为myExceptionHandler的异常处理函数,并使用set_exception_handler将其设置为PHP的异常处理函数。当抛出一个未被try...catch捕获的异常时,myExceptionHandler会被调用,并输出异常信息。

重要提示:

  • set_exception_handler 只能处理 未捕获的异常。如果异常被try...catch块捕获,则不会调用set_exception_handler
  • 异常处理函数应该处理异常,并可能终止脚本执行。
  • 自从 PHP 7 开始,异常处理函数接收一个 Throwable 对象,而不是 Exception 对象。ThrowableExceptionError 接口的父接口。这意味着你可以处理任何类型的可抛出对象。

第四幕:E_ALL的野望

E_ALL 是一个预定义的常量,代表着所有可能的错误级别。它通常用于 error_reporting 函数来设置 PHP 的错误报告级别。

error_reporting(E_ALL);

这行代码告诉 PHP 报告所有类型的错误,包括:

  • E_ERROR:致命的运行时错误
  • E_WARNING:运行时警告
  • E_PARSE:编译时语法解析错误
  • E_NOTICE:运行时提醒
  • E_CORE_ERROR:PHP 核心的致命错误
  • E_CORE_WARNING:PHP 核心的警告
  • E_COMPILE_ERROR:编译时致命错误
  • E_COMPILE_WARNING:编译时警告
  • E_USER_ERROR:用户产生的错误
  • E_USER_WARNING:用户产生的警告
  • E_USER_NOTICE:用户产生的提醒
  • E_STRICT:建议性的运行时错误
  • E_RECOVERABLE_ERROR:可捕获的致命错误
  • E_DEPRECATED:已弃用的功能
  • E_USER_DEPRECATED:用户产生的已弃用警告

E_ALLset_error_handler 的关系:

当使用 set_error_handler 时,E_ALL 作为 $error_types 参数的默认值,意味着你的自定义错误处理函数会处理所有类型的错误,除了 E_ERRORE_CORE_ERRORE_COMPILE_ERRORE_PARSE。这些错误是致命的,无法被用户定义的错误处理函数捕获。

E_ALLset_exception_handler 的关系:

E_ALLset_exception_handler 没有直接影响。set_exception_handler 仅仅处理未捕获的异常,而 E_ALL 控制的是错误报告级别。

第五幕:实战演练

咱们来几个例子,加深一下理解:

场景 1:自定义错误处理,忽略提醒

<?php

error_reporting(E_ALL); // 报告所有错误

function myErrorHandler($errno, $errstr, $errfile, $errline) {
    echo "<b>自定义错误:</b> [$errno] $errstr<br>";
    echo "  错误发生在:$errfile 第 $errline 行<br>";
    return true;
}

// 只处理警告和错误,忽略提醒
set_error_handler("myErrorHandler", E_WARNING | E_ERROR);

// 触发一个提醒
$undefined_variable; // 产生一个 E_NOTICE 错误

// 触发一个警告
$test = 1/0; // 产生一个 E_WARNING 错误

// 触发一个错误
trigger_error("这是一个用户定义的错误", E_USER_ERROR); // 产生一个 E_USER_ERROR 错误

echo "程序继续执行...n";

?>

在这个例子中,我们使用 set_error_handler 只处理 E_WARNINGE_ERROR 类型的错误,忽略 E_NOTICE 类型的提醒。因此,访问未定义的变量 $undefined_variable 不会触发自定义错误处理函数。

场景 2:自定义异常处理,记录日志

<?php

function myExceptionHandler(Throwable $exception) {
    $logMessage = "异常: " . $exception->getMessage() . "n";
    $logMessage .= "  文件: " . $exception->getFile() . ":" . $exception->getLine() . "n";
    $logMessage .= "  堆栈跟踪:n" . $exception->getTraceAsString() . "n";

    error_log($logMessage, 3, "error.log"); // 记录到文件

    echo "<b>发生了一个未捕获的异常,详情请查看 error.log 文件。</b><br>";

    // 可以选择终止脚本执行
    // die();
}

set_exception_handler("myExceptionHandler");

try {
    // 模拟数据库连接失败
    throw new Exception("无法连接到数据库");
} catch (Exception $e) {
    echo "捕获了异常: " . $e->getMessage() . "<br>";
}

// 抛出一个未捕获的异常
throw new Exception("这是一个未捕获的异常");

echo "程序继续执行...n"; // 这行代码不会执行,因为未捕获的异常会导致脚本终止
?>

在这个例子中,我们定义了一个异常处理函数 myExceptionHandler,用于记录未捕获的异常到 error.log 文件。 第一个异常被try...catch捕获,所以myExceptionHandler不会被调用。第二个异常没有被捕获,所以myExceptionHandler会被调用,并记录异常信息到日志文件。

场景 3:同时使用错误处理和异常处理

<?php

error_reporting(E_ALL);

function myErrorHandler($errno, $errstr, $errfile, $errline) {
    echo "<b>自定义错误:</b> [$errno] $errstr<br>";
    echo "  错误发生在:$errfile 第 $errline 行<br>";
    return true;
}

function myExceptionHandler(Throwable $exception) {
    echo "<b>未捕获的异常:</b> " . $exception->getMessage() . "<br>";
    echo "  异常发生在: " . $exception->getFile() . " 第 " . $exception->getLine() . " 行<br>";
}

set_error_handler("myErrorHandler");
set_exception_handler("myExceptionHandler");

// 触发一个错误
$test = 1/0;

// 抛出一个未捕获的异常
throw new Exception("这是一个测试异常");

?>

这个例子展示了如何同时使用自定义错误处理和异常处理。当发生除以零的错误时,myErrorHandler会被调用。当抛出一个未捕获的异常时,myExceptionHandler会被调用。

第六幕:总结与升华

我们用一个表格来总结一下set_error_handlerset_exception_handlerE_ALL之间的关系:

| 特性 | set_error_handler | set_exception_handler | E_ALL
| 动作 | 影响 | 影响 | 影响

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注