在第2章中,我们介绍了如何以普通文件的格式保存Bob汽车零部件商店的订单数据。我们知道,文件I/O(事实上,任何类型的I/O)是程序经常出现错误的地方。这就使得它成为应用异常处理的合理位置。
回顾初始代码,可以看到,写文件时可能会出现三种情况的错误:文件无法打开、无法获得写锁或者文件无法写入。我们为每一种可能性都创建了一个异常类。这些异常类的代码如程序清单7-4所示。
程序清单7-4 file_exceptions.php——文件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方法,从而可以解释所发生的异常类型。
我们重新编写了第2章的processorder.php文件,在其中使用了异常。该文件的新版本如程序清单7-5所示。
程序清单7-5 processorder.php——Bob汽车零部件商店程序的订单处理脚本,该脚本已经包括了异常处理
<?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./"tirest/".$oilqty./"oilt/"
.$sparkqty./"spark plugst$/".$totalamount
./"t/".$address./"n/";
//open file for appending
try
{
if(!([email protected](/"$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代码块。要说明这一点,我们只给出了两个catch代码块:一个用来处理fileOpen-Exception异常,而另一个用来处理Exception。由于其他异常都是从Exception继承过来的,它们将被第二个catch代码块捕获。catch代码块与每一个instanceof操作符相匹配。这就是为每一个类扩展自己异常类的理由。
一个重要警告:如果异常没有匹配的catch语句块,PHP将报告一个致命错误。