龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 操作系统 > LINUX系统 >

PHP开发学堂:细看Pear的错误处理

时间:2011-03-20 22:37来源:未知 作者:admin 点击:
分享到:
许多程序已经使用了PEAR的包。许多PHP程序员或多或少的熟悉了PEAR中的错误处理。但是这个机制并不局限于PEAR的包――所有人都能在他们的类和程序中使用这些方法。 这篇文章被分为两

  许多程序已经使用了PEAR的包。许多PHP程序员或多或少的熟悉了PEAR中的错误处理。但是这个机制并不局限于PEAR的包――所有人都能在他们的类和程序中使用这些方法。

  

  这篇文章被分为两个部分:首先我们将看看类中用于错误处理的函数,然后我们将看看如何基于PEAR错误处理机制来处理错误。

  

  我们的例子类称为cvs2db,它把数据从CSV文件插入到数据库的表中。因为数据可能是手写的,他们的数据应该在插入之前先得到验证――落实postcode。函数import()完成读入,检查和插入的工作;它返回损坏的记录数目。如果返回的值大于0,出错的记录集能够使用exportUnvalid()写入到新的CSV文件中。典型的用法是这样的:

  

  

  $cd = new csv2db();

  $dsn = 'mysql://root@localhost/csv2db';

  if( 0 import("./dat.csv", $dsn, 'address')) {

  $cd->exportUnvalid("./dat2.csv");

  }

  ?>

  

  可能的错误包括:

  

  要导入的CSV文件不存在,

  连接到数据库失败,

  记录集损坏,以及CSV导出文件无法创建。

  

  在提供错误信息的经典解决方案中你可能写这样的代码:

  

  import("./dat.csv", $dsn, 'address')

  switch($result) {

  case FILE_NOT_OPENED:

  ...

  break;

  case DATABASE_ERROR:

  ...

  break;

  default:

  if(0 exportUnvalid("./dat2.csv");

  } else {

  echo 'every thing ok!'

  }

  }

  ?>

  这对于短的脚本来说是可接受的也是常用的办法――但是对于错误处理经常受到关注的大程序来说不是这样。传统的可能性强迫类的作者做最终的决定!在大部分情况下,这个决定根据的是那时对类的调用而不是基于长期的使用和可重用代码的思想。一个灵活的错误处理机制是可重用代码的重要部分,PEAR Error API 就是这样的一种受到良好测试的机制。

  

  用户眼中的类

  除了那两个函数之外,类提供了一套错误处理函数和一个自己的错误对象称为DB2CVS_Error,它有一个特殊的本地化的错误信息的特性功能。

  

  现在我将向你展示如何在错误发生时控制类的行为。

  

  局部和全局错误处理

  你用setErrorHandling()管理错误处理;这个函数需要两个参数:第一个是错误模式,而第二个(可选的)参数是错误模式特定的选项。例如 setErrorHandling(PEAR_ERROR_PRINT, 'This error occurred %s') 还有 setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_WARNING)。

  

  这个函数的调用方式是一般行为中最重要的:静态还是实体。在类cvs2db中,我们能两者都用来设置错误处理,所有这些调用有相同的结构――为类设置错误模式:

  

  // per instance

  $cd = new csv2db();

  $cd->setErrorHandling(PEAR_ERROR_DIE):

  // static

  CVS2DB::setErrorHandling(PEAR_ERROR_DIE);

  PEAR::setErrorHandling(PEAR_ERROR_DIE);

  

  如果两者给出同样的结果,区别在哪?实体调用仅仅为那个类设置而静态调用对于所有使用PEAR_Error或者从那个类派生的所有类起作用。这个也作用于第一个静态命令CVS2DB::setErrorHandling(PEAR_ERROR_DIE)――虽然它看上去仅仅影响了cvs2db类。

  

  总结:作为一个实体函数使用命令意味着仅仅为这个实体(局部)设置错误模式,而作为静态函数来调用就是为整个脚本设置错误模式(全局)。

  

  setErrorHandling() 和 raiseError()

  

  两个函数都能够被静态调用和作为实体的函数调用。记住怎样的一个组合使得他们如何互相影响的很重要。

  

  基本上是:setErrorHandling()的静态调用仅仅影响raiseError()的静态调用――setErrorHandling()作为实体函数仅仅影响raiseError()作为静态函数调用。在类csv2db中,使用csv2db::setErrorHandling()来设置错误模式是不可行的,因为我们使用$this->raiseError(...)。解决这个闻天有一点小技巧――改写raiseError():

  

  function raiseError(...,$mode=null, $options=null,...) {

  if($mode==null && $this->_default_error_mode!=null) {

  $mode = $this->_default_error_mode;

  $options = $this->_default_error_options;

  }

  return PEAR::raiseError(...,$mode, $options,...);

  }

  这样,我们映射实体调用到静态上,如果你用错误模式调用raiseError(),然后这个模式将会覆盖这些设置――这里是指的是全局的设置。

  

  你应当当心错误是如何被类抛出的,如果你不小心,这可能导致不可预期的副作用。

  

  错误的模式

  对错误模式的了解对于使用PEAR的错误处理来说是重要的。PEAR错误处理让用户能够决定怎么去做――注意:下文中术语用户指的的是实际使用PEAR_Error程序的开发者而不是浏览脚本结果或者网页的用户。我将详细展示可能的错误模式。

  

  PEAR_ERROR_DIE――将这个模式开启,程序将终结并且将打印错误信息。可选的,你能定义一个printf()式的字符串,它能够用于产生信息;首先'%s'在字符串中将替代储存在错误对象中的错误信息。

  

  PEAR_ERROR_PRINT――仅仅打印错误信息,包括用于PEAR_ERROR_DIE的同样的可选用的字符串。

  

  PEAR_ERROR_RETURN――当错误发生时的一般行为;你能用类提供isError()函数或者PEAR::isError()检查错误。

  

  $db->setErrorhandling(PEAR_ERROR_RETURN)

  if(!csv2db::isError(0 import("./dat.csv", $dsn, 'address'))) {

  if(!csv2db::isError($cd->exportUnvalid("./dat2.csv")) {

  } else {

  // handle error

  }

  } else {

  // handle error

  }

  PEAR_ERROR_TRIGGER――这儿函数向PHP运行时错误行为一样。你必须定义哪种错误应该发生:E_USER_NOTICE,E_USER_WARNING或者E_USER_ERROR。他们直接和PHP本身产生的信息相对应。请注意,在错误信息中错误发生的那行(xxx on line yy)指的是在PEAR.php中调用trigger_error的那行――而不是错误直接发生的那行。

  

  PEAR_ERROR_CALLBACK――这是只在一个地方处理错误并且让你得代码不用考虑错误处理的最佳方式。它需要一个函数或者类函数来捕获错误,你能写一个listing 2中展示的那样的脚本,其中可以看到类相关错误对象的好处:import()函数抛出一个CSV2DB_Error给基于CSV的错误和一个DB_Error对象给相关于数据库访问的错误。

  

  Listing 2

  

  $cd = new csv2db();

  $cd->setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');

  $dsn = 'mysql://root@localhost/csv2db';

  if( 0 import("./dat.csv", $dsn, 'address')) {

  $cd->exportUnvalid("./dat2.csv");

  }

  

  function handleError($error) {

  if(DB::isError($error) {

  // handle database error

  }

  if(csv2db::isError($error) {

  switch($error->getCode()) {

  case FILE_NOT_OPENED :

  ...

  break;

  case CORRUPTED_RECORD :

  ...

  break;

  }

  }

  }

  

  单个的错误处理

  我们有两种可能的错误:我们能够忽略的错误(损坏的记录),以及使得程序无法运行的错误(找不到文件或者打不开数据库)。如果你在shell脚本中使用类,你可以让脚本终止于第二类错误。

  

  自然的,你可以写 $cd->setErrorHandling(PEAR_ERROR_DIE)――但是这可能在如果损坏的记录错误发生时导致问题。在这样的情况下你需要对某个错误停用或者替换错误处理办法的可能。解决办法时expectError(),如果你传递一个错误代码给这个函数,指定错误的错误模式将被单独于缺省错误模式地设置为PEAR_ERROR_RETURN。

  

  expectError()函数把传递来的错误代码储存在栈中,使用popExpected()移出最后传递的错误代码。自从PHP 4.3之后你还能使用delExpect()了;这个函数从栈中删除了指定错误代码的匹配,你不需要关心位置了。

  

  在实际使用中,是这样的:

  

  $cd->setErrorHandling(PEAR_ERROR_DIE);

  ...

  $cd->expectError(CORRUPTED_RECORD);

  $cd->import(...);

  $cd->popExpect();

  

  pushErrorHandling() 和 popErrorHandling() 用起来差不多;他们能够暂时的控制错误处理。例如:如果在 exportUnvalid() 中的文件不能打开,你想要忽略错误:

  

  PEAR::pushErrorHandling(PEAR_ERROR_RETURN);

  $cd->exportUnvalid("./dat2.csv");

  PEAR::popErrorHandling();

  

  注意调用方法的区别!expectError()/popExpect()必须作为实体函数来调用――pushErrorHandling和popErrorHandling可以静态调用。如果作为实体函数,那么他们仅仅影响那个实体。

  

  用户有很多可能性,这是否又意味着程序员要做很多的工作呢?是,是因为你要比return false做更多的事情;否,是因为PEAR Error API给你完成了很多工作。

  

  一些关于错误处理的思考

  作为好的程序员,你不应该从你的类的用户眼中遮掩起确切的错误原因。这阻止了简单的return false的用法;还要注意也可能被PHP自动型别转换为0――这对于import()函数来说意

精彩图集

赞助商链接