在里面 PHP手册 关于变量,我们可以阅读:
变量名称遵循与PHP中其他标签相同的规则。有效的变量名称以字母或下划线开头,后跟任意数量的字母,数字或下划线。作为正则表达式,它将表示为:'[a-zA-Z_ \ x7f- \ xff] [a-zA-Z0-9_ \ x7f- \ xff] *'
很明显,当我们尝试运行时:
$0-a = 5;
echo $0-a;
我们会得到Parse错误。这很明显。
但是在尝试某些事情时,我发现实际变量在使用这样的语法时可以包含任何字符(或者至少以数字开头并包含连字符):
${'0-a'} = 5;
echo ${'0-a'};
它没有任何问题。
还使用这样的变量变量:
$variable = '0-a';
$$variable = 5;
echo $$variable;
工作没有任何问题。
所以问题是 - 我在手册中引用的句子是不是真的,或者这可能是我所展示的不是真正的变量,或者它可能是在PHP手册中的其他地方记录的?
我已经验证了它 - 它似乎在PHP 5.6和7.1中都有效
问题是 - 使用这种结构是否安全?根据手册,它似乎根本不可能。
你可以选择 任何 变量的名称。 "i"
和 "foo"
是明显的选择,但是 ""
, "\n"
,和 "foo.bar"
是 也 有效。原因? PHP符号表只是一个字典:一个字符串键 零个或多个字节 映射到结构化值(称为 的zval)。有趣的是,有 二 访问此符号表的方法:词法变量和动态变量。
词汇变量就是你在里面读到的 “变量” 文档。词法变量在编译期间定义符号表键(即,当引擎处于lexing并解析代码时)。为了简化这个词法分析器,词法变量以a开头 $
sigil并且必须匹配正则表达式 [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
。以这种方式保持简单意味着解析器不必弄清楚,例如,是否 $foo.bar
是一个变量键入 "foo.bar"
或变量 "foo"
字符串与常量连接 bar
。
现在,动态变量就变得有趣了。动态变量允许您访问那些更不常见的变量名称。 PHP称之为 变量变量。 (我没有找到那个名字,因为它们的对立面在逻辑上是“常量变量”,这令人困惑。但我会在这里称它们为变量变量。)基本用法如下:
$a = 'b';
$b = 'SURPRISE!';
var_dump($$a, ${$a}); // both emit a surprise
变量变量的解析方式与词法变量不同。不是在lexing时定义符号表键,而是在at处计算符号表键 运行。逻辑是这样的:PHP词法分析器看到变量变量语法(或者 $$a
或更一般地说 ${expression}
),PHP解析器延迟表达式的评估直到运行时,然后在运行时引擎使用 表达的结果键入符号表。它比词汇变量更有效,但功能更强大。
代替 ${}
你可以有一个计算结果的表达式 任何字节序列。空字符串,空字节,全部。什么都可以。这很方便,例如,在 here文档。它作为PHP变量访问远程变量也很方便。例如,JSON 允许任何角色 在密钥名称中,您可能希望将它们作为直接变量(而不是数组元素)访问:
$decoded = json_decode('{ "foo.bar" : 1 }');
foreach ($decoded as $key => $value) {
${$key} = $value;
}
var_dump(${'foo.bar'});
以这种方式使用变量类似于使用数组作为“符号表”,如 $array['foo.bar']
,但变量变量方法是完全可以接受的,并且稍微快一点。
附录
通过“稍微快一点”,我们到目前为止在小数点右边说它们实际上是难以区分的。直到10 ^ 8符号访问,在我的测试中差异超过1秒。
Set array key: 0.000000119529
Set var-var: 0.000000101196
Increment array key: 0.000000159856
Increment var-var: 0.000000136778
失去清晰度和惯例可能不值得。
$N = 100000000;
$elapsed = -microtime(true);
$syms = [];
for ($i = 0; $i < $N; $i++) { $syms['foo.bar'] = 1; }
printf("Set array key: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
for ($i = 0; $i < $N; $i++) { ${'foo.bar'} = 1; }
printf("Set var-var: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
$syms['foo.bar'] = 1;
for ($i = 0; $i < $N; $i++) { $syms['foo.bar']++; }
printf("Increment array key: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
${'foo.bar'} = 1;
for ($i = 0; $i < $N; $i++) { ${'foo.bar'}++; }
printf("Increment var-var: %.12f\n", ($elapsed + microtime(true)) / $N);