讨论php的错误和异常处理机制-PHP7

资源魔 33 0

申明: 本文彩用 CC BY-NC-ND 4.0 受权。

原先的 PHP 只有谬误不异样。看一些老的文档你能看到很多谬误输入是间接 echo html 标签的。而古代一点的框架早曾经包裹好了所有,间接抛出异样就能够有比拟美丽的谬误显示页面,比方 rails 的 better errors。当然,PHP 的古代框架也曾经做的没有错了,比方 laravel。但是我司今朝仍是用 codeigniter 2,它的谬误以及异样解决还比拟粗陋。借着晋级到 PHP7 的契机梳理了一下 PHP 的谬误以及异样解决的机制。

保举教程:《PHP教程》

PHP 的谬误以及异样

PHP5 曾经完成了异样的解决,这以及其余言语差异没有年夜,无非就是 try, catch, uncaught,按下没有表,先说谬误。

PHP 的谬误

除了了异样 PHP5 常见的就是抛犯错误。你能够正在民间文档找到一切的谬误的界说,这些谬误能够大抵分为 WARNING, ERROR(fatal error), NOTICE 等1。PHP的谬误机制总结一文中给出了每一种谬误呈现的场景。

E_DEPRECATED(8192) 运转时告诉,启用后将会对正在将来版本中可能无奈失常工作的代码给出正告。

E_USER_DEPRECATED(16384) 是由用户本人正在代码中应用PHP函数 trigger_error() 来孕育发生的

E_NOTICE(8) 运转时告诉。示意剧本遇到可能会体现为谬误的状况

E_USER_NOTICE(1024) 是用户本人正在代码中应用PHP的trigger_error() 函数来孕育发生的告诉信息

E_WARNING(2) 运转时正告 (非致命谬误)

E_USER_WARNING(512) 用户本人正在代码中应用PHP的 trigger_error() 函数来孕育发生的

E_CORE_WARNING(32) PHP初始化启动进程中由PHP引擎外围孕育发生的正告

E_COMPILE_WARNING(128) Zend剧本引擎孕育发生编译时正告

E_ERROR(1) 致命的运转时谬误

E_USER_ERROR(256) 用户本人正在代码中应用PHP的 trigger_error()函数来孕育发生的

E_CORE_ERROR(16) 正在PHP初始化启动进程中由PHP引擎外围孕育发生的致命谬误

E_COMPILE_ERROR(64) Zend剧本引擎孕育发生的致命编译时谬误

E_PARSE(4) 编译时语法解析谬误。解析谬误仅仅由剖析器孕育发生

E_STRICT(2048) 启用 PHP 对代码的修正倡议,以确保代码具备最好的互操作性以及向前兼容性

E_RECOVERABLE_ERROR(4096) 可被捕获的致命谬误。 它示意发作了一个可能十分风险的谬误,然而尚未招致PHP引擎处于没有稳固的状态。 假如该谬误不被用户自界说句柄捕捉 (参见 set_error_handler() ),将成为一个 E_ERROR  从而剧本会终止运转。

E_ALL(30719) 一切谬误以及正告信息(手册上说没有蕴含E_STRICT, 通过测试实际上是蕴含E_STRICT的)。

常见的有:

<?php
// E_ERROR
nonexist(); // PHP Fatal error:  Call to undefined function nonexist()
throw new Exception(''); // 未捕捉异样也是 fatal error

// E_NOTICE
$a = $b; //  PHP Notice:  Undefined variable
$a = []; $a[2]; // PHP Notice:  Undefined offset: 2

// E_WARNNING
require 'nonexist.php' // warning and fatal error

因为汗青缘由,这个老旧的 ci2 框架有很多没有正当之处,比方会读取没有存正在的 log 文件;咱们对 PHP 也有一些没有标准的应用,比方:

<?php
$req = [];
$user_id = $req['user_id']; // PHP error:  Undefined offset
if (null === $user_id) { /* do something */}

咱们的代码很多中央较为依赖这类猎取没有存正在 key 失去 null 的体现,而每一次这样应用都是会有一个 E_NOTICE 谬误的。尽管能够经过 array_exists 来做 if else,但究竟结果比拟费事。PHP7 之后能够经过数据构造插件来应用 Map, Set, Vector 等明白的数据构造,从而较好的处理这个成绩。

PHP 对谬误的解决

假如不做任何设置装备摆设,PHP 的谬误是会间接打印进去的。陈旧的 PHP 使用也的确有这么做的。但古代使用显然不克不及这样,古代使用的谬误应该遵照一下规定2

肯定要让 PHP 陈诉谬误;

正在开发环境中要显示谬误;

正在消费环境中不克不及显示谬误;

正在开发以及消费环境中都要记载谬误。

正在消费环境下,谬误不克不及间接打印进去,应该记到 log 文件中,并前往用户一个笼统的谬误信息。set_error_handler 函数就是设置用户自界说的谬误解决函数,以解决剧本中呈现的谬误。咱们能够正在这个函数中将谬误信息打到 log 文件中,并对立前往谬误信息。

原本这个函数是搭配 trigger_error 函数应用的。用户经过 trigger_error 孕育发生 error,而后用 error_handler 来解决谬误。只是正在这类场景下往往「异样」更好用,以是这么用的其实不多。

正在前述的零碎自带的 16 种谬误中,有一局部相称首要的谬误其实不能被 error_handler 捕捉3

如下级此外谬误不克不及由用户界说的函数来解决: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING,以及正在挪用 set_error_handler() 函数所正在文件中孕育发生的年夜少数 E_STRICT。

这些谬误将无奈记载上去,同时也没有不便对立解决4。正在 PHP7 以前的 PHP 版本一个很年夜的痛点就是:发作了 E_ERROR 谬误,无奈捕捉,招致数据库的事务无奈回滚造成数据纷歧致5

另一个需求留意的是, error_handler 解决终了,剧本将会持续执行发作谬误的后一行。正在某些状况下,你可能心愿遇到某些谬误能够中缀剧本的执行。正在民间文档中已阐明,

同时留意,正在需求时你有责任应用 die()。 假如谬误解决顺序前往了,剧本将会持续执行发作谬误的后一行。

也就是说,某些状况下,咱们解决完 E_WARNING 之后,需求实时加入剧本(即 die() 或许 exit())。

PHP 异样

异样是对顺序谬误的一种优秀的解决形式,较于谬误,异样的优点是默许打印挪用栈,便于调试,可控等,能够参考一下鸟哥的文章咱们何时应该应用异样,明晰的点清楚明了谬误码以及异样的优缺陷。

对异样的解决也要遵照前述的谬误解决规定2。正在咱们的一样平常开发中,不成能保障能够 catch 一切的异样,而未被 catch 的异样将以 fatal error 的方式中缀剧本的执行并输入谬误信息。以是要借助 set_exception_handler,对立解决一切未被 catch 的异样。咱们能够像 error_handler 那样,正在 exception_handler 中解决 log,将数据库的事务回滚。

后面提到,error_handler 需求正在须要的时分手动中缀剧本, PHP 文档中给出的一种理论是,正在 error_handler 中 throw ErrorException,代码示例以下:

<?php
function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
    throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");

/* Trigger exception */
strpos();

这样但凡没有想疏忽的 error,城市以 Uncaught ErrorException 的方式前往并中缀剧本。

PHP 异样机制

鸟哥经过一个例子解说了 PHP 的异样的解决机制,正在这里转述一下。

<?php
function onError($errCode, $errMesg, $errFile, $errLine) {
    echo "Error Occurred\n";
    throw new Exception($errMesg);
}
 
function onException($e) {
    echo '********exception: ' . $e->getMessage();
}
 
set_error_handler("onError");
 
set_exception_handler("onException");

require("nonexist.php");

其运转后果为

  1. Error Occurred
  2. PHP Fatal error

而 onException 并无执行到,阐明正在 error_handler 中 throw exception 没有会被 exception_handler 截获。

require 没有存正在的文件会抛出两个谬误,

  1. WARNING : 正在PHP试图关上这个文件的时分抛出
  2. E_COMPILE_ERROR : 从PHP关上文件的函数前往失败当前抛出

PHP 中的异样解决机制以下:

而PHP正在遇到 Fatal Error 的时分,会间接 zend_bailout,而 zend_bailout 会招致顺序流程间接跳过下面代码段,也能够了解为间接 exit 了(longjmp),这就招致了 user_exception_handler 不机会发作作用。

PHP 谬误分类

综上所述,正在 PHP 中,谬误以及异样能够分为如下 3 个种别:异样,可截获谬误,不成截获谬误。异样以及可截获谬误尽管机理没有同,但能够当作是同一种解决形式,而不成截获谬误是另外一种,是一种较为辣手的谬误类型。即刻将会讲到,PHP7 中的 fatal error 是一种承继自 Throwable 的 Error,是能够被 try catch 住的。经过这一形式 PHP7 处理了这一难题。

PHP7 的谬误以及异样

PHP 7 扭转了年夜少数谬误的陈诉形式。没有同于传统(PHP 5)的谬误陈诉机制,如今年夜少数谬误被作为 Error 异样抛出(正在 PHP7 中,只有 fatal error 以及 recoverable error 抛出异样,其余 error 比方 warning 以及 notice 的体现没有变6)。PHP7 中的 Error 以及 Exception 的关系如图 6

interface Throwable
    |- Exception implements Throwable
        |- ...
    |- Error implements Throwable
        |- TypeError extends Error
        |- ParseError extends Error
        |- ArithmeticError extends Error
            |- pisionByZeroError extends ArithmeticError
        |- AssertionError extends Error

值患上留意的是,Error 类体现上以及 Exception 根本分歧,能够像 Exception 异样同样被第一个婚配的 try / catch 块所捕捉,假如不婚配的 catch 块,则挪用异样解决函数(事前经过 set_exception_handler() 注册7)进行解决。 假如还没有注册异样解决函数,则依照传统形式解决,被陈诉为一个致命谬误(Fatal Error)。但并不是承继自 Exception 类(要思考到以及 PHP5 的兼容性),以是不克不及用 catch (Exception $e) { ... } 来捕捉,而需求应用 catch (Error $e) { ... },当然,也能够应用 set_exception_handler 来捕捉。

然而,用户不克不及本人界说类完成 Throwable,这是为了保障只有 Exception 以及 Error 才能够抛出。

PHP7 的 ERROR 解决

PHP7 中的 fatal error 会抛出 Error,且能够被失常 catch 到:

<?php
$a = 1;
try {
  $a->nonexist();
} catch (Error $e) {
  // Handle error
}

也有些谬误场景下会抛出愈加具体的谬误,比方:

<?php
// TypeError
function test(int $i) {
  echo $i;
}
try {
  test('test');
} catch (TypeError $e) {
  // Handle error
}

// ParseError
try{
  eval('i=1;');
} catch (ParseError $e) { 
  echo $e->getMessage(), "\n";
}

// ArithmeticError
try {
    $value = 1 << -1;
} catch (ArithmeticError $e) {
    echo $e->getMessage(), "\n";
}

// pisionByZeroError
try {
    $value = 1 % 0;
} catch (pisionByZeroError $e) {
    echo $e->getMessage(), "\n";
}

Error 以及 Exception 的抉择

当需求自界说解决谬误的时分,应该抉择承继 Error 仍是 Exception 呢?

咱们留意到,PHP7 中是将已经的 fatal error 变为了 Error 抛出,而 fatal error 普通都是一些没有需求正在运转时解决的谬误,这类谬误旨正在提示顺序员,这里的代码写的有成绩,需求修复,而没有是逻辑上要 catch 它做某些营业。

因而,绝年夜少数状况下,咱们其实不需求承继 Error,乃至 catch Error 也没有常见,只正在某些需求 log,回滚数据库,清算现场等场所才需求这样做。

对谬误以及异样的一种理论

依据以上所述,咱们提炼了一个对谬误以及异样解决较好的理论。

  1. 关于营业中不该该呈现谬误之处,抛出 InternalException,而没有是 Error
<?php
class InternalException extends Exception { /*...*/ }

function find(Array $ids) {
  if (empty($ids)) {
    throw new InternalException('ids should not be empty');
  }
  ...
}
  1. 只正在需求清算现场的时分 catch Error
<?php
try { /*...*/ }
catch (Throwable $t) {
  // log, transaction rollback, cleanup...
}
  1. 未捕捉的 Error 以及 Exception 经过 set_exception_handler 做后续清算以及 log
  2. 其余谬误依然经过 set_error_handler 来解决,正在解决的时分应用愈加明白的 FriendlyErrorType,并抛出 ErrorException 记载挪用栈

FriendlyErrorType:

<?php
function FriendlyErrorType($type) 
{ 
    switch($type) 
    { 
        case E_ERROR: // 1 // 
            return 'E_ERROR'; 
        case E_WARNING: // 2 // 
            return 'E_WARNING'; 
        case E_PARSE: // 4 // 
            return 'E_PARSE'; 
        case E_NOTICE: // 8 // 
            return 'E_NOTICE'; 
        case E_CORE_ERROR: // 16 // 
            return 'E_CORE_ERROR'; 
        case E_CORE_WARNING: // 32 // 
            return 'E_CORE_WARNING'; 
        case E_COMPILE_ERROR: // 64 // 
            return 'E_COMPILE_ERROR'; 
        case E_COMPILE_WARNING: // 128 // 
            return 'E_COMPILE_WARNING'; 
        case E_USER_ERROR: // 256 // 
            return 'E_USER_ERROR'; 
        case E_USER_WARNING: // 512 // 
            return 'E_USER_WARNING'; 
        case E_USER_NOTICE: // 1024 // 
            return 'E_USER_NOTICE'; 
        case E_STRICT: // 2048 // 
            return 'E_STRICT'; 
        case E_RECOVERABLE_ERROR: // 4096 // 
            return 'E_RECOVERABLE_ERROR'; 
        case E_DEPRECATED: // 8192 // 
            return 'E_DEPRECATED'; 
        case E_USER_DEPRECATED: // 16384 // 
            return 'E_USER_DEPRECATED'; 
    } 
    return ""; 
}

error_handler:

<?php
function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
 	log FriendlyErrorType($severity);
    throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
  1. PHP中的谬误级别与详细报错信息分类 ↩

  2. PHP 最好理论之异样以及谬误 ↩ ↩2

  3. E_ERROR 无奈捕捉,E_RECOVERABLE_ERROR 能够,后者默许输入 Catachable fatal error ↩

  4. fatal error 会记载到 web 效劳器的 error.log,这一点需求留意,由于这个 log 的地位往往没有是 PHP 使用界说的,而是 web 效劳器界说的。 ↩

  5. PHP 中另有一个 register_shutdown_function 函数,它容许注册一个会正在 PHP 停止时执行的函数,这个函数能够捕捉 fatal error,究竟结果是只需是剧本中缀就能够捕捉的。ci2 并无应用这个办法,以是相干成绩不断不失去很好的处理,过后也不认识到这个函数的存正在,晋级 PHP7 之后能够经过 catch Error 来处理,便再也不需求这样解决了。 ↩

  6. Throwable Exceptions and Errors in PHP 7 ↩ ↩2

  7. 正在 PHP7 中,传入 exception_handler 的参数从 Exception 改成 Throwable,这象征着 exception_handler 能够截获 Error。 ↩

以上就是探讨php的谬误以及异样解决机制的具体内容,更多请存眷资源魔其它相干文章!

标签: php php7开发教程 php7开发资料 php7开发自学

上一个PHP7标量类型声明RFC详解-PHP7

下一个详解PHP的数据结构扩展-PHP7

抱歉,评论功能暂时关闭!