欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

拦截PHP各种异常和错误,发生致命错误时进行报警,万事防患于未然,php防患于未然_PHP教程

程序员文章站 2022-04-27 12:32:36
...

拦截PHP各种异常和错误,发生致命错误时进行报警,万事防患于未然,php防患于未然

在日常开发中,大多数人的做法是在开发环境时开启调试模式,在产品环境关闭调试模式。在开发的时候可以查看各种错误、异常,但是在线上就把错误显示的关闭。

上面的情形看似很科学,有人解释为这样很安全,别人看不到错误,以免泄露重要信息...

但是你有没有遇到这种情况,线下好好的,一上线却运行不起来也找不到原因...

一个脚本,跑了好长一段时间,一直没有问题,有一天突然中断了,然后了也没有任何记录都不造啥原因...

线上一个付款,别人明明付了款,但是我们却没有记录到,自己亲自去实验,却是好的...

种种以上,都是因为大家关闭了错误信息,并且未将错误、异常记录到日志,导致那些随机发生的错误很难追踪。这样矛盾就来了,即不要显示错误,又要追踪错误,这如何实现了?

以上问题都可以通过PHP的错误、异常机制及其内建函数'set_exception_handler','set_error_handler','register_shutdown_function' 来实现

'set_exception_handler' 函数 用于拦截各种未捕获的异常,然后将这些交给用户自定义的方式进行处理

'set_error_handler' 函数可以拦截各种错误,然后交给用户自定义的方式进行处理

'register_shutdown_function' 函数是在PHP脚本结束时调用的函数,配合'error_get_last'可以获取最后的致命性错误

这个思路大体就是把错误、异常、致命性错误拦截下来,交给我们自定义的方法进行处理,我们辨别这些错误、异常是否致命,如果是则记录的数据库或者文件系统,然后使用脚本不停的扫描这些日志,发现严重错误立即发送邮件或发送短信进行报警

首先我们定义错误拦截类,该类用于将错误、异常拦截下来,用我们自己定义的处理方式进行处理,该类放在文件名为'errorHandler.class.php'中,代码如下

/**
 * 文件名称:baseErrorHandler.class.php
 * 摘    要:错误拦截器父类
 */
require 'errorHandlerException.class.php';//异常类
class errorHandler
{
    public $argvs = array();

    public     $memoryReserveSize = 262144;//备用内存大小

    private $_memoryReserve;//备用内存

    /**
     * 方      法:注册自定义错误、异常拦截器
     * 参      数:void
     * 返      回:void
     */
    public function register()
    {
        ini_set('display_errors', 0);

        set_exception_handler(array($this, 'handleException'));//截获未捕获的异常

        set_error_handler(array($this, 'handleError'));//截获各种错误 此处切不可掉换位置

        //留下备用内存 供后面拦截致命错误使用
        $this->memoryReserveSize > 0 && $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);

        register_shutdown_function(array($this, 'handleFatalError'));//截获致命性错误
    }

    /**
     * 方      法:取消自定义错误、异常拦截器
     * 参      数:void
     * 返      回:void
     */
    public function unregister()
    {
        restore_error_handler();
        restore_exception_handler();
    }

    /**
     * 方      法:处理截获的未捕获的异常
     * 参      数:Exception $exception
     * 返      回:void
     */
    public function handleException($exception)
    {
        $this->unregister();
        try
        {
            $this->logException($exception);
            exit(1);
        }
        catch(Exception $e)
        {
            exit(1);
        }
    }

    /**
     * 方      法:处理截获的错误
     * 参      数:int     $code 错误代码
     * 参      数:string $message 错误信息
     * 参      数:string $file 错误文件
     * 参      数:int     $line 错误的行数
     * 返      回:boolean
     */
    public function handleError($code, $message, $file, $line)
    {
        //该处思想是将错误变成异常抛出 统一交给异常处理函数进行处理
        if((error_reporting() & $code) && !in_array($code, array(E_NOTICE, E_WARNING, E_USER_NOTICE, E_USER_WARNING, E_DEPRECATED)))
        {//此处只记录严重的错误 对于各种WARNING NOTICE不作处理
            $exception = new errorHandlerException($message, $code, $code, $file, $line);
            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
            array_shift($trace);//trace的第一个元素为当前对象 移除
            foreach($trace as $frame) 
            {
                if($frame['function'] == '__toString') 
                {//如果错误出现在 __toString 方法中 不抛出任何异常
                    $this->handleException($exception);
                    exit(1);
                }
            }
            throw $exception;
        }
        return false;
    }

    /**
     * 方      法:截获致命性错误
     * 参      数:void
     * 返      回:void
     */
    public function handleFatalError()
    {
        unset($this->_memoryReserve);//释放内存供下面处理程序使用

        $error = error_get_last();//最后一条错误信息
        if(errorHandlerException::isFatalError($error))
        {//如果是致命错误进行处理
            $exception = new errorHandlerException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
            $this->logException($exception);
            exit(1);
        }
    }

    /**
     * 方      法:获取服务器IP
     * 参      数:void
     * 返      回:string
     */
    final public function getServerIp()
    {
        $serverIp = '';
        if(isset($_SERVER['SERVER_ADDR']))
        {
            $serverIp = $_SERVER['SERVER_ADDR'];
        }
        elseif(isset($_SERVER['LOCAL_ADDR']))
        {
            $serverIp = $_SERVER['LOCAL_ADDR'];
        }
        elseif(isset($_SERVER['HOSTNAME']))
        {
            $serverIp = gethostbyname($_SERVER['HOSTNAME']);
        }
        else
        {
            $serverIp = getenv('SERVER_ADDR');
        }        
        
        return $serverIp; 
    }

    /**
     * 方      法:获取当前URI信息
     * 参      数:void
     * 返      回:string $url
     */
    public function getCurrentUri()
    {
        $uri = '';
        if($_SERVER ["REMOTE_ADDR"])
        {//浏览器浏览模式
            $uri = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
        }
        else
        {//命令行模式
            $params = $this->argvs;
            $uri = $params[0];
            array_shift($params);
            for($i = 0, $len = count($params); $i $len