PHP - 有效的变量名称

Mar*_*łek 14 php

在关于变量的PHP手册中,我们可以阅读:

变量名称遵循与PHP中其他标签相同的规则.有效的变量名称以字母或下划线开头,后跟任意数量的字母,数字或下划线.作为正则表达式,它将表示为:'[a-zA-Z_\x7f-\xff] [a-zA-Z0-9_\x7f-\xff]*'

很明显,当我们尝试运行时:

$0-a = 5;
echo $0-a;
Run Code Online (Sandbox Code Playgroud)

我们会得到Parse错误.这很明显.

但是在尝试某些事情时,我发现实际变量在使用这样的语法时可以包含任何字符(或者至少以数字开头并包含连字符):

${'0-a'} = 5;
echo ${'0-a'};
Run Code Online (Sandbox Code Playgroud)

它没有任何问题.

还使用这样的变量变量:

$variable = '0-a';
$$variable = 5;
echo $$variable;
Run Code Online (Sandbox Code Playgroud)

工作没有任何问题.

所以问题是 - 我在手册中引用的句子是不是真的,或者这可能是我所展示的不是真正的变量,或者它可能是在PHP手册中的其他地方记录的?

我已经验证了它 - 它似乎在PHP 5.6和7.1中都有效

问题是 - 使用这种结构是否安全?根据手册,它似乎根本不可能.

bis*_*hop 16

您可以从字面上为变量选择任何名称."i"并且"foo"是显而易见的选择,但是"","\n""foo.bar"同样有效的.原因?PHP符号表只是一个字典:零个或多个字节的字符串键映射到结构化值(称为zval).有趣的是,有两种方法可以访问这个符号表:词法变量和动态变量.

词汇变量是您在"变量"文档中阅读的内容.词法变量在编译期间定义符号表键(即,当引擎处于lexing并解析代码时).为了简化这个词法分析器,词法变量以一个$符号开头并且必须与正则表达式匹配[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
Run Code Online (Sandbox Code Playgroud)

变量变量的解析方式与词法变量的解析方式不同.不是在lexing时定义符号表键,而是在运行时评估符号表键.逻辑如下:PHP词法分析器看到变量变量语法($$a或者更一般地说${expression}),PHP解析器推迟表达式的评估直到运行时,然后在运行时引擎使用表达式结果来键入进入符号表.它比词汇变量更有效,但功能更强大.

${}你的内部可以有一个表达式,可以计算任何字节序列.空字符串,空字节,全部.什么都可以.例如,在heredocs中,这很方便.它作为PHP变量访问远程变量也很方便.例如,JSON 允许键名中的任何字符,您可能希望将它们作为直接变量(而不是数组元素)访问:

$decoded = json_decode('{ "foo.bar" : 1 }');
foreach ($decoded as $key => $value) {
    ${$key} = $value;
}
var_dump(${'foo.bar'});
Run Code Online (Sandbox Code Playgroud)

以这种方式使用变量变量类似于使用数组作为"符号表" $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
Run Code Online (Sandbox Code Playgroud)

失去清晰度和惯例可能不值得.

$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);
Run Code Online (Sandbox Code Playgroud)