from: http://www.jaceju.net/blog/archives/1121/
如何在 PHP 中平順地處理 Error 及 Exception ?
2010-04-23
在開發 PHP 的時候,最麻煩的事情之一就是處理錯誤。一個好的程式除了要將錯誤訊息呈現給使用者知道之外,也要讓該結束的部份正常結束才行。
而在 PHP5 之後,除了以往的 Error Handling 之外,還多了 Exception Handling ,使得程式變得更難去處理錯誤;所以大多數的開發者只能雙手一攤,讓這些錯誤訊息大剌剌地出現在使用者面前。
有沒有什麼好方法可以讓我們好好控制 Error 和 Exception 呢?
傳統的做法
在很多書籍和網路範例裡,當程式出錯時就是讓程序直接死掉,最常見的就是資料庫連線:
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
echo 'Connected successfully';
mysql_close($link);
或是在送出導向 header 後,就直接 exit :
header("Location: http://www.example.com/"); /* Redirect browser */
/* Make sure that code below does not get executed when we redirect. */
exit;
這些都不是好做法,因為有些流量較大的網站裡可能有多個資料庫連線,或是檔案的 handler 仍在開啟中;如果直接讓程序死亡或離開的話,就沒辦法將這些已經開啟的資源給正常關閉掉,進而造成系統的不穩定。
Exception 的處理
PHP5 中,有個 set_exception_handler 這個函式,它可以幫我們處理 Exception :
function exception_handler($exception)
{
echo "Uncaught exception: " , $exception->getMessage(), "\n";
}
set_exception_handler('exception_handler');
throw new Exception('Uncaught Exception');
echo "Not Executed\n";
不過我個人認為用 try…catch 會讓我們在程式流程上的彈性更大:
function inverse($x)
{
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo 'Hello World';
而在我研究過 Zend Framework 的做法後,發現它在處理 Exception 上更加聰明。 Zend Framework 在 Controller 中引入一個 Response 物件,所有對瀏覽器的輸出都要經過它 (例如 Header 、 Content Body 等等) ;而這個 Reponse 物件也同時控管著 Exception 是否要被輸出到瀏覽器端,讓程式開發者能有更大的空間處理 Exception 。
以下我簡單把 Zend Framework 在 Response 物件中處理 Exception 的概念整理成一個自製的 Response 類別:
class Response
{
private $_exceptions = array();
private $_renderExceptions = false;
public function setException(Exception $e)
{
$this->_exceptions[] = $e;
}
public function getExceptions()
{
return $this->_exceptions;
}
public function isException()
{
return !empty($this->_exceptions);
}
public function renderExceptions($flag = null)
{
if (null !== $flag) {
$this->_renderExceptions = $flag ? true : false;
}
return $this->_renderExceptions;
}
public function sendResponse()
{
echo "Header sending...\n";
$exception = '';
if ($this->isException() && $this->renderExceptions()) {
foreach ($this->getExceptions() as $e) {
$exception .= $e->getMessage() . "\n";
}
echo $exception;
}
echo "Body sending...\n";
}
}
主要的概念很簡單,就是 Response 物件先把 Exception 先收集起來,然後再視狀況如何處理,例如:
$response = new Response();
$response->renderExceptions(true); // 讓 Exception 呈現出來
try {
// 這裡處理我們真正要執行的動作
throw new Exception('TEST'); // 丟出一個測試用的例外
} catch (Exception $e) {
$response->setException($e); // 收集例外
}
if ($response->isException()) {
// 可以在這裡記錄 Exception
}
$response->sendResponse(); // 顯示所有結果 (包含 Header, Exception, Body 等)
透過了 Response 物件來管理 Exception ,就可以不必因為 Exception 而中斷我們的程式碼。
PHP Error 的處理
雖然 Exception 可以用 try…catch 控制程式流程,但 PHP Error 卻不行。
因為一般處理 PHP Error 的方法是透過 set_error_handler,而當執行完自訂的 Error Handler 後,我們卻只能選擇繼續執行下一行程式碼或將程式中斷離開,不然就是要利用全域變數來設定錯誤旗標以達到控制的目的。
$error = false;
function exceptionErrorHandler($errno, $errstr, $errfile, $errline)
{
global $error;
$error = true;
echo $errstr, "\n";
return true;
}
set_error_handler("exceptionErrorHandler");
strpos();
if (!$error) {
echo "Do normal process here.\n";
}
echo "end.\n";
不過 PHP5 也幫我們想好了,我們可以在 Error Handler 裡丟出 ErrorException ,就可以配合前面提到的 Response 物件做到更平順的 Exception 處理:
function exceptionErrorHandler($errno, $errstr, $errfile, $errline )
{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exceptionErrorHandler");
$response = new Response();
$response->renderExceptions(true); // 讓 Exception 呈現出來
try {
// 這裡處理我們真正要執行的動作
trigger_error('TEST', E_USER_ERROR); // 改用 trigger_error 來丟出測試用錯誤
} catch (Exception $e) {
$response->setException($e); // 收集例外
}
if ($response->isException()) {
// 可以在這裡記錄 Exception
}
$response->sendResponse(); // 顯示所有結果 (包含 Header, Exception, Body 等)
這邊最棒的是 Error Handler 丟出 ErrorException 後, try…catch 就會發生作用,而不再像 set_error_handler 這樣又返回中斷的地方繼續執行,一切就像行雲流水般那麼自然。
結論
一個運作良好的系統必須要對錯誤的發生有最大的掌控權,而不是放任它讓系統墜毀在五里霧之中。
雖然前面提到的處理方式也許不是最佳的,但希望透過這樣的介紹,讓大家能夠思考自己的程式應該如何去處理錯誤這件事。
就寫到這裡吧,收工~
分享到:
相关推荐
error与exception的区别查阅网上资料大多是java的解释,貌似php的异常处理过程与java差不多 java中的Object继承结构如下: Object—->Throwable——–> Exception —-> RuntimeException | Error Error都是unchecked...
PHP发送邮件失败解决方案,PHP发送邮件不成功解决办法,请下载压缩包中的代码,按照说明文档操作即可解决发送邮件问题
本文实例讲述了ThinkPHP提示错误Fatal error: Allowed memory size的解决方法。分享给大家供大家参考。具体分析如下: 如果你的ThinkPHP提示你:致命错误(Fatal error: Allowed memory size),根据网上说的提高...
本文实例讲述了php使用ZipArchive提示Fatal error: Class ZipArchive not found in的解决方法。分享给大家供大家参考。具体如下: ZipArchive是php自带的一个压缩与解压缩函数了,今天理所当然的情况中使用new Zip...
Niushop开源商城单商户V4 Niushop单商户V4版,微信商城、小程序商城,支持分销、团购、直播、秒杀、优惠券、自定义页面等众多营销功能,插件化开发,全开源,更适合二开。做最牛、功能最强大的开源商城 ...
您可能感兴趣的文章:php中try catch捕获异常实例详解PHP异常Parse error: syntax error, unexpected T_VAR错误解决方法PHP的异常处理类Exception的使用及说明php Try Catch异常测试PHP如何抛出异常处理错误php中...
无法正常调用,直接报错: 复制代码 代码如下: Fatal error: Uncaught exception ‘com_exception’ with message ‘Failed to create COM object `InternetExplorer.Application’: 拒绝访问。 原因是IE的COM组件...
use Whoops\Exception\ErrorException; use Whoops\Exception\Inspector; use Whoops\Handler\CallbackHandler; use Whoops\Handler\Handler; use Whoops\Handler\HandlerInterface; use Whoops\Util\...
PHP会出现eval()'d code error 错误提示的原因一般都是因为模板错误,原因有以下几种情况
7 Error and Exception Handling Part II: Using MySQL 8 Designing Your Web Database 9 Creating Your Web Database 10 Working with Your MySQL Database 11 Accessing Your MySQL Database from the Web with ...
(PHP 3, PHP 4, PHP 5) filesize — 取得文件大小 在php下filesize是用来计算文件大小的函数,我们可以通过filesize(‘file’)的形式来确定一个文件的字节大小,通过/1024来精确KB,GB等单位。 比如计算一个文件取两...
问题 我们项目的 Websocket Server 使用的 Swoole,最近在搭建 beta ...在 PHP 日志里,发现一条错误日志: ErrorException: Swoole\WebSocket\Server::push(): the connected client of connection[47] is not a webso
Chapter 7 Error and Exception Handling Part II: Using MySQL Chapter 8 Designing Your Web Database Chapter 9 Creating Your Web Database Chapter 10 Working with Your MySQL Database Chapter 11 Accessing...
根据错误报告级别将错误转换为ErrorException实例 运行PHP CLI ,向STDERR报告错误/异常(甚至是致命错误)并使用异常代码作为退出状态 允许停用@运算符 捕获致命错误 接受在内部关闭函数结束时执行的回调 接受回调...
在PHP每一个可独立执行的op array最后的ZEND_HANDLE_EXCEPTION是用来干什么呢? 让我们从一个问题说起, 上周的时候, blue5tar提了一个问题:”对于下面的代码, onError明明执行了, 但是onException却没有执行, 为什么...
Oriented PHP Advanced OOP Features Error and Exception Handling Strings and Regular Expressions Working with the File and Operating System PEAR Date and Time Forms Authenticating Your Users Handling ...
[think\exception\ErrorException] shell_exec() has been disabled for security reasons 宝塔建立个计划任务SEHLL脚本设置时间为每天晚上24点过后 php /www/wwwroot/open.xxx.cn/think xtask:start
Chapter 8: Error and Exception Handling Chapter 9: Strings and Regular Expressions Chapter 10: Working with the File and Operating System Chapter 11: Third-Party Libraries Chapter 12: Date and Time ...