Declaring new elements as references while destructuring within a foreach() head

php.internals

mickmackusa

2 years ago
I want to ask about a quirk that I happened upon. In truth, I expected my code to fail with a fatal error, but it turns out that while using array destructuring in the head of a foreach() loop it is permitted to declare new elements if the value is a reference. Is this a bug or an intended feature? If not a bug should a note/section clarify this behavior? https://www.php.net/manual/en/language.types.array.php My demonstration can be found at this evidently unhelpful question: https://stackoverflow.com/q/77741716/2943403

Kamil Tekiela

2 years ago
I don't know if this was designed this way purposefully, but it makes sense to me. I see nothing out of the ordinary here. It makes for some really nasty code, but that can probably be said about all code that uses references. If we try to simplify your example, we can see more clearly what's happening. First, let's take the loop out of the equation. $arr = []; ['foo' => &$v] = $arr; $v = 1; var_dump($arr); This creates an array with one element whose value is 1 and key is foo. That array restructuring is pretty much just a syntactic sugar for an assignment operation. So we can simplify this example further by replacing the array destructuring with this: $v = & $arr['foo']; Now, this looks like an average assignment of reference in PHP. The rules of assignment-by-ref state:
> If you assign, pass, or return an undefined variable by reference, it will get created.
https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign Since array elements have similar logic to plain variables, this applies to undefined keys, too. The foo key in the example above is created initially with a NULL value. Using the variable alias we created, we can assign a different value to it. Coming back to your example from SO, the array destructuring in foreach is just a convoluted way of assignment-by-ref. After simplifying it, it becomes: foreach ($array as $key => $row) { $x = & $array[$key]['new']; $x = $row['bar']; } Which looks pretty normal to me. I would say it's definitely not a bug. It's just a mix of two PHP features that together make up for some confusing-looking code.