ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
在PHP 7之前,**Exception**未实现[Throwable](https://www.php.net/manual/en/class.throwable.php)接口。 ``` Exception { /* 属性 */ protected string $message ;//异常消息内容 protected int $code ;//异常代码 protected string $file ;//抛出异常的文件名 protected int $line ;//抛出异常在该文件中的行号 /* 方法 */ public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] ) final public getMessage ( void ) : string//获取异常消息内容 final public getPrevious ( void ) : Throwable//返回异常链中的前一个异常 final public getCode ( void ) : int//获取异常代码 final public getFile ( void ) : string// 创建异常时的程序文件名称 final public getLine ( void ) : int//获取创建的异常所在文件中的行号 final public getTrace ( void ) : array//获取异常追踪信息 final public getTraceAsString ( void ) : string// 获取字符串类型的异常追踪信息 public __toString ( void ) : string//将异常对象转换为字符串 final private __clone ( void ) : void//异常克隆 } ``` php7+ ``` Exception implements Throwable{ /* 属性 */ protected string $message ;//异常消息内容 protected int $code ;//异常代码 protected string $file ;//抛出异常的文件名 protected int $line ;//抛出异常在该文件中的行号 /* 方法 */ public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] ) final public getMessage ( void ) : string//获取异常消息内容 final public getPrevious ( void ) : Throwable//返回异常链中的前一个异常 final public getCode ( void ) : int//获取异常代码 final public getFile ( void ) : string// 创建异常时的程序文件名称 final public getLine ( void ) : int//获取创建的异常所在文件中的行号 final public getTrace ( void ) : array//获取异常追踪信息 final public getTraceAsString ( void ) : string// 获取字符串类型的异常追踪信息 public __toString ( void ) : string//将异常对象转换为字符串 final private __clone ( void ) : void//异常克隆 } ``` 所以在php7+中,如果既想捕获异常有需要捕获错误$e是Error和Exception的基类Throwable ~~~ try{ ... }catch ( Throwable $e){ echo $e->getCode().'<br/>'; echo $e->getMessage().'<br/>'; echo $e->getLine().'<br/>'; echo $e->getFile().'<br/>'; } ~~~ 除了Exception之外还有Error类,[这个网站](https://3v4l.org/sDMsv)列出了各个版本的错误类 ***** ## 设置顶层异常处理器 (Top Level Exception Handler) set_exception_handler() 函数可设置处理所有未捕获异常的用户定义函数。(即没有用 try/catch 块来捕获的异常或者try/catch没有捕获到的) ~~~ function myException($exception) { echo "<b>Exception:</b> " , $exception->getMessage(); } set_exception_handler('myException'); //使用: throw new Exception('Uncaught Exception occurred'); ~~~ ## 设置顶层错误处理器set_error_handler 设置用户自定义的错误处理函数 E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING是不会被这个句柄处理的,也就是会用最原始的方式显示出来。不过出现这些错误都是编 译或PHP内核出错,在通常情况下不会发生。(也就是说如果在脚本执行前发生错误,由于在那时自定义程序还没有注册,因此就不会用到这个自定义错误处理程序) ``` //自定义的错误处理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "错误编号errno: $errno<br>"; echo "错误信息errstr: $errstr<br>"; echo "出错文件errfile: $errfile<br>"; echo "出错行号errline: $errline<br><br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注册错误处理方法来处理所有错误 echo $foo['bar']; // 由于数组未定义,会产生一个notice级别的错误 //人为触发错误 trigger_error('人为触发一个错误', E_USER_ERROR); trigger_error('人为触发一个错误', E_ERROR); ``` 这两个错误都会被自定义的函数捕获 ``` 错误编号errno: 8 错误信息errstr: Undefined variable: foo 出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php 出错行号errline: 13 错误编号errno: 256 错误信息errstr: 人为触发一个错误 出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php 出错行号errline: 16 错误编号errno: 256 错误信息errstr: 人为触发一个错误 出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php 出错行号errline: 17 ``` 7之前一大弊端,某些错误是处理不了的 如果错误发生在脚本执行之前(比如编译、文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册。 ``` //自定义的错误处理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "错误编号errno: $errno<br>"; echo "错误信息errstr: $errstr<br>"; echo "出错文件errfile: $errfile<br>"; echo "出错行号errline: $errline<br><br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注册错误处理方法来处理所有错误 foobar(3, 5); //调用未定义的方法将会产生一个Error级别的错误 ``` 结果未被自定义的函数捕获 ``` Fatal error: Call to undefined function foobar() in D:\phpstudy_pro\WWW\www.test.com\error.php on line 19 ``` 使用set_error_handler()后,error_reporting ()将会失效。也就是所有的错误(除上述的错误)都会交给自定义的函数处理。 例子:三种用法 ``` // 三种方法如下: 1: set_error_handler('NonClassFunction'); // 直接转到一个普通的函数 NonClassFunction 2: set_error_handler(array('CallbackClass', 'StaticFunction')); // 转到 CallbackClass 类下的静方法 StaticFunction 3: $o =new CallbackClass(); set_error_handler(array($o, 'CallbackFunction')); // 转到类的构造函数, //可以这样操作 class CallbackClass { function CallbackClass() { set_error_handler(array(&$this, 'CallbackFunction')); // the & is important } function CallbackFunction() { // refers to $this } } new CallbackClass()-> CallbackClass(); ``` 例子2:通过触发错误并以用户自定义的程序来进行内部异常的处理 ``` `// error handler function function myErrorHandler($errno, $errstr, $errfile, $errline) {     if (!(error_reporting() & $errno)) {         // This error code is not included in error_reporting, so let it fall //除非回调函数返回了FALSE,error_reporting()设置将不会起到作用         //返回false时,转到标准PHP错误处理程序         return false;     }     switch ($errno) {     case E_USER_ERROR:         echo "<b>My ERROR</b> [$errno] $errstr<br />\n";         echo "  Fatal error on line $errline in file $errfile";         echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";         echo "Aborting...<br />\n";         exit(1);         break;     case E_USER_WARNING:         echo "<b>My WARNING</b> [$errno] $errstr<br />\n";         break;     case E_USER_NOTICE:         echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";         break;     default:         echo "Unknown error type: [$errno] $errstr<br />\n";         break;     }     /* Don't execute PHP internal error handler */     return true; } // 测试错误处理的函数 function scale_by_log($vect, $scale) {     if (!is_numeric($scale) || $scale <= 0) {         trigger_error("log(x) for x <= 0 is undefined, you used: scale = $scale", E_USER_ERROR);     }     if (!is_array($vect)) {         trigger_error("Incorrect input vector, array of values expected", E_USER_WARNING);         return null;     }     $temp = array();     foreach($vect as $pos => $value) {         if (!is_numeric($value)) {             trigger_error("Value at position $pos is not a number, using 0 (zero)", E_USER_NOTICE);             $value = 0;         }         $temp[$pos] = log($scale) * $value;     }     return $temp; } // set to the user defined error handler $old_error_handler = set_error_handler("myErrorHandler"); // trigger some errors, first define a mixed array with a non-numeric item echo "vector a\n"; $a = array(2, 3, "foo", 5.5, 43.3, 21.11); print_r($a); // now generate second array echo "----\nvector b - a notice (b = log(PI) * a)\n"; /* Value at position $pos is not a number, using 0 (zero) */ $b = scale_by_log($a, M_PI); print_r($b); // this is trouble, we pass a string instead of an array echo "----\nvector c - a warning\n"; /* Incorrect input vector, array of values expected */ $c = scale_by_log("not array", 2.3); var_dump($c); // NULL // this is a critical error, log of zero or negative number is undefined echo "----\nvector d - fatal error\n"; /* log(x) for x <= 0 is undefined, you used: scale = $scale" */ $d = scale_by_log($a, -2.5); var_dump($d); // Never reached ?>` 以上例程的输出类似于: vector a Array ( [0] => 2 [1] => 3 [2] => foo [3] => 5.5 [4] => 43.3 [5] => 21.11 ) ---- vector b - a notice (b = log(PI) * a) <b>My NOTICE</b> [1024] Value at position 2 is not a number, using 0 (zero)<br /> Array ( [0] => 2.2894597716988 [1] => 3.4341896575482 [2] => 0 [3] => 6.2960143721717 [4] => 49.566804057279 [5] => 24.165247890281 ) ---- vector c - a warning <b>My WARNING</b> [512] Incorrect input vector, array of values expected<br /> NULL ---- vector d - fatal error <b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br /> Fatal error on line 35 in file trigger_error.php, PHP 5.2.1 (FreeBSD)<br /> Aborting...<br /> ``` `set_error_handler`用来捕捉`trigger_error` `set_exception_handler`用来捕捉`throw new Exception` 这是设定一个全局的异常处理函数,在异常没有被 try catch 处理时系统会将异常交于此函数处理 异常抛出但不使用 try catch 处理,系统会产生一个致命错误导致脚本退出执行,set\_exception\_handler 只是将没有被 try catch 处理的异常进行捕捉,自定义一些友好的信息输出,并不能阻止致命错误的产生,脚本依然会退出执行。 设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在`exception_handler`调用后异常会中止 set\_error\_handler 则会将用户在脚本中触发的错误拦截下来进行处理,而不再提交给系统,但它不会自动识别错误级别,我们需要手动判断是否为 ERROR 级的进行退出,还是 WARNING 或 NOTICE 级的进行提示后继续执行。 ## **thinkphp5** 另外thinkphp继承值Exception ``` namespace think; class Exception extends \Exception { /** * @var array 保存异常页面显示的额外 Debug 数据 */ protected $data = []; /** * 设置异常额外的 Debug 数据 * 数据将会显示为下面的格式 * * Exception Data * -------------------------------------------------- * Label 1 * key1 value1 * key2 value2 * Label 2 * key1 value1 * key2 value2 * * @access protected * @param string $label 数据分类,用于异常页面显示 * @param array $data 需要显示的数据,必须为关联数组 * @return void */ final protected function setData($label, array $data) { $this->data[$label] = $data; } /** * 获取异常额外 Debug 数据 * 主要用于输出到异常页面便于调试 * @access public * @return array */ final public function getData() { return $this->data; } } ``` **thinkphp5是怎么实现的呢** 在base.php文件中,用一句代码\think\Error::register();实现错误和异常处理机制的注册。 // 注册错误和异常处理机制 ``` \think\Error::register(); ``` 打开library/think/Error.php,register函数如下: ``` /** * 注册异常处理 * @access public * @return void */ public static function register() { error_reporting(E_ALL); // 设置错误级别 set_error_handler([__CLASS__, 'appError']); // 设置错误处理方式__CLASS__:获取当前的类名, set_exception_handler([__CLASS__, 'appException']); // 设置异常处理 register_shutdown_function([__CLASS__, 'appShutdown']); // 注册关闭函数 } ``` >[danger]注:set_error_handler("myError") 、set_exception_handler("myException")、register_shutdown_function("myShutdown")不仅可以接受函数,还可以接受类的方法(公开的静态方法及公开的非静态方法),但需要以数组形式传递,数组的第一个值为“类名”,第二个值为“方法名”。 **1、error_reporting()** error_reporting() 函数设置当前脚本的错误报告级别,规定报告哪种错误。E_ALL为显示所有的错误和警告信息,类似的级别还有E_ERROR—运行时致命的错误、E_WARNING—运行时非致命的错误、E_NOTICE-—运行时的通知。 **2、set_error_handler()** set_error_handler指定appError来处理系统错误,捕获错误后把错误以异常的形式抛出。当程序出现错误的时候自动调用appError函数,该函数实例化了一个PHP自带的错误异常类ErrorException,如果符合异常处理的,就将错误信息以异常的形式抛出来,否则将错误信息写入日志中。 **3、set_exception_handler()** set_exception_handler指定appException来处理用户抛出的异常,如果不是异常,就实例化ThrowableError类,将异常包装起来并抛出。 **4、register_shutdown_function()** register_shutdown_function指定appShutdown处理超时异常。 注册一个会在php中止时执行的函数 注册一个`callback`,它会在脚本执行完成或者`exit()`后被调用。 到此,框架的错误和异常处理机制就注册完了。 在注册错误和异常处理机制中,都是使用getExceptionHandler方法来获取异常处理的实例。 ``` /** * 获取异常处理的实例 * @access public * @return Handle */ public static function getExceptionHandler() { static $handle; if (!$handle) { // 异常处理 handle $class = Config::get('exception_handle'); if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle") ) { $handle = new $class; } else { $handle = new Handle; if ($class instanceof \Closure) { $handle->setRender($class); } } } return $handle; } ``` 可以看到 `$class = Config::get('exception_handle');` 这句代码,也就是可以通过修改配置参数来指定新的异常处理对象。 如果想使用自定义的错误和异常处理机制,首先在application/config.php文件中找到exception_handle的配置: ``` // 异常处理handle类 留空则系统默认使用 \think\exception\Handle 'exception_handle' => '', ``` 将其改为自己定义的 完整Exception类(包含命名空间): ``` 'exception_handle' => 'app\lib\exception\ExceptionHandler', ``` 再按照配置新建文件,为application/lib/exception/ExceptionHandler.php,重写这个Handle方法即可: ``` class ExceptionHandler extends Handle { public function render(Exception $e) { return json('这里是自定义的错误'); //return parent::render($e); // TODO: Change the autogenerated stub } } ``` 这样就可以使用自定义的错误和异常处理机制了。