## 1、PHP中异常的独特性 > PHP中的异常的独特性,即PHP中的异常不同于主流语言C++、java中的异常。在Java中,异常是唯一的错误报告方式,而在PHP中却不是这样,而是把所有不正常的情况都视作了错误进行处理。这两种语言对异常和错误的界定存在分歧。什么是异常什么是错误,两种语言的设计者存在不同的观点。 #### PHP中的异常 是程序在运行中出现不符合预期的情况及与正常流程不同的状况。一种不正常的情况,按照正常逻辑本不该出的错误,但仍然会出现的错误,这是属于逻辑和业务流程的错误,而不是编译或者语法上的错误。 #### PHP中的错误 是属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice都是错误,只是他们的级别不同而已,并且错误是不能被try-catch捕获的。 在PHP中遇到任何自身错误都会触发一个错误,而不是抛出异常。PHP一旦遇到非正常代码,通常都会触发错误,而不是抛出异常。因此,如果想要使用异常处理不可预料的问题,是办不到的。 **例子:** ``` <?php try { echo 1 / 0; } catch (Exception $e) { echo $e->getMessage(); } ``` **执行:** ![](https://img.kancloud.cn/58/93/5893f7465b4116795f795c189646333e_709x99.png) **结果:** 此时出现了一个警告级别的错误,程序终止。 **结论:** >[warning] PHP通常是无法自动捕获有意义的异常,它把所有不正常的情况都视作了错误,你要想捕获异常就得使用if....else结构,保证代码是正常的,然后判断进行手动抛出异常。 ## 2、PHP中的错误级别 PHP中的异常机制是不足的,绝大多数情况下无法自动抛出异常,必须使用`if....else`语句先进行判断,在进行手动抛出异常。 手动抛出异常的意义不大,是已经预料到的错误,这种方式将会使你陷入纷繁复杂的业务逻辑判断和处理中。 因此我们可以通过一些特殊的函数来自定义错误处理函数,来接管PHP原生的错误处理函数,然后再进行抛出异常。 接下来我们需要了解PHP中的一些错误。 #### 错误显示控制 ``` 全局:php.ini中设置display_error = on/off; 局部:ini_set("display_error", true/false); ``` >[danger] **问题:** php.ini中display_errors = Off失效的解决 PHP设置文件`php.ini`中明明已经设置`display_errors = Off`,但是在运行过程中,网页上还是会出现错误信息。  > **解决:** 经 查`log_errors= On`,据官方的说法,当这个`log_errors`设置为On,那么必须指定`error_log`文件,如果没指定或者指定的文件没有权限写入,那么照样会输 出到正常的输出渠道,那么也就使得`display_errors `这个指定的Off失效,错误信息还是打印了出来。于是将log_errors = Off,问题就解决了。 #### 选择性设置显示错误 ``` 全局:error_reporting = E_ALL | E_STRICT.... 局部:error_reporting(E_ERROR | E_WARNING | E_PARSE) ``` ``` E_ERROR 致命的运行错误。错误无法恢复,暂停执行脚本。 E_WARNING 运行时警告(非致命性错误)。非致命的运行错误,脚本执行不会停止。 E_PARSE 编译时解析错误。解析错误只由分析器产生。 E_NOTICE 运行时提醒(这些经常是你代码中的bug引起的,也可能是有意的行为造成的。) E_CORE_ERROR PHP 启动时初始化过程中的致命错误。 E_CORE_WARNING PHP启动时初始化过程中的警告(非致命性错)。 E_COMPILE_ERROR 编译时致命性错。这就像由Zend脚本引擎生成了一个E_ERROR。 E_COMPILE_WARNING 编译时警告(非致性错)。这就像由Zend脚本引擎生成了E_WARNING警告。 E_USER_ERROR 自定义错误消息。像用PHP函数trigger_error(程序员设置E_ERROR) E_USER_WARNING 自定义警告消息。像用PHP函数trigger_error(程序员设的E_WARNING警告) E_USER_NOTICE 自定义的提醒消息。像由使用PHP函数trigger_error(程序员E_NOTICE集) E_STRICT 编码标准化警告。允许PHP建议修改代码以确保最佳的互操作性向前兼容性。 E_RECOVERABLE_ERROR 开捕致命错误。像E_ERROR,但可以通过用户定义的处理捕获(又见set_error_handler()) E_ALL 所有的错误和警告(不包括 E_STRICT) (E_STRICT will be part of E_ALL as of PHP 6.0)14 16384 E_USER_DEPRECATED 15 30719 E_ALL ``` 一共有十五种,使用二进制代替,0000 0000 0000 0011 表示 E_ERROR和E_WARNING 例如: ``` error_reporting(3);  //只显示E_ERROR和E_WARNING错误 error_reporting(-1);  //只显示所有错误误 ``` >[warning] 注意 在开发阶段通常是显示所有错误,方便解决问题; 在生产阶段通常是隐藏错误,并将需错误记录到文件中(错误日志); `php.ini`中设置: ``` log_error = on/off;  //记录、不记录 error_log = php_errors.log  //设定错误日志文件(此时没有给定路径则在当前位置生成) ``` 还可以通过ini_set()进行设置。 ## 3、PHP中的异常处理 #### 3.1、set_error_handler(error_function, error_type) 使用`set_error_handler(error_function, error_type)`函数设置自定义错误处理函数,接管原错误处理函数。 ``` <?php class ErrorClass { // 必须静态public方法 public static function myError($errorNum, $errorMsg, $errorFile, $errorLine) { echo 'set_error_handler: ' . $errorNum . ': ' . $errorMsg . ' [ in ] ' . $errorFile . ' [ on ] ' . $errorLine . ' line '."\r\n"; } } set_error_handler(['ErrorClass', 'myError']); try { $a = 5 / 0; } catch (Exception $e) { echo "这里抛出异常 \r\n"; } ``` 访问结果 ``` bash-5.0# php exception-01.php set_error_handler: 2: Division by zero [ in ] /var/www/exception-01.php [ on ] 20 line ``` 由结果可知:我们自定义的`myError`方法截取了错误,此时我们可以主动的处理这些错误,抛出相应的异常。 >[warning] 但需要注意以下两点: 第一,如果存在该方法,相应的error_reporting()就不能在使用了。它将接管PHP原生错误处理函数,即所有的错误都会交给自定义的函数处理。 第二,此方法不能处理以下级别的错误: ``` E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING, ``` `set_error_handler() `函数所在文件中产生的`E_STRICT`,该函数只能捕获系统产生的一些`Warning、Notice`级别的错误。 >[danger] 注意: 如果在脚本执行前发生错误,由于此时自定义的错误处理函数还没有注册,因此就用不到这个自定义错误处理程序。 #### 3.2、register_shutdown_function(exception_function) 捕获PHP的错误:Fatal Error、Parse Error等,这个方法是PHP脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用。 通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:`error_get_last();`这个函数可以拿到本次执行产生的所有错误。`error_get_last();`返回的信息: ``` [type] - 错误类型 [message] - 错误消息 [file] - 发生错误所在的文件 [line] - 发生错误所在的行 ``` >[warning] 注意:当`parse-time(解析)`出错时是不会调用本函数的。只有在`run-time(运行)`出错的时候,才会调用本函数。即需要成功注册此函数才能使用。 **demo-01 解析错误不会执行** ``` echo 'string' register_shutdown_function('myshutdownfunc'); function myshutdownfunc() { if ($error = error_get_last()) { echo "<pre>"; print_r($error); echo "</pre>"; die; } } ``` 执行结果 ``` bash-5.0# php exception-01.php PHP Parse error: syntax error, unexpected 'register_shutdown_function' (T_STRING), expecting ',' or ';' in /var/www/exception-01.php on line 33 ``` **demo-02 执行错误会执行** ``` try { $a = 5 / 0; } catch (Exception $e) { echo "这里抛出异常 \r\n"; } register_shutdown_function('myshutdownfunc'); function myshutdownfunc() { if ($error = error_get_last()) { echo "<pre>"; print_r($error); echo "</pre>"; die; } } ``` 执行结果 ``` bash-5.0# php exception-01.php PHP Warning: Division by zero in /var/www/exception-01.php on line 26 PHP Stack trace: PHP 1. {main}() /var/www/exception-01.php:0 <pre>Array ( [type] => 2 [message] => Division by zero [file] => /var/www/exception-01.php [line] => 26 ) ``` #### 3.3、set_exception_handler(exception_function) **参数:** `error_function` 必需。规定未捕获的异常发生时调用的函数。该函数必须在调用 set\_exception\_handler() 函数之前定义。 这个异常处理函数需要需要一个参数,即抛出的 exception 对象。 **作用** * `set_exception_handler() `函数设置用户自定义的异常处理函数。 * 该函数用于创建运行时期间的用户自己的异常处理方法。 * 该函数会返回旧的异常处理程序,若失败,则返回 null。 >[danger] 提示:在这个异常处理程序被调用后,脚本会停止执行。 ``` function exception_handler($exception) { echo "Uncaught exception: ", $exception->getMessage(), "\n"; } set_exception_handler('exception_handler'); throw new Exception('Uncaught Exception'); echo "Not Executed 这里不会执行的\n"; ``` 执行结果 ``` bash-5.0# php exception-01.php Uncaught exception: Uncaught Exception ``` https://github.com/xx19941215/light-tips/issues/11 ## 小结 * **PHP把所有不正常的情况都视作了错误进行处理** * **PHP中的错误:** 是属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。**并且错误是不能被try-catch捕获的。** * **PHP中的异常:** 是属于逻辑和业务流程的错误,而不是编译或者语法上的错误。是程序在运行中出现不符合预期的情况及与正常流程不同的状况。一种不正常的情况,按照正常逻辑本不该出的错误,但仍然会出现的错误。 * 可以设置自定义错误处理函数`set_error_handler()`,接管原错误处理函数。如果存在该方法,相应的`error_reporting()`就不能在使用了。它将接管PHP原生错误处理函数,即所有的错误都会交给自定义的函数处理。该函数只能捕获系统产生的一些Warning、Notice级别的错误。 * PHP脚本执行结束前最后一个调用的函数`register_shutdown_function()`。比如脚本错误、die()、exit、异常、正常结束都会调用。 * `set_exception_handler()` 函数设置用户自定义的异常处理函数。该函数用于创建运行时期间的用户自己的异常处理方法。**在这个异常处理程序被调用后,脚本会停止执行(一旦抛出异常则后面的语句将不会执行)。**