PHP异常处理中的finally

0x01 异常处理

在做代码分析的时候发现了一个有意思的点,样例代码如下:

<?php
1.function test() {
2.    try {
3.        throw new Exception('foo');
4.    } catch (Exception $e) {
5.		$a = 'hello';
6.        return $a;
7.    } finally {
8.	    $a = $a." world\n";
9.	    echo $a;
    }
}

echo test();
?>

我们知道finally会在return之前执行,那么上图的执行顺序应该是
L5->L8->L9->L6
也就是

$a = 'hello'
$a = $a."world\n";
echo $a;
echo (return $a)	//这里把外面的echo放进来的

那么我们的预期输出应该是

hello world
hello world

然而实际的输出却是:

hello world
hello

为什么return $a中的$a的值没变呢?

0x02 原因分析

直接上opcode

L0:     V3 = NEW 1 string("Exception")
L1:     SEND_VAL_EX string("foo") 1
L2:     DO_FCALL
L3:     THROW V3
L4:     CATCH string("Exception") CV0($e)
L5:     ASSIGN CV1($a) string("hello")
L6:     T3 = QM_ASSIGN CV1($a)
L7:     T2 = FAST_CALL L9 T3
L8:     RETURN T3
L9:     ASSIGN_CONCAT CV1($a) string(" world\n")
L10:    ECHO CV1($a)
L11:    FAST_RET T2

可以看到L8中的RETURN接收的是变量T3的值,也就是$a='hello'的值。而在return之前执行的finally代码段的内容是通过FAST_CALL进行调用的,即先执行finally代码段可以理解为进行了一个函数调用,在执行完L7跳转到L9进行执行,最后到L11遇到FAST_RET回到T2(即L7)继续顺序执行。

因此传入finally代码段的$a是值传递,而不是引用传递,自然也不会影响RETURN的值

0x03 另一个样例

<?php
1.function test() {
2.    try {
3.        throw new Exception('foo');
4.    } catch (Exception $e) {
5.		$a = 'hello';
6.        return $a;
7.    } finally {
8.	    $a = $a." world\n";
9.	    echo $a;
10.		return $a;	
    }
}

echo test();
?>

这个样例代码输出的就是:

hello world
hello world

查看opcode如下:

L0:     V3 = NEW 1 string("Exception")
L1:     SEND_VAL_EX string("foo") 1
L2:     DO_FCALL
L3:     THROW V3
L4:     CATCH string("Exception") CV0($e)
L5:     ASSIGN CV1($a) string("hello")
L6:     T3 = QM_ASSIGN CV1($a)
L7:     T2 = FAST_CALL L9 T3
L8:     RETURN T3
L9:     ASSIGN_CONCAT CV1($a) string(" world\n")
L10:    ECHO CV1($a)
L11:    DISCARD_EXCEPTION T2
L12:    RETURN CV1($a)
L13:    FAST_RET T2

根据上面的分析,这里也很简单,不过是在FAST_CALL过程中遇到了RETURN,因此程序执行终止。(所以说FAST_CALL只是一个看起来类似于函数调用的指令,实际上还在当前空间内执行)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值