PHP之错误处理

↘锁芯ラ 提交于 2020-05-07 21:57:11

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.总结

通过错误处理的学习,我们能够很好地认识到错误和提示类型、错误提示等级的配置方式、错误处理的几种方法,并自定义错误处理器类,希望通过本次学习能够很好地让我们面对错误能够轻松定位解决。  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!