zend framework 中的 php 编码标准 (五) - 错误与异常
1. Zend Framework 的代码应该不存在 E_STRICT 兼容问题。在开启错误报告 error_reporting 级别为 E_ALL | E_STRICT 时,Zend Framework 的代码不应该抛出任何警告(E_WARNING, E_USER_WARNING),任何通知(E_NOTICE, E_USER_NOTICE)以及任何兼容问题(E_STRICT)。
这就是说,Zend Framework 尽量避免代码写法上的错误。而如果真的发生程序中断,也只能是逻辑错误。
2. Zend Framework 不应该存在 Php 错误,如果我们不得不遇到错误的话,请用异常来处理。Zend Framework 中有专门的异常类来为用户提供友好的异常处理。
例如 :
class Zend_Exception extends Exception
{
}
class Zend_Db_Exception extends Zend_Exception
{
}
class Zend_Db
{
public static function factory($adapter, $config = array())
{
// ...
if (!is_array($config)) {
/**
* @see Zend_Db_Exception
*/
require_once 'Zend/Db/Exception.php';
throw new Zend_Db_Exception('Adapter parameters must be in'
. 'an array or a Zend_Config object');
}
}
}
3. 在框架模块内的异常统一用 new 关键字构造是公认的良好习惯。
例如 :
require_once 'Zend_Component_SpecificException.php';
class Zend_Component
{
public function foo($condition)
{
if ($condition) {
throw new Zend_Component_SpecificException(
'一些友好的信息');
}
}
}
4. 异常必须延迟加载。
例如 :
// 正确
if ($condition) {
require_once 'Zend_Component_SpecificException.php';
throw new Zend_Component_SpecificException(
'一些友好信息');
} else {
// ...
}
// 错误
require_once 'Zend_Component_SpecificException.php';
if ($condition) {
throw new Zend_Component_SpecificException(
'一些友好信息');
} else {
// ...
}
5. 通俗地讲,如果用户希望 Zend Framework 模块做出一些超出其能力范围的工作时,那么抛出异常则是明智而正确的选择。相反,假如该模块能够处理用户的需求,但用户却给出各种意想不到的输入,这个时候,模块就不应该抛出异常,而应该正常运行下去。
例如 :
if ($canNotPerformThisAction) {
require_once 'Zend/Exception.php';
throw new Zend_Exception('不能执行此动作 !');
}
$foo = 'bar';
if ($foo == 'foo') {
echo '对的';
} else {
// 没必要抛出异常
}
6. 避免抛出 Exception 基类异常,而应该尽量使用派生异常类,这可以让人清楚知道问题所在。
例如 :
class Zend_Db
{
public static function factory($adapter, $config = array())
{
if (!is_array($config)) {
throw new Exception('我们根本不知问题发生在哪儿。');
}
if (!is_array($config)) {
require_once 'Zend/Db/Exception.php';
throw new Zend_Db_Exception("我们都知道问题出在 Zend_Db 这儿。");
}
}
}
7. 尽量避免去捕捉 Exception 基类异常。如果在 try 语句里面可能抛出多种异常的话,那么我们应该为各种异常准备各自独立的 catch 块,而不是仅用一个 catch 块去捕捉 Exception 基类异常。
例如 :
// index.php
try {
$app->run();
} catch (Zend_Db_Exception $e) {
die('数据库异常 !');
} catch (Zend_Acl_Exception $e) {
die('权限分配异常 !');
} catch (Zend_Auth_Exception $e) {
die('身份认证异常 !');
} catch (Zend_Exception $e) {
// 所有其它异常
}
8. 我们通常需要在类中通过拓展多个异常类来区分各种不同的情况。例如,我们需要创建两个异常类来区分 "参数错误" 和 "用户缺乏权限" 两种情况。
例如 :
class Zend_Db_Exception extends Zend_Exception
{
// 数据库异常基类
}
class Zend_Db_Select_Exception extends Zend_Db_Exception
{
// 用于处理 select 类异常
}
class Zend_Db_Table_Exception extends Zend_Db_Exception
{
// 用于处理数据库表的异常
}
9. 不要把所有诊断信息都放在异常的 message 里,我们可以在任何需要的时候创建自己的异常类的成员和方法,来为 catch 语句提供帮助。我们需要做的就是在 constructor 构建异常类时,传入正确的参数信息。
例如 :
class Zend_Exception extends Exception
{
}
class My_Exception extends Zend_Exception
{
private $_importantDiagnostic;
public function setImportantDiagnostic($value)
{
$this->_importantDiagnostic = $value;
}
public function getImportantDiagnostic()
{
return $this->_importantDiagnostic;
}
public function __construct($message = null, $code = 0, $value)
{
parent::__construct($message, $code);
$this->setImportantDiagnostic($value);
}
}
try {
if ($isMyFault) {
throw new My_Exception('没有信息', 0, '信息在这儿');
}
} catch (My_Exception $e) {
echo $e->getImportantDiagnostic();
}
10. 在错误发生的时候,程序不应该保持沉默,甚至对异常置之不理。而是应该要么修正它,要么抛出新的异常来代替它。
例如 :
try {
} catch (My_Exception $e) {
tryToCorrectIt($e);
} catch (My_Exception $e) {
throw new My_Exception($e->getMessage(), '110');
} catch (My_Exception $e) {
// 不作为是愚蠢的行为!
}
11. 我们应该为我们程序的不同层面准备不同的异常处理。例如,我们不应该把数据逻辑层的错误(即俗称 SQLException)搬到业务逻辑层。
例如 :
class My_Dao_User_Exception extends Zend_Db_Table_Exception
{
// 数据层 User 异常类
}
class My_Dao_User extends Zend_Db_Table_Abstract
{
// 数据层 User 对象
}
class My_Bis_User_Exception extends Zend_Exception
{
// 业务层 User 异常类
}
/**
* 业务层 User 对象
*/
class My_Bis_User
{
private $_daoUser = null;
public function setDaoUser($user)
{
$this->_daoUser = $user;
}
public function getDaoUser()
{
return $this->_daoUser;
}
public function throwSomething()
{
try {
if ($wrong) {
throw new My_Dao_User_Exception('你不应该把数据层的异常搬到这里!');
} else if ($right) {
throw new My_Bis_User_Exception('这才是正确的。');
}
} catch (Zend_Exception $e) {
// ...
}
}
}
12. 不要把异常处理机制当成控制流程,或者仅仅是返回某些值。
例如
function itIsWrong()
{
try {
// 做些小动作
} catch (Exception_One $e) {
doSomething($e);
} catch (Exception_Two $e) {
doSomething($e);
} catch (Exception_Three $e) {
return true;
}
}
function isRight()
{
try {
// 做些小动作
} catch (Exception_One $e) {
correctIt($e);
} catch (Exception_Two $e) {
correctIt($e);
} catch (Exception_Three $e) {
// 从不 return;
throw new Exception('我们抛出它,而不是返回什么东西!');
}
13. 在用 catch 语句块处理异常的时候,我们应该首先释放多余的内存资源,如数据库连接,网络资源连接等。Php 并不提供类似 finally 之类的语句来进行垃圾处理。
例如 :
try {
$db = getDbConnection();
throw new Exception('发生了错误。');
} catch (Exception $e) {
unset($db);
handleExceptionAndGoOn($e);
}