PHP学习(9)——错误和异常处理
1.Exception类
这个类是PHP为异常处理提供的内置类。构造函数的两个参数分别是错误消息和错误代码。
除了构造函数之外,该类还提供了如下的内置方法:
· getCode() 返回传递给构造函数的代码
· getMessage() 返回传递给构造函数的消息
· getFile() 返回产生异常的代码文件的完整路径
· getLine() 返回代码文件中产生异常的代码行号
· getTrace() 返回一个包含了产生异常的代码回退路径的数组
· getTraceAsString() 返回与getTrace()方向相同的信息,该信息将被格式化成一个字符串。
· _toString() 允许简单地显示一个Exception对象,并且给出以上所有方法可以提供的信息。
通过执行以下命令,可以获得相同的异常信息(以及代码回退路径):
echo $e;
回退路径显示了在发生异常时所执行的函数。
2.异常控制结构
异常处理的基本思想是代码在try代码块被调用执行。在PHP中,异常必须手动抛出。
异常控制结构:try...throw...catch
可以将多个catch代码块与一个try代码块进行关联。还可以在一个catch代码块产生新的异常。
如下代码抛出并捕获一个异常:
<?php try { throw new Exception("A terrible error has occurred", 42); } catch (Exception $e) { echo "Exception ". $e->getCode(). ": ". $e->getMessage()."<br />". " in ". $e->getFile(). " on line ". $e->getLine(). "<br />"; } ?>
这里我们使用了exception类的前四个方法,catch代码块报告了异常错误信息以及发生错误位置的说明。
3.用户自定义异常
可以扩展Exception类来创建自己的异常类。
可能需要继承的代码:
<?php class Exception{ function __construct(string $message=NULL,int $code=0){ if(func_num_args()){ $this->message = $message; } $this->code = $code; $this->file = __FILE__; // of throw clause $this->line = __LINE__; // of throw clause $this->trace = debug_backtrace(); $this->string = StringFormat($this); } protected $message = "Unknown exception "; // exception message protected $code = 0; // user defined exception code protected $file; // source filename of exception protected $line; // source line of exception private $trace; // backtrace of exception private $string; // internal only!! final function getMessage(){ return $this->message; } final function getCode(){ return $this->code; } final function getFile(){ return $this->file; } final function getTrace(){ return $this->trace; } final function getTraceAsString(){ return self::TraceFormat($this); } function _toString(){ return $this->string; } static private function StringFormat(Exception $exception){ // ... a function not available in PHP scripts // that returns all relevant information as a string } static private function TraceFormat(Exception $exception){ // ... a function not available in PHP scripts // that returns the backtrace as a string } } ?>
该类的大多数公有方法都是final的:这就意味着不能重载这些方法。我们可以创建自己的Exception子类,但是不能改变这些基本方法的行为。请注意,_toString()函数可以重载,因此我们可以改变异常的显示方式。也可以添加自己的方法。
用户自定义的Exception类示例:
<?php class myException extends Exception { function __toString() { return "<table border=\"1\"> <tr> <td><strong>Exception ".$this->getCode()." </strong>: ".$this->getMessage()."<br />"." in ".$this->getFile()." on line ".$this->getLine()." </td> </tr> </table><br />"; } } try { throw new myException("A terrible error has occurred", 42); } catch (myException $m) { echo $m; } ?>
在以上代码中,我们声明了一个新的异常类myException,该类扩展了Exception基类。该类与Exception类之间的差异在于重载了_toString()方法,从而为打印异常提供了一个更好的方法。
4.文件I/O相关的异常
写文件时可能会出现三种情况的错误:文件无法打开、无法获得写锁或者文件无法写入。我们为每一种可能性都创建了一个异常类:
<?php class fileOpenException extends Exception { function __toString() { return "fileOpenException ". $this->getCode() . ": ". $this->getMessage()."<br />"." in " . $this->getFile(). " on line ". $this->getLine() . "<br />"; } } class fileWriteException extends Exception { function __toString() { return "fileWriteException ". $this->getCode() . ": ". $this->getMessage()."<br />"." in " . $this->getFile(). " on line ". $this->getLine() . "<br />"; } } class fileLockException extends Exception { function __toString() { return "fileLockException ". $this->getCode() . ": ". $this->getMessage()."<br />"." in " . $this->getFile(). " on line ". $this->getLine() . "<br />"; } } ?>
Exception类的这些子类并没有执行任何特别的操作。事实上,对于这个应用程序的作用来说,可以让它们成为空的子类或者使用PHP所提供的Exception类。然而,我们为每个子类提供了_toString()方法,从而可以解释所发生的异常类型:
<?php require_once("file_exceptions.php"); // create short variable names $tireqty = $_POST['tireqty']; $oilqty = $_POST['oilqty']; $sparkqty = $_POST['sparkqty']; $address = $_POST['address']; $DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT']; ?> <html> <head> <title>Bob's Auto Parts - Order Results</title> </head> <body> <h1>Bob's Auto Parts</h1> <h2>Order Results</h2> <?php $date = date('H:i, jS F'); echo "<p>Order processed at ".$date."</p>"; echo '<p>Your order is as follows: </p>'; $totalqty = 0; $totalqty = $tireqty + $oilqty + $sparkqty; echo "Items ordered: ".$totalqty."<br />"; if( $totalqty == 0) { echo "You did not order anything on the previous page!<br />"; } else { if ( $tireqty > 0 ) { echo $tireqty." tires<br />"; } if ( $oilqty > 0 ) { echo $oilqty." bottles of oil<br />"; } if ( $sparkqty > 0 ) { echo $sparkqty." spark plugs<br />"; } } $totalamount = 0.00; define('TIREPRICE', 100); define('OILPRICE', 10); define('SPARKPRICE', 4); $totalamount = $tireqty * TIREPRICE + $oilqty * OILPRICE + $sparkqty * SPARKPRICE; $totalamount=number_format($totalamount, 2, '.', ' '); echo "<p>Total of order is ".$totalamount."</p>"; echo "<p>Address to ship to is ".$address."</p>"; $outputstring = $date."\t".$tireqty." tires \t".$oilqty." oil\t" .$sparkqty." spark plugs\t\$".$totalamount ."\t". $address."\n"; // open file for appending try { if (!($fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'ab'))) throw new fileOpenException(); if (!flock($fp, LOCK_EX)) throw new fileLockException(); if (!fwrite($fp, $outputstring, strlen($outputstring))) throw new fileWriteException(); flock($fp, LOCK_UN); fclose($fp); echo "<p>Order written.</p>"; } catch (fileOpenException $foe) { echo "<p><strong>Orders file could not be opened. Please contact our webmaster for help.</strong></p>"; } catch (Exception $e) { echo "<p><strong>Your order could not be processed at this time. Please try again later.</strong></p>"; } ?> </body> </html>
可以看到,以上脚本的文件I/O部分被封装在一个try代码块中。通常,良好的编码习惯要求try代码块的代码量较少,并且在代码块的结束处捕获相关异常。这使得异常处理代码更容易编写和维护,因为可以看到所处理的内容。
如果无法打开文件,将抛出一个fileOpenException异常;如果无法锁定该文件,将抛出一个fileLockException异常;而如果无法写这个文件,将抛出一个fileWriteException异常。
我们给出了两个catch代码块:一个用来处理fileOpen-Exception异常,而另一个用来处理Exception。由于其他异常都是从Exception继承过来的,它们将被第二个catch代码块捕获。catch代码块与每一个instanceof操作符相匹配。这就是为每一个类扩展自己异常类的理由。
如果异常没有匹配的catch语句块,PHP将报告一个致命错误。
请注意fopen()函数的调用仍然使用了@错误抑制操作符前缀。如果该函数调用失败,PHP将发出一个警告,根据php.ini中的错误报告设置不同,该警告可能会被报告或者记录。无论是否产生一个异常,这个警告仍然会发出。
好了,从零开始攻略PHP系列到这里就水完了,仅供参考,好多我也是一知半解,只是整理出来方便查阅。下个月会陆续发一些数据库的,也就是该书整理的第二篇。
整理自《PHP and MySQL Web 开发》