欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PHP中異常處理的一些方法整理

 更新時間:2015年07月03日 14:18:06   投稿:goldensun  
這篇文章主要介紹了PHP中異常處理的一些方法整理,盡管PHP并不能說是一種很優(yōu)秀的語言,但其相關(guān)技術(shù)依然在不斷得到改進,需要的朋友可以參考下

每一個新的功能添加到PHP運行時會創(chuàng)建一個指數(shù)隨機數(shù),通過這樣的方式開發(fā)者可以使用和甚至濫用這個新特性。然而,直到一些好的和壞的使用情況陸續(xù)出現(xiàn)開發(fā)者們才達成了共識。當這些新案例不斷浮現(xiàn),我們終于可以辨別出什么是最好或最壞的做法。

異常處理在PHP中的確無論如何都不算是一個新的特征。但在本文中,我們將討論在PHP 5.3中基于異常處理的兩個新的特點。第一個是嵌套異常第二是一套SPL(現(xiàn)在的PHP運行機制的一個核心擴展)的擴展的新的異常類型。這兩個新特性,這本書里都能找到最佳實踐值得各位去詳細研究。


特別要注意:這些特性中的一些已經(jīng)存在于低于5.3的PHP版本之中,或者至少能夠在低于5.3的版本之中被實現(xiàn).  而當本文提到 PHP 5.3, 并不是嚴責(zé)意義上的 PHP 運行時版本. 相反,它意味著代碼庫和項目是采用 PHP 5.3 作為最低版本的,但同時也是在新的發(fā)展階段出現(xiàn)的所有最佳實踐.  這個發(fā)展階段所凸顯的是特定的幾個像Zend Framework, Symfony, Doctrine 以及 PEAR 這樣的項目所進行的“2.0”嘗試.

背景

PHP 5.2  只有一個異常類 Exception。按照 Zend Framework / PEAR 的開發(fā)標準, 這個類是你的庫中所有異常類的基類。如果你創(chuàng)建一個名叫 MyCompany 的庫,按 Zend Framework / PEAR 的標準, 庫中所有的代碼文件都會以 MyCompany_ 開頭。要是你想給庫創(chuàng)建自己的異?;? MyCompany_Exception, 那就用該類繼承 Exception,然后再由組件(component )繼承和拋出該異常類。比如你有一個組件 MyCompany_Foo,你可以給它創(chuàng)建一個用在該組件內(nèi)部的異?;?MyCompany_Foo_Exception。這些異常能被捕捉 MyCompany_Foo_Exception,MyCompany_Exception 或 Exception 的代碼捉到。 對于庫中其他用到該組件的代碼來說,這是個三層的異常(或更多,取決于 MyCompany_Foo_Exception 的子類有幾層 ), 他們可以根據(jù)自己的需要處理這些異常。


在php5中,基本異常類已經(jīng)支持嵌套的特性了。什么是嵌套呢?嵌套是一種能力可以去捕獲特殊異常,或者捕獲參照原始異常而創(chuàng)建的一個新的異常對象。這將會允許caller屬性在更公開的類型的開銷庫中出現(xiàn)的兩種異常類上得到體現(xiàn),當然也會在具有原始異常行為的異常類上體現(xiàn)。

為什么這些特性很有用?通常,通過使用其他代碼來拋出自己的類型的異常是最有效的代碼。這些代碼可能是使用適配器模式封裝的提供一些適應(yīng)性更強強的函數(shù)的第三方代碼庫的代碼,或利用一些PHP擴展來拋出異常的簡單代碼。


例如,在組件 Zend_Db 中, 它使用了適配器模式來封裝特定的 PHP 擴展,來創(chuàng)建一個數(shù)據(jù)庫抽象層.  在一個適配器中, Zend_Db 封裝了 PDO, 而 PDO 會拋出它自己的異常 PDOException, Zend_Db 需要捕獲這些特定于 PDO 的異常,并讓它們以可預(yù)期且類型已知的 Zend_Db_Exception 重新被拋出. 這樣就給了開發(fā)者保證, Zend_Db 將總是拋出 Zend_Db_Exception 類型的異常(因此可以被捕獲), 而他們同時也可以在需要的時候訪問到最開始被拋出的 PDOException.

下面的示例展示了一個虛構(gòu)的數(shù)據(jù)庫適配器可能如何去實現(xiàn)嵌入式的異常:
 

class MyCompany_Database
{
 /**
  * @var PDO object setup during construction
  */
 protected $_pdoResource = null;
  
 /**
  * @throws MyCompany_Database_Exception
  * @return int
  */
 public function executeQuery($sql)
 {
  try {
   $numRows = $this->_pdoResource->exec($sql);
  } catch (PDOException $e) {
   throw new MyCompany_Database_Exception('Query was unexecutable', null, $e);
  }
  return $numRows;
 }
 
}

為了使用嵌入式的異常,你就得調(diào)用被捕獲異常的getPrevious()方法:
 

// $sql and $connectionParameters assumed
try {
 $db = new MyCompany_Database('PDO', $connectionParams);
 $db->executeQuery($sql);
} catch (MyCompany_Database_Exception $e) {
 echo 'General Error: ' . $e->getMessage() . "\n";
 $pdoException = $e->getPrevious();
 echo 'PDO Specific error: ' . $pdoException->getMessage() . "\n";
}

大多數(shù)最近被實現(xiàn)的PHP擴展都擁有OO(面向?qū)ο螅┙涌?  因此,這些API傾向于拋出異常,而不是發(fā)生錯誤終止。PHP中能夠拋出異常的擴展,稍微列舉出幾個就包括有PDO, DOM, Mysqli, Phar, Soap 以及 SQLite.

新特性:新核心異常類型

在PHP 5.3開發(fā)中,我們展示了一些有趣的新異常類型。這些異常在PHP 5.2.x中已經(jīng)存在,但最近還沒到“重新評估”異常的最佳實踐,現(xiàn)在他們會顯得更加引人注目。他們在SPL擴展中得以應(yīng)用,并在手冊中列出(這里)由于這些新的異常類型是PHP核心的一部分,也是SPL的一部分,它們可以被任何用PHP 5.3(及以上)運行代碼的人使用。雖然在編寫應(yīng)用程序?qū)拥拇a時,看起來不那么重要,但在我們寫或者使用代碼庫時,使用這些新異常類型變得更加重要


那么為什么新異常是普通類型?以前,開發(fā)者試圖通過在異常消息提醒中放入更多的內(nèi)容來賦予異常更多的含義。雖然這樣做是可行的,但是它有幾個缺點。一是你無法捕獲基于消息的異常。這可是一個問題,如果你知道一組代碼是同樣的異常類型與不同的提示消息對應(yīng)不同異常情況下,處理起來的難度將相當?shù)拇?。例如,一個認證類,在對$auth->authenticate();;它拋出異常的相同類型的(假設(shè)是異常),但不同的消息對應(yīng)兩個具體的故障:產(chǎn)生故障原因是認證服務(wù)器不能達到但是相同的異常類型卻提示失敗的驗證消息不同。在這種情況下(注意,使用異常可能不是處理認證響應(yīng)最好的方式),這將需要用字符串來解析消息從而處理這兩種不同的情況。

這個問題的解決辦法顯然是通過某種方式對異常進行編碼,這樣就可以在需要辨別如何對這種異常環(huán)境做出反應(yīng)的時候能夠更加容易的查詢到。第一個反應(yīng)庫是使用異常基類的$code屬性。另一個是通過創(chuàng)建可以被拋出且能描述自身行為的子類或者新的異常類。這兩種方法具有相同的明顯的缺點。兩者都沒有呈現(xiàn)出想這樣的最好的例子。兩者都不被認為是一個標準,因此每個試圖復(fù)制這兩種解決方案的項目都會有小的變化,這就迫使使用這需要回到文檔以了解所創(chuàng)建的庫中已經(jīng)有的具體解決方案?,F(xiàn)在通過使用SPL的新的類型方法,也稱作php標準庫;開發(fā)者就可以以同樣的方式在他們的項目中,并且復(fù)用這些項目的新的最佳的方法已經(jīng)出現(xiàn)。


第二個缺點是使用詳細信息的做法使得理解這些異常情況對那些非英語或英語能力有限的開發(fā)者來說十分困難。這可能會使的開發(fā)者在試圖理解異常信息的含義的過程十分的緩慢。許多開發(fā)者也會寫關(guān)于異常的文章,因為還未出現(xiàn)一個統(tǒng)一的整合過的標準所要有同這些開發(fā)者數(shù)量相同的不同的版本來描述異常消息所描述的情況。

所以我如何去使用它們,就用這些讓人無語的密密麻麻的細節(jié)描述?

現(xiàn)在在SPL中有總共13個新的異常類型。其中兩個可被視為基類:邏輯異常和運行時異常;兩種都繼承php異常類。其余的方法在邏輯上可以被拆分為3組:動態(tài)調(diào)用組,邏輯組和運行時組。

動態(tài)調(diào)用組包含異常 BadFunctionCallException和BadMethodCallException,BadMethodCallException是BadFunctionCallException(LogicException的子類)的子類,這意味著這些異常可以被其直接類型(譯者注:就是異常自身的類型,大家都知道異常有很多種)、LogicException,或者Exception抓到(譯者注:就是catch)你應(yīng)該在什么時候使用這些?通常,你應(yīng)該在由一個無法處理的__call()方法產(chǎn)生的情況,或者回調(diào)無法不是一個有效的函數(shù)(簡單說,當某些東西并非is_callable())時使用。

例如:
 

// OO variant
class Foo
{
 public function __call($method, $args)
 {
  switch ($method) {
   case 'doBar': /* ... */ break;
   default:
    throw new BadMethodCallException('Method ' . $method . ' is not callable by this object');
  }
 }
 
}
 
// procedural variant
function foo($bar, $baz) {
 $func = 'do' . $baz;
 if (!is_callable($func)) {
  throw new BadFunctionCallException('Function ' . $func . ' is not callable');
 }
}

一個直接的例子,在__call時call_user_func()。這組異常在開發(fā)各種API動態(tài)方法的調(diào)用、函數(shù)調(diào)用時非常有用,例如這是一個可以被SOAP和XML-RPC客戶端/服務(wù)端能夠發(fā)送和解釋的請求。


第二組是邏輯(logic )組。這組由DomainException、InvalidArgumentException、LengthException、OutOfRangeException組成。這些異常也是LogicException的子類,當然也是PHP的Exception的子類。在有狀態(tài)不定,或者錯誤的方法/函數(shù)的參數(shù)時使用這些異常。為了更好地理解這一點,我們先看看最后一組異常

最后一組是運行時(runtime )組。它由OutOfBoundsException、OverflowException、RangeException、UnderflowException、UnexpectedValueExceptio組成。這些異常也是RuntimeException的子類,當然也是PHP的Exception的子類。在“運行時”(runtime)的函數(shù)、方法發(fā)生異常時,這些異常(運行時組)會被調(diào)用


邏輯組和運行時組如何一起工作?如果你看看對象的剖析,通常是發(fā)生的是兩者之一。首先,對象將跟蹤并改變狀態(tài)。這意味著對象通常是不做任何事情。它可能會傳遞結(jié)構(gòu)給它,它可能會通過setter和getter設(shè)置一些東西(譯者注:例如$this->foo='foo'),或者,它可能會引用其他對象。第二,當對象不跟蹤或改變狀態(tài),這代表正在操作——做它該做的事。這是對象的運行時(runtime)。例如,在對象的一生中,它可能被創(chuàng)建,設(shè)置一些東西,那么它可能會被setFoo($foo),setBar($bar)。在這些時候,任何類型的LogicException應(yīng)該被提高。此外,當對象內(nèi)的方法被帶參數(shù)調(diào)用時,例如$object->doSomething($someVariation);在前幾行檢查$someVariation變量時,可能拋出一個LogicException。完成檢查$someVariation后,它繼續(xù)做它該做的doSomething(),這時被認為是它的“運行時”(runtime),在這段代碼中,可能拋出RuntimeExcpetions異常。


要理解得更好,我們來看看這個概念在代碼中的運用:
 

class Foo
{
 protected $number = 0;
 protected $bar = null;
 
 public function __construct($options)
 {
  /** 本方法拋出LogicException異常 **/
 }
  
 public function setNumber($number)
 {
  /** 本方法拋出LogicException異常 **/
 }
  
 public function setBar(Bar $bar)
 {
  /** 本方法拋出LogicException異常 **/
 }
  
 public function doSomething($differentNumber)
 {
  if ($differentNumber != $expectedCondition) {
   /** 在這里,拋出LogicException異常 **/
  }
   
  /**
   * 在這里,本方法拋出RuntimeException異常
   */ 
 }
 
}

現(xiàn)在理解了這一概念,那么,對代碼庫的使用者來說,這是做什么的呢?使用者可以隨時確定對象的異常狀態(tài),他們可以用異常的具體的類型來捕獲(catch)異常,例如InvalidArgumentException或LengthException,至少也是LogicException。通過這種級別的精度調(diào)整,和類型的多樣,他們可以用LogicException捕獲最小的異常,但也可以通過實際的異常類型獲得更好的理解。同樣的概念也適用于運行時的異常,可以拋出更多的特定類型的異常,并且不論是特定或非特定類型的異常,都可以被捕獲(catch)。它可以給使用者提供更詳細的情況和精確度。

下面是一個關(guān)于SPL異常的表,您可能會有興趣

類庫代碼中的最佳實踐

PHP 5.3 帶來了新的異常類型, 同時也帶給我們新的最佳實踐. 除了將某些特定的異常(如: InvalidArgumentException, RuntimeException)標準化外, 捕捉組件級的異常, 也很重要. 關(guān)于這方面, ZF2 wiki 和 PEAR2 wiki 上面有深入的探討.

簡而言之, 除了上面提到的各種最佳實踐, 我們還應(yīng)該用 Marker Interface 來創(chuàng)建一個組件級的異?;? 通過創(chuàng)建組件級的 Marker Interface, 用在組件內(nèi)部的異常既能繼承 SPL 的異常類型, 也能在運行時被各種代碼捕捉. 我們來看下列代碼:
 

// usage of bracket syntax for brevity
namespace MyCompany\Component {
 
 interface Exception
 {}
 
 class UnexpectedValueException 
  extends \UnexpectedValueException 
  implements Exception
 {}
 
 class Component
 {
  public static function doSomething()
  {
   if ($somethingExceptionalHappens) {
    throw new UnexpectedValueException('Something bad happened');
   }
  }
 }
 
}

如果調(diào)用上面代碼中的 MyCompany\Component\Component::doSomething() 函數(shù), doSomething() 拋出的異常可以當作下列異常類型捕捉: PHP 的 Exception, SPL 的 UnexpectedValueException, SPL 的 RuntimeException, 該組件的MyCompany\Component\UnexpectedValueException, 或該組件的 MyCompany\Component\Exception. 這為捕捉你的類庫組件中的異常提供了極大的便利. 此外, 通過分析異常的類型, 我們也能看出某個異常的含義.

相關(guān)文章

  • 完美解決PHP中文亂碼

    完美解決PHP中文亂碼

    PHP中文亂碼一般是字符集問題,編碼主要有下面幾個問題。
    2009-11-11
  • PHP中PDO的錯誤處理

    PHP中PDO的錯誤處理

    在使用PDO進行那個PHP和數(shù)據(jù)庫開發(fā)過程中,如果程序中碰到錯誤咋辦?我們這里描述PDO類的錯誤信息和異常處理。
    2011-09-09
  • PHP中ADODB類詳解

    PHP中ADODB類詳解

    1. 前言 ADODB 是 Active Data Objects Data Base 的簡稱,它是一種 PHP 存取數(shù)據(jù)庫的函式組件?,F(xiàn)在 SFS3 系統(tǒng) (校園自由軟件交流網(wǎng)學(xué)務(wù)系統(tǒng)) 計劃的主持人陳瑩光老師,決定采用此一組件,為了讓更多有心參與該項目的伙伴們能夠順利加入發(fā)展的行列,小弟認為有必要把 ADODB 的中文入門介紹寫出來,以方便伙伴們參考備查。
    2008-03-03
  • PHP操作MySQL的mysql_fetch_* 函數(shù)的常見用法教程

    PHP操作MySQL的mysql_fetch_* 函數(shù)的常見用法教程

    這篇文章主要介紹了PHP中操作MySQL的mysql_fetch函數(shù)的常見用法教程,文中提到了其下fetch_array和mysql_fetch_row以及mysql_fetch_object函數(shù)的使用,需要的朋友可以參考下
    2015-12-12
  • php實現(xiàn)將數(shù)組或?qū)ο髮懭氲轿募姆椒ㄐ〗Y(jié)【三種方法】

    php實現(xiàn)將數(shù)組或?qū)ο髮懭氲轿募姆椒ㄐ〗Y(jié)【三種方法】

    這篇文章主要介紹了php實現(xiàn)將數(shù)組或?qū)ο髮懭氲轿募姆椒?結(jié)合實例形式總結(jié)分析了PHP將數(shù)組或?qū)ο筠D(zhuǎn)換并寫入文件的三種常見操作技巧,需要的朋友可以參考下
    2020-04-04
  • php和asp語法上的區(qū)別總結(jié)

    php和asp語法上的區(qū)別總結(jié)

    在本篇文章中小編給大家分享了關(guān)于php和asp語法上的區(qū)別以及相關(guān)代碼知識點,需要的朋友們學(xué)習(xí)下。
    2019-05-05
  • PHP中4個加速、緩存擴展的區(qū)別和選用建議

    PHP中4個加速、緩存擴展的區(qū)別和選用建議

    這篇文章主要介紹了PHP中eAccelerator、memcached、xcache、APC等4個加速、緩存擴展的區(qū)別和選用建議,需要的朋友可以參考下
    2014-03-03
  • PHP7匿名類的用法示例

    PHP7匿名類的用法示例

    這篇文章主要介紹了PHP7匿名類的用法,結(jié)合實例形式分析了php7匿名類、匿名函數(shù)簡單定義與使用技巧,需要的朋友可以參考下
    2019-04-04
  • 如何在PHP中讀寫文件

    如何在PHP中讀寫文件

    這篇文章主要介紹了如何在PHP中讀寫文件,幫助大家更好的理解和學(xué)習(xí)php,感興趣的朋友可以了解下
    2020-09-09
  • PHP 強制性文件下載功能的函數(shù)代碼(任意文件格式)

    PHP 強制性文件下載功能的函數(shù)代碼(任意文件格式)

    PHP強制性文件下載函數(shù)代碼,使用此函數(shù)為用戶提供強制性的文件下載功能。
    2010-05-05

最新評論