PHP 5.0異常處理機制深度探索

來源:互聯網
上載者:User
異常處理    本文面向希望瞭解PHP5異常處理機制的程式員。閱讀本文你需要具有一定物件導向編程和PHP基礎。

  PHP5內建的異常類需要有以下成員方法:

__construct()建構函式,需要一個出錯資訊和一個可選的整型錯誤標記作參數getMessage()取得出錯資訊 getCode()
出錯的代碼 getFile()異常發生的檔案getLine()異常發生的行數 getTrace()跟蹤異常每一步傳遞的路線,存入數組,返回該數組 getTraceAsString()和getTrace()功能一樣,但可以將數組中的元素轉成字串並按一定格式輸出
  可以看出來,Exception 類的結構和Pear_Error 很相似。當你的指令碼中遇到一個錯誤,你可以建立你的異常對象:

$ex = new Exception( "Could not open $this->file" );
  Exception類的建構函式將接受一個出錯資訊和一個錯誤碼。

   使用 throw關鍵字

  建立一個Exception對象後你可以將對象返回,但不應該這樣使用,更好的方法是用throw關鍵字來代替。throw用來拋出異常:

throw new Exception( "my message", 44 );
  throw 將指令碼的執行中止,並使相關的Exception對象對客戶代碼可用。

  以下是改進過的getCommandObject() 方法:

  index_php5.php

<?php // PHP 5
require_once('cmd_php5/Command.php');
class CommandManager {
 private $cmdDir = "cmd_php5";

 function getCommandObject($cmd) {
  $path = "{$this->cmdDir}/{$cmd}.php";
  if (!file_exists($path)) {
   throw new Exception("Cannot find $path");
  }
 require_once $path;
 if (!class_exists($cmd)) {
  throw new Exception("class $cmd does not exist");
 }

 $class = new ReflectionClass($cmd);
 if (!$class->isSubclassOf(new ReflectionClass('Command'))) {
  throw new Exception("$cmd is not a Command");
 }
 return new $cmd();
}
}
?>
  代碼中我們使用了PHP5的反射(Reflection)API來判斷所給的類是否是屬於Command 類型。在錯誤的路徑下執行本指令碼將會報出這樣的錯誤:

Fatal error: Uncaught exception 'Exception' with message 'Cannot find command/xrealcommand.php' in /home/xyz/BasicException.php:10
Stack trace:
#0 /home/xyz/BasicException.php(26):
CommandManager->getCommandObject('xrealcommand')
#1 {main}
thrown in /home/xyz/BasicException.php on line 10
  預設地,拋出異常導致一個fatal error。這意味著使用異常的類內建有安全機制。而僅僅使用一個錯誤標記,不能擁有這樣的功能。處理錯誤標記失敗只會你的指令碼使用錯誤的值來繼續執行。

   Try-catch 語句

  為了進一步處理異常,我們需要使用try-catch語句—包括Try語句和至少一個的catch語句。任何調用 可能拋出異常的方法的代碼都應該使用try語句。Catch語句用來處理可能拋出的異常。以下顯示了我們處理getCommandObject()拋出的異常的方法:

  index_php5.php 後半段

<?php
// PHP 5
try {
 $mgr = new CommandManager();
 $cmd = $mgr->getCommandObject('realcommand');
 $cmd->execute();
} catch (Exception $e) {
 print $e->getMessage();
 exit();
}
?>


  可以看到,通過結合使用throw關鍵字和try-catch語句,我們可以避免錯誤標記“汙染”類方法返回的值。因為“異常”本身就是一種與其它任何對象不同的PHP內建的類型,不會產生混淆。

  如果拋出了一個異常,try語句中的指令碼將會停止執行,然後馬上轉向執行catch語句中的指令碼。

  如果異常拋出了卻沒有被捕捉到,就會產生一個fatal error。

處理多個錯誤

  在目前為止異常處理看起來和我們傳統的作法—檢驗返回的錯誤標識或對象的值沒有什麼太大區別。讓我們將CommandManager處理地更謹慎,並在建構函式中檢查command目錄是否存在。

  index_php5_2.php

<?php
// PHP 5
require_once('cmd_php5/Command.php');
class CommandManager {
 private $cmdDir = "cmd_php5";

 function __construct() {
  if (!is_dir($this->cmdDir)) {
   throw new Exception("directory error: $this->cmdDir");
  }
 }

 function getCommandObject($cmd) {
  $path = "{$this->cmdDir}/{$cmd}.php";
  if (!file_exists($path)) {
   throw new Exception("Cannot find $path");
  }
  require_once $path;
  if (!class_exists($cmd)) {
   throw new Exception("class $cmd does not exist");
  }

  $class = new ReflectionClass($cmd);
  if (!$class->isSubclassOf(new ReflectionClass('Command'))) {
   throw new Exception("$cmd is not a Command");
  }
  return new $cmd();
 }
}
?>


  這裡有兩個地方的調用可能導致程式出錯(__construct()和getCommandObject())。儘管如此,我們不需要調整我們的客戶代碼。你可以在try語句中增添眾多內容,然後在catch中統一處理。如果CommandManager 對象的建構函式拋出一個異常,則try語句中的執行中止,然後catch語句被調用捕捉相關的異常。同樣地,getCommandObject()也是如此。這樣,我們有同時存在兩個潛在的引發錯誤的地方,和一個唯一的語句來處理所有的錯誤。這讓我們的代碼看起來更加整潔,又可以滿足錯誤處理的要求。和前面提到的PHP的傳統的錯誤方法相比,顯然很有優勢。

  index_php5_2.php 後半段

  注意:儘管和index_php5.php相比,前半段代碼有兩個可能出錯的地方,這段代碼和index_php5.php的後半段完全相同。

<?php
// PHP 5
try {
 $mgr = new CommandManager(); // potential error
 $cmd = $mgr->getCommandObject('realcommand');
 // another potential error
 $cmd->execute();
} catch (Exception $e) {
 // handle either error here
 print $e->getMessage();
 exit();
}
?>


  還有一個地方我們沒有提到。我們怎樣區分不同類型的錯誤?例如,我們可能希望用一種方法來處理找不到目錄的錯誤,而用另一種方法來處理非法的command類。

  Exception類可以接受一個可選的整型的錯誤標識,這是在catch語句中區分不同錯誤類型的一個方法。

  index_php5_3.php

<?php
// PHP 5
require_once('cmd_php5/Command.php');
class CommandManager {
 private $cmdDir = "cmd_php5";
 const CMDMAN_GENERAL_ERROR = 1;
 const CMDMAN_ILLEGALCLASS_ERROR = 2;

 function __construct() {
  if (!is_dir($this->cmdDir)) {
   throw new Exception("directory error: $this->cmdDir", self::CMDMAN_GENERAL_ERROR);
  }
 }

 function getCommandObject($cmd) {
  $path = "{$this->cmdDir}/{$cmd}.php";
  if (!file_exists($path)) {
   throw new Exception("Cannot find $path", self::CMDMAN_ILLEGALCLASS_ERROR);
  }
  require_once $path;
  if (!class_exists($cmd)) {
   throw new Exception("class $cmd does not exist", self::CMDMAN_ILLEGALCLASS_ERROR);
  }

  $class = new ReflectionClass($cmd);
  if (!$class->isSubclassOf(new ReflectionClass('Command'))) {
   throw new Exception("$cmd is not a Command", self::CMDMAN_ILLEGALCLASS_ERROR);
  }
  return $class->newInstance();
 }
}
?>


  通過傳遞 CMDMAN_ILLEGALCLASS_ERROR和 CMDMAN_GENERAL_ERROR其中之一的參數給我們拋出的異常對象,我們就可以讓客戶代碼區分不同類型的錯誤,並定義不同的處理策略。

  index_php5_3.php

<?php // PHP 5
try {
 $mgr = new CommandManager();
 $cmd = $mgr->getCommandObject('realcommand');
 $cmd->execute();
} catch (Exception $e) {
 if ($e->getCode() == CommandManager::CMDMAN_GENERAL_ERROR) {
  // no way of recovering
  die($e->getMessage());
 } else if ($e->getCode() == CommandManager::CMDMAN_ILLEGALCLASS_ERROR) {
  error_log($e->getMessage());
  print "attempting recovery\n";
  // perhaps attempt to invoke a default command?
 }
}
?>


  我們也可以用另一種方法來實現這樣的效果—從最根本的Exception類中派生出代表不同類型異常的子類,再拋出和捕捉。

[1] [2] 下一頁  



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.