在许多「重异常」(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 ](http://xdebug.org/docs/basic)有一个 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](http://php.net/language.operators.errorcontrol)
* [SitePoint](http://www.sitepoint.com/)
* [Xdebug](http://xdebug.org/docs/basic)
* [Scream](http://php.net/book.scream)
### 错误异常类
PHP 可以完美化身为「重异常」的程序语言,只需要几行代码就能切换过去。基本上你可以利用 `ErrorException `类抛出「错误」来当做「异常」,这个类是继承自 Exception 类。
这在现在的需要框架中是常见的做法,比如 Symfony 和 Laravel。Laravel 默认使用 [Whoops](http://filp.github.io/whoops/)! 扩展包来处理错误,如果 `app.debug` 启动的话,会将错误当成异常显示出来,而关闭则会隐藏。
在开发过程中将错误当时异常抛出可以更好的处理它,如果在开发时发生异常,你可以将它 catch 声明中,在写下如何处理的代码。每捕捉一个异常,都会使你的应用程序越来越健壮。
更多关于如何使用 ErrorException 来处理错误的细节,可以参考 [ErrorException Class](http://laravel-china.github.io/php-the-right-way/errorexception)。
* [Error Control Operators](http://php.net/language.operators.errorcontrol)
* [Predefined Constants for Error Handling](http://php.net/errorfunc.constants)
* [error_reporting()](http://php.net/function.error-reporting)
* [Reporting](http://laravel-china.github.io/#error_reporting)
- 欢迎
- 入门指南
- 使用当前稳定版本
- 内置的WEB服务器
- Mac安装
- Windows安装
- 代码风格指南
- 语言亮点
- 编程范式
- 命名空间
- PHP标准库
- 命令行接口
- Xdebug
- 依赖管理
- Composer 与 Packagist
- PEAR
- 开发实践
- 基础知识
- 日期和时间
- 设计模式
- 使用UTF8编码
- 依赖注入
- 基本概念
- 复杂的问题
- 容器
- 延伸阅读
- 数据库
- MYSQL 扩展
- PDO 扩展
- 数据库交互
- 数据库抽象层
- 使用模板
- 好处
- 原生PHP模板
- 编译模板
- 延伸阅读
- 错误与异常
- 错误
- 异常
- 安全
- Web应用程序安全
- 密码哈希
- 数据过滤
- 配置文件
- 注册全局变量
- 错误报告
- 测试
- 测试驱动开发
- 行为驱动开发
- 其他测试工具
- 服务器与部署
- Platform as a Service (PaaS)
- 虚拟或专用服务器
- 共享服务器
- 构建及部署应用
- 虚拟化
- Vagrant
- Docker
- 缓存
- Opcode缓存
- 对象缓存
- 文档撰写
- 资源
- 社区
- Credits