繁体   English   中英

PHP - foreach 使用空合并运算符丢失引用

[英]PHP - foreach lose reference with null coalescing operator

Q1:我认为?? 在以下情况下什么都不做:

$a = [1, 2];
foreach ($a ?? [] as &$v) {
    $v++;
}
var_dump($a);

但为什么?

array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}

Q2:这更奇怪:

foreach ($a = [1, 2] as &$v) {
    $v++;
}
var_dump($a);
// output
array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}

我的想法:我认为这些表达式是不可引用的,但是foreach捕获错误或以某种方式然后复制。 工作参考:

$a = 1;
$c = &$a;

不工作:

$a = 1;
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);

?? 复印一份? 如果$a为空并且foreach将失败,我只是不想用if (isset($a))包装foreach

TL;DR对于您的情况,您可以考虑以这种方式使用空合并运算符:

$a = $a ?? [];
foreach ($a as &$v) { ... }

或者,根本不使用引用,通过使用array_map()或使用键在底层数组中进行修改。

第一季度

$a = [1, 2];
foreach ($a ?? [] as &$v) {
    $v++;
}
var_dump($a);

合并运算符使用原始数组的副本,然后在为null应用右手操作数。 因此,迭代发生在原始数组的副本上。

您可以将其与以下内容进行比较:

$a = [1, 2];
$x = $a ?? [];
$x[1] = 4;
var_dump($a); // [1, 2]

代码洞察

compiled vars:  !0 = $a, !1 = $v
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   8     0  E >   ASSIGN                                                   !0, <array>
   9     1        COALESCE                                         ~3      !0
         2        QM_ASSIGN                                        ~3      <array>
         3      > FE_RESET_RW                                      $4      ~3, ->8
... rest of looping code

FE_RESET_RW的第一个操作数是将被迭代的哈希变量,您可以看到它是~3而不是!0 (代码中的$a ),这是您期望发生的。

Q2

foreach ($a = [1, 2] as &$v) {
    $v++;
}

这里发生的是赋值$a = [1, 2]的返回值被用作迭代数组。

您可以将此行为与以下内容进行比较:

$x = $a = [1, 2];
$x[0] = 4; // modify in-place
var_dump($a); // [1, 2]

代码洞察

compiled vars:  !0 = $a, !1 = $v
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E >   ASSIGN                                           $2      !0, <array>
         1      > FE_RESET_RW                                      $3      $2, ->6
... rest of looping code

同样, $2FE_RESET_RW的第一个操作数,它是赋值结果,因此迭代不会发生在!0 (代码中的$a )上。

您可以使用扩展数组语法来获取索引,然后使用它来取消引用原始数组值:

$a = [1, 2];
foreach ($a ?? [] as $i => $v) {
    ++$a[$i];
}
var_dump($a);

但请注意,无论如何这可能是无用的,因为如果$a未设置(以便??合格),则循环将进行零次迭代,并且$a仍然不会为var_dump() (除非那是你需要的,我想......)

如果$a为空并且foreach将失败,我只是不想用if (isset($a))包装foreach

如果您没有将变量初始化为正确的类型,这是不可避免的,但这里有一些使用按引用传递、按引用返回和默认值的实用函数中的技巧:

function &test(&$array=[]) {
    return $array;
}

$a = [1, 2];

foreach (test($a) as &$v) {
    $v++;
}

如果$a未设置且不循环,则不会产生错误,但是在上面它会产生:

array(2) {
  [0]=>
  int(2)
  [1]=>
  &int(3)
}

在 PHP 中数组是按值赋值的(赋值副本),所以如果$a !== null那么$a ?? [] $a ?? []返回$a[1, 2] 所以$a不会被使用&$v引用这个值的值所修改。

对象是通过引用分配的,因此在这种情况下会返回一个引用并修改原始对象,除非未设置$a 那么你显然会得到:

注意:未定义变量:a

$a = (object)[1, 2];

foreach ($a ?? [] as &$v) {
    $v++;
}
var_dump($a);

这产生:

object(stdClass)#1 (2) {
  ["0"]=>
  int(2)
  ["1"]=>
  &int(3)
}

参考分配

也支持通过引用赋值,使用"$var = &$othervar; ”语法。 通过引用赋值意味着两个变量最终都指向相同的数据,并且不会在任何地方复制任何内容。

在这些情况下,它们都是表达式,不能被引用:

$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);

如果这只是一个练习,那很好,但如果你试图解决一个特定的问题,那么你需要概述这个更广泛的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM