本节书摘来自华章社区《PHP精粹:编写高效PHP代码》一书中的第1章,第1.7节异常,作者:(美) Davey Shafik,更多章节内容可以访问云栖社区“华章社区”公众号查看
1.7 异常
异常(exception)是一个处理错误的面向对象方法。一些PHP扩展像往常一样仍会报错;很多最新的扩展(例如PDO)将代替抛出异常。异常也是对象,而且Exception是PHP的一个内置类。一个Exception对象将包含发生错误的位置(文件名或代码行)、一条错误消息和(可选)一个错误代码等信息。
1.7.1 处理异常
首先我们看看如何处理可能会抛出异常的函数。我们会使用一个PDO示例,因为PDO扩展抛出异常。在这里代码会试图创建一个数据库连接,但是会失败,因为nonsense主机不存在:
这段代码阐明了try/catch结构。在try块中,我们将想要的代码放到应用程序中运行,但我们知道代码可能会抛出一个异常。在catch块中,可以添加一些应对错误的代码,无论是处理它还是记录它,采取任何行动都是恰当的。
注意,当发生一个异常时,就像这里试图连接到数据库一样,PHP没有运行try块中其余的代码而直接跳转到catch块中。在这个示例中,数据库连接失败意味着我们根本看不到已连接到数据库的消息,因为这一行代码根本无法运行。
无finally子句
如果你在其他语言中使用过异常,你可能习惯于try/catch/finally结构;PHP没有附加的finally子句。
1.7.2 为什么要处理异常
比起会引发不同层次错误的传统方法,异常是一个更简洁的错误处理方法。在执行代码的过程中,我们可以根据错误的严重程度对异常做出反应。我们可以对问题进行评估,然后告诉系统如何恢复,或顺利地摆脱困境。
将所有的异常作为对象意味着我们可以扩展异常(很快会有示例演示),并且可以自定义异常的数据和反应。我们已经知道如何使用对象工作,这使得我们能更简单地把复杂功能添加到错误处理系统中。
1.7.3 抛出异常
我们已经看到如何处理由PHP内置函数抛出的异常,但是我们自己如何抛出异常呢?是的,我们肯定能够做到这一点:
可根据需要为Exception类设置任何属性或添加任何方法。定义为一个空类也很常见,空类只是为了提供更多特定类型的异常,使我们识别出:应用程序的哪一部分遇到了问题而没有尝试以编程方式读取错误信息。
自动加载异常
此前,介绍了自动加载,为在哪里能找到类而定义规则,而在脚本中执行的代码并未包含这个类的定义。异常也是简单的对象,因此也可以使用自动加载功能来加载异常类。
通过所有特定的异常类可以捕捉不同的异常类型,将在下一节学习这一点。
1.7.5 捕捉特定类型的异常
思考如下这个代码示例可以抛出多个异常:
在这个示例中,开始实例化Courier和Parcel两个对象。Parcel对象既有地址又有重量;当发货的时候会检查它们。注意,这个示例使用了一个小小的rand()函数来产生各种包裹的重量!这是一个测试代码的有趣方法,因为有些包裹由于超重而引发异常。
在try块中,要求快递公司运送包裹。如果运气好,过程一切顺利,我们就会看到“parcel shipped”消息。这两个catch块让我们巧妙地处理失败结果,第一个catch块负责特定捕捉HeavyParcelException异常,任何其他类型的异常由第二个很普通的catch块捕捉。如果我们想首先捕捉Exception异常,所有的异常将最终在这里被捕捉,那么我们首先要保证这个catch块具有最特殊的异常类型。
实际上,这个catch块使用了类型提示来区分一个对象是否为可接受的类型。因此前面介绍的类型提示和多态性也适用于这里;一个HeavyParcelException异常也是一个Exception异常。
在这个示例中,异常从类的内部抛出,进而捕捉代码中调用对象方法的栈。没有捕捉到的异常会返回调用它的内容中,如果在这里它们仍然没有被捕捉,它们将继续通过调用栈向上抛出。当它们到达顶部仍然未被捕捉,我们将看到严重的错误:Uncaught Exception。
1.7.6 设定一个全局异常处理程序
为避免出现异常被抛出而代码捕捉失败的严重错误,可以为应用程序设定一个默认的行为。为做到这一点,使用了一个名为set_exception_handler()的函数。它接受一个回调作为它的参数,因此可以为使用的函数命名,例如,一个异常处理程序通常会在屏幕上给用户显示一个错误提示—这比一个严重错误的消息要好得多!
一个基本的异常处理程序类似于这样:
这里显示了一个异常处理程序,然后它调用set_exception_handler()方法注册这个函数来处理未捕获的异常。通常在脚本的开始部分声明和设置异常处理程序,或者放在引导文件中,如果你有一个的话。
默认错误处理
除了使用set_exception_hander()处理异常外,PHP也提供set_error_handler()方法处理错误。
示例异常处理程序使用error_log()函数在PHP的错误日志中写入错误的内容,打开日志文件会看到如下内容:
1.7.7 使用回调
刚才展示了回调函数的使用,正好我们可以看看是否还有其他可用的选择。回调广泛应用于PHP的各个方面,set_exception_handler()和set_error_handler()函数就是很好的例子。也可以使用回调,例如,在array_walk()这个函数中,要求PHP使用相同的操作,为一个数组中的每一个元素指定使用一个回调。
回调可以使用多种形式:
一个函数名
一个类名和一个方法名,其中方法是静态调用的
一个对象和一个方法名,其中调用的方法与提供它的对象相对应
一个closure(存储在变量中的一个函数)
一个lambda函数(就地声明的一个函数)
回调让我们使用匿名函数成为可能。为异常处理程序声明的匿名函数不会用于应用程序的任何其他地方,因此没必要为它取一个全局性的名称。在PHP手册的相关页面上还有更多关于匿名函数的内容。