- 目录
- 1. 前言
- 2. 错误类型和提示类型
- 2.1错误类型
- 2.2提示类型
- 3. 错误配置选项
- 3.1通过php.ini配置设置
- 3.2通过php函数设置
- 3.3通过ini_set()函数设置
- 3.4触发PHP错误函数
- 3.5自定义错误类型
- 4. PHP错误处理方法
- 4.1方法一:将错误日志保存在指定文件中
- 4.2方法二:将错误日志保存在系统日志中
- 4.3方法三:错误日志以邮件方式发送
- 5. 自定义错误处理器
- 5.1简介
- 5.2使用步骤
- 5.3实例演示
- 5.4封装自定义错误处理器类
- 5.5错误处理器类的测试
- 5.5.1通知错误测试
- 5.5.2警告错误测试
- 5.5.3手动抛出错误测试
- 6.脚本结束函数调用register_shutdown_function()
- 6.1含义
- 6.2使用场景
- 6.3案例演示
- 7.总结
1.前言
了解PHP的错误类型有助于我们很好地定位和解决bug,下面让我们一起深入了解吧。
代码分享:https://github.com/mtdgclub/ErrorDeal
2.错误类型和提示类型
说到PHP的错误我们先要搞清楚错误有几种错误类型,几种提示类型
2.1错误类型
- 语法错误
- 环境错误
- 逻辑错误
2.2提示类型
- 不推荐级别的错误—Deprecated
- 通知级别的错误—Notice
- 警告级别的错误—Warning
- 致命级别的错误—Fatal
- 语法解析错误—Parse
- 用户定义的错误—PHPE_USER_相关错误
其中,遇到致命级别错误和语法解析错误程序中止执行
3.错误配置选项
选项 | 描述 |
error_reporting | 设置错误报告级别 |
display_errors | 是否显示错误 |
log_errors | 设置是否将产生错误信息记录到日志或者error_log中 |
error_log | 设置脚本错误将记录到文件 |
log_errors_max_len | 设置log_errors的最大字节数 |
ignore_repeated_errors | 是否忽略重复错误信息 |
ignore_repeated_source | 是否忽略重复错误信息的来源 |
track_errors | 如果开启此项,最后一个错误将永远保存在$php_errormsg中 |
3.1通过php.ini配置设置
//设置错误显示级别error_reporting()函数 error_reporting= E_ALL&~E_NOTICE
3.2通过php函数设置
echo error_reporting();//输出全部错误码 error_reporting(E_ALL&~E_NOTICE);//显示某类错误码 error_reporting(0);//屏蔽全部错误,上线时候可设置该项 error_reporting(-1);//显示全部错误
3.3通过ini_set()函数设置
ini_set('error_reporting',0);//屏蔽全部错误,上线时候可设置该项 ini_set('error_reporting',-1);//显示全部错误 ini_set('display_errors',0);//关闭错误显示
PS:一般项目会在php.ini文件设置如下,具体视业务而定;常用的级别有E_ALL显示全部错误、~E_NOTICE忽略提示、~E_DEPRECATED忽略不推荐、~E_STRICT忽略严格
3.4触发PHP错误函数
PHP通过trigger_error()触发PHP错误,触发错误的功能不只限于PHP解释器,还可以通过trigger_error()函数主动触发错误。
代码如下:
<?php header('content-type:text/html;charset=utf8'); $num1=1; $num2='2a'; if(!(is_numeric($num1)&&is_numeric($num2))){ echo trigger_error('Num1和num2必须为合法数值',E_USER_NOTICE); }else{ echo $num2+$num1; } echo 'continue.....';
效果如下:
3.5自定义错误类型
- E_USER_NOTICE 自定义提示
- E_USER_WARNING 自定义警告
- E_USER_ERROR 自定义错误
4.PHP错误处理方法
4.1方法一:将错误日志保存在指定文件中
<?php ini_set('error_log','D:\phpStudy\PHPTutorial\WWW\ErrorDeal\error.log'); ini_set('display_errors',0);//关闭错误显示 error_reporting(-1);//错误不显示 ini_set('log_errors',1);//打开日志记录 ini_set('ignore_repeated_errors','on');//忽略消息重复 ini_set('ignore_repeated_source','on');//忽略消息来源 echo $test;
不报错,但生成错误文件
4.2方法二:将错误日志保存在系统日志中
<?php error_reporting(-1); ini_set('display_errors',0); ini_set('log_errors',1); ini_set('error_log','syslog'); openlog('PHP5.6.0',LOG_PID,LOG_SYSLOG); syslog(LOG_ERR,'this is a test of syslog'.date('Y/m/d H:i:s')); closelog();
查看错误日志(Windows 系统):
"我的电脑" ---- 右键 ----- 管理 ----- 事件查看器 ----- 信息
4.3方法三:错误日志以邮件方式发送
用邮件之前,先配置好邮件信息,修改php.ini(WAMP注意apache文件下的php.ini也要配置)
sendmail文件分享:https://pan.baidu.com/s/1dvMKgg42aj1RoEShsR_YLQ
提取码:5qoc
下载后将文件放在tools下面,比如,我用的是WAMP,路径如下
然后修改sendmail/sendmail.ini文件如下:
PS:windows环境下必须开启php_imap.dll扩展;QQ邮箱有第三方授权码,邮箱的密码写授权码
邮件发送测试代码:
<?php header('content-type:text/html;charset=utf8'); $to = "747245429@qq.com"; $subject = "我就是在测试php的mail函数,请qq别给我退回"; $message = "我就是在测试php的mail函数,请qq别给我退回"; $from = "747245429@qq.com"; $headers = "From: $from"; if($res=mail($to,$subject,$message,$headers)){ echo "发送成功"; }else{ echo "发送失败"; var_dump($res); }
发送结果:
错误日志以邮箱形式发送测试代码:
<?php error_reporting(-1); ini_set('display_errors',0); ini_set('log_errors',1); error_log('系统被人攻击啦',1,'747245429@qq.com');
发送结果:
5.自定义错误处理器
5.1简介
set_error_handler()函数:设置一个用户定义的错误处理函数
5.2使用步骤
- 创建错误处理函数
- 设置不同级别调用函数
- set_error_handler()函数指定接管错误处理
注意:使用set_error_handler()函数就会完全绕过我们php标准的错误处理程序
5.3实例演示
<?php header('content-type:text/html;charset=utf8'); error_reporting(-1); function customEorror($errno,$errmsg,$file,$line){ echo "错误代码[{$errno}]{$errmsg}",'<br />'; echo "错误行号[{$file}]文件的第{$line}行",'<br />'; echo "PHP版本".PHP_VERSION."(".PHP_OS.")",'<br />'; } set_error_handler('customEorror'); echo $test;
错误显示如下:
注意:致命错误不能调用自定义错误处理函数
问题:开启了自定义错误处理,如何关闭? //取消错误处理接管 restore_error_handler();
5.4封装自定义错误处理器类
<?php /** * 自定义错误处理器类 */ class MyErrorHandle { public $message = '';//错误信息 public $filename = '';//文件名 public $line = 0;//错误行号 public $vars = array(); public $_noticeLog = '\noticeLog.log'; //定义存储日志文件路径 /** * 析构函数,用来全局定义变量 * @param string $message 错误信息 * @param string $filename 文件名 * @param int $line 错误行号 * @param $vars */ public function __construct($message, $filename, $line, $vars) { $this->message = $message; $this->filename = $filename; $this->line = $line; $this->vars = $vars; } /** * 处理函数,通过$errno获得不同的错误处理方法 * @param string $errno 错误级别 * @param string $errmsg 错误信息 * @param string $filename 文件名 * @param int $line 错误行号 * @param $vars * @return bool|void */ public static function deal($errno, $errmsg, $filename, $line, $vars) { $self = new MyErrorHandle($errmsg, $filename, $line, $vars); switch ($errno) { case E_USER_ERROR: return $self->dealError(); break; case E_USER_WARNING: case E_WARNING: return $self->dealWarning(); break; case E_NOTICE: case E_USER_NOTICE: return $self->dealNotice(); break; default: return false; break; } } /** * 处理致命错误 **/ public function dealError() { ob_start(); debug_print_backtrace(); $backtrace = ob_get_flush(); $erroeMsg = <<<EOF 出现致命错误如下: 产生错误文件:{$this->filename}.PHP_EOL, 产生错误信息:{$this->message}.PHP_EOL, 产生错误行号:{$this->line}.PHP_EOL, 追踪信息:{$backtrace} EOF; error_log($erroeMsg, 1, '747245429@qqq.com'); exit(); } /** * 如何处理警告 **/ public function dealWarning() { ob_start(); debug_print_backtrace(); $backtrace = ob_get_flush(); $erroeMsg = <<<EOF 出现警告错误如下: 产生错误文件:{$this->filename}.PHP_EOL, 产生错误信息:{$this->message}.PHP_EOL, 产生错误行号:{$this->line}.PHP_EOL, 追踪信息:{$backtrace} EOF; return error_log($erroeMsg, 1, '747245429@qqq.com'); } /** * 如何处理提示信息 **/ public function dealNotice() { $datetime = date('Y-m-d', time()); $erroeMsg = <<<EOF 出现通知错误如下: 产生通知文件:{$this->filename}.PHP_EOL, 产生通知信息:{$this->message}.PHP_EOL, 产生通知行号:{$this->line}.PHP_EOL, 产生通知时间:{$datetime} EOF; return error_log($erroeMsg, 3, $this->_noticeLog); } }
5.5错误处理器类的测试
5.5.1通知错误测试
<?php require_once 'MyErrorHandle.php'; error_reporting(-1); set_error_handler(array('MyErrorHandle','deal')); echo $test;
结果如下:
5.5.2警告错误测试
<?php require_once 'MyErrorHandle.php'; error_reporting(-1); set_error_handler(array('MyErrorHandle','deal')); settype($var,'king');
结果如下:
5.5.3手动抛出错误测试
<?php require_once 'MyErrorHandle.php'; error_reporting(-1); set_error_handler(array('MyErrorHandle','deal')); trigger_error('我是手动抛出的致命错误',E_USER_ERROR);
结果如下:
注意:
以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。
6.脚本结束函数调用register_shutdown_function()
6.1含义
通过register_shutdown_function()函数,可以让我们设置一个当PHP脚本执行关闭时(执行完成或者意外死掉导致的PHP脚本关闭)就能够调用另一个函数.
6.2使用场景
页面强制被停止、程序代码意外终止或超时
6.3案例演示
<?php class Shutdown{ public function endScript(){ if(error_get_last()){ echo '<pre>'; print_r(error_get_last()); echo '</pre>'; } file_put_contents('D:\phpStudy\PHPTutorial\WWW\ErrorDeal\testError.log','this is a test'); die('end script'); } } register_shutdown_function(array(new Shutdown(),'endScript')); echo md6();
输出结果:
注意:
函数register_shutdown_function()是从内存中调用的,所以路径部分必须写绝对路径;输出的顺序,等执行完成了之后或者遇到exit/die导致的中止或者遇到致命错误导致的中止,才会去执行register_shutdown_function的中止方法;register_shutdown_function这个函数主要是用在处理致命错误的后续处理上,不过缺点也很明显,只能处理致命错误Fatal error,其他的错误包括最高错误Parse error也是没办法处理的。
PHP7中新增了Throwable异常类,这个类可以捕获致命错误,即可以使用try...catch(Throwable $e)来捕获致命错误,代码如下:
<?php try { // 将因为致命错误而中止 $a = new a(); // 这一句并没有执行,也没有输出 echo 'end'; } catch (Throwable $e) { print_r($e); echo $e->getMessage(); }
返回如下:
PS:PHP7更推荐使用Throwable来处理致命错误,使用Throwable来捕获的话比使用register_shutdown_function这个函数来得更方便,Error类也是可以捕获到致命错误,不过Error只能捕获致命错误,不能捕获异常Exception,而Throwable是可以捕获到错误和异常的,所以更推荐。
7.总结
通过错误处理的学习,我们能够很好地认识到错误和提示类型、错误提示等级的配置方式、错误处理的几种方法,并自定义错误处理器类,希望通过本次学习能够很好地让我们面对错误能够轻松定位解决。