13.4 错误处理
Error Handling
对于任何实际应用来说,错误处理是一个非常重要的部分, PHP提供了很多机制来帮助你处理错误,既可以用在开发过程中,也可以应用在生产环境中。
13.4.1 错误报告
Error Reporting
通常在一个PHP脚本中发生错误时,出错信息会被插入到当前脚本的输出中,如果错误是致命的(fatal),脚本的执行就会终止。
PHP将脚本错误分成3个等级:提示(notice)、警告(warning)和错误(error)。notice信息可能是脚本运行错误导致的,也可能只是在正常运行过程中出现的 (译注1)。warning标识着一个非致命性的错误;比较典型的是,当你以一个无效的参数调用函数时,warning信息就会出现。在产生warning信息后,脚本还将继续执行。error消息说明出现了致命的错误,脚本因此无法恢复(继续执行)。语法解析错误(parse error)是一种特殊类型的出错信息, 它发生在脚本有语法性的错误的时候。 除了语法解析错误,所有的其他的错误都是运行时(runtime)错误。
默认情况下, 除了运行时的notice信息,其他所有的情况都可以被捕捉并显示给用户。在php.ini文件中,你可以通过error_reporting选项来全局地改变这个行为。通过error_reporting()函数,你也可以在脚本中局部地改变错误报告的行为,同时在error_ reporting选项 和error_reporting()函数中,你都可以通过使用多个位操作符来合并不同的常量值的方法来指定你要捕捉和显示的状态。就像表13-1所列出的。
例如,报告所有Error级别的出错信息:
(E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR)
报告除了运行时notice之外的所有出错信息:
(E_ALL & ~E_NOTICE)
如果你在php.ini文件里设置了track_errors选项,当前错误的描述将会被保存进$PHP_ ERRORMSG中。
表13-1:Error-reporting的值
|
值 |
含 义 |
|
E_ERROR |
运行时错误 |
|
E_WARNING |
运行时警告 |
|
E_PARSE |
编译时语法解析错误 |
|
E_NOTICE |
运行时提示 |
|
E_CORE_ERROR |
由PHP内部生成的错误 |
|
E_CORE_WARNING |
由PHP内部生成的警告 |
|
E_COMPILE_ERROR |
由 Zend脚本引擎内部生成的错误 |
|
E_COMPILE_WARNING |
由 Zend脚本引擎内部生成的警告 |
|
E_USER_ERROR |
由调用trigger_error()函数生成的运行时错误 |
|
E_USER_WARNING |
由调用trigger_error()函数生成的运行时警告 |
|
E_USER_NOTICE |
由调用trigger_error()函数生成的运行时提示 |
|
E_ALL |
所有上面的内容 |
13.4.2 禁止出错信息
Error Suppression
通过在表达式的前面加上@操作符,你可以对单个表达式关闭出错信息。例如:
$value = @(2 / 0);
如果没有使用错误抑制操作符,通常表达式会发生“divide by zero”错误而中止运行。如上所示,加了操作符后表达式什么都没有做。错误抑制操作符并不能捕获语法解析错误,只能应用在几种类型的运行时错误上。
要完全地关闭错误处理,可使用:
error_reporting(0);
这样可以确保了当处理和运行你的脚本时,不管发生了什么错误,出错信息都不会被发送到客户端(除了语法解析出错信息,它不能够被抑制)。当然,这并不能阻止那些错误的发生,对于控制哪些出错信息要在客户端显示,有更好的选择,具体请参看后面的"自定义错误处理器"一节。
13.4.3 触发错误
Triggering Errors
你可以用trigger_error()函数在脚本中抛出一个错误:
trigger_error(message [, type]);
第一个参数是出错信息;第二个参数为一个可选参数,代表状态的等级,可以是 E_USER_ ERROR,E_USER_WARNING或者是E_USER_NOTICE(默认值)。
当你要自定义函数来检查参数是否完整时,触发错误是很有用的。例如,下面的函数用一个数字去除另外一个数字,如果第二个参数是零的话抛出一个错误。
function divider($a, $b) {
if($b == 0) {
trigger_error('$b cannot be 0', E_USER_ERROR);
}
return($a / $b);
}
echo divider(200, 3);
echo divider(10, 0);
66.666666666667
Fatal error: $b cannot be 0 in page.php on line 5#
13.4.4 自定义错误处理器
Defining Error Handlers
如果你希望更好地控制出错信息而不是仅仅隐藏任何出错信息(通常应该这么做),你可以为PHP提供一个自定义的错误处理器(error handler,也就是一个用于处理错误的函数)。当任何类型的错误发生的时候,错误处理器就会被调用,并且它可以做任何你希望它做的事情,例如向文件里面记录信息或美观地打印出错信息。创建错误处理器基本的步骤是创建一个错误处理函数并用set_error_handler( )来注册该函数。
你声明的错误处理函数既可以携带两个参数,也可以是5个。前两个参数是错误代码(error code)和出错信息。如果你的函数带有后3个参数,它们是错误发生所在的文件、错误发生所在的行号和错误发生的时刻的活动的符号表的副本。你的错误处理器应该检查当前用error_reporting()报告的错误的级别,并进行适当的处理。
对set_error_handler()的调用会返回当前的错误处理器。你可以通过调用带有返回值的set_error_handler( ) 函数,或者通过调用restore_error_handler( )函数来恢复使用上一个错误处理器。
下面的代码展示了如何使用一个错误处理器来格式化并打印出错信息:
function display_error($error, $error_string, $filename, $line, $symbols) {
echo "<p>The error '<b>$error_string</b>' occurred in the file '<i>$filename</i>'
on line $line.</p>";
}
set_error_handler('display_error');
$value = 4 / 0; // divide by zero error
<p>The error '<b>Division by zero</b>' occurred in the file
'<i>err-2.php</i>' on line 8.</p>#
13.4.4.1 使用错误处理器记录日志
PHP提供了内建的函数error_log(),可将出错信息记录到管理员希望放置错误日志的各种地方:
error_log(message, type [, destination [, extra_headers ]]);
第一个参数是出错信息。第二个参数指定出错信息记录到哪里:值为0的话就通过PHP标准的错误处理机制来记录出错信息;值为1的话就将错误通过电子邮件发送到destination 地址,并可以选择给邮件消息增加任何额外的头部信息(extra_headers);值为3的话就将错误追加到destination文件中。
要用PHP的日志记录机制保存出错信息,需要将参数type的值设置为0来调用error_ log()。通过在你的php.ini文件中改变error_log的值,你可以改变记录到哪个文件中。如果你设置error_log为syslog,就替换为使用system logger。
例如:
error_log('A connection to the database could not be opened.', 0);
要通过email来发送错误,需要以type值为1来调用error_log()。第三个参数是出错信息被发送到的email地址,可选的第四个参数可以用来指定附加的email headers。这里就是如果通过邮件发送出错信息:
error_log('A connection to the database could not be opened.', 1, 'errors@php.net');
最后,为了记录日志到文件中,以type值为3调用error_log()。第三个参数指定了记录日志的文件名称。
error_log('A connection to the database could not be opened.', 3,
'/var/log/php_errors.log');
示例13-5展示了一个错误处理器的例子,它将日志记入文件并在文件大于1KB的时候对日志文件进行重命名另存。
示例13-5:使用滚动日志的错误处理器
function log_roller($error, $error_string) {
$file = '/var/log/php_errors.log';
if(filesize($file) > 1024) {
rename($file, $file . (string) time( ));
clearstatcache( );
}
error_log($error_string, 3, $file);
}
set_error_handler('log_roller');
for($i = 0; $i < 5000; $i++) {
trigger_error(time( ) . ": Just an error, ma'am.\n");
}
restore_error_handler( );
通常来说,当你在开发一个网站时,你会希望当错误发生的时候,它们可以直接地显示在页面上。但是,一旦你的网站正式投入使用了,把所有内部出错信息显示给访问者就显得没什么意义了。一个比较普遍的方式是,当你的的网站投入使用的时候,在你的php.ini文件里使用这些命令:
display_errors = Off
log_errors = On
error_log = /tmp/errors.log
这告诉PHP不显示任何出错信息,直接把出错信息记录到 error_log参数指定的文件中去。
13.4.4.2 错误处理器中的输出缓冲
结合使用输出缓冲和错误处理函数,你可以发送不同的内容给用户, 这取决于是否有多种错误状况发生。例如,如果一个脚本需要连接到一个数据库,你可以禁止页面的输出,直到脚本成功地连接到数据库。
示例13-6展示了输出缓冲的使用,它延迟了页面的输出直到页面顺利地生成。
示例13-6:处理错误时的输出缓冲
<html> <head><title>Results!</title></head>
<body>
<?php
function handle_errors ($error, $message, $filename, $line) {
ob_end_clean( );
echo "<b>$message</b> <br/> in line $line <br/> of <i>$filename</i></body></html>";
exit;
}
set_error_handler('handle_errors');
ob_start( );
?>
<h1>Results!</h1>
Here are the results of your search:<p />
<table border=1>
<?php
require_once('DB.php');
$db = DB::connect('mysql://gnat:waldus@localhost/webdb');
if (DB::iserror($db)) die($db->getMessage( ));
// ...
?>
</table>
</body>
</html>
示例13-6中,在<body>元素开始之后,我们注册了错误处理器并开始输出缓冲。如果我们不能连接到数据库(或者如果在接下来的PHP代码中出了什么其他问题),头部和表格将不会被显示出来。代替它的是,用户只会看到出错信息,就像图13-1显示的那样。如果在PHP代码里没有任何错误发生,那么用户就只是简单地看到HTML页面。







