taoCMS是基于php+sqlite/mysql的国内最小(100Kb左右)的功能完善的CMS管理系统

php中的异常机制详解(2)

2011-08-18
 在上一节,提到php只有在用if-else进行判断抛出异常后才能捕获异常,或者是有内建的异常机制时,会先触发错误,再捕获异常。
    那php里的异常应该怎么用?在什么时候抛异常,什么时候捕获呢?什么场景下能应用异常?
    在下面三种场景下会用到异常处理机制:
    (1)对程序的悲观预测
     如果一个程序员对自己的代码带有“悲观情绪”,这里并不是指该程序员代码质量不高。他认为自己的代码无法一一处理各种可预见的不可预见的情况,那该程序员就会进行异常处理。假设一个场景,程序员悲观地认为自己的这段代码在高并发条件下可能产生死锁,那么他就会悲观地抛出异常,然后在死锁时进行捕获,对异常进行细致的处理。
    (2)程序的需要和对业务的关注
    如果程序员希望业务代码中不会充斥大堆的打印,调试等处理,通常他们会使用异常机制;或者业务上需要定义一些自己的异常,这个时候就需要自定义一个异常, 来对现实世界中各种各样的业务进行补充。比如上班迟到,这种情况,我就认为是一个异常,要收集起来,到月底集中处理,扣你工资;如果程序员希望有预见性地处理可能发生的会影响正常业务的代码,那么它需要异常。在这里,强调了异常是业务处理中必不可少的环节,不能对异常视而不见。异常机制认为,数据一致很重要,在数据一致性可能被破坏时,就需要异常机制来进行预先补救。
    举个例子,比如有个上传文件的业务需求,要把上传的文件保存在一个目录里,并在数据库里插入这个文件的记录,那么这两步就是互相关联密不可分的一个集成的业务,缺一不可。文件保存失败,而插入记录成功就会导致无法下载文件;而文件保存成功数据库写入失败,则会导致没有记录的文件成为死文件,永远得不到下载。
      那么我们假设文件保存成功后没有提示,但是保存失败会自动抛出异常,访问数据库也一样,插入成功没有提示,失败则自动抛出异常,我们就可以把这两个有可能抛出异常的代码段包在一个try语句里,然后在catch捕捉错误,在catch代码段里删除没有被记录到数据库的文件或者删除没有文件的记录,以保证业务数据的一致性。     因此,从业务这个角度讲,异常偏重于保护业务数据一致性,并且强调了对异常业务的处理。
      如果我们的代码中,只是象征性的try-catch,最后打印一个报错,over。这样的异常,不如不用,没有体现了异常的思想。所以,合理的代码应该如下:
<?php

try{

//可能出错的代码段

if(文件上传不成功) throw(上传异常);

if(插入数据库不成功) throw(数据库操作异常);}catch(异常){

必须的补救措施,如删除文件,删除数据库插入记录,这个处理很细致

}

//....

?>


也可以如下:
<?php

上传{

if(文件上传不成功) throw(上传异常);

if(插入数据库不成功) throw(数据库操作异常);

}

//其他代码...try{

上传;

其他;

}catch(上传异常){

必须的补救措施,如删除文件,删除数据库插入记录

}catch(其它异常){

记录log

}

?>

    上面的两种捕获异常的方式,前一种是在异常发生时,立刻捕获;后一种是分散抛异常,集中捕获。那到底应该是哪一种呢?
   如果我们的业务很重要,那么异常越早处理越好,以保证程序在意外情况下能保持业务处理的一致性。比如一个操作有多个前提步骤,突然最后一个步骤异常了,那么其他前提操作都要消除掉才行,保证数据一致性。并且在这种核心业务下,有大量的代码来做善后工作,进行数据补救,这是一种比较悲观的异常
  如果我们的异常不是那么重要,并且在单一入口,MVC风格的应用中,为了保持代码流程的统一,则常常采用后一种异常处理方式,这种异常处理更多强调了业务流程的走向,对善后工作并不是很关心。这是一种乐观的异常
    (3)语言级别的健壮性要求
      在这点上,php是缺失的。以java为例,java是一种面向企业级开发的语言,强调健壮性。java中支持多线程,java认为,多线程被中断这种情况是彻彻底底的无法预料和避免的。所以 java规定,凡是用了多线程,就必须正视这种情况。你要么抛出,不管它,要么捕获,进行处理。总之,你必须面对 InterruptedException这个异常,不准回避。也就是异常发生后应对重要数据业务进行补救,当然你可以不做,但是我会告诉你,这是你应该做的。    这类异常是强制的。更多的异常是非强制的,由程序员决定的。java对异常的这种分类和约束,保证了java程序的健壮性和可信赖度。
    那么异常的意义何在?
    异常就是无法控制的运行时错误,会导致出错时中断正常逻辑运行,该异常代码后面的逻辑都不能继续运行。那么try/catch的好处就是可以把异常造成的逻辑中断破坏降到最小范围内,并且经过补救处理措施后不影响业务逻辑的完整性,乱抛异常和只抛不捕获,或捕获而不补救,会导致数据混乱。 这就是异常处理的一个重要作用,就是在我们精确控制运行时流程的时候,在程序中断的时候,有预见的用try缩小可能出错的影响范围,再及时捕获异常的发生并作出相应的补救,以使逻辑流程仍然能回到正常轨道上来。
       怎样看php的异常?
       我们已经看到了php中的异常机制是很鸡肋的,绝大多数情况下无法自动抛异常,必须用if-else来先进行判断,再手工抛出异常。这种处理方式看起来,比较像是多此一举。手动抛异常的意义就不是很大了,因为你手动抛异常也就意味着你在代码里已经充分预期到错误的出现了,也就算不得真正的“异常”了,而是意料之中的了。还是陷入了纷繁复杂的业务逻辑判断和处理中。java和C++语言做的比较好的就是定义了一堆内置的常见的异常,不需要程序员判断各种异常情况后手工抛出,编译器会代我们进行判断业务是否发生错误,自动抛出异常。作为程序员,则只需要关心异常的捕获和随后补救,而不是像php中关注到底会发生哪些异常啊,用if-else来逐一判断,逐一抛异常。
    php的异常机制很不完美,很多情况下和if-else相比没有明显的优势,这也是php的异常没有普及的原因。当然了,使用了异常也能一定程度上降低耦合性。
    那怎么来完善php原先的异常处理机制呢?这时,就要借助php的错误处理了。PHP提供了一个set_error_handler函数,可以自定义错误处理函数,能够把非致命类型的错误处理都转向到自己定义的函数里进行分析和处理。但是因为出错的地方可能很多,集中处理的话要区分的情况很复杂,所以我们只用这个特性做个跳板。在自定义函数里我们手动抛一个异常出来,杀个回马枪,让try/catch可以捕获并处理这个运行时错误所带来的中断,从而实现扩大try/catch影响范围的目的.

类别:技术文章 | 阅读:71790 | 评论:0 | 标签:php 异常

想收藏或者和大家分享这篇好文章→

“php中的异常机制详解(2)”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

taoCMS发布taoCMS2.5Beta5(最后更新14年05月25日),请大家速速升级,欢迎大家试用和提出您宝贵的意见建议。

捐助与联系

☟请使用新浪微博联系我☟

☟在github上follow我☟

标签云

友情链接