66

如何使用自定义错误处理程序处理解析致命错误?

4

6 回答 6

81

实际上,您可以处理解析和致命错误。确实不会调用您使用 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 错误消息的风险。

我希望这可以帮助别人。

于 2011-09-06T00:29:44.103 回答
31

Simple Answer: You can't. See the manual:

The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.

For every other error, you can use set_error_handler()

EDIT:

Since it seems, that there are some discussions on this topic, with regards to using register_shutdown_function, we should take a look at the definition of handling: To me, handling an error means catching the error and reacting in a way that is "nice" for the user and the underlying data (databases, files, web services, etc.).

Using register_shutdown_function you cannot handle an error from within the code where it was called, meaning the code would still stop working at the point where the error occurs. You can, however, present the user with an error message instead of a white page, but you cannot, for example, roll back anything that your code did prior to failing.

于 2009-12-14T10:59:58.147 回答
31

You can track these errors using code like this:

(Parse errors can only be caught if they occur in other script files via include() or require(), or by putting this code into an auto_prepend_file as other answers have mentioned.)

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');
于 2009-12-14T11:08:24.577 回答
13

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

我意识到这里有几个人提到您无法捕获解析错误(类型 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 文件中。

  • 确保更改电子邮件地址和文件路径。
于 2011-06-10T02:58:08.260 回答
6

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

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

2)这个顶级脚本没有错误的假设必须是真的!这非常重要,index.php当您的自定义错误处理函数在index.php.

3)Php指令(也必须在中找到index.phpset_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');
于 2013-12-28T00:49:23.607 回答
4

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

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

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

1null表示is not setisset()

于 2010-09-02T07:36:44.320 回答