在許多「重異常」(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