PHP错误处理
PHP错误处理
一、PHP错误分类
- 不致命的:deprecated、notice、warning
- 致命的:error、语法解析错误
- 用户自定义的错误消息:trigger_error()
不致命的
1、deprecated 最低级别的错误
使用一些过期函数的时候会出现,程序将继续执行
例子:
$pattern = '[0-9]+';
$subject = 100;
//$int = preg_match($pattern,$subject);
$int = eregi($pattern,$subject);
var_dump($int);
preg_match(
$pattern
,$subject
)返回pattern
的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后将会停止搜索。如果发生错误preg_match()返回 FALSE。
2、notice 通知级别的错误
使用一些未定义变量、常量或者数组key没有加引号的时候出现
echo $a; //打印一个未定义变量
echo string; //string 会把当成常量
echo $array[name]; //name 会把当成常量
3、Warning 警告级别的错误
程序出问题,需要修改代码!也不影响程序继续执行
settype($var, 'init');//integer
致命的
1、fatal-error 致命级别的错误
程序报致命的错误,此时程序会在报错处终止程序继续执行
test('123');// 调用致命的错误
//Fatal error: Call to undefined function test()
2、parse error 语法解析错误
语法检查阶段报错,需要修改代码.属于高级的error错误,代码检查阶段中发现,不会执行代码!
echo 'nie ge'
//Parse error: syntax error, unexpected
用户自定义的错误
用户自定义的错误,用户手动抛出错误。由用户自己在代码中使用PHP函数 trigger_error() 来产生的。
header('content-type:text/html;charset=utf-8');
if ($divisor == 0) {
trigger_error("Cannot divide by zero", E_USER_ERROR);
}//error_types取值E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE(默认)
二、错误处理
错误处理就是提前做好准备,假如出现了错误,首先我们应该接收或是捕获到错误信息,然后给出错误处理方案,或写入日志、或发邮箱、或发短信等等;
1、显示或关闭错误信息
1、修改php.ini 开启 display_errors,需要重启服务器
display_errors = On
2、使用ini_set()函数 允许脚本临时覆盖(或叫动态修改)PHP配置文件中的设置
ini_set ('display_errors', 1);
2、设置合适的错误报告级别
错误级别和常数如下:
值 | 常量 | 说明 |
---|---|---|
1 | E_ERROR | 报告导致脚本终止运行的致命错误 |
2 | E_WARNING | 报告运行时的警告类错误(脚本不会终止运行) |
4 | E_PARSE | 报告编译时的语法解析错误 |
8 | E_NOTICE | 报告通知类错误,脚本可能会产生错误 |
2048 | E_STRICT | PHP5+,代码可以运行,但是php不建议这样写 |
32767 | E_ALL | 报告所有的可能出现的错误(不同的PHP版本,常量E_ALL的值也可能不同) |
1、在php.ini配置文件里设置error_reporting级别
error_reporting = E_ALL & ~E_NOTICE(~不包括)
2、使用error_reporting()函数(动态修改)
error_reporting (0); //不需要报告任何错误
error_reporting (E_ALL); //报告所有错误
error_reporting (E_ALL & ~E_NOTICE); //除了notice以外报告所有
3、函数set_error_handler('自定义函数')
在报错时候,使用自己定义的函数来处理脚本中出现的错误,也就是使用我们自己定义的方式来处理运行中的错误。另外该函数只能接收到非致命的报错。
error_function(error_level,error_message,error_file,error_line,error_context)
该函数必须有能力处理至少两个参数 (error level 和 error message),但是可以接受最多五个参数(可选的:file, line-number 和 error context):
参数 | 描述 |
---|---|
error_level | 必需。为用户定义的错误规定错误报告级别。必须是一个数字。 |
error_message | 必需。为用户定义的错误规定错误消息。 |
error_file | 可选。规定错误发生的文件名。 |
error_line | 可选。规定错误发生的行号。 |
error_context | 可选。规定一个数组,包含了当错误发生时在用的每个变量以及它们的值。 |
如果在自定义函数中返回false,则会在自定义函数处理完后交由系统默认错误处理器处理
ini_set('display_errors',0);
$debug = 1; // 开启显示错误提醒
// 自定义错误处理函数
function myErrorHandler($e_number, $e_message, $e_file, $e_line, $e_vars)
{
global $debug;
$message = "脚本 <strong>{$e_file}</strong> 中出现错误在第<span style=\"color:red;\">{$e_line}</span>行: $e_message";
if ($debug) {
echo '<div style="width:90%;padding:10px;border:1px solid green;">' . $message . '</div>';
}
}
set_error_handler('myErrorHandler');
4、函数error_log
error_log函数:专门用于日记记录。
0:通过PHP标准的错误处理机制来记录;
1:邮件发生到指定的地方;
3:使用指定的文件记录错误报告日志
php.ini配置:
//将会向PHP报告发生的每个错误
error_reporting = E_ALL;
//不显示满足上条 指令所定义规则的所有错误报告
display_errors = Off;
//开关
log_errors = On;
//设置每个日志项的最大长度
log_errors_max_len = 1024 ;
//将错误记录到当前系统日志,默认使用Apache记录错误日志
error_log=syslog;
//指定产生的 错误报告写入的日志文件位置
error_log = /usr/local/errors.log;
5、register_shutdown_function()函数
register_shutdown_function()
该函数是当页面程序停止运行的时候触发,并不是专门用来接收错误的。而页面终止运行的场景包括了:页面程序正常走完,程序中出现die/exit,或是页面抛了异常,还有就是出现了致命报错(除了语法解析)。通过这个函数就可以在脚本结束前判断这次执行是否有错误产生。这时就要借助于一个函数:error_get_last()
;
register_shutdown_function(function (){
if(($error = error_get_last()) !== null) {
// 处理报错,如果没有错误则返回 NULL。
}
});
#demo();
if( 1 ){
exit('hehe');
}
三、 语法解析错误问题
上面使用set_error_handle
和register_shutdown_function
均不能正常接收到语法解析的报错。原因是PHP的运行机制是,首先我们写的代码在运行前,PHP引擎会先进行语法的校验,如果语法没有问题,才会逐行执行我们的代码;所以假如我们代码中有语法错误,PHP引擎一开始就报错了,根本不会执行上面代码。
但是 我们可以借助引入文件
来解决这问题,这也许也算是框架里使用主入口的其中一个好处吧。下面的例子里在demo.php页面里定义了错误处理,在引入文件other.php
里出现了语法解析报错。
# demo.php
register_shutdown_function(function (){
$error = error_get_last();
if($error!==null){//处理错误
date_default_timezone_set(PRC);
$logInfo = date('Y-m-d H:i:s');
$logInfo .= "文件:{$error['file']} ,在页面的第{$error['line']},报了一个{$error['type']},具体错误信息是:{$error['message']}";
$dir = MESSAGE.DS.date('Y').DS.date('m').DS.date('d');
if(!file_exists($dir)){
mkdir($dir,0777,true);
}
file_put_contents($dir.DS.'error.log', $logInfo.PHP_EOL,FILE_APPEND);//路径需要写绝对路径
}
});
include_once 'other.php';//出现语法错误
自定义错误完整实例
//关闭错误信息
ini_set ('display_errors', 0);
//报告所有级别错误
error_reporting (E_ALL);
//设置默认时区
date_default_timezone_set('PRC');
//设置日志路径
define("DS",DIRECTORY_SEPARATOR);//斜杠兼容性,linux路径用‘/’,windows用‘/’or‘\’都可以
define("LOGS_PATH",dirname(__FILE__).DS.'logs'.DS);
if(!file_exists(LOGS_PATH)){
mkdir(LOGS_PATH,0777,true);
}
//设置自定义函数处理错误
set_error_handler(function($errno, $errstr, $errfile, $errline){
$errArr=["1"=>"error","2"=>"warning","4"=>"parse error","8"=>"notice","2048"=>"strict","8192"=>"deprecated"];
$date=date("Y/m/d H:i");
$logsTemp="[%s] 【%s】 文件:%s有报错,错误出现在第%s行,错误详情:%s".PHP_EOL;
$logs=sprintf($logsTemp,$date,$errArr[$errno],$errfile,$errline,$errstr);
file_put_contents(LOGS_PATH.'error.log',$logs,FILE_APPEND);
});
//页面停止运行时候触发,处理致命错误
register_shutdown_function(function (){
if(($err = error_get_last()) !== null) {
// 处理报错
$date=date("Y/m/d H:i");
$logsTemp="[%s] 【error】 文件:%s有报错,错误出现在第%s行,错误详情:%s".PHP_EOL;
$logs=sprintf($logsTemp,$date,$err['file'],$err['line'],$err['message']);
file_put_contents(LOGS_PATH.'error.log',$logs,FILE_APPEND);
}
});