PEAR提供了强大的错误处理机制。这篇文章向你展示如何从这个系统中获益。 $cd = new csv2db(); $dsn = 'mysql://root@localhost/csv2db'; if( 0 < $cd->import("./dat.csv", $dsn, 'address')) { $cd->exportUnvalid("./dat2.csv"); } 可能的错误包括: $cd = new csv2db(); $dsn = 'mysql://root@localhost/csv2db'; $result = $cd->import("./dat.csv", $dsn, 'address') switch($result) { case FILE_NOT_OPENED: ... break; case DATABASE_ERROR: ... break; default: if(0 < $result) { $cd->exportUnvalid("./dat2.csv"); } else { echo 'every thing ok!' } } 这对于短的脚本来说是可接受的也是常用的办法——但是对于错误处理经常受到关注的大程序来说不是这样。传统的可能性强迫类的作者做最终的决定!在大部分情况下,这个决定根据的是那时对类的调用而不是基于长期的使用和可重用代码的思想。一个灵活的错误处理机制是可重用代码的重要部分,PEAR Error API 就是这样的一种受到良好测试的机制。 // 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类。 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(),然后这个模式将会覆盖这些设置——这里是指的是全局的设置。 $db->setErrorhandling(PEAR_ERROR_RETURN) if(!csv2db::isError(0 < $d = $cd->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的那行——而不是错误直接发生的那行。 $cd = new csv2db(); $cd->setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); $dsn = 'mysql://root@localhost/csv2db'; if( 0 < $d = $cd->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; } } } 单个的错误处理 $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可以静态调用。如果作为实体函数,那么他们仅仅影响那个实体。 require_once 'PEAR.php'; require_once 'DB.php'; define("FILE_NOT_OPENED", 10); define("CORRUPTED_RECORD", 20); class csv2db extends PEAR { var $records = array(); var $unvalid = array(); function csv2db() { $this->PEAR("CSV2DB_Error"); } function import($file, $dsn, $table) { $this->PEAR("CSV2DB_Error"); if($fp = @fopen($file, 'r')) { while($data = fgetcsv($fp, 1024, ';')) { $this->records[] = $data; } fclose($fp); } else { return $this->raiseError(null, FILE_NOT_OPENED); } $unvalidCount = 0; $storeMode = $GLOBALS['_PEAR_default_error_mode']; $storeOpts = $GLOBALS['_PEAR_default_error_options']; $GLOBALS['_PEAR_default_error_mode'] = $this->_default_error_mode; $GLOBALS['_PEAR_default_error_options'] = $this->_default_error_options; $db = DB::connect($dsn); $GLOBALS['_PEAR_default_error_mode'] = $storeMode; $GLOBALS['_PEAR_default_error_options'] = $storeOpts; if(!DB::isError($db)) { $db->setErrorHandling($this->_default_error_mode, $this->_default_error_options); $qp = $db->prepare("INSERT INTO $table VALUES (?, ?, ?, ?)"); foreach( $this->records as $record) { if(preg_match('/\d{5}/', $record[2])) { $db->execute($qp, $record); } else { $unvalidCount++; $this->unvalid[] = $record; $this->raiseError(corrupted record, CORRUPTED_RECORD); } } $db->disconnect(); } else { return $db; } return $unvalidCount; } function exportUnvalid($file) { if($fp = @fopen($file, "w")) { foreach($this->unvalid as $data) { fwrite($fp, implode(';', $data)."\n", 1024); } fclose($fp); } else { return $this->raiseError(null, FILE_NOT_OPENED); } } function isError($data) { return (bool)(is_object($data) && (get_class($data) == 'CSV2DB_Error' || is_subclass_of($data, 'CSV2DB_Error'))); } } class CSV2DB_Error extends PEAR_Error { var $msgs = array( FILE_NOT_OPENED => array( 'de' => "Datei konnte nicht ge?ffnet werden", 'en' => "File couldn't be opened"), CORRUPTED_RECORD => array( 'de' => "fehlerhafter Datensatz", 'en' => "corrupted record") ); function CSV2DB_Error($message = null, $code = null, $mode = null, $level = null, $debuginfo = null) { $this->PEAR_Error(null, $code, $mode, $level, $debuginfo); } function getMessage($lang = "en") { return $this->msgs[$this->code][$lang]; } } 自己的错误对象 raiseError( $message, $code, $mode, $options, $userinfo, $errorclass, $skipmessage)&throwError( $message, $code, $userinfo) Parameter Description $storeMode = $GLOBALS['_PEAR_default_error_mode']; $storeOpts = $GLOBALS['_PEAR_default_error_options']; $GLOBALS['_PEAR_default_error_mode'] = $this->_default_error_mode; $GLOBALS['_PEAR_default_error_options'] = $this->_default_error_options; $db = DB::connect($dsn); $GLOBALS['_PEAR_default_error_mode'] = $storeMode; $GLOBALS['_PEAR_default_error_options'] = $storeOpts; 首先,全局的错误模式被保存了,然后全局的错误模式设置给了局部的错误模式并且最后几行,原来的错误模式被还原了。为什么?Connect()是一个静态函数!它必须使用PEAR::raiseError()。因而假如我们不保存并且还原设置,我们会遇到问题:看看listing 3——如果类在import()函数不能连接到数据库的时候会发生什么?因为对raiseError()的静态调用受到全局错误模式的影响,而不是局部的$cd->setErrorHandling(...)的影响,脚本终止执行 。实际上push和popErrorHandling()就是设计来用于这样的任务的——但是PHP中一个现下的bug看上去不幸的组织了它很好的工作。 ... PEAR::setErrorHandling(PEAR_ERROR_DIE) $cd = new csv2db(); $cd->setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); $dsn = 'mysql://root@localhost/csv2db'; if( 0 < $d = $cd->import("./dat.csv", $dsn, 'address')) { $cd->exportUnvalid("./dat2.csv"); } $db = DB::connect($dsn); $db->query(...); ... 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; } } } PEAR错误处理和PHP 5 $i = new csv2db(); $dsn = 'mysql://root@localhost/csv2db'; try { if( 0 < $d = $i->import("./dat.csv", $dsn, 'address')) { $i->exportUnvalid("./dat2.csv"); } } catch CSV2DB_Error { // fetch the error } 结论 |