错误与异常

错误,可以理解程序本身的错误,例如语法错误。而异常则更偏向于程序运行不符合预期或者不符合正常流程;对于 php 语言而言,处理错误和处理异常使用的机制完全不同,因此很容易让人产生困惑。

例如,我们希望通过捕获异常来处理除数为 0 的情况,但是在捕获到异常之前,php 就触发了错误。

1 try {
2     $a = 5 / 0;
3 } catch (exception $e) {
4     $e->getmessage();
5     $a = -1;  // 通过异常来处理 $a 为 0 的情况,但是实际上,捕获不到该异常
6 }
7 
8 echo $a;
9 // php warning:  division by zero

 

也就是说,php 将除数为 0 的情况当成了错误而触发,而不会自动抛出异常,因此没法捕获。类似的,在很多情况下,php 都没办法自动抛出异常。只能通过 if - else 语句判断再结合 throw 方法来并手动抛出异常。

上述情况的发生,主要还是因为异常机制是 php 向面向对象演进后得到的产物。而在此之前 php 的报错主要还是通过错误机制,因此,在很多情况下,php 的错误要比异常更有价值。不过 php7 开始统一这两者,使错误也可以像异常那样抛出(这部分内容将放在异常部分讲解)。

 

错误级别

php 中的错误可理解为 使脚本不运行不正常的情况,根据错误级别从高到低可划分为五类

  1. parse error 或 syntax error – 语法解析错误,触发该错误后,脚本完全无法运行;
  2. fatal error – 致命错误,触发该错误后,后面的脚本无法继续执行;
  3. warning error – 出现比较不恰当的地方,脚本可继续执行;
  4. notice error – 出现不恰当的地方,但是程度比 warning error 低,脚本可继续执行;
  5. deprecated error – 不推荐这么使用,未来可能会废弃,脚本可继续执行;

默认情况下,php 触发错误,并显示错误的级别及对应的提示。

parse error 示例 – 语句结尾不写分号

1 echo "abc"
2 // php parse error:  syntax error, unexpected end of file, expecting ',' or ';

 

fatal error 示例 – 使用不存在的函数

1 echo "before\n";
2 foo();
3 echo "after"; // 本行无法继续执行
4 // before
5 // php fatal error:  uncaught error: call to undefined function foo()

 

warning error 示例 – 引入不存在的文件

 1 $a = "foo";
 2 include('bar.php');
 3 echo $a; // 程序继续执行
 4 // php warning:  include(bar.php): failed to open stream: no such file or directory ...
 5 // foo
 6 notice error 示例 - 输出不存在的变量
 7 
 8 echo $foo;
 9 echo 12345;
10 // php notice:  undefined variable: foo
11 // 12345

 

deprecated error 示例 – 在一些字符串函数中传入数字而非字符串

1 strpos('12345', 3);
2 // php deprecated:  strpos(): non-string needles will be interpreted as strings in the future

 

除了默认触发消息外,用户也可以使用 set_error_handler 函数自定义错误处理,大多数错误类型都可以进行自定义处理,除了 e_error、 e_parse、 e_core_error、 e_core_warning、 e_compile_error、 e_compile_warning 外。

1 set_error_handler ( callable $error_handler [, int $error_types = e_all | e_strict ] ) : mixed

 

示例

 1 <?php
 2 // e_all - 处理全部错误类型
 3 set_error_handler('customerror', e_all);
 4 
 5 /**
 6  * @param  int $errno 错误的级别
 7  * @param  string $errstr  错误的信息
 8  * @param  string $errfile 错误的文件名(可选)
 9  * @param  string $errline 错误发生的行号(可选)
10  */
11 function customerror(int $errno, string $errstr, string $errfile, string $errline)
12 {
13     echo sprintf('错误消息为 %s', $errstr);
14 }
15 
16 $a = 5 / 0;  // 错误消息为 division by zero

 

用户也可以通过 trigger_error 函数来手动触发一个用户级别的错误(e_user_errore_user_warninge_user_noticee_user_deprecated)。

1 function division($a, $b) {
2     if($b == 0){
3         @trigger_error("0 不能作为除数", e_user_notice);
4         return -1;
5     }
6     return $a / $b;
7 }
8 
9 echo division(10, 0);

 

 

与错误有关的配置

一些错误处理相关的常用配置

  • error_reporting – 设置错误的报告级别
  • display_errors – 是否显示错误
  • display_startup_error – 是否显示 php 启动过程中的显示
  • log_errors – 设置是否将脚本运行的错误信息记录到服务器错误日志或者 error_log 之中

《modern php》提出了四个规则

  1. 一定要让 php 报告错误;
  2. 在开发环境中要显示错误;
  3. 在生产环境中不能显示错误;
  4. 在开发环境和生产环境中都要记录错误;

开发环境推荐配置

1 display_errors = on
2 display_startup_error = on
3 error_reporting = -1
4 log_errors = on

 

生产环境推荐配置

1 display_errors = off
2 display_startup_error = off
3 ; 报告 notice 以外的所有错误
4 error_reporting = e_all & ~e_notice
5 log_errors = on
6  

 

symfony 编码规范相关

异常和错误消息字符串必须使用 sprintf 来进行拼接;

throw new commandnotfoundexception(sprintf('command "%s" does not exist.', $name));

当错误类型为 e_user_deprecated 时,需要添加 @

@trigger_error("foo", e_user_deprecated);

参考资料

  • php errors: 4 different types (warning, parse, fatal, and notice error)
  • php: 预定义常量 – manual
  • php 核心技术与最佳实践 (豆瓣)
  • php: 运行时配置 – manual
  • php 规范 – symfony 代码规范 | php 技术论坛
  • 多phper在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、tp6,laravel,yii2,redis,swoole、swoft、kafka、mysql优化、shell脚本、docker、微服务、nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的加群(点击→)677079770