关于php eval( )函数 和 实际代码执行的 结果不一致
eval()
在php中这个eval()
函数是将字符串作为代码来执行
于是下面这段代码
<?php
$string = 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo $str. "\n";
eval("\$str = \"$str\";");
$str = "$str";
echo $str. "\n";
输出为
This is a $string with my $name in it. This is a cup with my coffee in it.
那么现在我用实际代码代替eval()
函数
<?php
$string = 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo $str. "\n";
$str = "$str";
echo $str. "\n";
这输出为
This is a $string with my $name in it. This is a $string with my $name in it.
其实我知道单引号的不解析变量,我只是不明白为什么使用eval()
函数的时候单引号中的变量被解析了!是因为在$str外加了双引号吗?如果是,那么在不使用eval函数的时候直接加双引号却不行呢!请问这个问题是出在哪里?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
$str = 'This is a $string with my $name in it.';
单引号不解析变量eval的时候,This is a $string with my $name in it.当做一条表达式, ""内的$var自然会被当做变量处理;
如果下面的那种情况改成
输出也就一致了。
至于你后面的$str = "$str"; 这里只是普通的赋值。
双引号内的变量解析不能“递归”
教你一招:双引号+花括号
$string = 'cup';
$name = 'coffee';
$str = "This is a {$string} with my {$name} in it.";
echo $str;
要理解上述现象,首先要知道以下两条规则:
Rule 1:单引号内不解析变量,双引号内解析变量
下面给出 Rule 1 的 PoC:
结果输出:
因此在
eval("\$str = \"$str\";");
语句中,\$str
是为了变量不被解析成字符串,\"
是为了转义内层的双引号,避免与外层的双引号闭合。Rule 2:PHP 字符串中嵌套引号的解析是由外向里的,且只解析一次
换句话说,即只解析最外层的引号,下面给出 Rule 2 的 PoC:
结果输出:
下面开始分析以下代码,为了方便理解与说明,对官网文档中示例代码的变量名稍作修改,并将 eval() 函数中的字符串语句抽离出来,单独执行:
以上代码输出如下,也就是本人困惑的地方:
$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"";
,简直完美、帅气、机智...咳咳,已经丧失理智到忘记相邻两个双引号会闭合吗?不行就转义吧,看我骚操作
$result_1 = "\"$origin\"";
,还有$result_1 = ''$origin'';
与$result_1 = '\'$origin\'';
!表演结束了吗?要不送一份 Rule 2 的 PoC 给你参考参考?以上故事告诉我们一个道理:单凭赋值语句是完成不了字符串单双引号转换的。
对于题主的疑问,看到分割线以上就 OK 了,想再深入理解的可继续看下面的玩法(每个玩法在上例基础上修改,之间互不干扰):
玩法 1:将 eval() 函数的第二层解析改为单引号:
此时输出结果为:
如果理解了上面
result_2
的输出原理,结果容易理解,即相当于执行了$result_2 = 'This is a $string with my $name in it.'
,自然与$origin
和result_1
相同。玩法 2:将
$origin
字符串两边改为双引号:此时输出结果为:
这种情况也容易理解,原始字符串一开始就是双引号,那自然是一直解析到底。
玩法 3:将 eval() 函数的第二层解析改为单引号,并将
$origin
字符串两边改为双引号:此时输出结果为:
输出结果与玩法 2 相同,不过注意一点,就是 eval() 函数第一层转义后,返回值为
$result_2 = 'This is a cup with my coffee in it.'
,即得到的是所有变量都解析后的字符串。玩法 4:将 eval() 函数的第一层解析改为单引号:
此时输出结果为:
输出结果与玩法 1 相同,但输出原理却不同了。第一层解析时,
$origin
是原封不动保留下来的,即$result_2 = "$origin"
,跟result_1
的情况完全相同。玩法 5:将 eval() 函数的第一层和第二层解析都改为单引号:
此时输出结果为:
这次的输出结果有点意思,跟玩法 4 一样,在第一层解析时,
$origin
原封不动保留下来,即$result_2 = '$origin'
,而且在第二层解析时也要原封不动地输出字符串$origin
。还有两种玩法(即分别在玩法 4 和玩法 5 的基础上,将
$origin
字符串两边改为双引号)大同小异,这里就不详述了,有兴趣的读者可以自行推导。