PHP5 OOP編程之代理與自訂異常(2)

日期:2006-09-28  作者:喜騰小二  來源:PHPChina


三、
抛出異常


  妳可能已經從上麵的程式碼中注意到,妳擷取的是一個稱為QueryException(我們將在後麵實現這個物件)的異常。一個異常類似於一個錯誤,然而卻更俱有一般性。描述一個異常的最好的方法是使用emergency。儘管一個emergency可以不會是“緻命的”,但是還是必須處理它。當在PHP中抛出一個異常時,執行的當前範圍很快地被終止,不管它是一個函式,try..catch塊還是指令檔本身。然後,該異常遍歷調用棧—終止每個執行範圍,直到或者在一個try..catch塊中擷取它或者它到達調用棧的頂部—此時它將生成一個緻命錯誤。

  異常處理是PHP
5中的另外一個新特徵,當與OOP聯用時,它能夠實現良好地控制錯誤處理和報告。一個try..catch塊是一種處理異常的重要機制。一旦被擷取,指令檔將會從異常被擷取和被處理的程式碼的下一行繼續執行。

  如果查詢失敗,妳需要改變妳的execute函式以抛出一個異常。妳將抛出一個稱為QueryException的自訂異常物件—導緻錯誤的DBQuery物件被傳遞給它。

  清單3.抛出一個異常。

/**
*執行當前查詢
*
* 執行當前查詢—用提供的參數代替任何點位符
* .
*
* @參數: mixed
$queryParams,... 查詢參數
* @返回:資源A—參考描述執行查詢的資源。
*/
public function
execute($queryParams = '')
{
 //例如: SELECT * FROM table WHERE name=:1S
AND type=:2I AND level=:3N
 $args = func_get_args();
 if
($this->stored_procedure) {
  /*調用compile函式以得到查詢*/
  $query =
call_user_func_array(array($this, 'compile'), $args);
 } else {

  /*一個存儲過程沒被初始化,因此,作為一種標準查詢來執行之*/
  $query = $queryParams;
 }

 $result = $this->db->query($query);
 if (! $result) {
  throw new
QueryException($this);
 }
 $this->result = $result;
 /*
注意現在我們怎麼返回物件本身,這使我們能夠從這個函式的返回結果中調用成員函式
 */
 return $this;
}

  四、
使用繼承抛出自訂異常


  在PHP中,妳可以抛出任何物件作為一個異常;但是,首先該異常應該繼承自PHP的內建異常類。透過建立妳自己的自訂異常,妳可以記錄其它有關於該錯誤的資訊,例如在一個日誌檔案中建立一個入口,或做妳喜歡做的任何事情。妳的自訂異常將要做如下幾件事情:

  
記錄由查詢產生的來自DB物件的錯誤訊息。

   給出查詢錯誤發生所在行程式碼的準確細節—透過檢查調用棧。

  
顯示錯誤訊息和查詢文字—當被轉換成一個字串時。

  為了得到錯誤資訊和查詢文字,需要對DBQuery物件作多處變更。

  1.
一個新的protected內容—compiledQuery—需要被新增到類中。

  2.
compile()函式使用查詢文字更新查詢compiledQuery內容。

  3.
應該加入一個檢索編譯的查詢文字的函式。

  4.
還應該加入一個函式—它得到當前的與DBQuery物件相關聯的DB物件。

  清單4.抛出一個異常。

class DBQuery
{
 /**
 *在調用compile()或execute()之後存儲查詢的編譯版本
 *

 * @var string $compiledQuery
 */
 protected $compiledQuery;

 /**
 * 返回編譯的查詢而不執行它。
 * @參數:mixed $params,...查詢參數
 *
@返回:字串—編譯的查詢
 */
 public function compile($params='')
 {
  if (!
$this->stored_procedure) {
   throw new Exception("存儲過程沒被初始化.");
  }

  /*代替參數*/
  $params = func_get_args(); //得到函式參數
  $query =
preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
  return
($this->compiledQuery = $this->add_strings($query)); //把字串放回查詢中
 }

 public function getDB()
 {
  return $this->db;
 }
 public
function getCompiledQuery()
 {
  return $this->compiledQuery;
 }

}

  現在,妳可以實現QueryException類。注意妳是如何遍歷調用棧以在指令檔中尋找實際導緻錯誤的位置的。這正好適用於當抛出異常的DBQuery物件是一個繼承自DBQuery物件的子類的情況。

  清單5:QueryException類。

/**
*查詢異常
*
*當試圖執行一個查詢時,如果一個錯誤發生,將由{@link
DBQuery}物件抛出錯誤
*/
class QueryException extends Exception
{
 /**

 * 查詢文字
 *
 * @var字串$QueryText;
 */
 protected
$QueryText;
 /**
 *來自資料庫的錯誤號/程式碼
 *
 * @var字串$ErrorCode
 */

 protected $ErrorNumber;
 /**
 *來自資料庫的錯誤訊息
 *
 *
@var字串$ErrorMessage
 */
 protected $ErrorMessage;

 /**
 *類構造器
 *
 * @參數:DBQuery $db,是抛出異常的查詢物件
 */
 public
function __construct(DBQuery $query)
 {
  /*得到調用棧*/
  $backtrace =
$this->GetTrace();
  /*把行和檔案設定到錯誤實際發生的位置*/
  if (count($backtrace) > 0) {

   $x = 1;
   /*如果查詢類被繼承,那麼我們需要略過由子類所進行的調用*/
   while((!
isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['class']) &&
is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||

(strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !==
false ) {
    /*迴圈執行,只要沒有行號或調用的函式是DBQuery類的一個子類*/
    ++$x;

    /*如果我們到達棧底,那麼我們使用第一個調用者*/
    if (($x) >= count($backtrace)) {

     $x = count($backtrace);
     break;
    }

   }
   /*如果上麵的迴圈至少執行一次,那麼我們可以把它減1以尋找實際的引起錯誤的程式碼行
   */
   if ($x !=
1) {
    $x -= 1;
   }

   /*最後,我們可以設定檔案和行號,這應該可以反映出引起錯誤的SQL陳述式*/
   $this->line =
$backtrace[$x]['line'];
   $this->file = $backtrace[$x]['file'];
  }

  $this->QueryText = $query->getCompiledQuery();
  $this->ErrorNumber =
$query->getDB()->errno();
  $this->ErrorMessage = $query->getDB()->error();

  /*調用超類的異常構造器*/
  parent::__construct('Query Error', 0);
 }

 /**
 *得到查詢文字
 *
 * @返回字串查詢文字
 */
 public function
GetQueryText()
 {
  return $this->QueryText;
 }
 /**

 *得到錯誤號
 *
 * @返回字串錯誤號
 */
 public function GetErrorNumber()

 {
  return $this->ErrorNumber;
 }
 /**
 *得到錯誤訊息
 *

 * @返回字串錯誤訊息
 */
 public function GetErrorMessage()
 {

  return $this->ErrorMessage;
 }
 /**
 *當物件被轉換為一個字串時調用。
 *
@返回字串
 */
 public function __toString()
 {
  $output = "Query
Error in {$this->file} on line {$this->line}nn";
  $output .= "Query:
{$this->QueryText}n";
  $output .= "Error: {$this->ErrorMessage}
({$this->ErrorNumber})nn";

  return $output;
 }
}

  至此,在本節開始看到的程式碼可以工作了。

  五、
結論


  在本文中,妳看到了代理是怎樣把與查詢相聯係的DB介麵對應到針對一個特定的查詢結果上的操作。DBQuery物件暴露相同的函式,例如fetch_assoc(),作為DB物件。然而,這些都是針對單個查詢起作用。妳還學習了如何使用自訂異常來給出詳細資訊—一個錯誤發生在何時何地,以及它們怎樣更好地控制錯誤的處理。

<<<返回技術中心

技術文章

站內新聞

我要啦免费统计