Q1:
I think the ??
will do nothing when:
$a = [1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
But why?
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
Q2: This is more strange:
foreach ($a = [1, 2] as &$v) {
$v++;
}
var_dump($a);
// output
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
My thinking:
I think the expressions are not referencable, but foreach
catch the error or somehow and then make a copy.
References that work:
$a = 1;
$c = &$a;
Do not work:
$a = 1;
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);
Dos ??
make a copy? I just don't want to wrap the foreach
with a if (isset($a))
if $a
is null and foreach
will fail.
TL;DR For your case, you could consider using the null coalesce operator in this manner:
$a = $a ?? [];
foreach ($a as &$v) { ... }
Or, don't use references at all, by either using array_map()
or by using the keys to make modifications in the underlying array.
$a = [1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
The coalesce operator uses a copy of the original array, and then applies the right hand operand if null
. Therefore, the iteration happens over a copy of the original array.
You could compare this to the following:
$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
The first operand of FE_RESET_RW
is the hash variable that will be iterated over, and you can see that it's ~3
instead of !0
($a
in your code), which is what you expected to happen.
foreach ($a = [1, 2] as &$v) {
$v++;
}
What happens here is that the return value of the assignment $a = [1, 2]
gets used as the array to iterate over.
You can compare this behaviour to something like this:
$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
Again, $2
is the first operand of FE_RESET_RW
, which is the assignment result, and so iteration will not happen against !0
($a
in your code).