当前位置: 代码迷 >> 综合 >> try...catch...finally 及其 opcode 解释
  详细解决方案

try...catch...finally 及其 opcode 解释

热度:91   发布时间:2023-12-09 11:43:16.0

在 PHP5.5 新增了 finally ,这个语法结构是鸟哥实现的,可以用来处理一些收尾工作。所以可以有以下搭配
try ... catch
try ... finally
try ... catch ... finally

catch 可以是多个用来捕获不同的异常,并且 finally 并不是必须的,比如在并发控制的时候释放锁的操作就可以放在 finally 里。

try ... catch ... finally 的逻辑关系是这样的。

img

对于finally来说, 一个比较容易让人迷惑的地方就是在finallyreturn, 因为finally必须保证一定被执行,所以如果我们在tryreturn了,finally也会被调用,那么如果finallyreturn呢?到底最后的return的值是那个呢? 在PHP中来说,如果在finallyreturn,那么就会覆盖原有的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 为操作码。

阅读流程:

  1. 赋值 !0 = 0
  2. 赋值 !0 = 1
  3. !0 赋值给临时变量 ~5
  4. ->10 表示跳到编号为 10 的位置
  5. 在编号为 10 的地方,!0 = 3
  6. DISCARD_EXCEPTION
  7. 返回 !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

阅读流程:

  1. 赋值 !0 = 0
  2. 赋值 !0 = 1
  3. !0 赋值给临时变量 ~5
  4. FAST_CALL指令是跳转,->10 表示跳到编号为 10 的位置
  5. 在编号为 10 的地方,!0 = 3
  6. FAST_RET回到上一个FAST_CALL的地方,并执行下一个指令,也就是编号为 4 的,返回临时变量 ~5,程序结束。

至于为什么要这么做取决于它的实现者根据合理性来定的,但是由此我们可以知道 return 语句并不是函数结束的标志,而是设置函数的返回值,内核会根据后面是否还有代码要执行,来决定是否结束本函数。没有的话则直接返回;有的话,则先将返回值放到临时变量,待后续代码执行完毕再返回此临时变量。

所以记住一下结论:

  • 如果 try, catchreturn,按照正常执行,然后执行 finally 的逻辑,再返回对应的 try, catch 里执行return
  • 如果 finally 中也有 return,则最终返回值为 finallyreturn 的值。
  • 特别注意:try, catch 中有 dieexitfinally 不会执行。