PHP错误与异常处理

  • 2019-07-24
  • 134
  • 0

开启错误配置选项

// 1.修改php.ini配置文件

// 2.error_reporting()函数设置
// 异常编号
echo error_reporting();

// 显示所有错误,除了通知错误
error_reporting(E_ALL&~E_NOTICE);

// 不显示所有错误
error_reporting(0);

// 显示错误
error_reporting(-1);

// 3 init_set()运行时设置
ini_set('error_reporting', 0);
ini_set('error_reporting', -1);
ini_set('error_reporting', 0);

trigger_error函数触发PHP错误

header("Content-Type:text/html;charset=utf-8");
$num1 = 1;
$num2 = '2a';
if ( ! (is_numeric($num1) && is_numeric($num2)))
{
//    trigger_error('num1和num2必须为合法数值', E_USER_NOTICE);
    trigger_error('num1和num2必须为合法数值', E_USER_WARNING);
//    trigger_error('num1和num2必须为合法数值', E_USER_ERROR);
}
else
{
    echo $num1 + $num2;
}
echo '程序继续向下执行,建议用trigger_error替代exit,die函数';

将错误日志保存到到指定文件

// 关闭错误显示,会自动记录到文件
ini_set('display_errors', 'off');
ini_set('date.timezone', 'PRC');

// 如果没有修改php.ini的权限
ini_set('log_errors', 'on');
ini_set('error_log', 'G:\error\testError.log'); // 重复消息忽略
ini_set('ignore_repeated_source', 'on');  // 重复消息来源忽略

// 设置错误级别
error_reporting(-1);

$username = 'admin';
$password = 'admin';
if ($username == 'admin' && $password == 'admin')
{
   echo '登录成功';
}
else
{
    $date = date('Y-m-d H:i:s', time());
    $ip = $_SERVER['REMOTE_ADDR'];
    $message = "用户{$username}在{$date}以{$password}尝试登录系统,IP:{$ip}";
    error_log($message); // 记录信息到文件
}

将错误日志保存在系统日志

ini_set('display_errors', 'off');
ini_set('log_errors', 'on');
ini_set('error_log', 'syslog'); 

也可以手动发送系统日志

// 建立一个和系统日志的连接
openlog('php7.1', LOG_PID, LOG_SYSLOG);
// 发送日志
syslog(LOG_ERR, 'this is a test of syslog');
// 关闭连接
closelog();

将错误日志以邮件立即发送

//php.ini里面要配置邮件客户端信息
error_reporting(-1);
ini_set('display_errors', 0);
ini_set('log_errors', 1);

error_log('当前系统被人攻击,产生致命错误', 1, 'xxxx@qq.com');

set_error_handler注册自定义错误处理函数

简单示例

/**
 * 自定义错误处理器
 * 1. 创建错误处理函数
 * 2. 设置不同级别调用函数
 * 3. set_error_handler函数指定接管错误处理(设置一个用户定义的错误处理函数)
 * 4. E_ERROR E_PARSE E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING E_STRICT 等级别错误不能由用户自定义函数处理
 */

header("Content-Type:text/html;charset=utf-8");
error_reporting(-1); # 显示所有锁雾
function error_handler($errno, $msg, $file, $line)
{
    echo "<b>错误代码: </b>[{$errno}] {$msg}<br>";
    echo "<b>错误行号: </b>{$file}文件中的第 {$line}行<br>";
}

set_error_handler('error_handler');
echo $test;   // 自定义函数接管
settype($var,'aa'); // 自定义函数接管
//test();    // 致命错误,系统接管

trigger_error('抛出一个警告', E_USER_ERROR); // 用户级致命错误,自定义

echo 'continue...';

restore_error_handler();  // 取消自定义注册的错误处理函数

set_error_handler('error_handler', E_ALL&~E_NOTICE);// ~除了xxx

<?php

// 自定义错误处理器
class MyErrorHandler
{
    private $_message = '';

    private $_file_name = '';

    private $_line = 0;

    private $_extra_vars = [];

    private $_notice_log = 'F:\notice\notice.log';

    public function __construct($message, $file_name, $line, $extra_vars)
    {
        $this->_message = $message;
        $this->_file_name = $file_name;
        $this->_line = $line;
        $this->_extra_vars = $extra_vars;
    }

    public static function deal($errno, $errmsg, $file_name, $line, $vars)
    {
        $self = new self($errmsg, $file_name, $line, $vars);
        switch ($errno)
        {
            case E_USER_ERROR:
                return $self->_deal_error();
                break;
            case E_USER_WARNING:
            case E_WARNING:
                return $self->_deal_warning();
                break;
            case E_NOTICE:
            case E_USER_NOTICE:
                return $self->_deal_notice();
                break;
            default:
                return false; // 系统处理
        }
    }

    /**
     * 处理致命错误
     */
    private function _deal_error()
    {
        // debug_print_backtrace:打印一条回溯,包括函数调用,被引入的文件和执行的代码信息
        // 不打印出来,放到内存缓冲区,然后通过邮件发送出去
        ob_start();   // 打开内存h缓冲
        debug_print_backtrace();
        $backtrace = ob_get_flush();
        $error_msg = <<<EOF
        出现了致命错误,如下:
        产生错误的文件: {$this->_file_name}
        产生错误的信息: {$this->_message}
        产生错误的行号: {$this->_line}
        追踪信息: {$backtrace}
EOF;
        error_log($error_msg, 1, '222222@qq.com');
        exit(1);
    }

    private function _deal_warning()
    {
        $error_msg = <<<EOF
        出现了警告错误,如下:
        产生警告的文件: {$this->_file_name}
        产生警告的信息: {$this->_message}
        产生警告的行号: {$this->_line}
EOF;
        return error_log($error_msg, 1, '222222@qq.com');
    }

    private function _deal_notice()
    {
        $datetime = date('Y-m-d H:i:s', time());

        $error_msg = <<<EOF
        出现了通知错误,如下:
        产生通知的文件: {$this->_file_name}
        产生通知的信息: {$this->_message}
        产生通知的行号: {$this->_line}
EOF;
        return error_log($error_msg, 3, $this->_notice_log);
    }
}

// 测试一下
error_reporting(-1);
set_error_handler(array('MyErrorHandler', 'deal')); // 执行myerrorhandler里面的deal
echo $test;

register_shutdown_handler()

当脚本执行完成或者意外死掉导致PHP,执行即将关闭时,这个函数将会被调用,使用场景:页面强制被停止,或者程序代码意外终止或超时.

class shutdown
{
    public function end_script()
    {
        # 得到最后产生的一个错误
        $last_errors = error_get_last();
        if ($last_errors)
        {
            echo '<pre>';
            print_r($last_errors);
            echo '</pre>';
        }
//        register_shutdown_function() 函数是通过内存调用的,当写路径的时候
//    已经脱离了当前脚本,所以写日志要用绝对路径
        file_put_contents('F:\error\error.txt', 'this is a test');
        die('end script');
    }
}
register_shutdown_function(array(new shutdown(), 'end_script'));
echo md6();

自定义异常处理类

如果异常不捕获处理,程序将会终止,并报出Fatal Error 错误。但异常捕获后程序可以继续执行,而真正的Fatal Error错误出现后程序就必须终止,异常可以使用 try{}catch(){} 来捕获捕获,捕获之后后续代码可以继续执行;而错误是无法使用 try{}catch(){} 捕获的,如果抛出了异常,就必须捕获它,否则程序终止执行。

class MyException extends Exception
{
    public function __construct($message = "", $code = 0)
    {
        parent::__construct($message, $code);
    }

    public function __toString()
    {
        $message = "<h2>出现异常了,信息如下:</h2>";
        $message .= "<p>" . __CLASS__ . "[$this->code]: {$this->message}</p>";
        return $message;
    }

    public function test()
    {
        echo 'this is a test';
    }

    public function stop()
    {
        exit('scipt end...');
    }

    // 自定义其他方法
}

try {
    throw new MyException('测试自定义异常类');
} catch (MyException $e) {
    echo $e->getMessage();
    echo '<hr>';
    echo $e;
    echo '<hr>';
    echo $e->test();
    echo '<hr>';
//    echo $e->stop();
} catch (Exception $ee) {
    echo '<hr>';
    echo '这是基类的输出,基类一般放在最后';
    echo $ee->getMessage();
}
echo 'continue';

class FileException extends Exception
{
    public function getDetails()
    {
        switch ($this->code)
        {
            case 0:
                return '没有提供文件';
                break;
            case 1:
                return '文件不存在';
                break;
            case 2:
                return '不是文件';
                break;
            case 3:
                return '文件不可泄';
                break;
            case 4:
                return '非法文件操作模式';
                break;
            case 5:
                return '数据写入失败';
                break;
            case 6:
                return '文件不能被关闭';
                break;
            default:
                return '非法';
                break;
        }
    }
}

class WriteData
{
    private $_message = '';

    private $_fp = '';

    public function __construct($filename = null, $mode = 'w')
    {
        $this->_message = "文件:{$filename} 模式:{$mode}";
        if (empty($filename)) throw new FileException($this->_message, 0);
        if (!file_exists($filename)) throw new FileException($this->_message, 1);
        if (!is_file($filename)) throw new FileException($this->_message, 2);
        if (!is_writable($filename)) throw new FileException($this->_message, 3);
        if (!in_array($mode, ['w', 'w+', 'a', 'a+'])) throw new FileException($this->_message, 4);
        $this->_fp = fopen($filename, $mode);
    }

    public function write($data)
    {
        if (@!fwrite($this->_fp, $data.PHP_EOL)) throw new FileException($this->_message, 5);
    }

    public function close()
    {
        if ($this->_fp)
        {
            if (@!fclose($this->_fp)) throw new FileException($this->_message, 6);
            $this->_fp = null;
        }
    }
}


try {
    $fp = new WriteData('test.txt', 'w');
    $fp->write('this is a test');
    $fp->close();

    echo '数据写入成功';
}catch (FileException $e) {
    echo "出现问题了,".$e->getMessage() . '<br />详细信息如下:' . $e->getDetails();
}

观察者模式处理异常信息

<?php
// 使用观察者模式来处理异常信息
interface Exception_Observer
{
    public function update(Observable_Exception $e);
}

class Observable_Exception extends Exception
{
    // 存储观察者
    private static $_observer = [];

    // 添加观察者
    public static function attach(Exception_Observer $observer)
    {
        self::$_observer[] = $observer;
    }

    public function __construct($message = null, $code = 0)
    {
        parent::__construct($message, $code);
        // 通知观察者
        $this->notify();
    }

    public function notify()
    {
        foreach (self::$_observer as $observer)
        {
            $observer->update($this);
        }
    }
}

// 观察者一
class Logging_Exception_Observer implements  Exception_Observer
{
    protected $_filename = "F:/error/logException.log";

    public function __construct($filename = null)
    {
        if ($filename != null && is_string($filename))
        {
            $this->_filename = $filename;
        }
    }

    public function update(Observable_Exception $e)
    {
        $message = "时间" . date("Y-m-d H:i:s") . PHP_EOL;
        $message .= "信息:" . $e->getMessage() . PHP_EOL;
        $message .= "追踪信息:".$e->getTraceAsString() . PHP_EOL;
        $message .= "文件:" . $e->getFile().PHP_EOL;
        $message .= "行号:" . $e->getLine().PHP_EOL;

        error_log($message, 3, $this->_filename);
    }
}

// 观察者二
class Emailing_Exception_Observer implements Exception_Observer
{
    protected $_email = '223@qq.com';

    public function __construct($email = null)
    {
        if ($email !== null && filter_var($email, FILTER_VALIDATE_EMAIL))
        {
            $this->_email = $email;
        }
    }

    public function update(Observable_Exception $e)
    {
        $message = "时间" . date("Y-m-d H:i:s") . PHP_EOL;
        $message .= "信息:" . $e->getMessage() . PHP_EOL;
        $message .= "追踪信息:".$e->getTraceAsString() . PHP_EOL;
        $message .= "文件:" . $e->getFile().PHP_EOL;
        $message .= "行号:" . $e->getLine().PHP_EOL;

        error_log($message, 1, $this->_filename);
    }
}

// 测试

Observable_Exception::attach(new Logging_Exception_Observer('test1.log'));

class MyException extends Observable_Exception
{
    public function test()
    {
        echo 'this is a test';
    }

    public function test1()
    {
        echo '我是自定义的方法处理这个异常';
    }
}

try {
    throw new MyException('出现异常了');
} catch (MyException $e) {
    echo $e->getMessage();
    echo '<hr>';
    echo $e->test();
}

自定义异常处理器

// 自定义异常处理器
header("Content-Type:text/html;charset=utf-8");

class ExceptionHandler
{
    protected $_exception;

    protected $_logFile = 'F:/exception/exception.txt';

    public function __construct(Exception $e)
    {
        $this->_exception = $e;
    }

    public static function handle(Exception $e)
    {
        $self = new self($e);
        $self->log();
        echo $self;
    }

    public function log()
    {
        error_log($this->_exception->getMessage().PHP_EOL, 3, $this->_logFile);
    }

    public function __toString()
    {
        $message = <<<EOF
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html" charset="utf-8" />
            <title>异常信息</title>
        </head>
        <body>
        <h1>出现异常了</h1>
        <p>刷新一下试试</p>
        <p><a href="mailto:2222@qq.com">联系管理员2222@qq.com</a></p>
        </body>
        </html>
EOF;
        return$message;
    }
}

set_exception_handler(['ExceptionHandler', 'handle']);

throw new Exception("测试");

像处理异常一样处理PHP错误

<?php
error_reporting(-1);
class ErrorToException extends Exception
{
    public static function handle($errstr = '', $errno = 0)
    {
        throw new self($errno, $errstr);
    }
}

set_error_handler(['ErrorToException', 'handle']);
set_error_handler(['ErrorToException', 'handle'], E_USER_WARNING|E_WARNING);

try {
    echo $test;
    echo '<hr>';
    gettype();
} catch (Exception $e) {
    echo $e->getMessage();
}

异常重定向

<?php

class ExceptionRedirectHandler
{
    protected $_redirect = '404.html';

    protected $_exception;

    protected $_logFile = 'F:/exception/exception.txt';

    public function __construct(Exception $e)
    {
        $this->_exception = $e;
    }

    public static function handle(Exception $e)
    {
        $self = new self($e);
        $self->log();
        // 重定向之前清除所有的输出缓冲
        while (@ob_end_clean());
        header('HTTP/1.1 307 Temporary Redirect');
        header('Cache-Control:no-cache,must-revalidate');
        header('Expires: Sat, 28 Mar 2019 22:32:48 GMT');
        header('Location:' . $self->_redirect);
        exit(1);
    }

    public function log()
    {
        error_log($this->_exception->getMessage().PHP_EOL, 3, $this->_logFile);
    }
}

set_exception_handler(['ExceptionRedirectHandler', 'handle']);

if (true)
{
    throw new Exception('异常跳转');
}

评论

还没有任何评论,你来说两句吧

粤ICP备17155863号

- 友情链接 - Theme by Qzhai