首页 -> Zend PHP 5 认证指南 第二版 -> 第六章:PHP的面向对象技术 Object Oriented Programming in PHP 6.5 异常 Exceptions
第六章:PHP的面向对象技术 Object Oriented Programming in PHP 6.5 异常 Exceptions
2010-04-28 14:05:21
作者:Siemen

尽管面向对象技术经过了多年的洗礼和成长,而异常这个机制却是最近才加入到PHP中的。异常提供了一个错误控制机制,它相对与传统的PHP的错误句柄来说提供了更高等级的控制。

以下例举了一些与“常规”PHP错误与异常的不同:

  • 异常作为对象,当错误发生时被创建(或者“抛出”)
  • 异常可以在脚本执行时操作不同的点,同时对于不同类型的异常进行它们各自的异常处理。
  • 所有的致命错误都不能被捕获
  • 异常可以在__construct方法中抛出,使实例化失败
  • 异常改变了程序流

6.5.1 异常类基础

在之前的章节提到过,异常对象肯定是直接或者迂回(例如通过继承)来自于Exception基类。后者与PHP融合,定义方法如下:

class Exception {
  // 错误提示信息
  protected $message = 'Unknow Exception';
 
  // 错误代码
  protected $code = 0;
 
  // 错误发生所在文件路径
  protected $path;
 
  // 错误发生所在行数
  protected $line;
 
  // 构造
  function __construct($message, $code = 0);
 
  // 返回消息
  final function getMessage();
 
  // 返回错误代码
  final function getCode();
 
  // 返回文件名
  final function getFile();
 
  // 返回错误发生所在行号
  final function getLine();
 
  // 返回错误跟踪数组
  final function getTrace();
 
  // 返回错误跟踪字符串
  final function getTraceAsString();
 
  // 返回一个字符串描述错误
  function __toString();
}

几乎所有的属性都会由解释器来填充——通常来说,只需要提供错误提示信息和错误代码。

因为Exception是一个(内建)类,所以可以继承它从而创建自己的异常,使其更好的与应用融合。

6.5.2 抛出异常

异常通常是在错误发生时通过throw关键字创建同时抛出异常:

作者提示:尽管通常类需要先创建对象并把引用赋予变量,而对于异常来说不需变量接收,直接实例化后抛出即可。

if ($error) {
  throw new Exception ("This is my error");
}

异常会“冒泡”直到在脚本中遇到异常捕获,或者导致一个致命错误。异常捕获通过一个try…catch块实现:

try {
  if ($error) {
    throw new Exception("这是我的错误");
  }
} catch (Exception $e) {
  // 错误处理
}

上例中,try{}中抛出任何异常都将被捕获,然后将代码传入catch{}中进行优雅的异常处理。

那么为什么要在catch()中传入的参数前面加上Exception的类型暗示;首先这里可以决定捕获更加具体的异常类的子类。因为可以自由的继承Exception类,这就意味着不同嵌套的try…catch可以捕获不同的类型的异常并且分别处理:

class myException extends Exception {}
 
try {
  try {
    try {
      new PDO("mysql:dbname=zce");
      throw new myException("An unknown error occurred.");
    } catch (PDOException $e) {
      echo $e->getMessage();
    }
  } catch (myException $e) {
    echo $e->getMessage();
  }
} catch (Exception $e) {
  echo $e->getMessage();
}

例子中我们嵌套了三个try…catch;最内部的将只捕获PDOException对象,而下一层则捕获自定义的myException对象,最外层的捕获其余异常。除了可以嵌套捕获外,还可以使用连续捕获达到相同效果:

try {
  new PDO("mysql:dbname=zce");
} catch (PDOException $e) {
  echo $e->getMessge();
} catch (myException $e) {
  echo $e->getMessge();
} catch (Exception $e) {
  echo $e->getMessge();
}

只要捕获到一次,下面的连续捕获就不会再进行。

除了那些无法捕获的致命异常,你可以将整个应用都用一个try…catch套起来——但这样做很不方便。幸运的是,有一个更好的解决办法——PHP允许定义一个“完全捕获”函数在异常出现时自动调用。函数利用调用set_exception_handler():

function handleUncaughtException($e) {
  echo $e->getMessage();
}
 
set_exception_handler("handleUncaughtException");
 
throw new Exception("捕获成功!");
 
echo "这里不会被显示出来";

需要提醒的是,因为完全捕获异常句柄只能在异常被冒泡到整个脚本的级别时才会调用,这只能相当于脚本外面包着整个try…catch——另一个方面说,这种情况只适用于在异常即将成为致命错误之前的最后处理机会,但是这也不会让程序继续往下走,就像上例中永远不会输出“这里不会被显示出来”一样,因为异常通过冒泡唤醒handleUncaughtException();脚本就会终止运行。

作者提示:如果想要恢复设定“完全捕获”函数之前的异常句柄,默认的致命错误,或者另一个自定义的异常处理句柄,可以通过restore_exception_handler()实现。