【发布时间】:2010-12-26 09:24:29
【问题描述】:
如何使用 custom 错误处理程序来处理 parse 和 fatal 错误?
【问题讨论】:
-
为什么不用
php -l捕获上游解析错误? -
如何在运行 PHP 程序的浏览器中做到这一点?
标签: php error-handling fatal-error parse-error
如何使用 custom 错误处理程序来处理 parse 和 fatal 错误?
【问题讨论】:
php -l 捕获上游解析错误?
标签: php error-handling fatal-error parse-error
简单的回答:你不能。见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,您无法从调用它的代码中处理错误,这意味着代码仍会在发生错误的地方停止工作。但是,您可以向用户显示错误消息而不是白页,但您不能,例如,回滚您的代码在失败之前所做的任何事情。
【讨论】:
includes(不是:require)的 all 类型的错误。所以你所需要的只是一个小的顶级.php 文件,它可以完成所有的捕获,然后includes 是真正的文件。使用NginX 和php-fpm(线索:cgi.fix_pathinfo=0)相对容易实现。
您可以使用如下代码跟踪这些错误:
(只有通过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');
【讨论】:
index.php 中,然后使用include(index.php) 将其包含在我的脚本中。但它仍然无法捕获解析错误(语法错误)。我认为如果不将其嵌入到 php.ini 中是不可能的。
解析错误的脚本总是被中断,无法处理。因此,如果直接或通过 include/require 调用脚本,则您无能为力。但是如果它被 AJAX、flash 或任何其他方式调用,有一种解决方法来检测解析错误。
我需要这个来处理swfupload 脚本。 Swfupload 是一个处理文件上传的 flash,每次上传文件时,它都会调用 PHP 处理脚本来处理文件数据 - 但没有浏览器输出,因此 PHP 处理脚本需要这些设置来进行调试:
if(!isset($_SESSION["swfupload"])) echo "parse error";
注1null表示is not set到isset()
【讨论】:
来自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 文件。
- 确保更改电子邮件地址和文件路径。
【讨论】:
实际上,您可以处理解析错误和致命错误。确实不会调用您使用 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 错误消息的风险。
我希望这对某人有所帮助。
【讨论】:
E_DEPRECATED 和 E_USER_DEPRECATED 也应该包含在自定义错误处理函数中。
根据我的经验,您可以捕获所有类型的错误、隐藏默认错误消息并显示您自己的错误消息(如果您愿意)。下面列出了您需要的东西。
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); 将确保不会向用户显示任何错误。
至于我说的myNonFatalErrorHandler和myShutdown这两个函数的实际内容,为了简单起见,这里就不放详细内容了。此外,其他参观者也举了很多例子。我只是提出一个非常简单的想法。
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');
【讨论】:
ini_set('display_errors', false); 隐藏 PHP 报告。还应注意,不需要 set_error_handler 函数,因为 register_shutdown_function 回调甚至会收到 E_NOTICE 错误,例如使用尚未定义的已定义常量。有了这种理解,许多程序就不需要异常处理(try/catch)了,那些在第一个检测到错误时停止的程序。