PHP:自定义错误处理程序 - 处理解析和致命错误

Posted

技术标签:

【中文标题】PHP:自定义错误处理程序 - 处理解析和致命错误【英文标题】:PHP : Custom error handler - handling parse & fatal errors 【发布时间】:2010-12-26 09:24:29 【问题描述】:

如何使用 custom 错误处理程序来处理 parsefatal 错误?

【问题讨论】:

为什么不用php -l 捕获上游解析错误? 如何在运行 PHP 程序的浏览器中做到这一点? 【参考方案1】:

简单的回答:你不能。见manual:

以下错误类型不能 使用用户定义的函数处理: E_ERROR、E_PARSE、E_CORE_ERROR、 E_CORE_WARNING,E_COMPILE_ERROR, E_COMPILE_WARNING 和大多数 E_STRICT 在文件中提出 调用 set_error_handler()。

对于其他所有错误,您可以使用set_error_handler()

编辑:

既然似乎有一些关于这个话题的讨论,关于使用register_shutdown_function,我们应该看看处理的定义:对我来说,处理错误意味着捕捉错误并以某种方式做出反应这对于用户基础数据(数据库、文件、Web 服务等)来说“很好”。

使用register_shutdown_function,您无法从调用它的代码中处理错误,这意味着代码仍会在发生错误的地方停止工作。但是,您可以向用户显示错误消息而不是白页,但您不能,例如,回滚您的代码在失败之前所做的任何事情。

【讨论】:

实际上您可以使用用户定义的函数来处理这些错误。您所要做的就是定义 register_shutdown_function。有关我在我的网站中实现的工作示例,请参阅下面的答案。 是的,你可以。但是,它也有一些缺点:请参阅上面的编辑 感谢您的更新。实际上,您可以做的不仅仅是向用户显示错误消息。您可以记录错误的详细信息,包括发生错误时设置的变量。例如,许多使用共享主机的人无法访问 Apache 日志。通过使用此功能,他们将能够记录严重错误并解决它们。此外,您可以尝试恢复事务。例如,如果在用户尝试下订单时发生错误,您可以将订单的所有详细信息转储到日志或电子邮件中,并尝试离线恢复。 或者,您可以通过像 NewRelic 这样的应用程序“处理”它们,让您捕获它们,然后主动监控和修复它们。如果您只是忽略它们,它们将永远不会得到处理。 如其他答案中所述,您可以捕获includes(不是:require)的 all 类型的错误。所以你所需要的只是一个小的***.php 文件,它可以完成所有的捕获,然后includes 是真正的文件。使用nginxphp-fpm(线索:cgi.fix_pathinfo=0)相对容易实现。【参考方案2】:

您可以使用如下代码跟踪这些错误:

(只有通过include()require() 或将此代码放入auto_prepend_file 中,如果它们发生在其他 脚本文件中,解析错误才能被捕获,正如其他答案所提到的那样。)

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)
        var_dump ($error);//do whatever you need with it
    


register_shutdown_function('shutdown');

【讨论】:

是的,有办法捕捉 E_PARSE!将错误处理函数放入 index.php 并包含/要求其他应该是站点逻辑的文件! 我能够防止显示解析错误的唯一方法是包含包含解析错误的脚本。 @LucasBatistussi,你仍然无法捕获 index.php 的解析错误 在 php 5.3 - win 7 上测试。解析错误时不会调用关闭函数。 @LucasBatistussi 你能解释一下吗?我将错误处理函数放在index.php 中,然后使用include(index.php) 将其包含在我的脚本中。但它仍然无法捕获解析错误(语法错误)。我认为如果不将其嵌入到 php.ini 中是不可能的。【参考方案3】:

解析错误的脚本总是被中断,无法处理。因此,如果直接或通过 include/require 调用脚本,则您无能为力。但是如果它被 AJAX、flash 或任何其他方式调用,一种解决方法来检测解析错误。

我需要这个来处理swfupload 脚本。 Swfupload 是一个处理文件上传的 flash,每次上传文件时,它都会调用 PHP 处理脚本来处理文件数据 - 但没有浏览器输出,因此 PHP 处理脚本需要这些设置来进行调试:

警告和通知 ob_start();在开始并通过 ob_get_contents() 将内容存储到会话中;在处理脚本的末尾:这可以通过另一个脚本显示到浏览器中 致命错误 register_shutdown_function() 使用与上述相同的技巧设置会话 解析错误 如果 ob_get_contents() 位于处理脚本的末尾并且之前发生过解析错误,则会话未填充(为空)。调试脚本可以这样处理:if(!isset($_SESSION["swfupload"])) echo "parse error";

注1null表示is not setisset()

【讨论】:

【参考方案4】:

来自http://www.php.net/manual/en/function.set-error-handler.php页面上的PHP.net cmets

我意识到这里有几个人提到您无法捕获解析错误(类型 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', 'Error from auto_prepend', print_r($e, true)); 
         
 
?> 

2) 然后添加这个“php_value auto_prepend_file /www/auto_prepend.php" 到您的 Web 根目录中的 .htaccess 文件。

确保更改电子邮件地址和文件路径。

【讨论】:

此解决方案涵盖 E_PARSE、E_COMPILE_ERROR 等。所以 Dan Soap 的回答和 php.net 手册是错误的。谢谢! 如果您使用的是 FastCgi,则无法在 .htaccess 中设置 PHP 值。相反,您使用本地 php.ini 文件并直接设置 auto_prepend_file = /www/auto_prepend.php【参考方案5】:

实际上,您可以处理解析错误和致命错误。确实不会调用您使用 set_error_handler() 定义的错误处理函数。方法是使用 register_shutdown_function() 定义一个关闭函数。这是我在我的网站上所做的工作:

文件 prepend.php(此文件将自动添加到所有 php 脚本中)。有关将文件附加到 PHP 的提示,请参见下文。

set_error_handler("errorHandler");
register_shutdown_function("shutdownHandler");

function errorHandler($error_level, $error_message, $error_file, $error_line, $error_context)

$error = "lvl: " . $error_level . " | msg:" . $error_message . " | file:" . $error_file . " | ln:" . $error_line;
switch ($error_level) 
    case E_ERROR:
    case E_CORE_ERROR:
    case E_COMPILE_ERROR:
    case E_PARSE:
        mylog($error, "fatal");
        break;
    case E_USER_ERROR:
    case E_RECOVERABLE_ERROR:
        mylog($error, "error");
        break;
    case E_WARNING:
    case E_CORE_WARNING:
    case E_COMPILE_WARNING:
    case E_USER_WARNING:
        mylog($error, "warn");
        break;
    case E_NOTICE:
    case E_USER_NOTICE:
        mylog($error, "info");
        break;
    case E_STRICT:
        mylog($error, "debug");
        break;
    default:
        mylog($error, "warn");



function shutdownHandler() //will be called when php script ends.

$lasterror = error_get_last();
switch ($lasterror['type'])

    case E_ERROR:
    case E_CORE_ERROR:
    case E_COMPILE_ERROR:
    case E_USER_ERROR:
    case E_RECOVERABLE_ERROR:
    case E_CORE_WARNING:
    case E_COMPILE_WARNING:
    case E_PARSE:
        $error = "[SHUTDOWN] lvl:" . $lasterror['type'] . " | msg:" . $lasterror['message'] . " | file:" . $lasterror['file'] . " | ln:" . $lasterror['line'];
        mylog($error, "fatal");



function mylog($error, $errlvl)

...do whatever you want...

如果在任何脚本中发现错误,PHP 将调用函数 errorHandler()。如果错误迫使脚本立即关闭,则由函数 shutdownHandler() 处理错误。

这是在我正在开发的网站上工作的。我还没有在生产中测试它。但它目前正在捕获我在开发时发现的所有错误。

我相信存在两次捕获相同错误的风险,每个函数一次。如果我在函数 shutdownHandler() 中处理的错误也被函数 errorHandler() 捕获,则可能会发生这种情况。

待办事项:

1 - 我需要开发一个更好的 log() 函数来优雅地处理错误。因为我仍在开发中,所以我基本上是将错误记录到数据库中并将其回显到屏幕上。

2 - 为所有 mysql 调用实现错误处理。

3 - 为我的 javascript 代码实现错误处理。

重要提示:

1 - 我在 php.ini 中使用以下行自动将上述脚本添加到所有 php 脚本中:

auto_prepend_file = "/homepages/45/d301354504/htdocs/hmsee/cgi-bin/errorhandling.php"

效果很好。

2 - 我正在记录并解决所有错误,包括 E_STRICT 错误。我相信开发干净的代码。在开发过程中,我的 php.ini 文件有以下几行:

track_errors = 1
display_errors = 1
error_reporting = 2147483647
html_errors = 0

当我上线时,我会将 display_errors 更改为 0,以降低我的用户看到难看的 PHP 错误消息的风险。

我希望这对某人有所帮助。

【讨论】:

似乎无法处理“解析错误”...尝试以下操作: echo "Cat"; echo "狗" echo "狮子"; 您必须在文件前添加处理解析错误! E_DEPRECATEDE_USER_DEPRECATED 也应该包含在自定义错误处理函数中。 如果我手动添加这样的文件,它不会捕获错误“abc();”,其中未定义 abc。显示“未处理”错误。请提供完整的测试示例。【参考方案6】:

根据我的经验,您可以捕获所有类型的错误、隐藏默认错误消息并显示您自己的错误消息(如果您愿意)。下面列出了您需要的东西。

1) 一个初始/***脚本,我们称之为index.php,您可以在其中存储自定义错误处理函数。自定义错误函数处理程序必须保持在顶部,以便它们捕获低于它们的错误,“下方”是指包含的文件中的错误。

2) 这个***脚本没有错误的假设一定是真的!这很重要,当您在 index.php 中找到自定义错误处理函数时,您无法在 index.php 中捕获致命错误。

3) Php 指令(也必须在index.php 中找到) set_error_handler("myNonFatalErrorHandler"); #为了捕捉非致命错误 register_shutdown_function('myShutdown'); #为了捕捉致命错误 ini_set('display_errors', false); #为了隐藏php向用户显示的错误 ini_set('log_errors',FALSE); #假设我们自己记录错误 ini_set('error_reporting', E_ALL); #我们喜欢报告所有错误

在生产过程中(如果我没记错的话)我们可以保留ini_set('error_reporting', E_ALL); 以便能够记录错误,同时ini_set('display_errors', false); 将确保不会向用户显示任何错误。

至于我说的myNonFatalErrorHandlermyShutdown这两个函数的实际内容,为了简单起见,这里就不放详细内容了。此外,其他参观者也举了很多例子。我只是提出一个非常简单的想法。

function myNonFatalErrorHandler($v, $m, $f, $l, $c)
 $some_logging_var_arr1[]="format $v, $m, $f, ".$err_lvl[$l].", $c the way you like";
 //You can display the content of $some_logging_var_arr1 at the end of execution too.


function myShutdown()

  if( ($e=error_get_last())!==null )
      $some_logging_var_arr2= "Format the way you like:". $err_level[$e['type']].$e['message'].$e['file'].$e['line'];
  
//display $some_logging_var_arr2 now or later, e.g. from a custom session close function

$err_lvl 可以是:

$err_lvl = array(E_ERROR=>'E_ERROR', E_CORE_ERROR=>'E_CORE_ERROR', E_COMPILE_ERROR=>'E_COMPILE_ERROR', E_USER_ERROR=>'E_USER_ERROR', E_PARSE=>'E_PARSE', E_RECOVERABLE_ERROR=>'E_RECOVERABLE_ERROR', E_WARNING=>'E_WARNING', E_CORE_WARNING=>'E_CORE_WARNING', E_COMPILE_WARNING=>'E_COMPILE_WARNING',
E_USER_WARNING=>'E_USER_WARNING', E_NOTICE=>'E_NOTICE', E_USER_NOTICE=>'E_USER_NOTICE',E_STRICT=>'E_STRICT');

【讨论】:

感谢添加函数不能与错误在同一个文件中。 最好使用Closure 作为错误处理程序,因为named functions can be overridden 现在 这是唯一适合我的答案。关键是 ini_set('display_errors', false); 隐藏 PHP 报告。还应注意,不需要 set_error_handler 函数,因为 register_shutdown_function 回调甚至会收到 E_NOTICE 错误,例如使用尚未定义的已定义常量。有了这种理解,许多程序就不需要异常处理(try/catch)了,那些在第一个检测到错误时停止的程序。

以上是关于PHP:自定义错误处理程序 - 处理解析和致命错误的主要内容,如果未能解决你的问题,请参考以下文章

错误处理

通过自定义错误页面处理 PHP / mysql 错误(包括致命错误)

Slim 框架忽略致命错误上的自定义错误处理程序

php中错误处理

php中错误处理

PHP 处理错误函数