characters

在许多「重异常」(exception-heavy) 的编程语言中,一旦发生错误,就会抛出异常。这确实是一个可行的方式。不过 PHP 却是一个 「轻异常」(exception-light) 的语言。当然它确实有异常机制,在处理对象时,核心也开始采用这个机制来处理,只是 PHP 会尽可能的执行而无视发生的事情,除非是一个严重错误。

举例来说:

$ php -a php > echo $foo; Notice: Undefined variable: foo in php shell code on line 1

这里只是一个 notice 级别的错误,PHP 仍然会愉快的继续执行。这对有「重异常」编程经验的人来说会带来困惑,例如在 Python 中,引用一个不存在的变量会抛出异常:

$ python >>> print foo Traceback (most recent call last):   File "<stdin>", line 1, in <module> NameError: name 'foo' is not defined

本质上的差异在于 Python 会对任何小错误进行抛错,因此开发人员可以确信任何潜在的问题或者边缘的案例都可以被捕捉到,与此同时 PHP 仍然会保持执行,除非极端的问题发生才会抛出异常。

错误严重性

PHP 有几个错误严重性等级。三个最常见的的信息类型是错误(error)、通知(notice)和警告(warning)。它们有不同的严重性: E_ERROR 、E_NOTICE和 E_WARNING。错误是运行期间的严重问题,通常是因为代码出错而造成,必须要修正它,否则会使 PHP 停止执行。通知是建议性质的信息,是因为程序代码在执行期有可能造成问题,但程序不会停止。 警告是非致命错误,程序执行也不会因此而中止。

另一个在编译期间会报错的信息类型是「E_STRICT」。这个信息用來建议修改程序代码以维持最佳的互通性并能与今后的 PHP 版本兼容。

更改 PHP 错误报告行为

错误报告可以由 PHP 配置及函数调用改变。使用 PHP 内置的函数 error_reporting(),可以设定程序执行期间的错误等级,方法是传入预定义的错误等级常量,这意味着如果你只想看到警告和错误 - 而非通知 - 你可以这样设定:

<?php error_reporting(E_ERROR | E_WARNING);

你也可以控制错误是否在屏幕上显示 (开发时比较有用)或隐藏后记录日志 (适用于正式环境)。如果想知道更多细节,可以查看 错误报告 章节。

行内错误抑制

你可以让 PHP 利用错误控制操作符 @ 来抑制特定的错误。将这个操作符放置在表达式之前,其后的任何错误都不会出现。

<?php echo @$foo['bar'];

如果 $foo['bar'] 存在,程序会将结果输出,如果变量 $foo 或是 'bar' 键值不存在,则会返回 null 并且不输出任何东西。如果不使用错误控制操作符,这个表达式会产生一个错误信息 PHP Notice: Undefined variable: foo 或 PHP Notice: Undefined index: bar 。

这看起来像是个好主意,不过也有一些讨厌的代价。PHP 处理使用 @ 的表达式比起不用时效率会低一些。过早的性能优化在所有程序语言中也许都是争论点,不过如果性能在你的应用程序 / 类库中占有重要地位,那么了解错误控制操作符的性能影响就比较重要。

其次,错误控制操作符会 完全 吃掉错误。不但没有显示,而且也不会记录在错误日志中。此外,在正式环境中 PHP 也没有办法关闭错误控制操作符。也许你认为那些错误时无害的,不过那些较具伤害性的错误同时也会被隐藏。

如果有方法可以避免错误抑制符,你应该考虑使用,举例来说,上面的程序代码可以这样重写:

<?php echo isset($foo['bar']) ? $foo['bar'] : '';

当 fopen() 载入文件失败时,也许是一个使用错误抑制符的合理例子。你可以在尝试载入文件前检查是否存在,但是如果这个文件在检查后才被删除,而此时 fopen() 还未执行 (听起来有点不太可能,但是确实会发生),这时 fopen()会返回 false 并且 抛出操作。这也许应该由 PHP 本身来解决,但这时一个错误抑制符才能有效解决的例子。

前面我们提到在正式的 PHP 环境中没有办法关闭错误控制操作符。但是 Xdebug 有一个 xdebug.scream 的 ini 配置项,可以关闭错误控制操作符。你可以按照下面的方式修改 php.ini。

xdebug.scream = On

你也可以在执行期间通过 ini_set 函数来设置这个值:

<?php ini_set('xdebug.scream', '1')

「Scream」这个 PHP 扩展提供了和 xDebug 类似的功能,只是 Scream 的 ini 设置项叫做 scream.enabled 。

当你在调试代码而错误信息被隐藏时,这是最有用的方法。请务必小心使用 scream ,而是把它当作暂时性的调试工具。有许多的 PHP 函数类库代码也许无法在错误抑制操作符停用时正常使用。

Error Control Operators

SitePoint

Xdebug

Scream

错误异常类

PHP 可以完美化身为「重异常」的程序语言,只需要几行代码就能切换过去。基本上你可以利用 ErrorException 类抛出「错误」来当做「异常」,这个类是继承自 Exception 类。

这在大量的现代框架中是一个常见的做法,比如 Symfony 和 Laravel。在调试(debug)模式 (或叫开发模式) 下,这些框架都能够展示一份简洁干净的 stack trace。

还有一些对错误(error)和异常(exception)的处理更加优秀的包(package),例如 Whoops!,它在 Laravel 中是默认安装的并且也可以被用在任意框架中。

在开发过程中将错误当作异常抛出可以更好的处理它,如果在开发时发生异常,你可以将它包在一个 catch 语句中具体说明这种情况如何处理。每捕捉一个异常,都会使你的应用程序越来越健壮。


Previous article: Next article: