PHP错误和异常处理

Deprecated提示

1、抛出Deprecated提示,虽然不影响运行,但是官方 不推荐使用 ,如 有些老版本的函数,需要用官方推荐的代替函数替换
2、Parse error、Fatal Error这种高级别错误会终止程序运行

生产线上错误日志控制

3、生产线上错误日志控制
线上的错误信息肯定是要记录的,error_reporting(0)这样会导致所有的错误信息不会记录,应该:error_reporting = E_ALL ,只要display_errors = Off,错误信息就不会再页面上显示,因为display_errors的优先级别更高。
特别要注意的是:
如果php.ini中log_errors= On,据官方的说法,那么必须指定error_log文件,如果没指定或者指定的文件没有权限写入,那么照样会输出到正常的输出渠道,那么也就使得display_errors 这个指定的Off失效,错误信息还是打印了出来。
总结下,在生产环境中错误信息记录错误日志:

<?php

error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1); 
ini_set('error_log','/hotdata/php_log/'.date('Y-m-d').'_error_log.txt');

trigger_error自定义错误显示

产生一个用户级别的 error/warning/notice 信息
trigger_error(‘错误提示内容’, E_USER 系列常量);
错误级别:
E_USER_NOTICE 继续执行
E_USER_WARNING 继续执行
E_USER_ERROR 程序不再执行

<?php
error_reporting(E_ALL);
// ini_set('display_errors',0);
ini_set('log_errors',1); 
ini_set('error_log','H:\\wamp\\PHPnow-1.5.6.1428396605\\htdocs\\'.date('Y-m-d').'_log.txt');    


$a = 1;

$b = '2';

if(is_numeric($a) && is_numeric($b))
{
    // trigger_error('必须传递数字类型的变量',E_USER_NOTICE);
    // trigger_error('必须传递数字类型的变量',E_USER_WARNING);
    trigger_error('必须传递数字类型的变量',E_USER_ERROR);
}else{
    echo $a + $b;
}

echo '程序继续运行';

将日志保存在系统日志中


<?php
error_reporting(E_ALL);
ini_set('display_errors',-1);
ini_set('log_errors',1); 
ini_set('error_log','syslog');  
// ini_set('ignore_user_abort()ore_repeated_errors','on');//忽略重复的信息
// ini_set('ignore_repeated_source','on');//忽略消息的来源
openlog('test', LOG_PID, 'this is test');
syslog(LOG_ERR, "This is a test: " . memory_get_usage(true));
closelog();

自定义错误处理器

PHP里有一套错误处理机制,可以使用set_error_handler接管PHP错误的处理,可以实现自定义的错误处理器

<?php

/**
* 自定义错误处理器
*/
class MyErrorHandler
{
    public $message = '';
    public $filename = '';
    public $line = 0;
    public $vars = array();
    public $_noticelog = 'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\run_log\\';
    public function __construct($message,$filename,$line,$vars)
    {
        $this -> message = $message;
        $this -> filename = $filename;
        $this-> line = $line;
        $this -> vars = $vars;
    }
    public static function deal($errorno,$errmsg,$filename,$line,$vars)
    {
        $self = new self($errmsg,$filename,$line,$vars);
        switch ($errorno) {
            case E_USER_ERROR:
                return $self -> dealError();
                break;
            case E_USER_WARNING:
                return $self -> dealWarning();
                break;
            case E_WARNING:
                return $self -> dealWarning();
                break;
            case E_NOTICE:
                return $self -> dealNotice();
                break;          
            case E_USER_NOTICE:
                return $self -> dealNotice();
                break;
            default:
                return False;
                break;
        }
    }
    /**
    * 处理错误信息
    */
    public function dealError()
    {
        ob_start();//开启内存缓冲
        debug_print_backtrace();
        $backtrace = ob_get_flush();//得到输出缓冲的内容
        $errormsg = <<<EOF
        出现了致命的错误,如下:
        产生错误的文件:{$this->filename}
        产生错误的信息:{$this->message}
        产生错误的行号:{$this->line}
        追踪信息:{$backtrace}
EOF;
        error_log($errormsg,1,'807010338@qq.com');
        exit(1);
    }
    /**
    * 处理警告信息
    */
    public function dealWarning()
    {

        $errormsg = <<<EOF
        出现了警告的错误,如下:
        产生警告的文件:{$this->filename}
        产生警告的信息:{$this ->message}
        产生警告的行号:{$this ->line}
EOF;
         return error_log($errormsg,1,'807010338@qq.com');
    }
    /**
    * 处理通知级别错误,写入日志
    */
    public function dealNotice()
    {
        $datetime = date('Y-m-d H:i:s');

        $errormsg = <<<EOF
        出现了通知的错误,如下:
        产生通知的文件:{$this->filename}
        产生通知的信息:{$this ->message}
        产生通知的行号:{$this ->line}
        产生通知的时间:{$datetime}
EOF;
        return error_log($errormsg,3,$this->_noticelog.date('Y-m-d').'.txt');
    }
}

测试上述代码:

<?php
require './MyErrorHandler.php';
error_reporting(-1);
set_error_handler(array('MyErrorHandler','deal'));
echo $test;
settype($var, 'nothistype');
trigger_error('手动抛出致命错误',E_USER_ERROR);
echo '没有运行';

使用register_shutdown_function()

1)通过register_shutdown_function()函数,可以让我们设置一个当执行关闭时可以被调用的另一个函数
2)当我们的脚本执行完成或意外死掉导致PHP执行即将关闭时,我们的函数将会调用

使用场景:
1、页面强制被停止
2、程序代码意外终止或超时

<?php
class Shutdown
{
    public function endScriptDeal()
    {
        if(error_get_last())
        {
            echo "<pre>";
            print_r(error_get_last());
            echo "</pre>";
        }
        $data = 'this is test';
        //输出到文件的路径必须为绝对路径,否则无法写入
        file_put_contents('H:\wamp\PHPnow-1.5.6.1428396605\htdocs\run_log\run_error.txt', $data);
        die('end script');
    }
}
echo 'hello world';
echo md6();//调用一个不存在的函数,将引发Fatal error
die;//register_shutdown_function之前有die或exit的情况下,下方代码将不执行;当然die或exit在其之后将可执行
register_shutdown_function(array(new Shutdown(),'endScriptDeal'));
// echo md4();

PHP中异常处理

  1. 异常处理认识
  2. 内置异常类
  3. 错误和异常的区别
  4. 自定义异常类
<?php
/*
try(){
    #code
    throw new Exception('异常信息');
}catch(Exception $e){
    echo $e -> getMessage();
}*/
//==下方代码异常没有抛出,所以未能捕获
$num = 0;
$num2 = 3;
try{
    $num = 3/0;
    var_dump($num);
}catch(Exception $e){
    echo $e -> getMessage();
    $num = 12;
}
echo 'continue';
echo $num.'<br/>';
//=======================
//主动抛出异常并捕获
//=======================
try
{
    $num1= 2;
    $num3= 0;
    if($num3 ==0)
    {
        throw new Exception('0不能被当作除数');
    }else{
        $res = $num1/$num3;
    }
}catch(Exception $e){
    echo $e -> getMessage();
    $num4 =33;
}

//==============
//没有catch块将报解析错误(语法错误)
//==============
// try
// {
//  $num1= 2;
//  $num3= 0;
//  if($num3 ==0)
//  {
//      throw new Exception('0不能被当作除数');
//  }else{
//      $res = $num1/$num3;
//  }
// }

//=================
//
<?php
//==============
//PDO内置异常处理,无需抛出即可捕获
//==============
try{
    $pdo = new Pdo("mysql:host=localhost;dbname=mysql",'root','333');
    var_dump($pdo);
    echo 'continue...';
}catch(PDOException $e){
    echo $e -> getMessage();
}
echo 'this is test'.'<br/>';

//==============
//Spl内置异常处理,也是不抛出异常也可捕获
//==============
try{
    $splObj = new SplFileObject('test.text','r');
    echo 'read file..';
}catch(Exception $e){
    echo $e ->getMessage();
}
echo 'this is Spl';

异常处理
当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 “catch” 代码块。
如果异常没有被捕获,而且又没用使用 set_exception_handler() 作相应的处理的话,那么将发生一个严重的错误(致命错误),并且输出 “Uncaught Exception” (未捕获异常)的错误消息。
处理处理程序应当包括:
try - 使用异常的函数应该位于 “try” 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一个异常。
throw - 这里规定如何触发异常。每一个 “throw” 必须对应至少一个 “catch”
catch - “catch” 代码块会捕获异常,并创建一个包含异常信息的对象
重新抛出异常
有时,当异常被抛出时,您也许希望以不同于标准的方式对它进行处理。可以在一个 “catch” 代码块中再次抛出异常。
脚本应该对用户隐藏系统错误。对程序员来说,系统错误也许很重要,但是用户对它们并不感兴趣。为了让用户更容易使用,您可以再次抛出带有对用户比较友好的消息的异常。
异常的规则
需要进行异常处理的代码应该放入 try 代码块内,以便捕获潜在的异常。
每个 try 或 throw 代码块必须至少拥有一个对应的 catch 代码块。
使用多个 catch 代码块可以捕获不同种类的异常。
可以在 try 代码块内的 catch 代码块中再次抛出(re-thrown)异常。
简而言之:如果抛出了异常,就必须捕获它。

//=============
//下方写法是错误的
//Fatal error: Uncaught exception 'Exception' with message 'Test2 Error Processing Request'
//=============
// try{
//  throw new Exception("Test Error Processing Request");
// }catch(Exception $e){
//  echo $e->getMessage();
//  throw new Exception("Test2 Error Processing Request");
// }catch(Exception $e){
//  echo $e ->getMessage();
// }

//============
//更正如下
//============
try{
    throw new Exception("Test Error Processing Request");
}catch(Exception $e){
    echo '<br/>'.$e->getMessage().'<br/>';
    try{
        throw new Exception("Test2 Error Processing Request");
    }catch(Exception $e){
        echo $e ->getMessage().'<br/>';
    }
}
echo 'continue...';

当自定义异常处理类后,在写catch捕获多个异常时,如果基类放上面,则其他子类则无法捕获异常

<?php

//==================
//基类可以捕获所有异常,基类放上面,下面再写子类的异常捕获就没有意义了
//==================
class MyException extends Exception
{

}
try{
    throw new MyException("Error Processing Request");  
}catch( Exception $e){
    echo '我是基类';
    echo $e -> getMessage();
}catch(MyException $e){
    echo '我是自定义子类';
    echo $e -> getMessage();
}

自定义异常类

<?php
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
{
    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, array('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;
        }
    }
    public function __destruct()
    {
        $this -> close();
    }
}


try{
    $fp = new WriteData('test.txt','w');
    $fp -> write('this is a test');
    $fp -> close();
    echo '数据写入成功';
}catch(FileException $e){
    echo '出现问题了'.$e->getMessage().'详细信息如下:'.$e->getDetails();
}

记录异常信息

<?php
class logException extends Exception
{
    public function __construct($message='',$code=0)
    {
        parent::__construct($message,$code);
        // error_log($this->getTraceAsString(),3,'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\test.txt');
        error_log($this->getMessage(),3,'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\test.txt');        
    }
}

try{
    $link = mysql_connect('localhost','root','11');
    if(!$link)
        throw new logException('数据库连接失败', 1);

}catch(logException $e){
    echo $e->getMessage();
}

观察者模式处理异常信息

Exception_observer.php

<?php
/**
*给观察者定义的规范
*强制指定类型来自于哪里
*/
interface  Exception_Observer
{
    public function update(observable_exception $e);
}

observable_exception.php

<?php

class observable_exception extends Exception
{
    public static $_observers = array();
    public static function attach(Exception_Observer $observer)
    {
        self::$_observers[] = $observer;
    } 
    public function __construct($message = null, $code = 0)
    {
        parent::__construct($message,$code);
        $this -> notify();
    }
    public function notify()
    {
        foreach (self::$_observers as $observer) 
        {
            $observer -> update($this);
        }
    }
}

Logging_Exception.php

<?php

/**
*  
*/
class Logging_Exception_Observer implements Exception_Observer
{
    protected $_filename = 'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\logException.log';
    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);//写入日志
    }
}

Emailing_Exception.php

<?php
class Emailing_Exception_Observer implements Exception_Observer
{
    protected $_email = '807010338@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->_email);//写入日志
    }   
}

测试代码
testObserver.php

<?php
require_once './Exception_observer.php';
require_once './observable_exception.php';
require_once './Logging_Exception.php';
require './Emailing_Exception.php';

observable_exception::attach(new Logging_Exception_Observer());//观察者1
observable_exception::attach(new Logging_Exception_Observer('test1.log'));//观察者2
observable_exception::attach(new Emailing_Exception_Observer());//观察者3
/**
*  
*/
class MyException extends observable_exception
{

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

try{
    throw new MyException("出现异常了,记录一下");

}catch(MyException $e)
{
    echo $e -> getMessage();
    echo '<hr/>';
    $e -> test();
    echo '<hr/>';
    $e ->test1();
}

自定义异常处理器

认识set_exception_handler
set_exception_handler.php

<?php
function exceptionHandler_1($e)
{
    echo '自定义的异常处理器——1<br/>函数名称:'.__FUNCTION__.'<br/>';
    echo '异常信息:'.$e->getMessage();
}

function exceptionHandler_2($e)
{
    echo '自定义的异常处理器——2<br/>函数名称:'.__FUNCTION__.'<br/>';
    echo '异常信息:'.$e->getMessage();
}
//直接抛出异常,不接收是会报错的
//Fatal error: Uncaught exception 'Exception' with message 'Error Processing Request' 
//throw new Exception("Error Processing Request");

set_exception_handler('exceptionHandler_1');
//当再定义一个异常处理器的时候,上一个会被覆盖
set_exception_handler('exceptionHandler_2');

//恢复到上一个定义过的异常处理器函数
restore_exception_handler();
restore_exception_handler();//Fatal error: Uncaught exception 'Exception' with message
throw new Exception("Error Processing Request");

自定义异常处理器
ExceptionHandler.class.php

<?php

class ExceptionHandler 
{
    protected $_exception;
    protected $_logFile = 'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ExceptionHandler.log';
    public function __construct(Exception $e)
    {echo 00;
        $this ->_exception = $e;
    }
    public static function handle(Exception $e)
    {
        $self = new self($e);
        $self ->log();
        echo $self;
    }
    /**
    * 重写Exception的__toString()方法
    */
    public function __toString()
    {
            $message = <<<EOF
                            <!DOCTYPE html>
                            <html>
                            <head>
                                    <title>出现异常了</title>
                                    <h1>出现异常了</h1>
                                    <a href='mailto:807010338@qq.com'>联系管理员807010338@qq.com</a>
                            </head>
                            <body>

                            </body>
                            </html>
EOF;
            return $message;
    }        
    public function log()
    { echo 11;
        error_log($this->_exception -> getMessage().PHP_EOL,3,$this->_logFile);
    }   
}   

set_exception_handler(array('ExceptionHandler','handle'));
throw new Exception('this is a test.....');//该信息会被捕获
try{
    throw new Exception('this is a test');
}  catch (Exception $e){
    echo $e ->getMessage();//该信息不会被捕获
}

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

认识ErrorException
ErrorException.php

<?php

function exception_error_handler($errno,$errstr,$errfile,$errline)
{
    throw new ErrorException($errstr,0,$errno,$errfile,$errline);
}

set_error_handler('exception_error_handler');

try {
    echo gettype();
} catch (Exception $exc) {
    echo $exc->getMessage();
}

ErrorToException.php

<?php
//======================================
//下方代码注释后运行结果
//Notice: Undefined variable: test in H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ErrorToException.php on line 16
//Warning: Wrong parameter count for gettype() in H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ErrorToException.php on line 17
//Warning: test in H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ErrorToException.php on line 18
//=======================================
//======下方代码运行结果=================
//Notice: Undefined variable: test in H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ErrorToException.php on line 24
//Wrong parameter count for gettype()
//=======================================
class ErrorToException extends Exception
{
    public static function handle($errno,$errstr)
    {
        throw new self($errstr,$errno);
    }

}
set_error_handler(array('ErrorToException','handle'));
set_error_handler(array('ErrorToException','handle'),E_USER_WARNING|E_WARNING);
//echo $test;
//==================================================
//======测试代码=======
try{
    echo $test;
    gettype();
    trigger_error('test',E_USER_WARNING);
}  catch (Exception $e){
    echo $e->getMessage();
}

发生错误时将用户重定向到一个404页面

ExceptionRedirct.php

<?php

class ExceptionRedirctHandler
{
    public  $redirct = '404.html';
    protected  $_exception;
    protected  $_logFile = 'H:\wamp\PHPnow-1.5.6.1428396605\htdocs\ExceptionRedirctHandler.log';
    /**
     * 获得异常对象
     * @param Exception $e
     */
    public function __construct(Exception $e) 
    {
        $this->_exception = $e;
    }
    /**
     * 接收一个异常对象
     * @param Exception $e
     */
    public static function  handle(Exception $e)
    {
        $self = new self($e);
        $self -> log();
        while (@ob_end_clean());//清除所有的ob缓冲
        header('HTTP/1.1 307 Temporary Redirect');
        header('cache-control:no-cache,must-revalidate');
        header('Expires:Tus,10 May 2015 22:35:44 GMT');//过期时间
        header("Location:".$self->redirct);
        exit();
    }
    public function log()
    {
        error_log($this->_exception->getMessage().PHP_EOL,3,  $this->_logFile);
    }
}
set_exception_handler(array('ExceptionRedirctHandler','handle'));
$link = mysql_connect('localhost','root11','root');
if(!$link){
    throw new Exception('数据库连接失败,抓紧查看情况');
}

404.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<title>404 Not Found</title>
<style type="text/css">
<!--
.t {
        font-family: Verdana, Arial, Helvetica, sans-serif;
        color: #CC0000;
}
.c {
        font-family: Verdana, Arial, Helvetica, sans-serif;
        font-size: 11px;
        font-weight: normal;
        color: #000000;
        line-height: 18px;
        text-align: center;
        border: 1px solid #CCCCCC;
        background-color: #FFFFEC;
}
body {
        background-color: #FFFFFF;
        margin-top: 100px;
}
-->
</style>
</head>
<body>
<div align="center">
  <h2><span class="t">404 Not Found</span></h2>
  <table border="0" cellpadding="8" cellspacing="0" width="460">
    <tbody>
      <tr>
        <td class="c">The requested URL was not found on this server.</td>
      </tr>
    </tbody>
  </table>
</div>
</body>
</html>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值