设为首页 收藏本站
查看: 476|回复: 0

[经验分享] php error exception

[复制链接]

尚未签到

发表于 2017-3-21 10:54:46 | 显示全部楼层 |阅读模式
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 這樣又返回中斷的地方繼續執行,一切就像行雲流水般那麼自然。
結論

一個運作良好的系統必須要對錯誤的發生有最大的掌控權,而不是放任它讓系統墜毀在五里霧之中。

雖然前面提到的處理方式也許不是最佳的,但希望透過這樣的介紹,讓大家能夠思考自己的程式應該如何去處理錯誤這件事。

就寫到這裡吧,收工~

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-352927-1-1.html 上篇帖子: php配置问题 下篇帖子: php session配置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表