技术开发 频道

PHP 开发经典教程 (Part 12):程序缺陷排除

PHP (第十二章): 程序缺陷排除- 第二部分-

扣动扳机

到目前为止,我们一直在讨论处理由PHP自身所产生的错误,但为什么要停留在那里呢?PHP也允许你使用其内建的错误处理系统来生成你自己自定义的错误。

这是通过一个名为trigger_error()的函数来完成的,该函数允许你生成为用户所保留的三种错误类型中的任意一个:E_USER_NOTICE、E_USER_WARNING和E_USER_ERROR。当这些错误被触发时,PHP内建的处理器将会自动唤醒以处理这些错误。
<?php // function to test a number // generates E_USER_WARNING if number is a float // generates E_USER_ERROR is number is negative function testNumber($num) { // float // trigger a warning if (is_float($num)) { trigger_error("Number $num is not an integer", E_USER_WARNING); } // negative // trigger a fatal error if ($num < 0) { trigger_error("Number $num is negative", E_USER_ERROR); } } // test the function with different values testNumber(100); testNumber(5.6); testNumber(-8); ?>
如果你打算用一个自定义的错误处理器来处理你自定义的错误……,嗯,你只是很难满足的,不是吗?请看下这个例子,该例子重新编写了之前的脚本以使用用户自定义的错误处理器:
<?php // function to test a number // generates E_USER_WARNING if number is a float // generates E_USER_ERROR is number is negative function testNumber($num) { // float // trigger a warning if (is_float($num)) { trigger_error("Number $num is not an integer", E_USER_WARNING); } // negative // trigger a fatal error if ($num < 0) { trigger_error("Number $num is negative", E_USER_ERROR); } } // custom error handler function myErrorHandler($type, $msg, $file, $line, $context) { switch ($type) { // warnings case E_USER_WARNING: // report error print "Non-fatal error on line $line of $file: $msg <br />"; break; // fatals case E_USER_ERROR: // report error and die() die("Fatal error on line $line of $file: $msg <br />"); break; // notices default: // do nothing break; } } // set the name of the custom handler set_error_handler('myErrorHandler'); // test the function with different values testNumber(100); testNumber(5.6); testNumber(-8); ?>
 请注意,如果用户产生致命错误发生的话,那么调用die()函数是自定义的处理器的责任(PHP不会自动做这个的)。
你也可以使用同样的方法来处理异常。请向下滚动鼠标,让我向你展示如何去做。

捕获

如果你正在使用PHP 5,那么除了目前为止所讨论的关于新的异常模型技术(异常是另外一种错误)之外你也有其他的选择。异常是PHP中新出现的技术(虽然它们已经在诸如Java和Python语言中多年了)而且它们正在引起人们大量的激动。

在基于异常的方法中,程序代码被try()块结构包围起来然后该代码所产生的异常被catch()块结构“捕获”和解决。多个catch()块是可能的,其中每一个处理不同的错误类型;这就允许开发人员捕获不同类型的错误而且执行适当的异常处理。

下面是一个典型的try-catch()块看上去的样子:
try { execute this block } catch (exception type 1) { execute this block to resolve exception type 1 } catch (exception type 2) { execute this block to resolve exception type 2 }
诸如此类……

当PHP遇到被包含在try-catch()块内部的代码时,它首先试图执行try()块内部的代码。如果该代码被处理时无异常产生,那么控制就转移到try-catch()块之后的代码行。然而,如果在运行try()块内部的代码时产生了异常,那么PHP在发生异常的地方停止该try()块的执行然后检查每个catch()块以查看是否有一个该异常的处理器。如果找到一个处理器,那么在适当的catch()块内部的代码得到执行;如果没有找到,那么就产生一个致命的错误。使用异常以一种良好的方式来处理该致命错误甚至也是可能的;请访问http://www.php.net/set-exception-handler以获得更多的关于这方面的内容:
异常本身通过PHP的throw语句来产生。Throw语句需要被传递一个描述性消息和一个可选的错误代码。当异常被产生时,该描述和代码将会对异常处理器变得可用。

想要看看这是如何工作的吗?下面是一个例子:
<?php // PHP 5 error_reporting(0); // try this code try { $file = 'somefile.txt'; // open file if (!$fh = fopen($file, 'r')) { throw new Exception('Could not open file!'); } // read file contents if (!$data = fread($fh, filesize($file))) { throw new Exception('Could not read file!'); } // close file fclose($fh); // print file contents echo $data; } // catch errors if any catch (Exception $e) { print 'Something bad just happened...'; } ?>
如果文件不存在或不可读,那么throw语句将会产生一个异常(基本上,PHP内建异常对象的一个实例)然后为其传递一个描述错误的消息。当这样的异常被产生时,程序控制转到第一个catch()块。如果该catch()块可以处理该异常类型,那么该catch()块内部的代码得到执行。如果第一个catch()块不能处理所产生的异常,那么程序控制就转移到下一个catch()块中。

不要在该点上对“异常类型”过多担心(所有的都会被简单介绍)。目前,所有你需要知道的就是上面的catch()块将会捕获所有的异常而无论其类型。

现在,之前的代码清单中有一个问题。虽然catch()块将会捕获异常然后打印一条消息,但是它不能显示throw语句所发送的异常的描述性消息。为了访问该消息和一些其他有趣的信息片段,有必要使用一些Exception对象内建的方法。请看之前脚本的修订版,其对此做了解释说明:
<?php // PHP 5 error_reporting(0); // try this code try { $file = 'somefile.txt'; // open file if (!$fh = fopen($file, 'r')) { throw new Exception('Could not open file!', 12); } // read file contents if (!$data = fread($fh, filesize($file))) { throw new Exception('Could not read file!', 9); } // close file fclose($fh); // print file contents echo $data; } // catch errors if any catch (Exception $e) { print '<h2>Exception</h2>'; print 'Error message: ' . $e->getMessage() . '<br />'; print 'Error code: ' . $e->getCode() . '<br />'; print 'File and line: ' . $e->getFile() . '(' . $e->getLine() . ')<br />'; print 'Trace: ' . $e->getTraceAsString() . '<br />'; } ?>
当你运行该脚本时,你会看到异常处理器所产生的消息包含如下内容:
? throw语句发送的描述性数据,
? 错误代码(也是被throw语句发送的)
? 异常发生地的文件名称和行号,以及
? 以类层次结构指示异常过程的堆栈跟踪信息,如果有的话。
该数据是通过在catch()块内分别调用Exception对象的getMessage()、getCode()、getFile()、getLine()和getTraceAsString()方法而产生的。

添加一些类

你可以通过子类化的Exception对象然后使用多个catch()块以不同的方法来处理不同的异常。下面的例子是对这点的一个简单解释:
<?php // PHP 5 // sub-class the Exception class class NegativeNumException extends Exception {} class OutOfRangeException extends Exception {} class FloatException extends Exception {} // function to test a number function testNumber($num) { // float // trigger an exception if (is_float($num)) { throw new FloatException($num); } // negative // trigger an exception if ($num < 0) { throw new NegativeNumException($num); } // out of range // trigger an exception if ($num > 1000 || $num < 100) { throw new OutOfRangeException($num); } } // try this code try { testNumber(-19); } // catch errors, if any catch (NegativeNumException $e) { print 'A negative number was provided ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />'; } catch (OutOfRangeException $e) { print 'The number provided is out of range ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />'; } catch (FloatException $e) { print 'The number provided is not an integer ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />'; } catch (Exception $e) { print 'Error message: ' . $e->getMessage() . '<br />'; print 'Error code: ' . $e->getCode() . '<br />'; print 'File and line: ' . $e->getFile() . '(' . $e->getLine() . ')<br />'; print 'Trace: ' . $e->getTraceAsString() . '<br />'; } ?>
在该例子中,我已经从基本的对象创建了三种新的Exception子类,每个可能的错误一个子类。接着,我为每个异常类型建立了catch()块,然后编写了特定于每种类型的异常处理代码。根据发生哪类异常(你可以向testNumber()函数发送不同的值来产生不同的异常),调用适当的catch()块然后打印不同的错误消息。

请注意,因为PHP会一直使用与异常类型匹配的第一个catch()块,而且因为Exception类匹配所有的异常,所以catch()块必须按照最精确的catch()块优先的顺序进行排列。这个在上述例子中已经实现,上述例子中,catch()块出现在列表的最后。

下面是另外一个例子,该例子解释说明了一个更加有用的应用程序(在用户验证的类中使用异常模型以提供易于理解的错误处理)。请看:
<?php // PHP 5 // class definition class userAuth { // define properties private $username; private $passwd; private $passwdFile; // constructor // must be passed username and non-encrypted password public function __construct($username, $password) { $this->username = $username; $this->passwd = $password; } // set .htaccess-style file to check for passwords public function setPasswdFile($file) { $this->passwdFile = $file; } // perform password verification public function authenticateUser() { // check that the file exists if (!file_exists($this->passwdFile)) { throw new FileException("Password file cannot be found: " . $this->passwdFile); } // check that the file is readable if (!is_readable($this->passwdFile)) { throw new FileException("Unable to read password file: ". $this->passwdFile); } // read file $data = file($this->passwdFile); // iterate through file foreach ($data as $line) { $arr = explode(":", $line); // if username matches, test password if ($arr[0] == $this->username) { // get salt and crypt(), assuming encryption $salt = substr($arr[1], 0, 2); // if match, user/pass combination is correct if ($arr[1] == crypt($this->passwd, $salt)) { echo "User was authenticated"; // do some other stuff } // otherwise return exception else { throw new AuthException("Incorrect password"); break; } } else { // could not find a username match // return exception throw new AuthException("No such user"); } } } // end class definition } // subclass exceptions class FileException extends Exception {}; class AuthException extends Exception {}; // try the code try { // create instance $ua = new userAuth("joe", "secret"); // set password file $ua->setPasswdFile("password.txt"); // perform authentication $ua->authenticateUser(); } // catch authentication failures, if any catch (FileException $e) { // print file errors print "A file error occurred. ".$e->getMessage(); } catch (AuthException $e) { // an authentication error occurred print "An authentication error occurred. ".$e->getMessage(); // more normally, redirect to new page on auth errors, e.g. // header ('Location: login_fail.php'); } catch (Exception $e) { print "An unknown error occurred"; } ?>
这里,根据错误类型,FileException()或者AuthException()将会被产生(被相应的catch()块处理)。我们注意到,异常处理框架理解和扩展是多么的容易。准确地讲,正是由于它的易于使用和扩展帮助新的PHP 5模型的评价超过了早期的更基本的处理应用错误的技术。
恩,那就是目前的内容。为了更多的PHP 101,请尽快返回!
0
相关文章