Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
557 views
in Technique[技术] by (71.8m points)

php引用传值问题

image.png

$arr = [];
$str = "傻逼";
$treeArr = &$arr;
for($i=0; $i < 2; $i++) {
    $word = mb_substr($str,$i,1);
    $treeArr = &$treeArr[$word] ?? $treeArr = false;
}
print_r($arr);

请大佬解答一下为什么会得到如下结果?没看太明白,感谢
image.png


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

如果你使用一些 IDE ,就会提示你 Only variables can be passed by reference,译为:只能通过引用传递变量

图片.png

foreach (range('A', 'B') as $i) {
    $b = &$b[$i] ? $b[$i] : $b = false;
}

使用快速修复,IDE 会将你的代码修复为

foreach (range('A', 'B') as $i) {
    $f = $b[$i] ? $b[$i] : $b = false;
    $b = &$f;
}

再参考文档中的内容。

以下内容可以通过引用传递:

  • 变量,例如 foo($a)
  • New 语句,例如 foo(new foobar())
  • 从函数中返回的引用.

任何其它表达式都不能通过引用传递,结果未定义。

图片.png

简单来说,这就是一种错误使用,类似的还有:


sort(explode('','Hello'));

array_push(['A'],['B']);

这些都会引起这个错误。

如果你以为我只是说到这里,那不就偏题了。

因为引用不能对表达式起作用,所以现在的代码实际上就等同于下面的代码。(错误的例子,下面有详细,正确的

foreach (range('A', 'B') as $i) {
    $b = &$b[$i];
    $b = false;
}

补充内容
补充:这里的代码转换有些唐突,可能有些难以理解,下面使用 php-parser 解析一下代码

use PhpParserError;
use PhpParserNodeDumper;
use PhpParserParserFactory;
// 这里包含的是代码
$code = file_get_contents('ref.php');
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}
";
    return;
}
$dumper = new NodeDumper;
echo $dumper->dump($ast) . "
";

使用 php-parser 打印语法树。

图片.png

...
 stmts: array(
    0: Stmt_Expression(
        expr: Expr_Ternary(
            cond: Expr_AssignRef(
                var: Expr_Variable(
                    name: b
                )
                expr: Expr_ArrayDimFetch(
                    var: Expr_Variable(
                        name: b
                    )
                    dim: Expr_Variable(
                        name: i
                    )
                )
            )
            if: Expr_ArrayDimFetch(
                var: Expr_Variable(
                    name: b
                )
                dim: Expr_Variable(
                    name: i
                )
            )
            else: Expr_Assign(
                var: Expr_Variable(
                    name: b
                )
                expr: Expr_ConstFetch(
                    name: Name(
                        parts: array(
                            0: false
                        )
                    )
                )
            )
        )
    )
)
...

可以看到,虽然三元运算符的优先级高于 = 符号,但是因为引用不能引用表达式,所以在语法阶段就被拆分开来了,也就是说,实际运行时是下面这样的。

foreach (range('A', 'B') as $i) {
    ($b = &$b[$i]) ? $b[$i] : $b = false;
}

补充内容结束

查询手册可知,引用符号的优先级高于三元运算符。
图片.png

所以 $b = &$b[$i],就会被赋值 $b

这时候 $b[$i] 肯定是不存在的,但是又根据手册可知,当引用一个不存在的变量时,php 将会自动初始化这个变量。

图片.png
现在运行代码,第一次循环时,运行如下。
= 的运算优先级低于 & 所以这时候 $b就被重新引用给了 $a['A'] 就变成了初始化后的 null

因为 $a['A'] 是不存在的,刚被初始化,所以就是 null ,这时候就要执行三元运算符中 false 的部分 ,即 $b 又被赋给了 false ,所以这时候,$a['A'] = null 也就等于了 false,现在 $b 引用着的就是 $a['A']

图片.png

现在运行第二遍,现在 $b 已经被引用给了 $a['A'] ,所以现在循环出来的 $i = B 的,就成了初始化 $a['A']['B'] 了,因为一开始初始化是 null ,看断点中的 $b 值是 null ,但是紧接着又赋值了 false

图片.png

? 好了,就是这样。


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

62 comments

56.6k users

...