在 PHP5.5 新增了 finally
,这个语法结构是鸟哥实现的,可以用来处理一些收尾工作。所以可以有以下搭配
try ... catch
try ... finally
try ... catch ... finally
catch
可以是多个用来捕获不同的异常,并且 finally
并不是必须的,比如在并发控制的时候释放锁的操作就可以放在 finally
里。
try ... catch ... finally
的逻辑关系是这样的。
对于finally
来说, 一个比较容易让人迷惑的地方就是在finally
中return
, 因为finally
必须保证一定被执行,所以如果我们在try
中return
了,finally
也会被调用,那么如果finally
也return
呢?到底最后的return
的值是那个呢? 在PHP中来说,如果在finally
中return
,那么就会覆盖原有的return
值。
function tt()
{
try {
return 1;} catch (Exception $e) {
} finally {
return 2;}
}echo tt(); // 2
返回 2
如何理解这个事呢,为了弄清里面的细节,我们可以查看 opcodes 。
function tt()
{
$a = 0;try {
$a = 1;return $a;} catch (Exception $e) {
$a = 2;} finally {
$a = 3;return $a;}
}echo tt();
php -dvld.active=1 test.ph
...
filename: D:\dev\php\my\test.php
function name: tt
number of ops: 15
compiled vars: !0 = $a, !1 = $e
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------6 0 E > ASSIGN !0, 08 1 ASSIGN !0, 19 2 QM_ASSIGN ~5 !03 > FAST_CALL ->104 > > RETURN ~55* JMP ->810 6 E > > CATCH 'Exception', !111 7 > ASSIGN !0, 212 8 > FAST_CALL ->109 > > JMP ->1413 10 > ASSIGN !0, 314 11 DISCARD_EXCEPTION12 > RETURN !013* FAST_RET16 14 > > RETURN null
!0 = $a, !1 = $e
为变量,#*
为 opcode
编号,line
为代码文件行号,op
为操作码。
阅读流程:
- 赋值
!0 = 0
- 赋值
!0 = 1
- 将
!0
赋值给临时变量~5
->10
表示跳到编号为10
的位置- 在编号为
10
的地方,!0 = 3
DISCARD_EXCEPTION
- 返回
!0
,程序已结束,后面不再执行,但是从最后一行看出,任何函数如果没有返回值则会默认返回null
我们在来分析一个 finally 中没有 return 语句的情况。
function tt()
{
$a = 0;try {
$a = 1;return $a;} catch (Exception $e) {
$a = 2;} finally {
$a = 3;}
}echo tt();
php -dvld.active=1 test.ph
...
filename: D:\dev\php\my\test.php
function name: tt
number of ops: 13
compiled vars: !0 = $a, !1 = $e
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------6 0 E > ASSIGN !0, 08 1 ASSIGN !0, 19 2 QM_ASSIGN ~5 !03 > FAST_CALL ->104 > > RETURN ~55* JMP ->810 6 E > > CATCH 'Exception', !111 7 > ASSIGN !0, 212 8 > FAST_CALL ->109 > > JMP ->1213 10 > ASSIGN !0, 311 > FAST_RET15 12 > > RETURN null
阅读流程:
- 赋值
!0 = 0
- 赋值
!0 = 1
- 将
!0
赋值给临时变量~5
FAST_CALL
指令是跳转,->10
表示跳到编号为10
的位置- 在编号为
10
的地方,!0 = 3
FAST_RET
回到上一个FAST_CALL
的地方,并执行下一个指令,也就是编号为4
的,返回临时变量~5
,程序结束。
至于为什么要这么做取决于它的实现者根据合理性来定的,但是由此我们可以知道 return 语句并不是函数结束的标志,而是设置函数的返回值,内核会根据后面是否还有代码要执行,来决定是否结束本函数。没有的话则直接返回;有的话,则先将返回值放到临时变量,待后续代码执行完毕再返回此临时变量。
所以记住一下结论:
- 如果
try, catch
有return
,按照正常执行,然后执行finally
的逻辑,再返回对应的try, catch
里执行return
。 - 如果
finally
中也有return
,则最终返回值为finally
中return
的值。 - 特别注意:
try, catch
中有die
或exit
则finally
不会执行。