PHP Conference Japan 2024

set_error_handler

(PHP 4 >= 4.0.1, PHP 5, PHP 7, PHP 8)

set_error_handler设置用户自定义的错误处理函数

描述

set_error_handler(?callable $callback, int $error_levels = E_ALL): ?callable

设置一个用户函数(callback)来处理脚本中的错误。

此函数可用于在运行时定义自定义错误处理程序,例如在需要在发生严重错误时进行文件/数据清理的应用程序中,或者在响应某些条件(使用trigger_error())时触发错误时。

必须记住,除非回调函数返回 false,否则标准 PHP 错误处理程序将完全绕过 error_levels 指定的错误类型。error_reporting() 设置将无效,并且无论如何都会调用错误处理程序 - 但是,仍然可以读取 error_reporting 的当前值并采取适当的措施。

还要注意,如果需要,处理程序有责任通过调用 exit() 来停止脚本的执行。如果错误处理函数返回,则脚本执行将继续执行导致错误的语句之后的下一条语句。

以下错误类型无法使用用户定义的函数处理:E_ERRORE_PARSEE_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING(无论它们在哪里引发),以及在调用 set_error_handler() 的文件中引发的大部分 E_STRICT

如果在脚本执行之前发生错误(例如在文件上传时),则无法调用自定义错误处理程序,因为它此时尚未注册。

参数

callback

如果传递 null,则处理程序将重置为其默认状态。否则,处理程序是一个具有以下签名的回调函数

handler(
    int $errno,
    string $errstr,
    string $errfile = ?,
    int $errline = ?,
    array $errcontext = ?
): bool
errno
第一个参数 errno 将传递引发的错误级别(作为整数)。
errstr
第二个参数 errstr 将传递错误消息(作为字符串)。
errfile
如果回调函数接受第三个参数 errfile,则将传递引发错误的文件名(作为字符串)。
errline
如果回调函数接受第四个参数 errline,则将传递引发错误的行号(作为整数)。
errcontext
如果回调函数接受第五个参数 errcontext,则将传递一个指向错误发生时活动符号表的数组。换句话说,errcontext 将包含在触发错误的范围内存在的每个变量的数组。用户错误处理程序不得修改错误上下文。
警告

从 PHP 7.2.0 开始,此参数已弃用,从 PHP 8.0.0 开始已移除。如果函数定义此参数且没有默认值,则调用时将引发“参数太少”的错误。

如果函数返回 false,则正常的错误处理程序将继续执行。

error_levels

可用于屏蔽 callback 函数的触发,就像 error_reporting ini 设置控制显示哪些错误一样。如果没有设置此掩码,则无论 error_reporting 设置如何,callback 将针对每个错误都被调用。

返回值

返回先前定义的错误处理程序(如果存在)。如果使用内置错误处理程序,则返回 null。如果之前的错误处理程序是类方法,则此函数将返回一个包含类名和方法名的索引数组。

变更日志

版本 描述
8.0.0 errcontext 已移除,不再传递给用户回调函数。
7.2.0 errcontext 已弃用。现在使用此参数会发出 E_DEPRECATED 通知。

范例

示例 #1 使用 set_error_handler()trigger_error() 进行错误处理

下面的示例演示了通过触发错误并使用用户定义的函数处理它们来处理内部异常。

<?php
// 错误处理函数
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(
error_reporting() & $errno)) {
// 此错误代码未包含在 error_reporting 中,因此将其传递给标准 PHP 错误处理程序
return false;
}

// $errstr 可能需要转义:
$errstr = htmlspecialchars($errstr);

switch (
$errno) {
case
E_USER_ERROR:
echo
"<b>我的错误</b> [$errno] $errstr<br />\n";
echo
"致命错误在第 $errline 行,文件 $errfile";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"终止...<br />\n";
exit(
1);

case
E_USER_WARNING:
echo
"<b>我的警告</b> [$errno] $errstr<br />\n";
break;

case
E_USER_NOTICE:
echo
"<b>我的通知</b> [$errno] $errstr<br />\n";
break;

default:
echo
"未知错误类型: [$errno] $errstr<br />\n";
break;
}

/* 不要执行 PHP 内部错误处理程序 */
return true;
}

// 用于测试错误处理的函数
function scale_by_log($vect, $scale)
{
if (!
is_numeric($scale) || $scale <= 0) {
trigger_error("x<=0时log(x)未定义,您使用了:scale = $scale", E_USER_ERROR);
}

if (!
is_array($vect)) {
trigger_error("输入向量不正确,需要数值数组", E_USER_WARNING);
return
null;
}

$temp = array();
foreach(
$vect as $pos => $value) {
if (!
is_numeric($value)) {
trigger_error("第 $pos 位置的值不是数字,使用 0", E_USER_NOTICE);
$value = 0;
}
$temp[$pos] = log($scale) * $value;
}

return
$temp;
}

// 设置为用户自定义的错误处理程序
$old_error_handler = set_error_handler("myErrorHandler");

// 触发一些错误,首先定义一个包含非数值项的混合数组
echo "向量 a\n";
$a = array(2, 3, "foo", 5.5, 43.3, 21.11);
print_r($a);

// 现在生成第二个数组
echo "----\n向量 b - 一个通知 (b = log(PI) * a)\n";
/* 第 $pos 位置的值不是数字,使用 0 */
$b = scale_by_log($a, M_PI);
print_r($b);

// 这会出错,我们传递的是字符串而不是数组
echo "----\n向量 c - 一个警告\n";
/* 输入向量不正确,需要数值数组 */
$c = scale_by_log("不是数组", 2.3);
var_dump($c); // NULL

// 这是一个致命错误,零或负数的对数未定义
echo "----\n向量 d - 致命错误\n";
/* x<=0时log(x)未定义,您使用了:scale = $scale" */
$d = scale_by_log($a, -2.5);
var_dump($d); // 未到达
?>

以上示例的输出类似于

vector a
Array
(
    [0] => 2
    [1] => 3
    [2] => foo
    [3] => 5.5
    [4] => 43.3
    [5] => 21.11
)
----
vector b - a notice (b = log(PI) * a)
<b>My NOTICE</b> [1024] Value at position 2 is not a number, using 0 (zero)<br />
Array
(
    [0] => 2.2894597716988
    [1] => 3.4341896575482
    [2] => 0
    [3] => 6.2960143721717
    [4] => 49.566804057279
    [5] => 24.165247890281
)
----
vector c - a warning
<b>My WARNING</b> [512] Incorrect input vector, array of values expected<br />
NULL
----
vector d - fatal error
<b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
  Fatal error on line 35 in file trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
Aborting...<br />

另请参阅

添加注释

用户贡献的注释 35 条注释

Philip
11 年前
仅靠此函数无法捕获致命错误,有一个简单的解决方法。以下是我的 error.php 文件的一部分,该文件处理应用程序中的错误和异常。在有人抱怨之前,我要补充一点,我不在乎我是否使用全局变量,此文件是我小型框架的一部分,如果没有“config”变量,应用程序也会崩溃。

<?php

/**
* 错误处理器,通过新的ErrorException将流程传递给异常日志记录器。
*/
function log_error( $num, $str, $file, $line, $context = null )
{
log_exception( new ErrorException( $str, 0, $num, $file, $line ) );
}

/**
* 未捕获异常处理器。
*/
function log_exception( Exception $e )
{
global
$config;

if (
$config["debug"] == true )
{
print
"<div style='text-align: center;'>";
print
"<h2 style='color: rgb(190, 50, 50);'>发生异常:</h2>";
print
"<table style='width: 800px; display: inline-block;'>";
print
"<tr style='background-color:rgb(230,230,230);'><th style='width: 80px;'>类型</th><td>" . get_class( $e ) . "</td></tr>";
print
"<tr style='background-color:rgb(240,240,240);'><th>信息</th><td>{$e->getMessage()}</td></tr>";
print
"<tr style='background-color:rgb(230,230,230);'><th>文件</th><td>{$e->getFile()}</td></tr>";
print
"<tr style='background-color:rgb(240,240,240);'><th>行号</th><td>{$e->getLine()}</td></tr>";
print
"</table></div>";
}
else
{
$message = "类型: " . get_class( $e ) . "; 信息: {$e->getMessage()}; 文件: {$e->getFile()}; 行号: {$e->getLine()};";
file_put_contents( $config["app_dir"] . "/tmp/logs/exceptions.log", $message . PHP_EOL, FILE_APPEND );
header( "Location: {$config["error_page"]}" );
}

exit();
}

/**
* 检查致命错误,用于解决set_error_handler无法处理致命错误的问题。
*/
function check_for_fatal()
{
$error = error_get_last();
if (
$error["type"] == E_ERROR )
log_error( $error["type"], $error["message"], $error["file"], $error["line"] );
}

register_shutdown_function( "check_for_fatal" );
set_error_handler( "log_error" );
set_exception_handler( "log_exception" );
ini_set( "display_errors", "off" );
error_reporting( E_ALL );
elad.yosifon@gmail.com
11 年前
<?php
/**
* 基于 E_* 错误类型抛出异常
*/
set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
// 使用 @ 运算符抑制错误
if (0 === error_reporting()) { return false;}
switch(
$err_severity)
{
case
E_ERROR: throw new ErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_WARNING: throw new WarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_PARSE: throw new ParseException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_NOTICE: throw new NoticeException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_CORE_ERROR: throw new CoreErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_CORE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_COMPILE_ERROR: throw new CompileErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_COMPILE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_ERROR: throw new UserErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_WARNING: throw new UserWarningException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_NOTICE: throw new UserNoticeException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_STRICT: throw new StrictException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_RECOVERABLE_ERROR: throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_DEPRECATED: throw new DeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line);
case
E_USER_DEPRECATED: throw new UserDeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line);
}
});

class
WarningException extends ErrorException {}
class
ParseException extends ErrorException {}
class
NoticeException extends ErrorException {}
class
CoreErrorException extends ErrorException {}
class
CoreWarningException extends ErrorException {}
class
CompileErrorException extends ErrorException {}
class
CompileWarningException extends ErrorException {}
class
UserErrorException extends ErrorException {}
class
UserWarningException extends ErrorException {}
class
UserNoticeException extends ErrorException {}
class
StrictException extends ErrorException {}
class
RecoverableErrorException extends ErrorException {}
class
DeprecatedException extends ErrorException {}
class
UserDeprecatedException extends ErrorException {}
aditycse@gmail.com
8年前
<?php
/**
* 用于将所有 PHP notices、warnings 等错误信息记录到文件中,前提是启用了错误报告且 display_errors 关闭。
* 用于生产环境,将所有类型的 PHP 代码错误记录到文件中,以便进一步调试和提升代码性能。
* @author Aditya Mehrotra<aditycse@gmail.com>
*/
error_reporting(E_ALL);
ini_set("display_errors", "off");
define('ERROR_LOG_FILE', '/var/www/error.log');

/**
* 自定义错误处理器
* @param integer $code 错误代码
* @param string $description 错误描述
* @param string $file 文件名
* @param interger $line 行号
* @param mixed $context 上下文
* @return boolean
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
$displayErrors = ini_get("display_errors");
$displayErrors = strtolower($displayErrors);
if (
error_reporting() === 0 || $displayErrors === "on") {
return
false;
}
list(
$error, $log) = mapErrorCode($code);
$data = array(
'level' => $log,
'code' => $code,
'error' => $error,
'description' => $description,
'file' => $file,
'line' => $line,
'context' => $context,
'path' => $file,
'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
);
return
fileLog($data);
}

/**
* 将数据写入文件
* @param mixed $logData 日志数据
* @param string $fileName 文件名
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
$fh = fopen($fileName, 'a+');
if (
is_array($logData)) {
$logData = print_r($logData, 1);
}
$status = fwrite($fh, $logData);
fclose($fh);
return (
$status) ? true : false;
}

/**
* 将错误代码映射到错误文字和日志位置。
*
* @param int $code 要映射的错误代码
* @return array 错误文字和日志位置的数组。
*/
function mapErrorCode($code) {
$error = $log = null;
switch (
$code) {
case
E_PARSE:
case
E_ERROR:
case
E_CORE_ERROR:
case
E_COMPILE_ERROR:
case
E_USER_ERROR:
$error = '致命错误';
$log = LOG_ERR;
break;
case
E_WARNING:
case
E_USER_WARNING:
case
E_COMPILE_WARNING:
case
E_RECOVERABLE_ERROR:
$error = '警告';
$log = LOG_WARNING;
break;
case
E_NOTICE:
case
E_USER_NOTICE:
$error = '通知';
$log = LOG_NOTICE;
break;
case
E_STRICT:
$error = '严格标准';
$log = LOG_NOTICE;
break;
case
E_DEPRECATED:
case
E_USER_DEPRECATED:
$error = '已弃用';
$log = LOG_NOTICE;
break;
default :
break;
}
return array(
$error, $log);
}

//调用自定义错误处理器
set_error_handler("handleError");

print_r($arra); //未定义变量
print_r($dssdfdfgg); //未定义变量
include_once 'file.php'; //找不到该文件或目录
?>
steve962 at gmail dot com
6年前
使用此函数的返回值时要小心。因为它返回旧的处理器,您可能会尝试执行以下操作:

<?php
function do_something()
{
$old = set_error_handler(“my_error_handler”);
//执行您希望由 my_error_handler 处理的操作
set_error_handler($old);
}
?>

这可以工作,但它会让您陷入困境,因为每次执行此操作时,它都会导致内存泄漏,因为旧的错误处理程序会被放入堆栈中,供 restore_error_handler() 函数使用。

因此,始终使用该函数恢复旧的错误处理程序

<?php
function do_something()
{
set_error_handler(“my_error_handler”);
//执行您希望由 my_error_handler 处理的操作
restore_error_handler();
}
?>
dannykopping at gmail dot com
10年前
请记住,当尝试在 PHP >= 5.3 中的命名空间类上设置静态定义的错误处理程序时,需要使用类命名空间

<?php
set_error_handler
('\\My\\Namespace\\Bob::errorHandler');
?>
Klauss
7年前
大家好。我不知道这是否是先前版本中的旧行为,但是目前,您可以将异常和错误处理程序设置为私有或受保护的方法,前提是您在可以访问该方法的上下文中调用`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 )
{
//在此处处理异常。
}
}
?>

注意:这些方法必须与回调参数签名匹配。
Jacob Slomp
11 年前
如果您不想让您的客户端看到错误,并且您想领先一步,这可能会很有用。

即使是解析错误,它也会通过电子邮件发送错误。

set_error_handler() 无法满足我的需求。

<?php
ini_set
('log_errors',TRUE);
ini_set('error_log','tiny_uploads/errors.txt');

if(
$_SERVER['REMOTE_ADDR'] != "YOUR IP ADDRESS"){
ini_set('display_errors',false);
}

function
byebye(){

$dir = dirname(__FILE__);
if(
file_exists($dir."/tiny_uploads/errors.txt")){

$errors = file_get_contents($dir."/tiny_uploads/errors.txt");

if(
trim($errors)){

$head = "From: php_errors@".str_replace('www.','',$_SERVER['HTTP_HOST'])."\r\n";

$errors .= "---------------------------------------------\n\n";

$errors .= "\n\n服务器信息:\n\n".print_r($_SERVER, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nCOOKIE:\n\n".print_r($_COOKIE, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nPOST:\n\n".print_r($_POST, 1)."\n\n";
$errors .= "---------------------------------------------\n\n";

$errors .= "\n\nGET:\n\n".print_r($_GET, 1)."\n\n";


mail("YOUR@EMAIL.COM","PHP 错误 ".$_SERVER['HTTP_HOST']."", $errors , $head);

$fp = fopen($dir."/tiny_uploads/errors.txt","w+");
fputs($fp, "");
fclose($fp);
}
}
}
register_shutdown_function("byebye");
?>
kalle at meizo dot com
14年前
这可能对正在寻找一种方法来获取致命错误(例如最大内存分配问题,无法通过用户定义函数处理)回溯以精确定位问题的人有所帮助。

在一个托管许多共享公共PHP包含文件的服务器上,我设置在一个地方

<?php
@ini_set ("error_log", "/my/path/php.err-" . $_SERVER ["HTTP_HOST"] . "-" . $_SERVER ["REMOTE_ADDR"] . "-" . $_SERVER ["REQUEST_METHOD"] . "-" . str_replace ("/", "|", $_SERVER ["REQUEST_URI"]));
?>

我实际上也使用了我省略的一些软件附加信息,但这样,你就能更整齐地找到错误,例如:

/my/path/php.err-website.com-127.0.0.1-GET-path|index.html?xyz

这至少极大地帮助了我精确定位问题所在,而不是以前只看到内存不足而不知道是哪个站点/页面(因为PHP错误只包含它内存不足的最新PHP代码,这通常只是一个共享的包含文件,而不是实际的页面)。
webmaster at paramiliar dot com
16年前
我们需要使用错误处理程序来处理SQL错误,同时传递查询,以便也记录查询,这就是我们想出的方法,它是一种有点难看的桥梁,但它100%有效。

<?php

function myErrorHandler($errno, $errstr, $errfile, $errline){
switch (
$errno) {
case
E_USER_ERROR:
if (
$errstr == "(SQL)"){
// 处理SQL错误
echo "<b>SQL 错误</b> [$errno] " . SQLMESSAGE . "<br />\n";
echo
"查询语句: " . SQLQUERY . "<br />\n";
echo
"在第 " . SQLERRORLINE . " 行,文件 " . SQLERRORFILE . " ";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"终止...<br />\n";
} else {
echo
"<b>我的错误</b> [$errno] $errstr<br />\n";
echo
"致命错误在第 " $errline" 行,文件 " $errfile" ";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo
"终止...<br />\n";
}
exit(
1);
break;

case
E_USER_WARNING:
case
E_USER_NOTICE:
}
/* 不要执行PHP内部错误处理程序 */
return true;
}

// 用于测试错误处理的函数

function sqlerrorhandler($ERROR, $QUERY, $PHPFILE, $LINE){
define("SQLQUERY", $QUERY);
define("SQLMESSAGE", $ERROR);
define("SQLERRORLINE", $LINE);
define("SQLERRORFILE", $PHPFILE);
trigger_error("(SQL)", E_USER_ERROR);
}

set_error_handler("myErrorHandler");

// 触发SQL错误
$query = "SELECT * FROM tbl LIMIT 1";
$sql = @mysql_query($query)
or
sqlerrorhandler("(".mysql_errno().") ".mysql_error(), $query, $_SERVER['PHP_SELF'], __LINE__);


?>
wfinn at riverbed dot com
16年前
"以下错误类型无法通过用户定义函数处理:E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING以及在调用set_error_handler()的文件中引发的多数E_STRICT错误。"

这并不完全正确。set_error_handler()无法处理它们,但ob_start()至少可以处理E_ERROR。

<?php

function error_handler($output)
{
$error = error_get_last();
$output = "";
foreach (
$error as $info => $string)
$output .= "{$info}: {$string}\n";
return
$output;
}

ob_start('error_handler');

will_this_undefined_function_raise_an_error();

?>
dorphalsig@NOSPAMgmail.com
13年前
这实际上可以捕获致命错误……

<?php
function shutdown()
{
$a=error_get_last();
if(
$a==null)
echo
"没有错误";
else
print_r($a);

}
register_shutdown_function('shutdown');
ini_set('max_execution_time',1 );
sleep(3);
?>

它将输出
Array ( [type] => 1 [message] => Maximum execution time of 1 second exceeded [file] => /path/to/file_name.php [line] => 136 )
nizamgok@gmail.com
15年前
我意识到这里有些人提到无法捕获解析错误(类型 4,E_PARSE)。这是不正确的。以下是我如何操作的。我希望这对某些人有所帮助。

1) 在 Web 根目录下创建一个“auto_prepend.php”文件并添加以下内容

<?php
register_shutdown_function
('error_alert');

function
error_alert()
{
if(
is_null($e = error_get_last()) === false)
{
mail('your.email@example.com', '来自 auto_prepend 的错误', print_r($e, true));
}
}
?>

2) 然后将此“php_value auto_prepend_file /www/auto_prepend.php”添加到 Web 根目录下的 .htaccess 文件中。

* 请确保更改电子邮件地址和文件路径。
silkensedai@online.fr
17年前
我创建了一个错误处理程序,它还可以打印回溯信息,并且可以在某些错误时终止。如果您希望在发现每个错误时都终止,这将非常有用。

<?php

function my_error_handler($errno, $errstr, $errfile, $errline){
$errno = $errno & error_reporting();
if(
$errno == 0) return;
if(!
defined('E_STRICT')) define('E_STRICT', 2048);
if(!
defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR', 4096);
print
"<pre>\n<b>";
switch(
$errno){
case
E_ERROR: print "错误"; break;
case
E_WARNING: print "警告"; break;
case
E_PARSE: print "解析错误"; break;
case
E_NOTICE: print "提示"; break;
case
E_CORE_ERROR: print "核心错误"; break;
case
E_CORE_WARNING: print "核心警告"; break;
case
E_COMPILE_ERROR: print "编译错误"; break;
case
E_COMPILE_WARNING: print "编译警告"; break;
case
E_USER_ERROR: print "用户错误"; break;
case
E_USER_WARNING: print "用户警告"; break;
case
E_USER_NOTICE: print "用户提示"; break;
case
E_STRICT: print "严格提示"; break;
case
E_RECOVERABLE_ERROR: print "可恢复错误"; break;
default: print
"未知错误 ($errno)"; break;
}
print
":</b> <i>$errstr</i> in <b>$errfile</b> on line <b>$errline</b>\n";
if(
function_exists('debug_backtrace')){
//print "backtrace:\n";
$backtrace = debug_backtrace();
array_shift($backtrace);
foreach(
$backtrace as $i=>$l){
print
"[$i] in function <b>{$l['class']}{$l['type']}{$l['function']}</b>";
if(
$l['file']) print " in <b>{$l['file']}</b>";
if(
$l['line']) print " on line <b>{$l['line']}</b>";
print
"\n";
}
}
print
"\n</pre>";
if(isset(
$GLOBALS['error_fatal'])){
if(
$GLOBALS['error_fatal'] & $errno) die('fatal');
}
}

function
error_fatal($mask = NULL){
if(!
is_null($mask)){
$GLOBALS['error_fatal'] = $mask;
}elseif(!isset(
$GLOBALS['die_on'])){
$GLOBALS['error_fatal'] = 0;
}
return
$GLOBALS['error_fatal'];
}

?>

用法

<?php
error_reporting
(E_ALL); // 将报告所有错误
set_error_handler('my_error_handler');
error_fatal(E_ALL^E_NOTICE); // 除 E_NOTICE 外,任何错误都将导致终止
?>
francois vespa
13年前
这是在从终端(CLI 接口)使用 PHP 时需要注意的一点。从命令行,即使您有一些用户错误处理程序函数,因此 STDERR 不会显示,致命错误仍将导致 PHP 解释器显示错误文本。您无法通过 PHP 来解决这个问题。如果使用 UNIX/Linux,您可以在命令末尾添加“ 2>/dev/null”以强制 STDERR 不显示。
Marcelius
15年前
另一种捕获 PHP 致命错误的方法

<?php
error_reporting
(E_ALL);
ini_set('display_errors', 0);

function
shutdown(){
$isError = false;
if (
$error = error_get_last()){
switch(
$error['type']){
case
E_ERROR:
case
E_CORE_ERROR:
case
E_COMPILE_ERROR:
case
E_USER_ERROR:
$isError = true;
break;
}
}

if (
$isError){
echo
"脚本执行终止 ({$error['message']})";
} else {
echo
"脚本执行完成";
}
}

register_shutdown_function('shutdown');
?>

注意,这只会捕获运行时错误。因此,在不存在的类中调用方法或重复声明函数不会触发关闭处理程序。
roy
22年前
一个有用的注意事项 - 如果你的错误处理程序本身抛出错误,PHP 足够聪明地使用默认错误处理程序来处理它。这样,你就不会陷入无限的死亡循环。至少在 PHP 4.2 中似乎是这样。

(当然,有一些方法可以创建你的处理程序来处理这种情况,但对于一般用途,最好还是保持这种方式。)
匿名用户
18年前
为了尊重 PHP 的 error_reporting() 函数的值,请使用

<?
if( ($level & error_reporting()) == 0 ) return;
?>
phpmanual at NO_SPHAMnetebb dot com
21年前
给定这段代码

class CallbackClass {
function CallbackFunction() {
// 引用 $this
}

function StaticFunction() {
// 不引用 $this
}
}

function NonClassFunction() {
}

在 PHP 中似乎有三种方法可以设置回调函数(以 set_error_handler() 为例)

1: set_error_handler('NonClassFunction');

2: set_error_handler(array('CallbackClass', 'StaticFunction'));

3: $o =& new CallbackClass();
set_error_handler(array($o, 'CallbackFunction'));

以下内容也可能有用

class CallbackClass {
function CallbackClass() {
set_error_handler(array(&$this, 'CallbackFunction')); // &很重要
}

function CallbackFunction() {
// 引用 $this
}
}

文档并没有清楚地概述这三个例子。
nicolas dot grekas+php at gmail dot com
11 年前
如果你想确保在不重置处理程序堆栈的情况下调用原生 PHP 错误处理程序(因为 set_error_handler(null) 会这样做),你可以简单地将 $error_types 设置为零来调用 set_error_handler。这与例如 error_get_last() 结合使用特别有用。

<?php

// var_dump 或其他任何内容,因为由于 0 的原因,这永远不会被调用
set_error_handler('var_dump', 0);
@
$undef_var;
restore_error_handler();

// error_get_last() 现在处于已知状态:
// 未定义变量:undef_var

... // 做一些事情

$e = error_get_last();

...

?>
phil at propcom dot co dot uk
11 年前
重要的是要注意,如果 E_STRICT 错误触发错误处理程序,而错误处理程序又尝试使用尚未加载的类,则不会调用已注册的 SPL 自动加载器。

在这种情况下,你应该手动加载错误处理程序所需的类。
jtrick77 at gmail dot com
11 年前
对于任何对实际转换的错误代码及其含义感兴趣的人

1 E_ERROR (整数) 严重的运行时错误。这些表示无法从中恢复的错误,例如内存分配问题。脚本执行将停止。
2 E_WARNING (整数) 运行时警告(非致命错误)。脚本执行不会停止。
4 E_PARSE (整数) 编译时解析错误。解析错误应该只由解析器生成。
8 E_NOTICE (整数) 运行时通知。表示脚本遇到了一些可能表示错误的内容,但也可能在脚本运行的正常过程中发生。
16 E_CORE_ERROR (整数) 在 PHP 初始启动期间发生的致命错误。这类似于 E_ERROR,但它是由 PHP 的核心生成的。
32 E_CORE_WARNING (整数) 在 PHP 初始启动期间发生的警告(非致命错误)。这类似于 E_WARNING,但它是由 PHP 的核心生成的。
64 E_COMPILE_ERROR (整数) 致命的编译时错误。这类似于 E_ERROR,但它是由 Zend 脚本引擎生成的。
128 E_COMPILE_WARNING (整数) 编译时警告(非致命错误)。这类似于 E_WARNING,但它是由 Zend 脚本引擎生成的。
256 E_USER_ERROR (整数) 用户生成的错误消息。这类似于 E_ERROR,但它是通过使用 PHP 函数 trigger_error() 在 PHP 代码中生成的。
512 E_USER_WARNING (整数) 用户生成的警告消息。这类似于 E_WARNING,但它是通过使用 PHP 函数 trigger_error() 在 PHP 代码中生成的。
1024 E_USER_NOTICE (整数) 用户生成的通知消息。这类似于 E_NOTICE,但它是通过使用 PHP 函数 trigger_error() 在 PHP 代码中生成的。
2048 E_STRICT (整数) 启用此选项可让 PHP 建议对代码进行更改,以确保代码的最佳互操作性和向前兼容性。从 PHP 5 开始,但在 PHP 5.4.0 之前未包含在 E_ALL 中。
4096 E_RECOVERABLE_ERROR (整数) 可捕获的致命错误。它表示可能发生了危险的错误,但没有使引擎处于不稳定状态。如果错误没有被用户定义的处理程序捕获(另见 set_error_handler()),则应用程序将中止,因为它是一个 E_ERROR。从 PHP 5.2.0 开始。
8192 E_DEPRECATED (整数) 运行时通知。启用此选项可接收有关将来版本中将无法使用的代码的警告。从 PHP 5.3.0 开始。
16384 E_USER_DEPRECATED (整数) 用户生成的警告消息。这类似于 E_DEPRECATED,但它是通过使用 PHP 函数 trigger_error() 在 PHP 代码中生成的。从 PHP 5.3.0 开始。
32767 E_ALL (整数) 所有错误和警告(已支持),但 PHP 5.4.0 之前的 E_STRICT 除外。PHP 5.4.x 中为 32767,PHP 5.3.x 中为 30719,PHP 5.2.x 中为 6143,以前为 2047。

(摘自 https://php.net/manual/en/errorfunc.constants.php)
匿名用户
20年前
似乎当你让 PHP 知道你有一个自定义错误处理程序时,你无法在类中更新/设置新变量。示例

<?php
class error {
var
$error;

function
error() {
$this->setIni(); // 这会导致 PHP 忽略对类的所有其他更改。
}

function
handler() {
echo
$this->error.'!!';
}

function
setText($text) {
$this->error = $text;
}

function
setIni() {
set_error_handler(array($this, 'handler'));
}
}

$eh = new error;
$eh->setText('Error! <br>'); // 这不会被保存

trigger_error('text', E_USER_ERROR);
// 打印 '!!'
?>

应该怎么做
<?php
class error {
var
$error;

function
error() {
// 暂时不要让 PHP 知道我们的错误处理程序
}

function
handler() {
echo
$this->error.'!!';
}

function
setText($text) {
$this->error = $text;
}

function
setIni() {
set_error_handler(array($this, 'handler'));
}
}

$eh = new error;
$eh->setText('Error! <br>'); // 这将有效
$eh->setIni(); // 当你准备好配置类时调用此方法。所有将被调用的其他方法都不会影响 PHP 的错误处理

trigger_error('text', E_USER_ERROR);
// 打印 'Error! <br>!!'
?>
periklis
14年前
如何在 php 5.2 中处理致命错误

<?php
register_shutdown_function
('shutdownFunction');
function
shutDownFunction() {
$error = error_get_last();
if (
$error['type'] == 1) {
// 执行你的操作
}
}
?>
stepheneliotdewey at GmailDotCom
17年前
手册中指出

"errcontext 将包含触发错误的作用域中存在的每个变量的数组。用户错误处理程序不得修改错误上下文。"

但是你知道为什么你不能修改错误上下文吗?似乎 errcontext 是(实际上如果不是文字上的话)通过获取 $GLOBALS 并将非全局局部变量作为附加条目添加到该数组中创建的,然后通过引用传递整个内容。

(如果你设置了一个自定义错误处理程序,然后在其中打印 print_r($errcontext),你可以证明这是正确的,因为 $GLOBALS 将被打印为一个递归数组。)

换句话说,手册中的语言具有误导性,因为 `errcontext` **不是** 错误触发时存在的变量的副本,而是对调用脚本中*现有活动变量*的引用。

这包括超全局变量,例如 `$_SERVER`、`$_POST`、`$_GET` 等,以及作用域内的所有用户定义变量。

这意味着如果您修改 `errcontext`,您将修改这些其他变量,这不仅会影响您的错误处理函数的生命周期,还会影响调用脚本的生命周期。

如果您计划在错误处理函数中停止执行,这无关紧要,但如果您修改 `$errcontext` 并在处理错误后返回程序的正常流程,则会导致意外行为,因为变量将保持修改状态。例如,如果您在自定义错误处理函数中取消设置 `$_SERVER`,则在函数结束后返回生成错误的页面后,它将保持取消设置状态。

手册应该对此进行更清晰的说明,首先在上面的“参数”部分中使用&符号(&)标记 `errhandler` 为按引用传递,如下所示:

handler ( int $errno, string $errstr [, string $errfile [, int $errline [, array &$errcontext]]] )
Steffen Staehle
19 年前
关于在将应用程序从 php 4.2.1 迁移到 php 4.3.9 时注意到的 `set_error_handler()` 使用行为的两个说明(我还没有 php 5.0 可用,这可能不适用于那里!)。

1. 设置系统错误处理程序

如果您想在设置了自己的错误处理程序后再次设置标准 php 错误处理程序,则在 php 4.2.1 中可以通过传入空字符串来实现。

<?php

function my_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果此处发生错误,则会调用标准错误
// (以避免递归)

// 执行一些有用的操作
// ...
}

$last_handler = set_error_handler("my_handler");

// 此后,$last_handler == ""

// 恢复标准错误处理程序

$last_handler = set_error_handler("");

// 此后,$last_handler == "my_handler"

?>

在 php 4.3.9 中,完全相同的代码现在会引发错误。

set_error_handler() 期望参数 1 '' 为有效的回调函数。

(由于对 set_error_handler() 的第一次调用的返回值仍然是空字符串 "",我不明白这还能如何完成。我不太需要这个,因为我使用自己定义的处理程序,如下所示,但这可能需要注意。)

2. 设置您自己的“二级”处理程序

如果您已设置了自己的错误处理程序,并且想要在执行过程中将其替换为另一个处理程序(标准 php 错误处理程序除外),请注意,在错误处理程序内部使用 `set_error_handler` 时的返回值为空字符串 "",而不是以前处理程序的名称!这并不奇怪,因为在执行您自己定义的错误处理程序期间,php 会将其替换为标准 php 错误处理程序,以避免处理程序内部出现问题时出现无限循环。如果您像我一样想要嵌套处理程序,这才是有趣的。我的设计的背景

第一级处理程序:记录到数据库
第二级处理程序:记录到平面文件(如果记录到数据库失败)
第三级处理程序:打印到标准输出(如果记录到平面文件失败)(最终这是系统处理程序)。

<?php

function my_fallback_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果此处发生错误,则会调用标准错误
// (以避免递归)

// 执行一些有用的操作
// ...

} // my_fallback_handler

function my_handler($log_level, $log_text, $error_file, $error_line)
{
// 如果此处发生错误,则会调用标准错误
// (以避免递归)

// 但我们希望有一个与标准错误处理程序不同的回退处理程序

$last_handler = set_error_handler("my_fallback_handler");

// 我预计 $last_handler == "my_handler"
// (它在 my_handler() 之外是这样的)
// 但在这里它是空字符串 ""

// 执行一些有用的操作
// ...

// 现在再次设置一级处理程序:
// (不要使用 $last_handler 作为参数,
// 因为它等于 "")

$last_handler = set_error_handler("my_handler");

}
// my_handler

$last_handler = set_error_handler("my_handler");

?>
a dot ross at amdev dot eu
5 年前
我缺少一种链接错误处理程序的方法。`set_error_handler` 没有提供这种方法。您必须绕过一些障碍才能使其工作,但通过利用函数的返回值,它是完全可能的。这是一个例子

<?
$previous = set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$previous) {
/* 您的自定义错误处理代码在此处。*/

// 如果定义了另一个错误处理程序,则调用它。
if ($previous) {
return $previous($errno, $errstr, $errfile, $errline, $errcontext);
} else {
// 使用标准 PHP 错误处理程序。
return false;
}
});
?>
gernovich at ya dot ru
1 天前
显示所有错误处理程序
<?php
do {
$handler = set_error_handler(fn()=>false);
if(
$handler !== null) {
var_dump($handler);
restore_error_handler();
restore_error_handler();
}
} while (
$handler !== null);
?>
David Spector
4 年前
PHP 手册没有非常清楚地说明如何处理 `@` 运算符错误消息。

这是一个有效的代码

// 如果是 `@` 运算符,则什么也不做
$errLevel=error_reporting(E_ALL);
if ($errLevel===0)
return true; // 忽略以 `@` 为前缀的表达式错误
RGraph
1 年前
一个简单的错误处理程序,使错误更易于查看

//
// 将错误处理程序设置为一个可以打印出更多
// 可见错误的处理程序
//
set_error_handler(function ($errno, $errstr)
{
$str = '<div style="margin: 20px; background-color: #fdd; border: 3px solid red; padding: 10px; border-radius: 15px; line-height: 25px"><b>Error: </b>%s (error level: %s)</div>';

printf($str, $errstr, $errno);
});
kaioker
3 年前
超简单的错误代码到人类可读的转换

function prettycode($code){
return $code == 0 ? "FATAL" : array_search($code, get_defined_constants(true)['Core']);
}
Alex M
3 年前
如果您是编程新手,并且想知道如何将这些错误报告值的组合添加到 .htaccess 文件中。这是一个简短的指南。

使用 PHP 函数 `error_reporting`,我们可以使用按位加运算符 `|` 将选项加在一起。但是我们不能在 .htaccess 文件中使用这些常量,在我的例子中,我不知道如何添加按位数字。

因此,解决方案可以将选定的选项强制转换为 int

echo (int)(E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR) ;
->263

然后您可以在 .htaccess 中使用 263

php_value error_reporting 263

在我的情况下,我需要将这些错误显示在我的调试服务器上。但是组合可能与我的不同。
chris at ocproducts dot com
8年前
请注意,错误处理程序不会递归运行。如果您在错误处理程序运行期间(在错误处理程序本身或从中调用的代码中)遇到错误,则不会再次调用错误处理程序。

这对 `$php_errormsg` 有细微的影响。如果您依赖错误处理程序来阻止某些类型的错误消息进入 `$php_errormsg`(通过 `return true;`,因为 `error_reporting` 不会影响 `$php_errormsg` 设置),那么这将不适用于在该错误处理程序内调用的任何代码。
mmtache at yahoo dot com
21年前
`@` 运算符将 `error_reporting()` 值设置为 0。
这意味着您也可以将其与您自己的错误处理程序一起使用。例如

function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars) {
if (error_reporting())
echo $errmsg;
}
set_error_handler("userErrorHandler");

function test(){
trigger_error("Error Message", E_USER_WARNING);
}

@test(); // 不会输出任何内容
ash
17年前
一个错误处理函数,它处理错误和异常;还包含一个回溯,包括可能的函数参数。

<?php

$cfg
= array();
$cfg['debug'] = 1;
$cfg['adminEmail'] = 'name@domain.tld';

function
errorHandler($errno, $errstr='', $errfile='', $errline='')
{
// 如果错误已经被@抑制
if (error_reporting() == 0) {
return;
}

global
$cfg;

// 检查函数是否被异常调用
if(func_num_args() == 5) {
// 由trigger_error()调用
$exception = null;
list(
$errno, $errstr, $errfile, $errline) = func_get_args();

$backtrace = array_reverse(debug_backtrace());

}else {
// 捕获的异常
$exc = func_get_arg(0);
$errno = $exc->getCode();
$errstr = $exc->getMessage();
$errfile = $exc->getFile();
$errline = $exc->getLine();

$backtrace = $exc->getTrace();
}

$errorType = array (
E_ERROR => '错误',
E_WARNING => '警告',
E_PARSE => '解析错误',
E_NOTICE => '通知',
E_CORE_ERROR => '核心错误',
E_CORE_WARNING => '核心警告',
E_COMPILE_ERROR => '编译错误',
E_COMPILE_WARNING => '编译警告',
E_USER_ERROR => '用户错误',
E_USER_WARNING => '用户警告',
E_USER_NOTICE => '用户通知',
E_STRICT => '严格通知',
E_RECOVERABLE_ERROR => '可恢复错误'
);

// 创建错误消息
if (array_key_exists($errno, $errorType)) {
$err = $errorType[$errno];
} else {
$err = '捕获异常';
}

$errMsg = "$err: $errstr$errfile$errline 行";

// 开始回溯
foreach ($backtrace as $v) {

if (isset(
$v['class'])) {

$trace = '在类 '.$v['class'].'::'.$v['function'].'(';

if (isset(
$v['args'])) {
$separator = '';

foreach(
$v['args'] as $arg ) {
$trace .= "$separator".getArgument($arg);
$separator = ', ';
}
}
$trace .= ')';
}

elseif (isset(
$v['function']) && empty($trace)) {
$trace = '在函数 '.$v['function'].'(';
if (!empty(
$v['args'])) {

$separator = '';

foreach(
$v['args'] as $arg ) {
$trace .= "$separator".getArgument($arg);
$separator = ', ';
}
}
$trace .= ')';
}
}

// 显示错误消息,如果启用了调试
if($cfg['debug'] == 1) {
echo
'<h2>调试信息</h2>'.nl2br($errMsg).'<br />
追溯: '
.nl2br($trace).'<br />';
}

// 如何处理
switch ($errno) {
case
E_NOTICE:
case
E_USER_NOTICE:
return;
break;

default:
if(
$cfg['debug'] == 0){
// 发送邮件给管理员
if(!empty($cfg['adminEmail'])) {
@
mail($cfg['adminEmail'],'关键错误在 '.$_SERVER['HTTP_HOST'], $errorText,
'From: 错误处理器');
}
// 结束并显示错误消息
exit(displayClientMessage());
}
else
exit(
'<p>中止执行。</p>');
break;

}

}
// errorHandler()结束

function displayClientMessage()
{
echo
'包含错误消息的HTML页面';

}

function
getArgument($arg)
{
switch (
strtolower(gettype($arg))) {

case
'string':
return(
'"'.str_replace( array("\n"), array(''), $arg ).'"' );

case
'boolean':
return (bool)
$arg;

case
'object':
return
'object('.get_class($arg).')';

case
'array':
$ret = 'array(';
$separtor = '';

foreach (
$arg as $k => $v) {
$ret .= $separtor.getArgument($k).' => '.getArgument($v);
$separtor = ', ';
}
$ret .= ')';

return
$ret;

case
'resource':
return
'resource('.get_resource_type($arg).')';

default:
return
var_export($arg, true);
}
}

?>
devermin at ti0n dot net
14年前
在工作中,我遇到一些代码存在未捕获的错误,这些错误会导致数据完整性丢失(例如,使用file_get_contents调用web服务失败却静默处理,之后将垃圾数据插入数据库)。

这是我找到的解决方案,它将特定类型的错误转换为异常,之后可以根据错误类别(使用错误代码)有选择地进行处理。

<?php
ini_set
('error_reporting',E_ALL^E_NOTICE);

## 保留前 10 位用于初始错误编号
define('EMASK',(~0)<<10);
define('ECODEMASK',~EMASK);
## 错误类别
define('IOERROR', 1<<10);
define('EMPTYPARMS', 1<<11);
define('FAILURE', 1<<12);
## 错误字符串模式 => 代码

$catch_me=array(
"/^(file_get_contents)\((.*)\).*failed to open stream: (.*)/ " =>
array (
'mesg' => "IO::打开流失败",
'code' => IOERROR | FAILURE
),
"/^fopen\(.*\): Filename cannot be empty/" =>
array(
'msg' => "参数::为空",
'code' => EMPTYPARMS
)
);
function
error_2_exception($errno, $errstr, $errfile, $errline,$context) {
global
$catch_me;
foreach (
$catch_me as $regexp => $res) {
if(
preg_match($regexp,$errstr,$match)){
throw new
Exception($res['mesg'],$res['code']|( $errno & EMASK ) );
}
}
/* 切换回 PHP 内部错误处理程序 */
return false;
}
## => 需要捕获此错误
$f=file_get_contents("mlsdkfm");
## 暂时不希望破坏现有的错误行为 (因此未捕获)
$f=file_get_contents('');
## 魔法
set_error_handler("error_2_exception");
## 行为保持不变
$f=file_get_contents('');
try {
## 现在无法工作的 Web 服务会引发异常 \o/
$f=file_get_contents("mlsdkfm");
} catch(
Exception $e) {
## 我可以按类别分组异常
echo ( $e->getCode() & FAILURE ) ? "\n严重错误\n" : "\n轻微错误";
}

?>
To Top