什么时候应该使用PHP Exceptions?

Jos*_*osh 13 php error-handling exception-handling exception try-catch

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

bej*_*bee 7

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

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

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

class RegistrationFailed extends Exception {}
Run Code Online (Sandbox Code Playgroud)

现在,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
Run Code Online (Sandbox Code Playgroud)

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


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

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

但是,它是一个ajax应用程序,因此当有人未登录时,有可能会尝试手动访问ajax URL.这异常,因此我们会这样处理它.

  • 我不同意Sk8erPeter捕获泛型异常.如果您不知道如何处理异常,则不应该抓住它.相反,你应该让它向上传播到可以处理它的东西.如果没有什么可以处理它,你可能应该在顶层捕获它并记录它,以便你知道代码中有一个错误. (3认同)

Adi*_*tal 6

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

失败 - 当操作不成功时

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

例如,如果机器人试图步行到目的地但没有到达目标,那就是失败。但如果它摔断了一条腿或者屋顶掉下来压在它身上,那就是例外了。

如果屋顶掉下来,我会抛出屋顶掉下来的异常。

如果机器人错过了标记,我不会抛出异常,我会返回 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;
Run Code Online (Sandbox Code Playgroud)


ere*_*non 5

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

例:

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

    /* ... nominal case */
}
Run Code Online (Sandbox Code Playgroud)

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

  • 返回带有无效的返回值(这是C实践,例如:return -1或using status flags)

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


Sta*_*nov 5

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

我编写了一份通用的例外指南最佳实践,这是我在对该主题进行广泛研究后提出的。这些指南中的大多数都可以用于支持异常的任何语言的所有项目。一些指南将是 Java 特定的。最后,您需要有一套强大的指导方针,可以帮助您处理和处理异常和错误情况。

这里有几点需要考虑

不要向您的客户公开内部的、特定于实现的细节

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

为复杂项目创建自己的异常层次结构

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

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

记录所有异常

在每个公共方法的 javadoc 定义中仔细记录您的包/模块/应用程序抛出的所有异常。不这样做会使您的 API 用户感到沮丧,并导致他们不信任您的 API 文档。你真的不希望你的客户挖掘你的源代码只是为了发现你正在抛出一个特定的异常,对吧?

尽早抛出异常。

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

正确记录异常

遵循日志框架的指导方针,以便正确记录异常及其消息和堆栈跟踪。你也不想松