我一直在思考异常,尤其是PEAR_Exception。您可能想直接跳到阅读有关如何使用PEAR_Exception
的内容,以及我对首次使用该类的一些想法。如果您想了解背景,请继续阅读。
我已经在PEAR上为一个名为File_Fortune的包创建了一个包提案,这是一个用于读取和写入财富文件的OOP接口。多年来,我一直在家庭网站上为此使用perl模块,现在我开始进行PHP转换,我想我应该从构建块开始。
在创建提案时,我从一个仅限PHP5的版本开始,尽管我发现除了public
/private
/protected
/static
关键字。对于错误处理,我决定试用PEAR_ErrorStack,因为我一直听说它是PEAR中错误处理的新“首选”方法。(老实说,在使用它之后,我对它不太满意;抛出PEAR_Error
更容易,也更容易操作——但那是另一篇文章的主题——而且异常更容易,虽然打字更多。)
我收到的关于提案的第一条评论是问题:“为什么是PHP5?”(Paul对这种反应并不太惊讶。)我考虑了一下,决定不是除了我需要采取一些额外的步骤才能实际测试PHP4版本之外,真的所有这些都是必要的。所以,我做了一个PHP4版本。
好吧,然后发生了一些争论,许多开发人员说,“为什么不PHP5?”所以,我又回到了PHP5。然后其他人说,“使用PEAR_Exception
。”所以,我开始研究它,我们终于进入了这篇文章的主题。
异常处理是PHP5给PHP带来的进步之一。我很高兴它可用,因为我习惯了perl中的异常处理(这实际上与PHP的模型完全不同,但基础是相同的)。当我看到使用它的建议时,我意识到异常处理将使该包成为一个可靠的PHP5包。同时,我想知道为什么我没有想到。我猜我编写的PHP代码比perl代码多了一段时间了……
问题是关于PEAR_Exception
的文档非常少,我在列表中得到的提示虽然有助于我提出建议,但给我留下了很多问题。
对于那些还没有使用过PEAR_Exception
的人,这里是基础知识:
-
创建一个文件来保存您的异常类。(是的,复数;我会说的)。如果你正在开发一个PEAR风格的包,你想把它放在与你的包名相关的目录中。所以,由于我正在开发
File_Fortune
,我的异常类变成了File/Fortune/Exception.php
。 -
创建一个扩展
PEAR_Exception
的类的基本异常类:class File_Fortune_Exception extends PEAR_Exception { }
注意:它不会覆盖任何内容。它只是创建一个伪命名空间。
-
对于每个独特的异常类型,扩展您的基类:
class File_Fortune_FileException extends File_Fortune_Exception { } class File_Fortune_HeaderException extends File_Fortune_Exception { }
(是的,您应该为每个文档块创建文档块,描述它们的用途。)
-
在您的代码中,抛出异常而不是引发错误:
if (false === ($fh = fopen($filename))) { throw new File_Fortune_FileException('Unable to open file'); }
-
在您的phpDoc块中,使用
@throwsException_Class_Name
和一些描述性文本
简而言之就是这些。
它的美妙之处在于您可以真正将错误与返回值分开——错误不再是可能的返回值:
try { $fortune = $ff->getRandom(); } catch (File_Fortune_Exception $e) { echo "Couldn't get fortune: " . $e->getMessage(); }
我在系统中看到的问题是你最终会得到一堆异常类,每个异常类都有一个veeeerrryyylooooonnnnngggg名称,这会导致大量输入,可能会出现拼写错误(我在用于调用投票),以及比代码更多的错误处理的可能性,这取决于可能的异常数量以及您想要检查它们的仔细程度:
try { $fortune = $ff->getRandom(); } catch (File_Fortune_BadHeaderFileException $e) { echo "Could not parse header file"; } catch (File_Fortune_HeaderFileException $e) { echo "Could not open header file"; } catch (File_Fortune_BadFileException $e) { echo "Badly formed fortune file"; } catch (File_Fortune_Exception $e) { // Catch-all for File_Fortune errors... echo "Couldn't get fortune: " . $e->getMessage(); }
我开始思考一定有更好的方法。不过,我还没有真正想出一个。然而,到目前为止,我的想法是拥有一个异常类,并在其中定义一些类常量或静态变量——很像PEAR_Error
/PEAR_ErrorStack
,它们映射到整数值——并将这些值映射到实际的错误消息(也可能在类中进行本地化)。然后,当抛出错误时,它可能是这样的:
if (false === ($fh = fopen($filename))) { throw new File_Fortune_Exception(1); } // or if (false === ($fh = fopen($filename))) { throw new File_Fortune_Exception(File_Fortune_Exception::FILE); }
构造函数将被覆盖以根据传递的代码设置代码和消息(如果传递的是字符串,那将是消息)。然后您可以测试单个异常类,如果您需要更细粒度的控制,则可以使用$e->getCode()
检查类型。
我非常乐意讨论各种可能性。异常是检查代码中真正异常行为的绝佳方式;在PHP5中,它们似乎也非常快速和轻量级(尽管我没有实质性数据来支持该声明,除了API响应能力)。我希望看到更多人与他们一起开发。
关于这一点,其他PHP开发人员如何看待异常处理?我听说有人说它太“goto-ish”(我不确定我是否遵循了这种思路),其他人更喜欢PEAR_Error
的简单性。发表评论!