Zend Framework 中的 Php 编码标准 (五) - 错误与异常
原文 : http://kbs.kimbs.cn/blog/list/post/27/title/zend-framework-coding-standards-for-php-errors-and-exceptions
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
);
}
复制代码
14. 当然,其它语言的关于异常处理的规范也是可以借鉴的。当中有很多东西都是相通而不存在语言界限的。