什么时候应该使用PHP Exceptions?

时间:2010-12-15 15:07:09

标签: php exception exception-handling error-handling try-catch

我已经看过许多教程,演示简单的尝试捕获,比如打开文件的行为。但我从来没有见过一个很大的“真实”例子。有人可以向我提供一些他们已经或将要使用例外的案例吗?是否真的有必要扩展异常类只是为了抛出异常?最后,当抛出异常时,它会导致脚本退出(); ?或者,它是否记录并继续执行脚本?

7 个答案:

答案 0 :(得分:5)

异常是为了处理错误(至少在PHP中)。假设您处于例行程序中,并且发生了无法在当前上下文中处理的错误。

示例:

<?php
/**
 * @throws Exception_NoFile
 */
function read_file($file) {
    if(!file_exists($file)) {
        throw new Exception_NoFile($file);
    }

    /* ... nominal case */
}

在这种情况下,您无法继续使用名义案例,因为没有要处理的文件。你必须选择:

  • 返回无效的返回值(这是C练习,例如:return -1或使用状态标志)

  • 抛出异常,希望有人会抓住它。如果您的客户端代码除外,没问题,它可能会尝试其他路径或重新抛出异常。如果您的客户端还没有准备好处理请求的文件不存在的情况......您的代码将失败并显示未缓存的异常,就像在另一种方法中读取不存在的文件一样。

答案 1 :(得分:5)

我们在项目中广泛使用例外。

一个特定实例适用于要求用户登录或注册的操作。我们在错误条件下使用Exceptions进行流量控制。如果当前用户未登录,则抛出异常。然后,异常处理程序将它们重定向到登录页面。

以我们的注册操作为例,我们扩展了Exception,如下所示:

class RegistrationFailed extends Exception {}

现在,在注册码中的catch语句中,我们可以测试RegistrationFailed异常并相应地处理它。否则,当异常不是RegistrationFailed时,我们允许它冒泡,因为我们对它不感兴趣。

try {
    // do registration here
}
catch(RegistrationFailed $e) {
    // handle the failed registration
}
catch(SomeOtherException $e) {
    // handle other errors like this...
}

// All other errors will not be caught and will bubble up

另一个例子是我们的包装器类,开发人员必须扩展它们。我们使用Reflection来确保子类正确实现了它们的方法并提供了正确的接口。如果不是,我们通过异常通知该类的开发人员,让他们知道子类必须提供特定的接口或方法。


编辑: 我已经可以听到有关“您不应该使用错误处理进行流量控制!”的评论。但是,对于上面讨论的项目,这是必要的。

在程序的正常流程中,由于许多验证规则可能会失败,例如密码太短,预计会失败注册。

但是,它是一个ajax应用程序,所以有人可能会在没有登录时手动访问ajax url。这个作为异常,因此我们就这样处理它。

答案 2 :(得分:3)

你应该查看symfony框架 - 他们确实在那里使用了很多例外。

他们使用Exception来配置错误,比如说你忘了把文件放在控制器希望找到它的地方 - 这将是一个例外,因为没有任何框架可以做到这一点。

他们使用Exception来处理未知错误:数据库由于一些奇怪的原因而失败,没有什么框架可以做到这一点 - 所以它抛出异常

他们为不同的环境提供不同的异常处理程序。当在“devel”模式下发生异常时,您将获得一个包含堆栈跟踪和解释的精美页面,当您处于“生产”模式时,您将重定向到自定义500页。

答案 3 :(得分:2)

异常处理很棘手。它需要仔细考虑手头的项目以及处理错误的方式。您应该尝试在项目的早期定义您的例外指南并遵守它。

我已经写了一篇关于这个主题的广泛研究后我提出的一般Exceptions Guidelines最佳实践。这些指南中的大多数可以用于支持异常的任何语言的所有项目。一些准则将是Java特定的。最后,您需要有一套强大的指南,可以帮助您处理和处理异常和错误情况。

以下是需要考虑的一些要点

不要向客户公开内部的,特定于实现的详细信息

避免向客户端暴露内部实现特定的异常,尤其是第三方库中包含的异常。这是一个面向对象的通用经验法则,它对您的异常层次结构设计有效。您无法控制第三方库,它可以更改其例外签名并中断与您的客户的所有API合同。而是将这些第三方异常(例如SQLException)包装在您自己的自定义异常中。通过这种方式,您可以更灵活地在将来更改第三方库,而不会破坏客户的API合同。

为复杂项目创建自己的例外层次结构

一般来说,为更复杂的模块创建自己的异常层次结构,尤其是在第三方库中处理特定于实现的异常时。您的每个软件包/模块都有自己的顶级通用异常。对于Java,至少应定义一个继承自RuntimeException的Java。在自定义异常中包装所有特定于实现的异常,以便客户端仅依赖于自定义异常和/或通用Java异常。这将为您提供更大的灵活性,以便在不破坏API合同的情况下重构特定于实现的代码。

如果需要更细粒度的错误处理,那么您可以进一步子类化自定义异常以处理特定情况并允许错误恢复。例如,如果要连接到SQL数据库,则可以以这样的方式抛出ConnectionTimeoutException:如果需要,客户端可以在放弃之前重试连接N次。这样您以后可以将数据库引擎更改为NoSQL,并且仍然允许重新连接,并且客户端代码将保持不变。

记录所有例外情况

在每个公共方法的javadoc定义中仔细记录package / module / app抛出的所有异常。如果不这样做会使您的API用户感到沮丧并导致他们不相信您的API文档。你真的不希望你的客户挖掘你的来源只是为了发现你正在抛出一个特定的例外,对吗?

尽早抛出异常。

检查公共API方法的所有输入,并在发现预期参数与提供的参数之间存在不一致时立即抛出异常。抛出异常越早,数据损坏的可能性就越小,因为错误的数据不会进入代码的更深层次。它还可以及时为您的客户提供有价值的反馈,而不是深入您的代码,其中某些内容会因“内部错误”或NullPointerException等错误消息而引发模糊异常。

正确记录异常

遵循日志记录框架的准则,以便使用其消息和堆栈跟踪正确记录异常。你不想放弃

答案 4 :(得分:1)

我觉得很多人把“失败”和“例外”混为一谈。 “错误”这个词可以指代其中任何一个,但我将它用于失败。

失败 - 操作失败时

例外 - 当出现意外或不正常的流动情况时

例如,如果机器人试图走到目的地并错过了标记 - 那就是失败。但如果它断了一条腿或屋顶落在它上面,这是一个例外。

如果屋顶倒塌,我会抛出屋顶倒塌的例外情况。

如果机器人错过了标记,我不会抛出异常,我会返回false或返回错误消息,例如“由于屋顶掉落而无法到达目的地。”

try {
  Walk to the cabinet;
}
catch (RoofFell_Exception $e) {
  return "Could not reach the destination because the roof fell.";
}
catch (Legbroke_Exception $e) {
  return "Could not reach the destination because a leg broke.";
}

if($current_location == 'cabinet') {
  return "Destination reached";
}
return false;

答案 5 :(得分:0)

异常只是一种将边缘情况或错误(实际上只是大边缘情况事件)从大量代码中移出的方法,以阻止它们使99%的基本代码流与大量交换机混乱/ IFS。

您可以将它们视为一种反向切换语句,其中try {}内的事件确定哪个(如果有的话)catch块也会发生。

这意味着如果你不喜欢它们,你就不必使用它们。但它们可以使代码更容易阅读。

答案 6 :(得分:0)

使用异常处理的好地方是程序尝试连接或访问I / O(文件,数据库,网络,设备)时。

  1. 当调用代码块(函数/方法)尝试访问文件时使用异常处理。

  2. 当调用代码块(函数/方法)尝试数据库连接时使用异常处理。

  3. 当调用代码块(函数/方法)尝试在数据库上运行查询时,请使用异常处理(任何尝试访问数据库表/视图等)。< / p>

  4. 您可以对网络连接或访问说同样的话。

  5. 内存访问需要I / O(包括在$ _SESSION文件中存储内容),但大多数初学者不会将整个程序置于try...catch结构的范围内。在Matt Doyle的书 Beginning PHP 5.3 ,ch中可以找到使用异常和扩展Exception类的一个很好的例子。 20,p。 652-60。

    我可能还会说,学习将异常处理的使用与set_error_handler()块内的trigger_error()error_log()catch相结合可以让您保留自定义,开发人员友好的错误消息,您可能已经回应了开发中的输出设备(浏览器/标准输出)。也就是说,在生产中,你的php.ini将关闭display_errors并且log_errors将打开。如果与所述数据库的连接失败,您可能会回复“哎呀,我无法连接到会计数据库”,只需将相同的文本字符串发送到error_log(),您的个人错误消息仍然可以记录。< / p>

    示例:

    function custom_handler($arg1, $arg2, $arg3, $arg4, $arg5)
    {
      ...
      ...
      error_log(blah, blah, blah)
    }
    
    set_error_handler('custom_handler');  <--takes over this duty from PHP
    $error = NULL;
    
    try
    {
         if(!connect_to_mythical_database('accounting'))
         {
              $error = 'I cannot connect to the accounting database';
              throw new Exception(supply-correct-arguments);
         }
    }
    catch (Exception $e)
    {
         trigger_error(supply-correct-arguments); <-- does what 'custom_handler' instructs.
         error_log($error, blah, blah); <---Your friendly message here
         header('Location: http://www.myhomepage.com');
         exit;
    }
    

    注意:在custom_handler()内,您可以使用error_log()在文件中记录PHP错误消息,通过电子邮件发送,或者同时记录和发送标准PHP错误消息。因此,trigger_error()由PHP(默认)或'custom_handler'控制,它可以实现异常处理程序的error_log()', all of which can be activated by the catch`块。