[TOC]

PHP错误和异常处理

PHP的错误和异常是两个概念PHP的错误处理:1.语法错误2.环境错误3.逻辑错误PHP的异常类型:

PHP7的错误和异常

PHP 7 改变了大多数错误的报告方式。
不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为Error异常抛出。这种 Error 异常可以像Exception异常一样被第一个匹配的try/catch块所捕获。
如果没有匹配的catch块,则调用异常处理函数(事先通过set_exception_handler()注册)进行处理。
如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。Error类并非继承自Exception类,所以不能用catch (Exception $e) { ... }来捕获Error。
你可以用catch (Error $e) { ... },或者通过注册异常处理函数(set_exception_handler())来捕获Error。$string = 1;
try {//未定义此对象$string->abc();
} catch (Exception $e) {echo "process exception";
} catch (Error $e) {echo "process error";
}PHP7 中出现了 Throwable 接口,该接口由 Error 和 Exception 实现,用户不能直接实现 Throwable 接口,而只能通过继承 Exception 来实现接口
可以直接这么写// Code that may throw an Exception or Error.
$string = 1;
try {//未定义此对象$string->abc();
}catch (Throwable $t){echo "process error";
}

PHP的错误

  • 常见错误
常见错误:常见的有16中,这里只说常用的.PHP.ini的错误模块Error handing and logging中有介绍Deprecated 最低级的错误,表示为不推荐的操作代码继续执行比如调用一些已启用的预定义变量时Notice        通知级别错误  代码继续执行比如调用未声明的变量,比如数组下标没有引号,又没有这个常量会暴出一个,没有这个常量.Waring      警告级别错误代码继续执行语法出现不恰当的情况,参数少几个,或者参数类型有误Fatal error    致命级别的错误代码终止执行比如调用不存在的函数parse error  语法解析错误  最高级别错误代码中止执行代码还没有执行,进行语法扫描时的错误.如果出现该错误,其他的错误都不会显示了.E_USER_  相关的错误   用户定义错误如 E_user的warning E_user的error等等手动抛出异常的时候用.

错误对照表

数字  常量  说明
1   E_ERROR     致命错误,脚本执行中断,就是脚本中有不可识别的东西出现
举例: Error:Invalid parameters. Invalid parameter name
2   E_WARNING   部分代码出错,但不影响整体运行
举例: Warning: require_once(E:/include/config_base.php)
4   E_PARSE     字符、变量或结束的地方写规范有误
举例:  Parse error: syntax error, unexpected $end in
8    E_NOTICE   一般通知,如变量未定义等
举例:  Notice: Undefined variable: p in E:\web\index.php on line 17
16      E_CORE_ERROR    PHP进程在启动时,发生了致命性错误
举例:  暂无
32      E_CORE_WARNING  在PHP启动时警告(非致命性错误)
举例:  暂无
64  E_COMPILE_ERROR     编译时致命性错误
举例:  暂无
128     E_COMPILE_WARNING   编译时警告级错误
举例:  暂无
256     E_USER_ERROR    用户自定义的错误消息
举例:  暂无
512     E_USER_WARNING  用户自定义的警告消息
举例:  暂无
1024    E_USER_NOTICE   用户自定义的提醒消息
举例:  暂无
2047    E_ALL   以上所有的报错信息,但不包括E_STRICT的报错信息
举例:  暂无
2048    E_STRICT    编码标准化警告,允许PHP建议如何修改代码以确保最佳的互操作性向前兼容性。
  • PHP 配置文件中与错误相关选项
常用错误配置:通过PHP的配置文件来设置# 设置错误报告级别error_reporting配置文件中有详细描述,可以结合使用也可以单独使用error_reporting = E_ALL&~E_NOTICE  显示所有错误, 并且除了notice错误& 表示并且| 表示或者~ 表示除了^ 表示除了E_ALL 所有的错误和警告E_NOTICE,运行时的提醒。基本无大碍可以正常使用。E_WARNING,运行时的警告,部分功能失效,脚本继续执行E_ERROR,致命的错误,阻止脚本运行(快死了,脑袋没了)# 是否以内嵌形式显示错误display_errors    display_errors = On/Off    显示/关闭此处对解析错误无效,因为他属于语法检测阶段.# 是否记录错误日志到文件中log_errors     log_errors = On/Off    显示/关闭# 指定错误输出到文件的路径error_logerror_log = /var/log/php/php.logerror_log = syslog设置与系统日志保存在一起指定到系统日志中需要openlog()之类的操作,具体不写了我从来都不用# 设置记录日志的文件最大的大小log_errors_max_len# 是否忽略重复的错误消息ignore_repeated_errors# 是否忽略重复的来源的错误消息ignore_repeated_source# 最后一个错误消息将永远保存在$php_errormsg的# 预定义变量中track_errors通过PHP代码动态设置函数来设置错误级别,不传值的话,默认得到当前错误级别的位掩码
error_reporting()屏蔽所有错误,解析错误无效error_reporting(0)显示所有错误error_reporting(-1)显示所有的错误信息,并且不显示notice错误error_reporting('E_ALL&~E_NOTICE');运行时设置配置选项的值
init_set()屏蔽所有错误,解析错误无效init_set('error_reporting',0)显示所有错误init_set('error_reporting',-1)隐藏错误信息init_set('display_errors',0)
  • 设置错误的级别
触发错误的功能, 不仅咸鱼PHP的解析器.
我们也可以手动触发错误
trigger_error('要抛出的提示语句',E_USER_NOTICE);
是user_error()的别名trigger_error('数据类型有误',E_USER_NOTICE);
trigger_error('数据类型有误',E_USER_WARNING);
trigger_error('数据类型有误',E_USER_ERROR);
  • 记录错误 发送错误
# 将错误记录到指定的文件中log_errors = Onerror_log = /var/log/php/php.error.log这时候如果上线了记得关掉错误显示display_errors = Offerror_reporting = E_ALL或者ini_set('display_errors','off');error_reporting(-1);ini_set('log_error','on');ini_set('error_log','/var/log/php/php.error.log');附加设置:# 显示重复的错误消息ini_set('ignore_repeated_errors','on');# 显示重复来源的错误消息ini_set('ignore_repeated_source','on');# 即将错误信息已邮件的形式发送
error_log($num);
$num = 0; 发送到 PHP 的系统日志
$num = 1;发送到参数 destination 设置的邮件地址
.........等等首先需要配置好PHP的mail或者sendmail
error_log('程序崩溃',1,'邮箱地址');

设置自定义错误处理器

  • 接管系统错误处理set_error_handler();
设置一个用户定义的错误处理函数,通过该函数进行自定义错误的处理
set_error_handler( callable $error_handler [, int $error_types = E_ALL | E_STRICT ])//直接输入函数名进行调用
set_error_handler('deal');
//数组形式, 第一个为类型, 第二个为方法名.
set_error_handler(['myerrorhandler', 'deal']);$error_handler
所要调用的回调函数回调函数需要的参数:
errno 错误级别即错误号
errstr 包含了错误的信息
[errfile] 包含错误的文件名
[errline] 包含了错误发生的行号
errcontext 是一个指向错误发生时活动符号表的array.PHP7.2后被弃用了$error_types
指定错误级别
就像error_reporting的ini设置能够控制错误显示一样.
此参数能够用于屏蔽 error_handler 的触发。
如果没有该掩码, 无论 error_reporting 是如何设置的,
error_handler 都会在每个错误发生时被调用。取消自定义错误处理的接管
restore_error_handler();
该函数可以关闭set_error_handler()设置的自定义
但是在实际使用当中, 我发现他的作用是恢复到上一次的设定.
比如设置了两次set_error_handler();
调用一次restore_error_handler();只会恢复到第一次set_error_handler.
调用第二次restore才会还原到为set_error_heandler状态./**
会绕过PHP标准的错误处理程序,除非回调函数返回了false.
error_reporting()设置将不会起到作用,而错误处理函数将继续执行
不能被用户自定义函数处理的错误:
E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,
和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。
如果错误发生在脚本执行之前(比如文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册
*///我的简单示例/*** Method  customError* @desc  自定义错误处理函数** @author  LiuHao <lh@btctrade.com>* @param string $errno 错误号* @param string $errmsg 错误信息* @param string $file 错误的文件路径* @param string $line 错误所在行号** @return  void*/
function customError($errno, $errmsg, $file, $line)
{echo "错误代码: {$errno}{$errmsg}\n" . PHP_EOL;echo "错误行号: {$file}文件中的第{$line}行\n" . PHP_EOL;echo "PHP版本: " . PHP_VERSION . "(" . PHP_OS . ")" . PHP_EOL;//如果要停止进程请使用exitexit();
}//设置调用的函数
set_error_handler('customError');//不可被自定义错误处理, 捕捉的错误
func(); //致命错误
$a      //语法错误
  • 设置自定义错误处理器

//代码示例
<?php/*** @Author:  LiuHao* @Date:  2018/2/10 下午3:26* @Email:  lh@btctrade.com* @File:  myerrorhandler.php* @Desc:  自定义错误处理器*/
class myerrorhandler
{public $message  = '';public $filename = '';public $line     = 0;public $vars     = [];/*** myerrorhandler constructor.* @param string $message 消息* @param string $filename 文件名* @param string $line 行号* @param array $vars 额外信息* @author  liuhao <lh@btctrade.com>*/public function __construct($message, $filename, $line, $vars){$this->message = $message;$this->filename = $filename;$this->line = $line;$this->vars = $vars;}/*** Method  deal* @desc  ......** @author  LiuHao <lh@btctrade.com>* @static* @param $errno* @param $errmsg* @param $filename* @param $line* @param $vars** @return  void*/public static function deal($errno, $errmsg, $filename, $line, $vars){$self = new self($errno, $errmsg, $filename, $line, $vars);//根据不同的错误号来选择处理方式switch ($errno) {case E_USER_ERROR:return $self->dealError();break;case E_WARNING:case E_USER_WARNING:return $self->dealWaring();break;case E_NOTICE:case E_USER_NOTICE:return $self->dealNotice();break;default:return false;}}public function dealError(){//开启内存缓冲, 防止内容输出ob_start();//返回一条debug追踪回溯debug_print_backtrace();//得到内存缓冲中的内容$backtrace = ob_get_flush();$errMsg = <<<EOF出现了致命错误,如下:产生错误的文件:{$this->filename}产生错误的信息:{$this->message}产生错误的行号:{$this->line}追踪信息:{$backtrace}
EOF;exit($errMsg);}public function dealWaring(){$dateTime = date('Y-m-d H:i:s', time());$errMsg = <<<EOF出现了警告错误,如下:产生警告的文件:{$this->filename}产生警告的信息:{$this->message}产生警告的行号:{$this->line}产生通知的时间:{$dateTime}
EOF;self::log($errMsg);}public function dealNotice(){$dateTime = date('Y-m-d H:i:s', time());$errMsg = <<<EOF出现了通知错误,如下:\n产生通知的文件:{$this->filename} - 产生通知的信息:{$this->message} - 产生通知的行号:{$this->line} - 产生通知的时间:{$dateTime} -
EOF;self::log($errMsg);}/*** Method  log* @desc  ......** @author  LiuHao <lh@btctrade.com>* @param $msg* @param string $path* @param string $file** @return  void*/public function log($msg, $path = './log', $file = '/error.log'){$filePath = $path . $file;//文件信息不存在, 创建if (!is_dir($path)) {mkdir($path, '0777', true);}if (!file_exists($filePath)) {touch($filePath);}//打开文件资源,追加错误信息$handler = fopen($filePath, 'a+');fwrite($handler, $msg);fclose($handler);}}//打开所有错误信息的捕捉
error_reporting(-1);//展示所有错误信息
ini_set('display_errors',0);//设置自定义错误捕捉
set_error_handler(['myerrorhandler', 'deal']);//notice错误
echo $a;
//致命错误, 不可捕捉
test();//语法错误,不可捕捉
$sys

当进程被关闭时被调用的函数-可以用来捕捉致命错误

进程停止调用函数
退出进程调用的函数<?php
/*** @Author:  LiuHao* @Date:  2018/2/11 下午11:39* @Email:  lh@btctrade.com* @File:  shutdown.php* @Desc:  ...*/// 设置一个当知心给关闭时可以被调用的函数
// 页面被强制停止
// 程序代码意外停止或者超时
//register_shutdown_function();class shutdown
{public function enScript(){if (error_get_last()) {//最后一次错误的数组var_dump(error_get_last());}//必须为绝对路径,//因为register_shutdown_ 是从内存中进行调用的.// 也就就是说PHP运行完之后调用的.$path = '/Users/liuhao/www/demo/project_demo/error/log/shutdown.log';file_put_contents($path, 'this is test');exit(1);}
}//数组形式, 类名,方法名, 传参 , 注意非静态方法的类名需要new一下, 静态方法直接输入类名
//register_shutdown_function([ new shutdown(),'enScript']);

PHP中的异常处理

  • 错误和异常的区别
/*** PHP中的异常处理** 异常和错误的区别是什么** 异常:*      程序运行和正常状况不同时, 我们认为出现异常.*      使用throw抛出异常, 然后使用catch 来捕获异常.*      每一个catch对应一个try, 可以使用多个catch也可以在try内嵌套多个try  catch*      如果所有的try catch都没有命中, 代码就会继续执行*/
//如:
/*** try {* //代码段:* //0不能作为除数* $num = 3 / 0;* var_dump($num);** } catch (Exception $e) {* echo $e->getMessage();* }*///这样写实没有走到catch段中的, 代码会继续执行
//PHP代码首先触发的本身的错误, 不会手动触发异常,不会自动走到catch中来抛出异常
//需要手动throw来抛出一个异常
//如果在try代码块中出现错误, 需要使用throw来抛出一个异常,这时候在catch中才能够捕捉的到异常.
//throw 后的代码不会再运继续运行
//catch捕获到异常后程序会继续执行, 如果想要退出,可以使用exit来退出进程
//部分PHP的内置异常类时可以自行抛出的如PDO
$num = null;
try {//代码段://0不能作为除数$num = 3 / 0;var_dump($num);} catch (Exception $e) {echo $e->getMessage();
}echo 'continue';//手动抛出异常,然后使用catch来捕获try {$num1 = 3;$num2 = 0;if ($num2 == 0) {throw new Exception('0不能作为除数', '333');} else {$res = $num1 / $num2;}} catch (Exception $e) {echo 'Msg: ' . $e->getMessage();echo '<hr/>';echo 'Code: ' . $e->getCode();echo '<hr/>';echo 'file: ' . $e->getFile();echo '<hr/>';echo 'line: ' . $e->getLine();echo '<hr/>';
}//PDO异常自行抛出, 该案例就没就行手动抛出异常, 而是自行抛出的
try {//这里故意输错了密码,可以发现,执行抛出了异常, 这里的continu代码没再运行,而throw中的代码则会继续执行$pdo = new PDO('mysql:host=127.0.0.1;dbname=mysql', 'root', 'aa');var_dump($pdo);echo '<hr/>';echo 'continue 代码继续执行';
} catch (PDOException $e) {echo $e->getMessage();echo '<hr/>';echo 'throw 继续执行';
}//splfile也是这样和pdo的异常处理类似, 类似的还有很多不在逐一测试
try {$splObj = new SplFileObject('test.txt', 'r');var_dump($splObj);echo '<hr/>';echo 'spl continue 代码继续执行';
} catch (Exception $e) {echo $e->getMessage();echo '<hr/>';echo 'spl throw 继续执行';echo '<hr/>';
}/*** 错误和异常的区别,两个不同的东西* PHP遇到错误时, 首先触发的时本身的错误, 不会手动抛出异常, 可以通过throw来抛出异常, 然后使用catch来捕获异常* 错误:*      没法让错误在调用的时候向上传递, 因为遇到错误必须马上处理.* 异常:*      可以--的向上传递知道我们进行捕获,异常也可以自定义处理信息.**///多个catch进行嵌套操作try {throw  new Exception('测试异常1');} catch (Exception $e) {echo $e->getMessage();echo "<hr/>";try {throw new Exception('测试异常2');} catch (Exception $e) {echo $e->getMessage();}
}
echo '<hr/>';
echo 'continue...';
  • 自定义异常类
/*** 自定义异常类* 只需继承Exception的基类即可* 只有两个方法可以重写* __contruct 构造方法* __toString 样式展示*/
class myException extends Exception
{/*** myException constructor.* @param string $message* @param int $code* @param Throwable|null $previous@author  liuhao <lh@btctrade.com>* 重写构造函数*/public function __construct($message = "", $code = 0, Throwable $previous = null){//调用父类构造函数,保证所有变量都被正确的赋值parent::__construct($message, $code, $previous);}/*** Method  __toString* @desc  重写父类的样式展示方法** @author  LiuHao <lh@btctrade.com>** @return  void*/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('script end ...');}}//调用自定义异常类进行处理
try {echo '出现了异常';echo "<hr/>";throw new myException('测试自定义异常', 3);
} catch (Exception $e) {echo $e->getMessage();echo "<hr/>";echo $e;echo "<hr/>";echo $e->test();echo "<hr/>";
//    echo $e->stop();
}echo 'continue ....';//多个try catch 同时利用自定义异常处理类来进行捕获
// 这里在基类的错误异常类中,也是可以调用子类中定义的方法
// 一般Exception我们都放在最后使用,如果要用的话.
// 不然程序会先进入Exception 就没法用我们自己定义的错误处理类了
try {echo "<hr/>";throw new myException('测试自定义异常');
} catch (Exception $e) {echo "<hr/>";echo 'Exception';echo "<hr/>";echo $e->getMessage();echo "<hr/>";echo $e->test();} catch (myException $e) {echo "<hr/>";echo 'myException';echo $e->getMessage();
}echo "<hr/>";
echo 'continue ....';
  • 自定义文件写入异常类,以及调用自定义异常类写入文件

/*** Class  FileException** @author  liuhao <lh@btctrade.com>* @desc  自定义文件写入异常类*/
class  FileException extends Exception
{public function getDetailes(){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** @author  liuhao <lh@btctrade.com>* @desc  调用自定义文件写入异常类*/
class WriteData
{private $_message = '';private $_fp;public function __construct($filename = '', $model = 'w'){$this->_message = "文件{$filename} 模式:{$model}";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_writeable($filename)) {throw new FileException($this->_message, 3);}if (!in_array($model, ['w', 'w+', 'a', 'a+'])) {throw new FileException($this->_message, 4);}$this->_fp = fopen($filename, $model);}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)) {$this->_fp = null;throw new FileException($this->_message, 6);}}}public function __destruct(){// TODO: Implement __destruct() method.$this->close();}
}try {//文件不存在
//    $fp = new WriteData('text.txt', 'w');//没有传文件
//    $fp = new WriteData();$fp = new WriteData('./log/log.txt', 'a+');$fp->write('this is test');} catch (FileException $e) {echo '出现问题了' . $e->getMessage() . '; 详细情况如下' . $e->getDetailes();
}
  • 简单的记录和发送异常信息
/*** Class  LogException** @author  liuhao <lh@btctrade.com>* @desc  简单的记录和发送异常信息*/
class LogException extends Exception
{public function __construct($message = "", $code = 0, Throwable $previous = null){parent::__construct($message, $code);//保存错误信息到文件error_log($this->getMessage(), 3, './log/log.txt');}
}//这时候会爆出waring错误信息, 同时也会写入到错误文件当中
//此处以MySQL连接发生错误为例
try {$link = mysqli_connect('127.0.0.1', 'root11', '');if (!$link) {throw new LogException('数据库连接失败');}
} catch (LogException $e) {$e->getMessage();
}

利用观察者模式处理异常信息


<?php/*** @Author:  LiuHao* @Date:  2018/3/1 下午9:22* @Email:  lh@btctrade.com* @File:  Exception_Observer.php* @Desc:  观察者的接口, 主要是让所有的观察者都来遵循该接口的规范,不管以后有多少个观察者都要使用该接口*/
interface Exception_Observer
{/*** Method  update* @desc  通过类型限制, 强制限制必须来自该Observable_Exception对象的** @author  LiuHao <lh@btctrade.com>* @param Observable_Exception $e** @return  mixed*/public function update(Observable_Exception $e);
}<?php/*** @Author:  LiuHao* @Date:  2018/3/1 下午9:48* @Email:  lh@btctrade.com* @File:  Logging_Exception.php* @Desc:  记录日志文件的观察者*/
class Logging_Exception_Observer implements Exception_Observer
{protected $_filename = './error/logException.log';/*** Logging_Exception_Observer constructor.* @param null $fileName* @author  liuhao <lh@btctrade.com>* 构造函数*/public function __construct($fileName = null){if (!empty($fileName) && is_string($fileName)) {$this->_filename = $fileName;}}/*** Method  update* @desc  完善父类接口中的该方法, 同时父类中的改方法是含有类型限制的,仅限Observable_Exception类的对象使用** @author  LiuHao <lh@btctrade.com>* @param Observable_Exception $e** @return  void*/public function update(Observable_Exception $e){// TODO: Implement update() method.$message = "时间: " . date('Y-m-d H:i:s', time()) . PHP_EOL;$message .= "信息: " . $e->getMessage() . PHP_EOL;$message .= "追踪信息: " . $e->getTraceAsString() . PHP_EOL;$message .= "文件: " . $e->getFile() . PHP_EOL;$message .= "行号" . $e->getLine() . PHP_EOL;$message .= '------------------------------' . PHP_EOL;//写入日志文件,error_log的类型为3位写入文件error_log($message, 3, $this->_filename);}
}<?php/*** @Author:  LiuHao* @Date:  2018/3/1 下午10:04* @Email:  lh@btctrade.com* @File:  Emailling_Exception_Observer.php* @Desc:  邮件发送的观察者*/
class Emailling_Exception_Observer implements Exception_Observer
{protected $_email = 'email@email.com';public function __construct($email = null){if ($email != null && filter_var($email, FILTER_VALIDATE_EMAIL)) {$this->_email = $email;}}/*** Method  update* @desc  完善父类接口中的该方法, 同时父类中的改方法是含有类型限制的,仅限Observable_Exception类的对象使用** @author  LiuHao <lh@btctrade.com>* @param Observable_Exception $e** @return  void*/public function update(Observable_Exception $e){// TODO: Implement update() method.$message = "时间: " . date('Y-m-d H:i:s', time()) . PHP_EOL;$message .= "信息: " . $e->getMessage() . PHP_EOL;$message .= "追踪信息: " . $e->getTraceAsString() . PHP_EOL;$message .= "文件: " . $e->getFile() . PHP_EOL;$message .= "行号" . $e->getLine() . PHP_EOL;//发送邮件,errlr_log类型为1为发送邮件, 暂时没有配置所以注释掉了
//        error_log($message, 1, $this->_email);var_dump('邮件发送成功');}}<?php/*** @Author:  LiuHao* @Date:  2018/3/1 下午8:49* @Email:  lh@btctrade.com* @File:  Observable_Exception.php* @Desc:  观察者的类,继承Exception_Observer接口基类*/
class Observable_Exception extends Exception
{//定义静态属性,保存观察者的信息public static $_observers = array();/*** Method  attch* @desc  用以添加观察者** @author  LiuHao <lh@btctrade.com>* @static* @param Exception_Observer $observer 通过类型暗示进行限制传参** @return  void*/public static function attch(Exception_Observer $observer){//动态添加观察者成员self::$_observers[] = $observer;}/*** Observable_Exception constructor.* @param string $message* @param int $code* @param Throwable|null $previous* @author  liuhao <lh@btctrade.com>* 在构造函数中, 当程序发生异常, 通知各位观察者*/public function __construct($message = "", $code = 0, Throwable $previous = null){//如果这里没有形参, 或者形参设置不对, 会导致输出的信息不正确//次数的message设置为什么, 后面输出的message就是什么parent::__construct($message, $code, $previous);//通知观察者$this->notify();}/*** Method  notify* @desc  异常通知方法** @author  LiuHao <lh@btctrade.com>** @return  void*/public function notify(){//遍历所有观察者,逐个发送错误信息foreach (self::$_observers as $observer) {//每一个观察者都调用一下update方法,update方法来自Exception_Observer类//该类中的该方法有类型限制,定义了必须来自Observable_Exception类的对象$observer->update($this);}}}<?php
/*** @Author:  LiuHao* @Date:  2018/3/1 下午10:09* @Email:  lh@btctrade.com* @File:  test_Exception.php* @Desc:  异常处理观察者模式, 测试文件*///include 'Exception_Observer.php';
//include 'Observable_Exception.php';
//include 'Logging_Exception_Observer.php';
//include 'Emailling_Exception_Observer.php';//添加观察者, attch有类型
Observable_Exception::attch(new Logging_Exception_Observer());
//添加第二个观察者,同时制定日志写入的位置
Observable_Exception::attch(new Logging_Exception_Observer('./error/log_Exception1.log'));
//添加第三个观察者
Observable_Exception::attch(new Emailling_Exception_Observer());/*** Class  MyException** @author  liuhao <lh@btctrade.com>* @desc  测试用来抛出异常信息*/
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 '</hr>';echo $e->getMessage();echo '</hr>';$e->test();echo '</hr>';$e->test1();
}

自定义异常处理器

set_exception_handler — 设置用户自定义的异常处理函数
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。
使用try catch正常捕捉的,不能被set_exception_handler捕捉restore_exception_handler — 恢复之前定义过的异常处理函数。
在使用 set_exception_handler() 改变异常处理函数之后,此函数可以 用于还原之前的异常处理程序(可以是内置的或者也可以是用户所定义的函数)。
例如上面使用了两次set_exception_handler()
这里第一次使用restore_exception_handler()只能恢复到第一次set;
再次restore的时候,才可以彻底取消set<?php
/*** @Author:  LiuHao* @Date:  2018/3/3 上午1:18* @Email:  lh@btctrade.com* @File:  myException.php* @Desc:  自定义异常处理函数,用以处理未被捕获的异常,即没有放在try catch中的异常 set_exception_handler()* set_exception_handler(); 设置一个用户定义的异常处理函数,和错误处理set_error_handler()用户一样* restore_exception_handler(); 恢复到上一次定义过的异常处理函数**/function exceptionHandler_1(Exception $e)
{echo '自定义异常处理器1 函数名: ' . __FUNCTION__ . PHP_EOL;echo '异常信息: ' . $e->getMessage() . PHP_EOL;
}function exceptionHandler_2(Exception $e)
{echo '自定义异常处理器2 函数名: ' . __FUNCTION__ . PHP_EOL;echo '异常信息: ' . $e->getMessage() . PHP_EOL;
}//这里没有使用try catch 也没有在之前, 设置set_exception_handler, 所以会抛出一个致命错误
//throw new Exception('这里遇到了异常');//这里会正常的被exceptionHandler_1捕捉到异常信息
//set_exception_handler('exceptionHandler_1');
//throw new Exception('再次抛出一个异常信息');//这里会正常的被exceptionHandler_2捕捉到异常信息
set_exception_handler('exceptionHandler_1');
set_exception_handler('exceptionHandler_2');
//加了这个之后,这里的异常就会被exceptionHandler_1捕捉到, 再次restore_则会返回到未设置状态
//注意该函数前面必须至少有一个set_exception_handler 不是必须紧紧挨着
restore_exception_handler();
throw new Exception('又再次抛出一个异常信息');<?php/*** @Author:  LiuHao* @Date:  2018/3/4 上午4:43* @Email:  lh@btctrade.com* @File:  myException_class.php* @Desc:  自定义异常处理类*/
class ExceptionHandler
{protected $_exception;public function __construct(Exception $e){$this->_exception = $e;}public static function handler(Exception $e){$self = new self($e);$self->log();echo $self;}public function log(){error_log($this->_exception->getMessage() . PHP_EOL, 3, './myException.log');}public function __toString(){// TODO: Implement __toString() method.//这里的eof结尾必须顶格,否则报错$message = <<<EOF
恭喜你,爆出异常了, 哈哈哈哈.
EOF;return $message;}
}set_exception_handler(['ExceptionHandler', 'handler']);//这里正确被try catch捕捉到的异常信息, 不会被set_exception_handler捕捉
try {throw new Exception('正确被trye catch捕捉的异常信息');
} catch (Exception $e) {echo $e->getMessage() . PHP_EOL;
}//抛出的异常成功被ExceptionHandler捕捉,同时同时显示了出来
throw new Exception('测试自定义异常信息');

如何像处理异常那样处理错误


<?php
/*** @Author:  LiuHao* @Date:  2018/3/4 上午5:21* @Email:  lh@btctrade.com* @File:  ErrorException.php* @Desc:  利用PHP自带的ErrorException把错误像异常一样进行处理*/
function exception_error_handler($errno, $errstr, $errfile, $errline)
{throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}set_error_handler('exception_error_handler');//此处这里如果没有try catch的话,无法调用到set_error_handler中设置的exception_error_handler函数
try {echo gettype();
} catch (Exception $e) {echo $e->getMessage();
}<?php/*** @Author:  LiuHao* @Date:  2018/3/4 上午5:56* @Email:  lh@btctrade.com* @File:  ErrorToException.php* @Desc:  自定义错误捕捉类, 想捕捉异常一样去捕捉*/
class ErrorToException extends Exception
{public static function handle($errno, $errstr){throw new self($errstr, $errno);}
}//set_error_handler(['ErrorToException', 'handle']);
//设置捕捉错误的, 错误等级, 只捕捉下面两种类型的错误
set_error_handler(['ErrorToException', 'handle'], E_USER_NOTICE | E_NOTICE);try {//因为上面限制了错误信息的捕捉级别, 而这里的gettype是一个waring所以不会被捕捉//但是$test属于Notice, 就会被捕捉的到//trigger_error抛出的时一个用户级别的Notice所以也会被捕捉的到,但是由于$test被捕捉了,所以不会向下执行//如果注释掉$test就会明白这一点echo gettype();echo PHP_EOL;echo $test;echo PHP_EOL;trigger_error('tes*t', E_USER_NOTICE);
} catch (Exception $e) {echo $e->getMessage();
}

在发生异常时将用户重定向到另一个页面

<?php/*** @Author:  LiuHao* @Date:  2018/3/4 上午6:40* @Email:  lh@btctrade.com* @File:  ExceptionRedict.php* @Desc:  将异常进行重定向到404页面*/
class ExceptionRedict
{public    $redirect = '404.html';protected $_exception;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 Temporay Redirct');header('Cache-Control:no-cache,must-revalidate');header('Expire:Sat 28 Mar 2015 13:28:48 GMT');header('Location:' . $self->redirect);exit(1);}public function log(){error_log($this->_exception->getMessage() . PHP_EOL, 3, './ExceptionRedict.log');}
}set_exception_handler(['ExceptionRedict', 'handle']);$link = mysqli_connect('127.0.0.1', 'rootadsf', 'root');
if (!$link) {throw new Exception('数据库可能被攻击');
}

转载于:https://my.oschina.net/chinaliuhan/blog/3064490

PHP错误和异常处理相关推荐

  1. PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别)

    PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别) 一.总结 一句话总结: throwable接口+Error类 在PHP7更新中有一条:更多的Error变为可捕获的Exce ...

  2. php捕获Fatal error错误与异常处理

    php捕获Fatal error错误与异常处理 参考文章: (1)php捕获Fatal error错误与异常处理 (2)https://www.cnblogs.com/jkko123/p/108403 ...

  3. python错误-python错误和异常处理怎处理你知道么

    原标题:python错误和异常处理怎处理你知道么 异常处理 什么是异常? 首先要清楚,什么是异常,异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常 ...

  4. PHP常用功能块_错误和异常处理 — php(32)

    一.错误和异常处理 1.1 错误类型和基本的调试方法 PHP程序的错误发生一般归属于下列三个领域: 语法错误: 语法错误最常见,并且也容易修复.如:代码中遗漏一个分号.这类错误会阻止脚本的执行. 运行 ...

  5. python中错误和异常处理

    错误和异常处理 在python中一共有2种错误:一种是语法错误,另外一种是异常. 语法错误 语法错误也叫做解析错误,是指python无法正确的识别代码的造成的.根本原因在于人的行为:手残,脑残和眼残的 ...

  6. Python 迭代器,错误、异常处理

    迭代器 迭代器可以用来遍历字符串.列表.元组.集合.字典. myString="hello" myIter=iter(myString) ##iter()函数可以获取元素集的一个迭 ...

  7. php面向对象异常处理,PHP 错误和异常处理(下)

    PHP 错误和异常处理(下) 由 学院君 创建于9个月前, 最后更新于 7个月前 版本号 #1 1723 views 2 likes 0 collects 上篇我们讲了 PHP 中的错误报告和捕获,今 ...

  8. 韩顺平php视频笔记79 80 错误和异常处理的机制 错误处理器 错误触发器

    注意:php中 php中错误和异常处理的机制 如果没有错误处理机制怎样? <?php //打开文件- $fp=fopen("aaa.txt","r"); ...

  9. Golang错误和异常处理的正确姿势

    Golang错误和异常处理的正确姿势 错误和异常是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从 ...

最新文章

  1. J2EE(一)——开发简单WEB服务器
  2. 给介绍下对象呗?这回答绝对专业!
  3. Knockout 监控数组对象属性
  4. 我为什么鼓励工程师写blog
  5. Angular library 学习笔记
  6. 一步步编写操作系统 6 启动bochs
  7. creo外观库_Proe软件技巧,颜色库下载及其使用方法揭晓
  8. CCF 201412-1 门禁系统
  9. Linux多进程的应用
  10. 【转】VS2010安装包制作
  11. PyTorch:Encoder-RNN|LSTM|GRU
  12. 【Python游戏】用Python实现一个2048小游戏 | 附带源码
  13. h5 默认为移动端页面_HTML5默认登录页面
  14. 微型计算机系统结构中的总线,微型计算机的总线结构
  15. 一分钟给大量视频褪色并加马赛克
  16. QT 打开PDF文件或图片文件
  17. ubuntu安装搜狗输入法导致系统崩溃
  18. hugo博客html创建目录,1构建个人博客--使用Hugo快速成型(示例代码)
  19. PAT乙级10019题——C语言
  20. 数据分析从零到精通第三课 python自动化和BI数据可视化实战

热门文章

  1. 总结及寒假计划 2019.1
  2. 高精度加法竖式模拟器
  3. [原创]-[UiPath] UiPath中关于日期的操作
  4. 【C/C++】内存对齐(超详细,看这一篇就够了)
  5. 开发应用程序的Andr​​oid - 入门
  6. 莫烦nlp——ELMO一词多义
  7. 占优策略名词解释_《曼昆经济学》名词解释——第17章 寡头
  8. MDD | TO-252封装选型指南
  9. python 爬虫获取书籍名字
  10. 【课程设计】通讯录管理系统(源码 + 详解)