在PHP manual on变量中,我们可以阅读:
变量名遵循与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中都有效
另外一个问题是 - 使用这种结构是否安全?根据手册,它似乎根本不可能。
答案 0 :(得分:16)
您可以为变量选择任何名称。 "i"
和"foo"
是明显的选择,但""
,"\n"
和"foo.bar"
也有效。原因? PHP符号表只是一个字典:零或更多字节的字符串键映射到结构化值(称为zval)。有趣的是,有两种方式来访问这个符号表:词法变量和动态变量。
词汇变量是您在"variables"文档中阅读的内容。词法变量在编译期间定义符号表键(即,当引擎处于lexing并解析代码时)。为了简化这个词法分析器,词法变量以$
sigil开头,并且必须与正则表达式[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
匹配。以这种方式保持简单意味着解析器不必弄清楚,例如,$foo.bar
是由"foo.bar"
键入的变量还是与常量连接的变量"foo"
字符串bar
。
现在,动态变量就变得有趣了。动态变量允许您访问那些更不常见的变量名称。 PHP调用这些variable variables。 (我不喜欢那个名字,因为他们的对立面是逻辑上的#34;常数变量",这很令人困惑。但我在这里称它们为变量变量。)基本用法就像:
$a = 'b';
$b = 'SURPRISE!';
var_dump($$a, ${$a}); // both emit a surprise
变量变量的解析方式与词法变量不同。不是在lexing时定义符号表键,而是在运行时评估符号表键。逻辑如下:PHP词法分析器看到变量变量语法($$a
或更一般${expression}
),PHP解析器推迟表达式的评估,直到运行时,然后是运行时引擎使用表达式的结果键入符号表。它比词汇变量更多的工作,但更强大。
在${}
内,你可以有一个表达式,其值为任何字节序列。空字符串,空字节,全部。什么都可以。这很方便,例如heredocs。它也可以方便地将远程变量作为PHP变量访问。例如,密钥名称中的JSON allows any character,您可能希望将它们作为直接变量(而不是数组元素)访问:
$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);
答案 1 :(得分:1)
我在php.net(西班牙语版)上看到过$función,$año*之类的东西,所以一直想尝试,但从未尝试过。但是,由于某种原因,我写了一些变量,例如$ 1a($ 1st),$ 2a,$ 3a ...并且它起作用了,不需要'$ {1a}'或其他东西,但是,Phpstorm像这样警告我 “预期:分号”和 “表达式不可分配:常量引用”。 因此,在阅读了您在本文中的经验之后,为了使编辑器免于警告并避免将来可能出现的问题,我将所有内容都更改为$ a,$ b,$ c等。 注意:西班牙语中的$ ano(年)是肛门,因此没人喜欢使用它。