set_exception_handler

(PHP 5, PHP 7, PHP 8)

set_exception_handler 设置用户定义的异常处理函数

描述

set_exception_handler(?callable $callback): ?callable

如果在 try/catch 块中没有捕获异常,则设置默认异常处理程序。在调用 callback 之后,执行将停止。

参数

callback

当发生未捕获异常时要调用的函数。此处理函数需要接受一个参数,该参数将是抛出的 Throwable 对象。 ErrorException 都实现了 Throwable 接口。这是处理程序签名

handler(Throwable $ex): void

可以传递 null 来将此处理程序重置为其默认状态。

返回值

返回先前定义的异常处理程序,或在错误时返回 null。如果之前没有定义处理程序,则也会返回 null

示例

示例 #1 set_exception_handler() 示例

<?php
function exception_handler(Throwable $exception) {
echo
"Uncaught exception: " , $exception->getMessage(), "\n";
}

set_exception_handler('exception_handler');

throw new
Exception('Uncaught Exception');
echo
"Not Executed\n";
?>

参见

添加注释

用户贡献的注释 18 个注释

匿名
14 年前
您应该注意的事情

异常处理程序处理之前未捕获的异常。异常的本质是它会中断程序的执行 - 因为它声明了一个程序无法继续的异常情况(除非您捕获它)。

由于它尚未被捕获,您的代码表明它没有意识到这种情况,无法继续。

这意味着:当异常处理程序已经被调用时,返回脚本是不可能的,因为未捕获的异常不是通知。对于类似的事情,请使用您自己的调试或通知日志系统。

此外:虽然仍然可以从脚本中调用函数,因为异常处理程序已经被调用,来自该代码段的冒泡异常将不会再次触发异常处理程序。php 将在不留下任何信息的情况下死亡,除了“未捕获的异常,未知堆栈帧”。因此,如果您从脚本中调用函数,请确保您在异常处理程序内部使用 try..catch 捕获可能发生的任何异常。

对于那些误解了异常处理程序本质含义的人:它的唯一用途是优雅地处理脚本的终止,例如在 Facebook 或维基百科等项目中:呈现一个漂亮的错误页面,最终隐藏不应泄露到公共(相反,您可能希望写入日志或将错误信息发送给系统管理员或类似的东西)。

换句话说:将所有 php 错误从错误处理程序重定向到使用异常(包括通知)是一个非常愚蠢的想法,如果您不打算每次未设置变量(例如)就终止脚本。

我的两分钱。
ohcc at 163 dot com
4 年前
从 PHP 7.4 开始,在用户定义的关闭函数中抛出的异常可以被用户定义的异常处理程序捕获。

<?php
set_error_handler
(
function(
$level, $error, $file, $line){
if(
0 === error_reporting()){
return
false;
}
throw new
ErrorException($error, -1, $level, $file, $line);
},
E_ALL
);

register_shutdown_function(function(){
$error = error_get_last();
if(
$error){
throw new
ErrorException($error['message'], -1, $error['type'], $error['file'], $error['line']);
}
});

set_exception_handler(function($exception){
// ... more code ...
});

require
'NotExists.php';
Glen
17 年前
如果您希望类实例来处理异常,以下是如何执行:

<?php
class example {
public function
__construct() {
@
set_exception_handler(array($this, 'exception_handler'));
throw new
Exception('DOH!!');
}

public function
exception_handler($exception) {
print
"Exception Caught: ". $exception->getMessage() ."\n";
}
}

$example = new example;

?>

请参阅第一篇文章(Sean 的)以获取静态示例。正如 Sean 指出的,exception_handler 函数必须声明为 public。
pinkgothic at gmail dot com
14 年前
如果您正在处理敏感数据,并且您不想在抛出异常时记录有关变量内容的详细信息,您可能会发现自己沮丧地寻找构成正常堆栈跟踪输出的各个部分,以便您可以保留其可读性,但只需更改几件事。在这种情况下,这可能对您有所帮助

<?php

function exceptionHandler($exception) {

// 这些是我们的模板
$traceline = "#%s %s(%s): %s(%s)";
$msg = "PHP 致命错误: 未捕获异常 '%s',消息为 '%s',位于 %s:%s\n堆栈跟踪:\n%s\n 在 %s 的第 %s 行抛出";

// 随意修改你的跟踪信息,这里
$trace = $exception->getTrace();
foreach (
$trace as $key => $stackPoint) {
// 我正在将参数转换为其类型
// (防止密码以外的任何内容被记录为 'string')
$trace[$key]['args'] = array_map('gettype', $trace[$key]['args']);
}

// 构建你的跟踪行
$result = array();
foreach (
$trace as $key => $stackPoint) {
$result[] = sprintf(
$traceline,
$key,
$stackPoint['file'],
$stackPoint['line'],
$stackPoint['function'],
implode(', ', $stackPoint['args'])
);
}
// 跟踪总是以 {main} 结束
$result[] = '#' . ++$key . ' {main}';

// 将跟踪行写入主模板
$msg = sprintf(
$msg,
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
implode("\n", $result),
$exception->getFile(),
$exception->getLine()
);

// 随意记录或输出
error_log($msg);
}

?>

如果你不喜欢 sprintf() 或者重复的 $exception->getFile() 和 $exception->getLine() 调用,当然可以随意替换它们 - 认为这只是一个部分的编译。
mastabog at hotmail dot com
18 年前
一种行为没有得到足够多的记录或讨论,但很常见,那就是如果从全局异常处理程序抛出异常,就会发生致命错误(没有堆栈帧抛出异常)。也就是说,如果你通过调用 set_exception_handler() 定义自己的全局异常处理程序,并且你从它内部抛出异常,那么就会发生这种致命错误。不过这很自然,因为 set_exception_handler() 定义的回调只在未捕获(未处理)的异常上被调用,所以如果你从那里抛出一个异常,那么你就会得到这个致命错误,因为没有剩余的异常处理程序(你通过调用 set_exception_handler() 覆盖了 php 内部处理程序),因此它没有堆栈帧。

示例

<?php

function myExceptionHandler (Exception $ex)
{
throw
$ex;
}

set_exception_handler("myExceptionHandler");

throw new
Exception("这应该会导致致命错误,并且此消息将丢失");

?>

将导致致命错误: 没有堆栈帧抛出异常

如果你跳过/注释掉 set_exception_handler("...") 行,那么内部 PHP 全局处理程序将捕获异常并将异常消息和跟踪(作为字符串)输出到浏览器,允许你至少看到异常消息。

虽然始终使用 set_exception_handler() 函数定义自己的全局异常处理程序是一个非常好的主意,但你应该注意,永远不要从它中抛出异常(或者如果你这样做,请捕获它)。

最后,每个严肃的程序员都应该使用具有调试功能的 IDE。通过使用简单的调试“单步进入”命令,跟踪这样的错误将变得微不足道(就我而言,我推荐使用 Zend IDE v5.2,这是本文撰写时的推荐版本)。我已经在互联网上看到过很多关于人们想知道为什么此消息会弹出的信息。

干杯

p.s. 与此无关的其他导致此错误的原因是当你从析构函数中抛出异常时(背后的原因是类似的,由于 php 引擎正在关闭页面,因此全局处理程序可能不再存在)。
frank at netventures dot com dot au
16 年前
大家好,我刚刚开始使用异常套件而不是普通的 PHP 错误套件。对于那些正在寻找一种面向对象的方法来做到这一点,而无需查看 Glen 和 Sean 的示例的人(第一课:始终阅读日志!),这里有你的解决方案

<?php

class NewException extends Exception
{
public function
__construct($message, $code=NULL)
{
parent::__construct($message, $code);
}

public function
__toString()
{
return
"代码: " . $this->getCode() . "<br />消息: " . htmlentities($this->getMessage());
}

public function
getException()
{
print
$this; // 这将打印上面方法 __toString() 的返回值
}

public static function
getStaticException($exception)
{
$exception->getException(); // $exception 是此类的实例
}
}

set_exception_handler(array("NewException", "getStaticException"));
throw new
NewException("抓住我!!!", 69);

?>

如果我遗漏了一些明显的东西,请告诉我,因为我把眼镜忘在家里了,而且我刚从墨尔本杯比赛回来(如果我赢了,我就不应该还在工作了!)。
marques at displague dot com
16 年前
frank,

你的异常处理程序被配置为所有异常的处理程序,但是如果抛出了一个基本的 'Exception',你的静态方法将出错,因为 'Exception' 没有 'getException'。正因为如此,我认为将未捕获的处理程序设为扩展 Exception 的类没有实际意义。

我喜欢使用通用 Exception 处理类的静态方法的想法。

<?php
class ExceptionHandler {
public static function
printException(Exception $e)
{
print
'未捕获 '.get_class($e).', 代码: ' . $e->getCode() . "<br />消息: " . htmlentities($e->getMessage())."\n";
}

public static function
handleException(Exception $e)
{
self::printException($e);
}
}

set_exception_handler(array("ExceptionHandler", "handleException"));

class
NewException extends Exception {}
try {
throw new
NewException("抓住我一次", 1);
} catch (
Exception $e) {
ExceptionHandler::handleException($e);
}

throw new
Exception("抓住我两次", 2);
?>

输出
未捕获 NewException, 代码: 1<br />消息: 抓住我一次
未捕获 Exception, 代码: 2<br />消息: 抓住我两次

可以做更多有趣的事情,比如重新格式化,并选择性地显示或通过电子邮件发送它们。但是这个类可以作为这些函数的很好的容器。
匿名
10 年前
默认情况下,堆栈跟踪很难读,因此你可能需要用 <pre> 标签将其包装起来。在这里,我还将其包装在一个 <div> 中,并设置类 'alert alert-danger',这是 Bootstrap CSS 框架中的 CSS 类,用于将其样式设置为红色。
<?php

function exception_handler($exception) {
echo
'<div class="alert alert-danger">';
echo
'<b>致命错误</b>: 未捕获异常 \'' . get_class($exception) . '\' with message ';
echo
$exception->getMessage() . '<br>';
echo
'堆栈跟踪:<pre>' . $exception->getTraceAsString() . '</pre>';
echo
'thrown in <b>' . $exception->getFile() . '</b> on line <b>' . $exception->getLine() . '</b><br>';
echo
'</div>';
}

set_exception_handler('exception_handler');
Josef Spak
9 years ago
在 GNU/Linux 上,当调用异常处理程序时,PHP 将以退出状态码 0 而不是 255 结束。

您可以在自定义错误处理程序的末尾使用 exit() 调用更改退出状态码。
mc-php-doco at oak dot homeunix dot org
18 年前
这在使用“-r”标志调用 PHP 二进制文件时似乎不起作用。

例如,如果我像这样运行它

php -r '
function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "\n";
}

set_exception_handler("exception_handler");

throw new Exception("Uncaught Exception");
echo "Not Executed\n";
'

或者如果我把它放在一个文件中并像这样运行它

php -r 'include "./tmp.php";'

我得到一个堆栈跟踪,而不是调用函数“exception_handler”。如果像这样运行它

php tmp.php

它运行良好。

(为什么要从“-r”运行代码?有时在 include 周围添加一些东西(如调用 microtime 用于基准测试),或者包含一个库,然后从库中调用一些函数,所有这些都是以一种临时的方式完成的,而无需创建新文件。)

PHP 版本 5.1.2 和 5.0.4。
sean at seanodonnell dot com
18 年前
在类中使用“set_exception_handler”函数,定义的“exception_handler”方法必须声明为“public”(如果使用“array('example', 'exception_handler')”语法,最好是“public static”)。

<?php
class example {
public function
__construct() {
@
set_exception_handler(array('example', 'exception_handler'));
throw new
Exception('DOH!!');
}

public static function
exception_handler($exception) {
print
"Exception Caught: ". $exception->getMessage() ."\n";
}
}

$example = new example;

echo
"Not Executed\n";
?>

将“exception_handler”函数声明为“private”会导致致命错误。

[derick: red. 更新了关于 static 的说明]
joshua dot boyle-petrie at its dot monash dot edu
15 years ago
感谢 mastabog,我们知道在异常处理程序中抛出异常会导致致命错误和调试噩梦。避免在那里抛出异常应该很容易。

但是,如果您使用自定义错误处理程序将错误转换为 ErrorExceptions,突然之间,就有很多新方法会导致您不小心在异常处理程序中抛出异常。

<?php
function error_handler($code, $message, $file, $line)
{
if (
0 == error_reporting())
{
return;
}
throw new
ErrorException($message, 0, $code, $file, $line);
}
function
exception_handler($e)
{
// ... 正常的异常处理代码在这里
print $undefined; // 这是根本问题
}
set_error_handler("error_handler");
set_exception_handler("exception_handler");
throw new
Exception("Just invoking the exception handler");
?>
输出: 致命错误: 异常抛出但没有堆栈帧,位置未知,行号 0

我发现避免这种情况的最佳方法是将异常处理程序中的所有内容都包装在一个 try/catch 块中。
<?php
function exception_handler($e)
{
try
{
// ... 正常的异常处理代码在这里
print $undefined; // 这是根本问题
}
catch (
Exception $e)
{
print
get_class($e)." thrown within the exception handler. Message: ".$e->getMessage()." on line ".$e->getLine();
}
}
?>
输出: ErrorException thrown within the exception handler. Message: Undefined variable: undefined on line 14

这加快了调试速度,并为在异常处理程序中意外抛出的任何其他异常提供了一些可扩展性。

另一种解决方案是在异常处理程序的开头恢复错误处理程序。虽然这对于避免 ErrorExceptions 来说是一个灵丹妙药,但调试消息然后依赖于 error_reporting() 级别和 display_errors 指令。为什么要提到这一点?对于生产代码来说,它可能更可取,因为我们更关心隐藏用户错误,而不是方便的调试消息。
Anonymous
11 years ago
从 PHP 5.4.11 开始……

不是

输出: 致命错误: 异常抛出但没有堆栈帧,位置未知,行号 0

这段代码

<?php
function error_handler($code, $message, $file, $line)
{
if (
0 == error_reporting())
{
return;
}
throw new
ErrorException($message, 0, $code, $file, $line);
}
function
exception_handler($e)
{
// ... 正常的异常处理代码在这里
print $undefined; // 这是根本问题
}
set_error_handler("error_handler");
set_exception_handler("exception_handler");
throw new
Exception("Just invoking the exception handler");
?>

现在返回

致命错误: 未捕获异常“ErrorException”,消息为“未定义变量:undefined”,位置为“C:\Apache2\htdocs\error\test.php:13”。堆栈跟踪: #0 C:\Apache2\htdocs\error\test.php(13): error_handler(8, 'Undefined varia...', 'C:\Apache2\htdo...', 13, Array) #1 [internal function]: exception_handler(Object(Exception)) #2 {main} thrown in C:\Apache2\htdocs\error\test.php on line 13

因此,似乎在异常处理程序中抛出的异常现在绕过了异常处理程序。
dev at codesatori dot com
7 years ago
对于那些希望将 PHP 错误转换为 ErrorExceptions(有用),但对每个 E_NOTICE 等等导致脚本停止感到沮丧的人。在您的错误处理程序中,只需创建 ErrorException,然后要么抛出它(脚本停止),要么将对象直接传递给您的异常处理程序函数(脚本继续)。

<?php
const EXIT_ON_ALL_PHP_ERRORS = false; // 或者 true

function proc_error($errno, $errstr, $errfile, $errline)
{
$e = new ErrorException($errstr, 0, $errno, $errfile, $errline);

if (
EXIT_ON_ALL_PHP_ERRORS) {
throw
$e; // 这将停止您的脚本。
} else {
proc_exception($e); // 这将让它继续。
}
}

set_error_handler("proc_error");
set_exception_handler("proc_exception");
?>

您可以进一步自定义脚本停止或允许继续的错误严重级别(从 $errno 获取,将位掩码与错误级别常量匹配)。上面的代码只是允许 PHP 错误的默认行为通过。

如果你的异常处理程序同时接收错误 ErrorExceptions(包含严重性级别等)和其他类型的未捕获异常,那么使用 <?php if ($e instanceof ErrorException) { // ... } ?> 条件来处理这种差异。我不得不进一步将其包装在 <?php try { $errno = $e->getSeverity(); } catch (Exception $x) { $errno = 0 } ... ?> 中,因为一些 EE 莫名其妙地缺少严重性级别属性(尚未深入研究并找出原因)。
klaussantana at gmail dot com
6 年前
大家好。我不知道这是否是先前版本的旧行为,但目前你可以将异常和错误处理程序设置为私有或受保护的方法,前提是你在可以访问该方法的上下文中调用 `set_exception_handler()` 或 `set_error_handler()`。

示例
<?PHP
$Handler
= new class ()
{
public function
__construct ()
{
set_error_handler([$this, 'HandleError');
set_exception_handler([$this, 'HandleException']);
}
protected function
HandleError ( $Code, $Message, $File = null, $Line = 0, $Context = [] )
{
// 在这里处理错误。
}
private function
HandleException ( $Exception )
{
// 在这里处理异常。
}
}
?>

注意:这些方法必须与回调参数签名匹配。我放了
Anonymous
14 年前
当未捕获的异常被处理时,执行不会返回到脚本,而是(出乎意料,至少在我这边是这样)终止。

我正在将 `set_error_handler()` 和 `set_exception_handler()` 结合使用来开发我当前的系统(在使用 Xampp 的 v5.3.0 上)。假设触发了两个 `E_USER_NOTICES`,脚本将在第一个被处理后就终止。

<?php
set_exception_handler
( 'exc_handler' );
function
exc_handler($exception) {
echo
"Uncaught exception: " , $exception->getMessage(), "\n";
}
function
errorone() {
throw new
Exception("Test 1");
}
function
errortwo() {
throw new
Exception("Test 2");
}
function
test() {
errorone();
errortwo();
}
test();
test();
?>

而不是打印(正如我预期的那样)"Uncaught exception: Text 1\nUncaught exception: Text 2\nUncaught exception: Text 1\nUncaught exception: Text 2\n"

它只打印了一次。我尝试了许多不同的方法,但我无法弄清楚如何在异常处理程序运行后将执行返回到脚本。

如果有人有关于如何返回执行(因此,允许脚本记录未捕获的异常但继续处理)的解决方案,请给我发邮件!谢谢!
ch at westend dot com
18 年前
似乎虽然异常本身包含一个回溯,但在进入异常处理程序时,`debug_backtrace()` 函数的堆栈为空。这非常不方便。请参阅错误 #36477。
reg dot php dot manual at entropy dot ch
17 年前
根据我的经验,`static` 关键字对于作为类的方法而不是独立函数的错误处理程序至关重要。

static function exceptionHandler($exception)

可以正常工作,但

function exceptionHandler($exception)

无法正常工作,会导致 "Fatal error: Exception thrown without a stack frame in Unknown on line 0" 消息。

"public" 是可选的,因为它默认情况下是公开的(但明确写出来可能更清晰)。
To Top