PHP5之前的錯誤處理 在PHP5之前的程序錯誤處理多使用以下三種辦法: 1.使用trigger_error()或die()函數來生成一個腳本層次的警告(warning)或致命錯誤(fatal error); 2.在類方法或函數中返回一個錯誤標記(如false),也可能設置一個之後可以檢查的屬性或全局變量(如$error),然後在適合的地方檢驗其值再決定是否繼續執行程序(如if($error==1){}); 3.使用PEAR處理錯誤; (一)使用die()或trigger_error() 你可以使用die()函數來結束程序運行。以下是一個簡單的類,它嘗試從一個目錄中加載一個類文件。 <? 代碼列表 index.php ?> <?php // PHP 4 require_once('cmd_php4/Command.php'); class CommandManager { var $cmdDir = "cmd_php4"; function getCommandObject($cmd) { $path = "{$this->cmdDir}/{$cmd}.php"; if (!file_exists($path)) { die("Cannot find $path\n"); } require_once $path; if (!class_exists($cmd)) { die("class $cmd does not exist"); } $ret = new $cmd(); if (!is_a($ret, 'Command')) { die("$cmd is not a Command"); } return $ret; } } ?> 這是一個用PHP實現“Command Pattern設計模式”的簡單例子(請參看《Java與模式》)。使用這個類的程序員(客戶程序員client coder)可以將一個類放到目錄中(例中為cmd_php4目錄)。一旦文件和其中包含的類同名,並且這個類是Command類的子類,我們的類方法將生成一個可用的Command對象。Command類中定義了一個 execute()方法用來執行找到的命令,即 getCommandObject()方法返回的對象將執行execute(). 我們再看看父類Command類,我們將它存在cmd_php4/Command.php文件中。 <? 代碼列表cmd_php4/Command.php ?> <?php // PHP 4 class Command { function execute() { die("Command::execute() is an abstract method"); } } ?> 你可以看到,Command是PHP4中抽象類的實現,我們無法直接將其實例化,而必須先從中派生中子類然後再實例化。當我們使用PHP5後,我們可以使用更好的方式—使用abstract關鍵字將類和方法聲明為“抽象”: <? 代碼列表 ?> <?php // PHP 5 abstract class Command { abstract function execute(); } ?> 下面是對上面的抽象類的實現,覆寫了execute()方法,在其中加入真正可以執行的內容。這個類命名為realcommand,可以在cmd_php4/realcommand.php文件中找到。 <? 代碼列表 cmd_php4/realcommand.php ?> <?php // PHP 4 require_once 'Command.php'; class realcommand extends Command { function execute() { print "realcommand::execute() executing as ordered sah!\n"; } } ?> 使用這樣的結構可以使代碼變得很靈活。你可以在任何時候增加新的Command類,而不需要改變外圍的框架。但是你不得不注意一些潛在的中止腳本執行的因素。我們需要確保類文件存在,並且在文件中該類存在,並且該類是Command的子類(就像realcommand一樣)。 在例子中,如果我們嘗試尋找類的操作失敗,腳本執行將會中止,這體現了代碼的安全性。但這段代碼不靈活,沒有足夠的彈性。極端的反映是類方法只能進行積極正面的操作,它只負責找出和實例化一個Command對象。它無法處理更大范圍內腳本執行的錯誤(當然它也不應該負責處理錯誤,如果我們給某個類方法加上太多與周邊代碼的關聯,那麼這個類的重用將會變得困難,不易擴展)。 盡管使用die()避免了在getCommandObject()方法中嵌入腳本邏輯的危險,它對於對於錯誤的反應顯得過於激烈—-馬上中止程序。事實上有時候我們並不希望在找不到想要的類文件時就馬上停止執行程序,也許我們有一個默認的命令讓程序繼續執行。 我們或許可以通過trigger_error()生成一個用戶警告來代替,使程序更具有靈活性。 <? 代碼列表 Index2.php ?> <?php // PHP 4 require_once('cmd_php4/Command.php'); class CommandManager { var $cmdDir = "cmd_php4"; function getCommandObject($cmd) { $path = "{$this->cmdDir}/{$cmd}.php"; if (!file_exists($path)) { trigger_error("Cannot find $path", E_USER_ERROR); } require_once $path; if (!class_exists($cmd)) { trigger_error("class $cmd does not exist", E_USER_ERROR); } $ret = new $cmd(); if (!is_a($ret, 'Command')) { trigger_error("$cmd is not a Command", E_USER_ERROR); } return $ret; } } ?> 如果你使用trigger_error()函數來替代die(),你的代碼在處理錯誤上會更具優勢,對於客戶程序員來說更易於處理錯誤。trigger_error()接受一個錯誤信息和一個常量作為參數。常量為: 常量 含義 E_USER_ERROR A fatal error E_USER_WARNING A non-fatal error E_USER_NOTICE A report that may not represent an error 你可以設計一個錯誤處理器,然後再定義一個處理器選擇函數set_error_handler()來使用這個錯誤處理器。 <? 代碼列表 Index2.php 後半段?> <?php // PHP 4 function cmdErrorHandler($errnum, $errmsg, $file, $lineno) { if($errnum == E_USER_ERROR) { print "error: $errmsg\n"; print "file: $file\n"; print "line: $lineno\n"; exit(); } } $handler = set_error_handler('cmdErrorHandler'); $mgr = new CommandManager(); $cmd = $mgr->getCommandObject('realcommand'); $cmd->execute(); ?> set_error_handler()接受一個函數名作為參數。如果觸發了一個錯誤,參數中的這個函數會被調用來處理錯誤。函數需要傳入四個參數:錯誤標志,錯誤信息,出錯文件,出錯處的行數。你也可以將一組數組傳遞給set_error_handler()。數組中的第一個元素必須是錯誤處理器將調用的對象,第二個元素是錯誤處理函數的名稱。 可以看出,我們的錯誤處理器相當簡單簡陋,還可以改進。然而盡管你可以在錯誤處理器添加某些功能,如記錄出錯信息,輸出debug數據等,這仍然是一個過於粗糙的錯誤處理途徑。你的選擇僅限於已經考慮到的出錯情況。例如捕捉一個E_USER_ERROR錯誤,如果你願意的話可以不中止腳本的執行(不使用exit()和die()),但如果這樣做的話,可能會引起一些很微妙的bug,本來應該中止的程序卻繼續執行了。
(二) 返回錯誤標記 腳本層次的錯誤處理比較粗糙但很有用。盡管如此,我們有時需要更大的靈活性。我們可以使用返回錯誤標識的辦法來告訴客戶代碼“錯誤發生了!”。這將程序是否繼續,如何繼續的責任交給客戶代碼來決定。 這裡我們改進了前面的例子來返回一個腳本執行出錯的標志(false是一個常用的不錯的選擇)。 <? 代碼列表 index3.php ?> <?php // PHP 4 require_once('cmd_php4/Command.php'); class CommandManager { var $cmdDir = "cmd_php4"; function getCommandObject($cmd) { $path = "{$this->cmdDir}/{$cmd}.php"; if (!file_exists($path)) { return false; } require_once $path; if (!class_exists($cmd)) { return false; } $ret = new $cmd(); if (!is_a($ret, 'Command')) { return false; } return $ret; } } ?> 這意味著你可以根據環境來處理多個錯誤,而不會在第一個錯誤發生時馬上停止程序的執行。 <? 代碼列表 ?> <?php // PHP 4 $mgr = new CommandManager(); $cmd = $mgr->getCommandObject('realcommand'); if (is_bool($cmd)) { die("error getting command\n"); } else { $cmd->execute(); } ?> 或者只是記錄錯誤: <? 代碼列表 ?> <?php // PHP 4 $mgr = new CommandManager(); $cmd = $mgr->getCommandObject('realcommand'); if(is_bool($cmd)) { error_log("error getting command\n", 0); } else { $cmd->execute(); } ?> 使用像“false”這樣的錯誤標志的好處是直觀,但是明顯給出的信息量不夠,我們無法得知到底是在哪一個環節上錯而導致返回false。你可以再設置一個error屬性,這樣在產生錯誤後輸出出錯信息。 <? 代碼列表 index4.php?> <?php // PHP 4