错误处理
PHP5引入了异常(exception),异常是从PHP5内置的Exception类(或其子类)实例化得到的特殊对象。Exception类型的对象用于存放和报告错误信息。Exception类的构造方法接受两个可选的参数:信息字符串和错误代码。这个类提供了一些有用的方法来分析错误条件。Exception类提供的错误通知和调试信息(特别是getTrace()和getTraceAsString()方法返回到信息)还有用。
- 抛出异常
可以联合使用throw关键字和Exception对象来抛出异常。这会停止执行当前方法,并负责将错误返会给调用代码。
//conf01.xml
<?xml version="1.0" ?>
<conf>
<item name="user">bob</item>
<item name="pass">newpass</item>
<item name="host">localhost</item>
</conf>
class Conf
{
private $file;
private $xml;
private $lastmatch;
public function __construct($file)
{
$this->file = $file;
if (!file_exists($file)) {
throw new Exception("file 'file' does not exist");
}
$this->xml = simplexml_load_file($file);
}
public function write()
{
if (!is_writable($this->file)) {
throw new Exception("file '{$this->file}' is not writeable");
}
file_put_contents($this->file, $this->xml->asXML());
}
public function get($str)
{
$matches = $this->xml->xpath("/conf/item[@name=\"$str\"]");
if (count($matches)) {
$this->lastmatch = $matches[0];
return (string) $matches[0];
}
return null;
}
public function set($key, $value)
{
if (!is_null($this->get($key))) {
$this->lastmatch[0] = $value;
return;
}
$conf = $this->xml->conf;
$this->xml->addChild('item', $value)->addAttribute('name', $key);
}
}
__construct()和write()方法可以在工作时不停地检查文件错误,而让其他更合适的代码来响应检测到的错误。如果调用可能抛出异常的方法,那么可以把调用语句放在try子句中。try子句由关键字try及其后的括号组成。try子句必须跟着至少一个catch子句才能处理错误:
try {
$conf = new Conf(dirname(__FILE__) . "/conf01.xml");
print "user: " . $conf->get('user') . "\n";
print "host: " . $conf->get('host') . "\n";
$conf->set("pass", "newpass");
$conf->write();
} catch (Exception $e) {
die($e->__toString());
}
- 异常的子类化
如果要创建用户自定义的异常类,可以从Exception类继承。
实际上,定义多个catch子句时,也只需要一个try子句。调用哪个catch子句取决于抛出异常的类型和参数中类的类型提示。下面我们定义Exception的一些简单子类:
class XmlException extends Exception
{
private $error;
public function __construct(LibXmlError $error)
{
$shortfile = basename($error->file);
$msg = "[{$shortfile},line {$error->line},col {$error->column}]{$error->message}";
$this->error = $error;
parent::__construct($msg, $error->code);
}
public function getLibXmlError()
{
return $this->error;
}
}
class FileException extends Exception
{
}
class ConfException extends Exception
{
}
当SimpleXml扫描到一个损坏的XML文件时,会生成LibXmlError对象。现在在代码中使用这些异常类,并修改__construct()和write():
class Conf
{
//...
public function __construct($file)
{
$this->file = $file;
if (!file_exists($file)) {
throw new Exception("file 'file' does not exist");
}
$this->xml = simplexml_load_file($file,null,LIBXML_NOERROR);
if (!is_object($this->xml)) {
throw new XmlException(libxml_get_last_error());
}
print gettype($this->xml);
$matches=$this->xml->xpath("/conf");
if (!count($matches)) {
throw new ConfException("could not find root element: conf");
}
}
public function write()
{
if (!is_writable($this->file)) {
throw new FileException("file '{$this->file}' is not writeable");
}
file_put_contents($this->file, $this->xml->asXML());
}
//...
__construct()根据遇到的错误类型,抛出XmlException、FileException或ConfException。可选参数LIBXML_NOERROR给simplexml_load_file(),用于一直出错警告的直接输出,并在警告发生之后留给XmlException类来处理。遇到一个不规范的XML文件时,simplexml_load_file()不会返回对象,这样我就知道发生了错误,然后用libxml_get_last_error()访问错误。
如果$file属性指向一个不可写的文件,你们write()方法就会抛出FileException。
class Runner
{
public function init()
{
try {
$conf = new Conf(dirname(__FILE__) . "/conf01.xml");
print "user: " . $conf->get('user') . "\n";
print "host: " . $conf->get('host') . "\n";
$conf->set("pass", "newpass");
$conf->write();
} catch (FileException $e) {
//文件权限问题或者文件不存在
} catch (XmlException $e) {
//XML文件损坏
} catch (ConfException $e) {
//错误的XML文件格式
} catch (Exception $e) {
//后备捕捉器,正常情况下不应该被调用
}
}
}
catch子句的调用取决于抛出的异常类型。第一个匹配的类型子句将会被执行,所以记得要把通用的类型放到最后,特定的类型放到前面。另外,有关于“后备”(backstop)子句通常是个好办法,以防开发时在代码中要增加新的异常。如果异常到最后也没有被捕获,那么将发生致命错误。