前言

最近项目中有一个功能需要实现:调试模式下, 将所有错误提前输出, 再输出页面内容.

为实现上述功能, 需使用到Exception, Error相关Handler方法, 发现有许多坑, 故写此文与大家分享.

推荐PHP视频教程:https://www.php.cn/course/list/29/type/2.html

主要函数

此篇文章重点关注以下几个函数

1、error_reporting()

2、set_error_handler()

3、set_exception_handler()

4、register_shutdown_function()

5、error_get_last()

以下本文中重点讲解问题列表:

1、error_reporting() error_get_last() 有什么联系?

2、set_error_handler() set_exception_handler() 绑定的handler什么时候才会启动? 它们有什么联系?

3、register_shutdown_function()通常跟Exception/Error有关系么?

解疑:

1. error_reporting()error_get_last() 有什么联系?

link: php.net - error_reporting()
link: php.net - error_get_last()

a:int error_reporting ([ int $level ] )

大家应该再熟悉不过了, 因此不再赘述.

b:array error_get_last ( void )

获取最后发生的错误

通常用来获取PHP运行过程中的Fatal Error错误(PHP 5).

这两个函数在字面上关联性并不强, 但请观察以下代码及输出

<?phperror_reporting(E_ALL & ~E_NOTICE);$a = $b;  //E_NOTICEprint_r(error_get_last());/* output:Array(    [type] => 8    [message] => Undefined variable: b    [file] => /app/t.php    [line] => 3)*/

error_get_last()虽然说明了获取最后发生的错误, 实际上也是如此. 但却没有说明, 被error_reporting()忽略掉的错误是否有可能被获取到, 因此, 当我们使用error_get_last()时需要注意平时忽略掉的错误, 如: E_DEPRECATED

2. set_error_handler()set_exception_handler() 绑定的handler什么时候才会启动? 它们有什么联系?

link: php.net - set_error_handler()
link: php.net - set_exception_handler()

a、mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )

设置用户自定义的错误处理函数

通常在PHP脚本运行过程中, 出现一些非中断性错误时触发.
我们会用这个来记录错误日志或直接输出等操作.

注意:

FALSE: 标准的错误处理依然会被执行(标准错误处理根据 display_errors = true/false 决定是否输出到stderr)

1、参数$error_types大多设定为error_reporting(), 但建议设定为E_ALL, 具体哪些错误需要被处理, 哪些不需要, 在handler内进行判断明显更加灵活.

2、以下级别的错误不能由用户定义的函数来处理: E_ERROR E_PARSEE_CORE_ERRORE_CORE_WARNING E_COMPILE_ERRORE_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT

3、handler被触发后, 并不会中断PHP运行.

4、bool error_handler ( int $errno , string $errstr [, string $errfile [, int $errline [, array $errcontext ]]] )
注意error_handler的返回值:

b、callable set_exception_handler ( callable $exception_handler )

设置用户自定义的异常处理函数
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。

注意:

注意点中2, 3项轻描淡写了一下PHP 5/PHP 7之间的不同却透露出重要的消息(坑..)
PHP 7中, exception_handler 不再只接受Exception了, 并且接收了Error错误.
link: php.net - PHP7 Errors列表

1、exception_handler 调用后异常会中止(脚本终止).

2、PHP 5, PHP 7的exception_handler并不相同.
PHP 5: void handler ( Exception $ex )
PHP 7: void handler ( Throwable $ex )

3、自 PHP 7 以来,大多数错误抛出 Error 异常,也能被捕获。 Error Exception 都实现了 Throwable 接口。

因此, set_error_handler() set_exception_handler() 之间的关系也迎刃而解:

PHP 5:

1、set_error_handler(): 负责非中断行错误.

2、set_exception_handler(): 负责没有被catch的异常(会中断).

3、Fatal Error等: 并不会被两者管理, 正常输出到屏幕上(弊端).

PHP 7:

1、set_error_handler(): 负责非中断行错误.

2、set_exception_handler(): 负责没有被catch的异常, Error(会中断)

3、Fatal Error等: 由set_exception_handler()管理.

3. register_shutdown_function()通常跟Exception/Error有关系么?

link: php.net - register_shutdown_function()

注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用。

根据说明可以得出结论, 它与Exception/Error完全没关系.
提出这个问题, 主要是因为, 在PHP5中Fatal Error并没有明确的接收地点, 所以我们通常配合error_get_last()来接收Fatal Error

<?php register_shutdown_function('shutdown_function');unknown_function();function shutdown_function() {  print_r(error_get_last());}/* output:Array(    [type] => 1    [message] => Uncaught Error: Call to undefined function unknown_function() in /app/t.php:3Stack trace:#0 {main}  thrown    [file] => /app/t.php    [line] => 3)*/

然而随着PHP 7的到来, Error已经可以被set_exception_handler()捕捉了, 再通过error_get_last()就多余了. shutdown中更多的是一些版本冗余的工作.

实例

前言中的需求: 调试模式下, 将所有错误提前输出, 再输出页面内容.
以下是demo, 省去了环境判断(debug环境), 大家可以根据下面这段代码, 了解本文中所说的各种handler的触发和调用情况.

<?php/*要求: 将所有异常打印在屏幕最上方*//* Fatal Error 中断脚本 -> shutdown_handler *///设置错误级别define("END_ERRORS", '--END ERRORS--' . PHP_EOL . PHP_EOL);ini_set('display_errors', true);ini_set('error_reporting', E_ALL & ~E_DEPRECATED);set_error_handler('usr_err_handler', error_reporting()); //注册错误处理函数set_exception_handler('usr_ex_handler'); //注册异常处理函数register_shutdown_function('shutdown_handler');    //注册会在php中止时执行的函数$global_errors = [];    //用于记录所有错误$errnos = [             //错误级别    0 => 'ERROR',//PHP7 ERROR的CODE    1 => 'E_ERROR',//FATAL ERROR(PHP5), E_ERROR    2 => 'E_WARNING',    4 => 'E_PARSE',    8 => 'E_NOTICE',    16 => 'E_CORE_ERROR',    32 => 'E_CORE_WARNING',    64 => 'E_COMPILE_ERROR',    128 => 'E_COMPILE_WARNING',    256 => 'E_USER_ERROR',    512 => 'E_USER_WARNING',    1024 => 'E_USER_NOTICE',    2048 => 'E_STRICT',    4096 => 'E_RECOVERABLE_ERROR',    8192 => 'E_DEPRECATED',    16384 => 'E_USER_DEPRECATED',    30719 => 'E_ALL',];function reset_errors(){    global $global_errors;    $global_errors = [];}function get_errnostr($errno){    global $errnos;    return $errnos[$errno];}function set_errnos($errno, $errstr){    global $global_errors;    $global_errors[] = [        'errno' => $errno,        'errnostr' => get_errnostr($errno),        'errstr' => $errstr,    ];}function print_errors($prefix){    global $global_errors;    foreach ($global_errors as $err) {//由于handler中依然有可能有error 因此放最后        printf("[%s]: %s, %d, %s\n",            $prefix, $err['errnostr'], $err['errno'], $err['errstr']);    }}//用户异常处理函数 (进来就中断脚本) PHP5只有Exception进来   PHP7Error和Exception//PHP7中 void handler (Throwable $ex) 可捕获Error和Exception两种异常, 暂不管//http://php.net/manual/en/language.errors.php7.php PHP7 Error阅读//内部如果有Error则触发Error函数, 再回到错误行继续执行function usr_ex_handler($ex){    $content = ob_get_clean();  //让Exception/Error提前展示    print_errors('EX ERROR');    reset_errors();    $errnostr = get_errnostr($ex->getCode());    $errno = $ex->getCode();    $errstr = $ex->getMessage();    if ($ex instanceof Exception) {        printf("[EXCEPTION]: %s, %d, %s\n", $errnostr, $errno, $errstr);    } else {//针对PHP7  $ex instanceof Error        printf("[EX FATAL ERROR]: %s, %d, %s\n", $errnostr, $errno, $errstr);    }    //由于handler中依然有可能有error 因此放最后    print_errors('EX ERROR');    reset_errors();    echo END_ERRORS;    echo $content;    return;}//用户错误处理函数//E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING不能被用户处理function usr_err_handler($errno, $errstr, $errfile, $errline, $errcontext){    set_errnos($errno, $errstr);    return true;    //如果函数返回 FALSE,标准错误处理处理程序将会继续调用。}//用户PHP终止函数function shutdown_handler(){    $content = ob_get_clean();  //让Exception/Error提前展示    $err = error_get_last();//检查一下是否有遗漏掉的错误 php5 fatal error    if ($err['type'] & error_reporting()) {        set_errnos($err['type'], $err['message']);    }    print_errors('ST ERROR');    reset_errors();    echo $content;}ob_start();echo 'Main function...', PHP_EOL;//搞事情//throw new Exception('这是一个异常');trigger_error('这是一个用户error');//E_USER_NOTICEif (version_compare(PHP_VERSION, '7.0.0') >= 0) {    mcrypt_encrypt();//E_WARNING, E_DEPRECATED} else {    mysql();}unknown_function(); //fatal error$content = ob_get_clean();//优先输出错误print_errors('MA ERROR');if (!empty($global_errors)) {    echo END_ERRORS;}reset_errors();//输出正文内容echo $content;

更多相关文章

  1. 关于php mysqli函数的总结与详细介绍(二)
  2. 关于PHP Mysqli函数的一些整理以及详细介绍(一)
  3. 关于php mysqli函数的一些总结和实例(四)
  4. 关于php mysqli函数的一些总结和详细介绍(五)
  5. 关于php mysqli函数的一些总结及实例(三)
  6. php中函数的定义
  7. php函数原理
  8. 关于PHP中异常错误的处理详细介绍
  9. php中if函数用法

随机推荐

  1. android 屏幕监听滑动事件
  2. Android Studio第一章Big task1 - Experi
  3. Android Wear Preview - UI
  4. Android的之我见
  5. android中的后退键——onBackPressed()的
  6. 使用android快速开发框架afinal 开发andr
  7. Android如何为kernel添加一个驱动
  8. android:process=":remote" .
  9. android学习日记01-搭配开发环境
  10. Android 各版本Gradle离线下载