双美元符+{}:${${variable}}是一种比较常见的用法,但是它的实现原理是什么呢?今天来探究一下:

提及这种用法,还得先说一下PHP的String类型
php.net上指出,一个字符串可以用4种方式表达:单引号,双引号,heredoc语法结构,nowdoc语法结构
这里heredoc语法结构和双引号形式类似,同样nowdoc结构对应单引号

单引号:单引号包围起来的字符串中,单引号必须转义才能使用(\'),否则会报语法错误;而用于转义的反斜线自身,则要用两个反斜线(\\),即也需要转义. 文档说"其他任何形式的反斜线都被当成反斜线本身:也就是说如果想使用其它转义序列例如 \r 或者 \n,并不代表任何特殊含义,就单纯是这两个字符本身",这就是说单引号包围起来的其他转义序列根本没有实现转义.

反过来说,\是可以直接作为反斜线使用的,因此单引号包围的字符串中\\和\其实都能表示反斜线本身,验证实例如下:

<?php
echo 'You deleted C:\\*.*?';
echo 'You deleted C:\*.*?';
echo 'This will not expand: \n a newline';
echo 'Variables do not $expand $either';
?>

双引号:当字符串包围在双引号中,PHP会对一些特殊字符解析,这里\n,\r,\t都正常解析为转义符,而且反斜线、美元符、双引号都需要转义:\\, \$, \"

heredoc和nowdoc不如引号用起来方便,但是可以实现相同的目的

再说变量解析,文档的说法是当字符串遇到双引号或heredoc结构定义时,其中的变量将会被解析;变量解析有两种语法规则,简单的和复杂的,其中复杂规则的显著标记是用花括号包围的表达式;

对于简单规则而言,当 PHP 解析器遇到一个美元符号($)时,它会和其它很多解析器一样,去组合尽量多的标识以形成一个合法的变量名。可以用花括号来明确变量名的界线。

<?php
$juice = "apple";
echo "He drank some juice made of ${juice}s.";
?>

复杂语法:复杂语法不是因为其语法复杂而得名,而是因为它可以使用复杂的表达式。

任何具有 string 表达的标量变量,数组单元或对象属性都可使用此语法。只需简单地像在 string 以外的地方那样写出表达式,然后用花括号 { 和 } 把它括起来即可。由于 { 无法被转义,只有 $ 紧挨着 { 时才会被识别。可以用 {\$ 来表达 {$。

<?php
$great = 'fantastic'; // 无效,输出: This is { fantastic}
echo "This is { $great}"; // 有效,输出: This is fantastic
echo "This is {$great}";
echo "This is ${great}";
echo "This is the value of the var named $name: {${$name}}";
?>

测试得到以上两条echo变量great的语句输出同样的结果,所以{${$name}}其实和${${name}}也是一样的

另外,php.net的文档中给出了Note:

"函数、方法、静态类变量和类常量只有在 PHP 5 以后才可在 {$} 中使用。然而,只有在该字符串被定义的命名空间中才可以将其值作为变量名来访问。只单一使用花括号 ({}) 无法处理从函数或方法的返回值或者类常量以及类静态变量的值。"

这里说的很清楚,只有单一{}无法处理返回值等,因为像${name}只是得到变量的名字,告诉我们这是变量name,即得到一个$name,假如我需要$name的值,那我就得对他再解析一次
所以像:

<!--
#GOAL: gather some phpinfo(); $str=@(string)$_GET['str'];
eval('$str="'.addslashes($str).'";');
-->

实现的方法是传入参数:str=${${phpinfo()}}
这里如果直接上phpinfo(),由于有addslashes的作用会出现\"的情况使得命令无法执行
${phpinfo()}告诉我们最里面这个是变量,名为phpinfo(),接下来的一层花括号将其解析为字符串"phpinfo()"
另外,{}有时候也可以当[]使用,文档中有说明:"Note: string 也可用花括号访问,比如 $str{42}",这里$str{42}==$str[42]

在《白帽子讲web安全》一书中也提到了同样的应用:
”PHP的curly systax能导致代码执行,它将执行花括号间的代码,并将结果替换回去,如下例:
<?php $var = "I was innocent until ${`ls`}" appeared here; ?>"
ls得到执行,列出本地目录并将结果返回

随机推荐

  1. JavaScript垃圾回收(三)——内存泄露

    一.JavaScript内存监测工具 在讨论内存泄露之前,先介绍几款JavaScript内存监测工具. IE的sIEve与JSLeaksDetector(这两个可以在下面的附件中下载),firefox ...

  2. java线程(2)--同步和锁

    参考转载:http://rainyear.iteye.com/blog/1734311 http://turandot.iteye.com/blog/1704027 http://www.cnblog ...

  3. c# 多线程系列二 自定义线程执行器

    看了第一篇文章,多线程系列,看到了在线程执行任务队列有了一定的了解~! 那么今天我来讲讲,怎么样构建通用的自定义线程概念! 线程执行任务,肯定要有目标,但是如果写死了,那么一个线程处理执行职能按照思路 ...

  4. shell脚本二

    在shell脚本一 中,我讨论了shell脚本的语法规范,shell脚本的变量,以及shell脚本的测试语句. 仅仅懂得这些只能写简单的脚本,在简单的脚本中各条语句按顺序执行,从而实现自动化的管理,顺 ...

  5. No assembly found containing an OwinStartupAttribute

    自从在ASP.NET MVC中使用NuGet添加SignalR类库之后,再次运行程序时,它出现了一个异常: Server Error in '/' Application. The following ...

  6. Model元数据提供机制小结

    在最开始先我得说说我看这部分的情况,最开始被各种ModelMetadata和各种ModelMetadataProvider给搞晕了,就几页书花了我好大的精力去看,直到后来看了一幅类图,细细看各个类之间 ...

  7. Redis使用总结

    1.Redis安装 redis的安装非常的简单,而且Redis并不依赖其他环境和标准库,很容易上手,这可能也是它流行的一个原因.这里为了测试方便,用的都是windows 环境下测试.下载Windows ...

  8. 如何显示二进制流的图片(利用img控件)

    之前在http://www.cnblogs.com/JsonZhangAA/p/5568575.html博文中是利用的image控件来显示的二进制流图片,我现在想的是能 通过普通的<img id ...

  9. 使用MiniProfiler跟踪MVC + EF + Bootstrap 2 权限管理系统的性能消耗

    安装MiniProfiler 在MVC + EF + Bootstrap 2 权限管理系统入门级(附源码)文章中下载了它的源码,调试模式下打开一个页面都要再2.5秒以上,所以使用MiniProfile ...

  10. iTextSharp快速使用指南

    前言 最近公司一直使用iText开发PDF报表,使用一段时间之后发现iText这个类库虽然是庞大无比,但作为程序猿我们不需要知道所有的类和方法,我们只需要知道如何使用即可. 所以这篇文章就是告诉大家如 ...