- 目录
- 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.总结
通过错误处理的学习,我们能够很好地认识到错误和提示类型、错误提示等级的配置方式、错误处理的几种方法,并自定义错误处理器类,希望通过本次学习能够很好地让我们面对错误能够轻松定位解决。
来源:oschina
链接:https://my.oschina.net/mtdg/blog/4269768