PHP - 有效的变量名称

问题描述 投票:13回答:2

在关于变量的PHP manual中,我们可以读到:

变量名称遵循与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中都有效

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

php
2个回答
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。 (我不喜欢那个名字,因为它们的对立面在逻辑上是“常量变量”,这很令人困惑。但我会在这里称它们为变量变量。)基本用法如下:

$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
投票

我在php.net(西班牙语版)上看过像$función,$año*这样的东西,总是想尝试,但从来没有。然而,出于某种原因,我写了一些vars,如$ 1a($ 1),$ 2a,$ 3a ...而且它工作,没有'$ {1a}'或者需要的东西,但是,Phpstorm正在唠叨我的警告,如“预期:分号“和”表达式不可分配:常量引用“。因此,在阅读了本文中的经验,并清除编辑器中的警告并避免可能的未来问题后,我只需将所有内容更改为$ a,$ b,$ c等。注意:$ ano(年)西班牙语是肛门,所以没人喜欢用它

© www.soinside.com 2019 - 2024. All rights reserved.