【问题标题】:Subclassing exceptions子类化异常
【发布时间】:2011-04-02 18:22:51
【问题描述】:

我们都同意为不同的任务使用不同的异常类型是可行的方法。

但是,我们最终会创建像这样的幽灵文件:

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Dojo
 * @subpackage View
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
 */

/**
 * @see Zend_Dojo_Exception
 */
require_once 'Zend/Dojo/Exception.php';

/**
 * @category   Zend
 * @package    Zend_Dojo
 * @subpackage View
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Dojo_View_Exception extends Zend_Dojo_Exception
{
}

那么Zend_Dojo_ExceptionZend_Exception 相同……

这个问题有什么通用的方法吗?

throw new \My\Just\Declared\Exception\ (which extends \My\Just\Exception) 这样的东西,所以我不必创建并需要所有这些幽灵文件?

【问题讨论】:

  • 这个“行为”会在zf2中重写framework.zend.com/wiki/display/ZFDEV2/…
  • 什么行为?从那个链接我看到的只是声明接口的能力?我没有看到任何关于延迟编写异常的信息(类的代码在实例化之前甚至不存在)?我错了,只是误读了链接吗?
  • @robert 这是一个好消息,但并不能解决创建新异常的问题。也许新的 Zend_Autoloader 可以做到这一点。
  • 对不起,我刚醒来,误解了这个问题。没关系-_-
  • “我们都同意……”——是吗!? ;-)

标签: php zend-framework exception exception-handling error-handling


【解决方案1】:

有些人使用autoloader 即时创建异常。

【讨论】:

    【解决方案2】:

    在良好的实践中,不是真的...但是,如果您真的想要这样做,您可以做一些黑客攻击,但我仍然认为它们更邪恶。

    例如,其中一种技巧是通过自动加载器将eval 那些类变为存在。这很糟糕,因为如果有人曾经 greps 为异常的定义或你的包抛出的异常,他们将成为一大堆的回报......

    public static function load($name) {
        $parts = explode('_', $name);
        if (strtolower(end($parts)) == 'exception') {
            //make it extend the proper exception
            array_pop($parts); //get rid of the last Exception bit
            array_pop($parts);
            $parts[] = 'Exception';
            $parent = implode('_', $parts);
            $code = 'class '.$name.' extends '.$parent . '{}';
            eval($code);
        }
    }
    

    但再次强调,这通常是个坏主意。

    就我个人而言,我继承了多个“基本异常”(通常是SPL exceptions)。例如Database_Connection_Exception 可能扩展RuntimeException,尝试提交未打开的事务可能会抛出Database_Not_In_Transaction_Exception,它可能扩展LogicException。关键是,单独声明它们可以让您做的不仅仅是直接继承(更不用说更好的文档,因为人们可以一眼看到定义的异常,并且您实际上可以重写方法以更好地满足您的需求如果合适的话)...

    编辑:基于您提到 Zend 倾向于为每个子包执行一个异常,这就是我的做法...

    基本上,我有一些在整个应用程序中使用的“全局”异常。这些包括(但不限于):ClassNotFoundExceptionFileNotFoundExceptionNotCallableException 和其他一些。基本上只是那些不是特定于包的,但需要传达比核心 PHP 异常更多的含义......

    然后,我只在包级别声明异常。在该目录 (package/exceptions) 中,我根据需要声明每个异常。所以一个子包可能有5个或10个例外来区分不同的情况,而另一个子包(同一个包内)可能没有。所以我根据需要声明它们,以便异常意味着发生了什么。

    我这样做的原因很简单。我不关心 where 引发的异常(如果我真的这样做了,我可以检查异常内部自动生成的回溯)。我关心why 抛出异常。这让我可以正确处理异常......

    【讨论】:

    • 我不使用 Zend(我只是不关心它)。但是我将所有异常都放入了特定于包的子类别中。所以你会在Database 类别/包的Database_Exceptions 子类别下拥有所有例外......我认为这只是一个传统,最后有Exception。是的,您可以 grep 进行通话,但我仍然认为有定义更好(因为看到 throw new Database_Connection_Exception() 很少告诉您有关异常的信息,但看到 class Database_Connection_Exception extends RuntimeException 可能会告诉您更多信息(数据库是一个简单的例子)。
    • eval 的另一个缺点是在 IDE 中没有代码完成......
    【解决方案3】:

    在我看来,异常是或者应该只在开发模式下可见。生产模式不应向用户/客户端显示异常,而是显示“遇到问题”或类似内容的简单页面。最好记录这些异常。最重要的是,它们主要用于调试或在需要控制执行流程时(在预期时)。您可以简单地一直抛出Zend_Exception,但这只会使您的异常过于笼统,并且很难在 try..catch 中找到原因

    我通常会创建 ghost 文件——正如你所说的那样——主要是在“包”级别或当一个方法预计会抛出异常或另一个时(这样我就可以捕捉到那个非常特殊的例外并因此处理它)。

    您最终可能会得到许多 ghost 文件,但这只会使您的项目更有条理,并且最终易于调试。而且由于这些异常只是在需要的基础上加载的,因此有很多异常不会影响性能。

    【讨论】:

    • 没错,但这不是重点。正如我所写,我们都同意我们需要那些幽灵文件。但创建它们是一项无聊的工作。由于我们是程序员,我们可以编写一些智能代码来为我们完成这个简单的任务。我们不能吗?
    • @takeshin:永远不要忘记canshould 之间存在显着差异。引用 Michael Crichton,Yeah, but your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should... 作为开发人员,我们可以采取一些捷径,但总要权衡...我并不是说您不应该这样做,但如果您 可以创建一个解决方案,关注您是否应该打扰(或者这是否与微优化相同)...
    • @ircmaxell,真的,真的。我只是问我们是否应该。您上面描述的方法并不完美,但比 Zend 方法有用得多(每个子包也有单独的例外)。理想的解决方案是为每个包手动定义基本异常的单独命名空间(以告知它们扩展了哪些标准异常),然后自动生成异常,如您所述。然而,这个解决方案也有一些缺点,所以我想最好的办法是保持我们现在的状态:)
    • @takeshin:请参阅我最近对原始答案的编辑,以了解有关这一点的一些见解...
    【解决方案4】:

    您似乎专注于将这些作为“幽灵”类的想法——没有实现的类或标记接口。坦率地说,你没有抓住重点。

    在 ZF1 中,异常类仅是组件级别,并且该级别的所有异常都接收相同的异常类。这实际上只允许以下类型的捕获:

    • 全局级别(捕获“异常”)
    • 组件级(捕获组件级异常)

    这仅比简单地到处抛出“异常”好一点;您需要仔细检查异常消息以了解问题所在。

    现在,去仔细阅读提案。

    提案的重点是在捕获异常时允许更多级别的粒度:

    • 不在乎是什么异常?捕捉 \Exception。
    • 寻找特定组件的异常,但不关心除此之外的细节?捕获该组件的异常接口。
    • 想要查找特定类型的 SPL 异常?抓住这些(在 ZF2 中,异常类实现组件异常接口,并扩展适当的 SPL 异常)。
    • 想要在组件中捕获特定的异常类型?抓住它。

    基本上,我们现在只对异常 TYPE 允许更大的粒度;如果您尝试的操作可能引发多个相同类型的异常,您只需检查该消息。通常情况下,情况并非如此。

    SPL 异常在语义上相当丰富,ZF 中的许多异常最好归类为这些类型(例如,无效参数应该引发 InvalidArgumentException;无法解析插件将是一个很好的 RuntimeException;等等)。使用 ZF1,这是不可能的——我们必须从组件级别的异常中继承,句号。通过迁移到标记接口,我们既可以捕获组件级别的异常,也可以捕获 SPL 级别的异常,还可以捕获更具体的异常类型。

    【讨论】:

      【解决方案5】:

      事实上 PHP 已经有了异常子类,完整列表请参见here。因此关于它们的使用文档非常有限,您可以查看this post on php exceptions了解每个的含义。

      【讨论】:

        猜你喜欢
        • 2022-01-02
        • 1970-01-01
        • 2016-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-07
        • 2016-09-14
        相关资源
        最近更新 更多