php的eval相同,关于php eval( )函数 和 实际代码执行的 结果不一致

要理解上述现象,首先要知道以下两条规则:

Rule 1:单引号内不解析变量,双引号内解析变量

下面给出 Rule 1 的 PoC:

$name = 'Alice';

$x = 'hello $name'; //单引号内变量不被解析

$y = "hello $name"; //双引号内变量会被解析

echo $x, '
';

echo $y, '
';

?>

结果输出:

hello $name

hello Alice

因此在 eval("\$str = \"$str\";"); 语句中,\$str 是为了变量不被解析成字符串,\" 是为了转义内层的双引号,避免与外层的双引号闭合。

Rule 2:PHP 字符串中嵌套引号的解析是由外向里的,且只解析一次

换句话说,即只解析最外层的引号,下面给出 Rule 2 的 PoC:

$name = 'Alice';

$x = 'hello $name'; //单引号内变量不被解析

echo $x, '
'; //直接输出变量$x,即字符串hello $name

echo '$x', '
'; //变量$x在单引号内不被解析,故输出字符串$x

echo "$x", '
'; //变量$x在双引号内会被解析,故输出$x的值,即字符串hello $name

echo "'$x'", '
'; //只有双引号起作用,变量$x会被解析,故输出字符串'hello $name'

echo '"$x"', '
'; //只有单引号起作用,变量$x不被解析,故输出字符串"$x"

echo "'\$x'", '
'; //只有双引号起作用,变量$x被转义而不被解析,故输出字符串'$x'

echo '"\$x"', '
'; //只有单引号起作用,变量$x不被解析,故输出字符串"\$x"

echo '\'$x\'', '
'; //只有外层单引号起作用,内层单引号需转义,变量$x不被解析,故输出字符串'\$x'

echo "\"$x\"", '
'; //只有外层双引号起作用,内层双引号需转义,变量$x会被解析,故输出字符串"hello $name"

echo "
";

$y = "hello $name"; //双引号内变量会被解析

echo $y, '
'; //直接输出变量$y的实际值,即字符串hello Alice

echo '$y', '
'; //变量$y在单引号内不被解析,故输出字符串$y

echo "$y", '
'; //变量$y在双引号内会被解析,故输出$y的值,即字符串hello Alice

echo "'$y'", '
'; //只有双引号起作用,变量$y会被解析,故输出字符串'hello Alice'

echo '"$y"', '
'; //只有单引号起作用,变量$y不被解析,故输出字符串"$y"

echo "'\$y'", '
'; //只有双引号起作用,变量$y被转义而不被解析,故输出字符串'$y'

echo '"\$y"', '
'; //只有单引号起作用,变量$y不被解析,故输出字符串"\$y"

echo '\'$y\'', '
'; //只有外层单引号起作用,内层单引号需转义,变量$y不被解析,故输出字符串'\$y'

echo "\"$y\"", '
'; //只有外层双引号起作用,内层双引号需转义,变量$y会被解析,故输出字符串"hello Alice"

?>

结果输出:

hello $name

$x

hello $name

'hello $name'

"$x"

'$x'

"\$x"

'$x'

"hello $name"

hello Alice

$y

hello Alice

'hello Alice'

"$y"

'$y'

"\$y"

'$y'

"hello Alice"

下面开始分析以下代码,为了方便理解与说明,对官网文档中示例代码的变量名稍作修改,并将 eval() 函数中的字符串语句抽离出来,单独执行:

$string = 'cup';

$name = 'coffee';

$origin = 'This is a $string with my $name in it.';

echo $origin. "\n";

$result_1 = "$origin";

echo $result_1. "\n";

eval("\$result_2 = \"$origin\";");

echo $result_2. "\n";

?>

以上代码输出如下,也就是本人困惑的地方:

This is a $string with my $name in it.

This is a $string with my $name in it.

This is a cup with my coffee in it.

$origin 的输出没有问题,根据 Rule 1 即可理解;

$result_1 的输出为什么不变呢?理解了 $result_1 = "$origin"; 就明白了。根据 Rule1,$origin 会被解析,得到它本身的值,即字符串 'This is a $string with my $name in it.' (注意该字符串两边的引号与 $origin 的相同,此处为单引号),因此 $result_1 的输出与 $origin 一样;

$result_2 为什么会改变,是最难理解的。根据 Rule 2,可知 eval() 函数最外层的双引号会被解析,内层被转义的双引号原封不动,因此函数内执行的语句相当于 $result_2 = "This is a $string with my $name in it.",注意该字符串两边已变成双引号,故$result_2` 字符串中的变量会被解析。

讲到这里,肯定有人疑问:不是说解析后字符串两边引号跟 $origin 相同吗?按理说应该是 $result_2 = "'This is a $string with my $name in it.'",那么输出应该跟 Rule 2 PoC 中的 echo "'$x'" 一样啊!

请注意,此处因为有个 eval() 函数,所以可以有双层解析,即第一层是解析 eval() 函数最外层的双引号,第二层是解析字符串中的双引号。类似 echo "'$x'" 的引号嵌套,只有单层解析,因此 result_1 中 'This is a $string with my $name in it.' 的单引号与 $origin 的相同,而 result_2 中 "This is a $string with my $name in it." 的引号则由第二层的双引号赋予。

可能还有读者不死心,非要在赋值语句中构造双层解析,那我们就来试试看吧。在 $result_1 = "$origin"; 的基础上,再加个双引号吧,得到 $result_1 = ""$origin"";,简直完美、帅气、机智...

PHP Parse error: syntax error, unexpected '$origin' (T_VARIABLE)

咳咳,已经丧失理智到忘记相邻两个双引号会闭合吗?不行就转义吧,看我骚操作 $result_1 = "\"$origin\"";,还有 $result_1 = ''$origin''; 与 $result_1 = '\'$origin\'';!表演结束了吗?要不送一份 Rule 2 的 PoC 给你参考参考?

以上故事告诉我们一个道理:单凭赋值语句是完成不了字符串单双引号转换的。

对于题主的疑问,看到分割线以上就 OK 了,想再深入理解的可继续看下面的玩法(每个玩法在上例基础上修改,之间互不干扰):

玩法 1:将 eval() 函数的第二层解析改为单引号:

注意:第二层解析的单引号不需要转义,因为第一层是双引号。

eval("\$result_2 = '$origin';");

此时输出结果为:

This is a $string with my $name in it.

This is a $string with my $name in it.

This is a $string with my $name in it.

如果理解了上面 result_2 的输出原理,结果容易理解,即相当于执行了 $result_2 = 'This is a $string with my $name in it.',自然与 $origin 和 result_1 相同。

玩法 2:将 $origin 字符串两边改为双引号:

$origin = "This is a $string with my $name in it.";

此时输出结果为:

This is a cup with my coffee in it.

This is a cup with my coffee in it.

This is a cup with my coffee in it.

这种情况也容易理解,原始字符串一开始就是双引号,那自然是一直解析到底。

玩法 3:将 eval() 函数的第二层解析改为单引号,并将 $origin 字符串两边改为双引号:

$origin = "This is a $string with my $name in it.";

eval("\$result_2 = '$origin';");

此时输出结果为:

This is a cup with my coffee in it.

This is a cup with my coffee in it.

This is a cup with my coffee in it.

输出结果与玩法 2 相同,不过注意一点,就是 eval() 函数第一层转义后,返回值为 $result_2 = 'This is a cup with my coffee in it.',即得到的是所有变量都解析后的字符串。

玩法 4:将 eval() 函数的第一层解析改为单引号:

注意:第二层解析的双引号不需要转义,因为第一层是单引号,并且单引号内本身就不会转义 $,所以 $ 前面的转义字符也可以去除。

eval('$result_2 = "$origin";');

此时输出结果为:

This is a $string with my $name in it.

This is a $string with my $name in it.

This is a $string with my $name in it.

输出结果与玩法 1 相同,但输出原理却不同了。第一层解析时,$origin 是原封不动保留下来的,即 $result_2 = "$origin",跟 result_1 的情况完全相同。

玩法 5:将 eval() 函数的第一层和第二层解析都改为单引号:

注意:第二层解析的单引号此时需要转义,因为第一层也是单引号。

eval('$result_2 = \'$origin\';');

此时输出结果为:

This is a $string with my $name in it.

This is a $string with my $name in it.

$origin

这次的输出结果有点意思,跟玩法 4 一样,在第一层解析时,$origin 原封不动保留下来,即 $result_2 = '$origin',而且在第二层解析时也要原封不动地输出字符串 $origin。

还有两种玩法(即分别在玩法 4 和玩法 5 的基础上,将 $origin 字符串两边改为双引号)大同小异,这里就不详述了,有兴趣的读者可以自行推导。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值