skip to content

In PHP 8.5, what does a destructuring assignment like [$lat, $lng] = $point do when $point lacks those keys or is not an array?

level: middleimportance: should knowfreq 30%

answer

  1. the code keeps running with null
  2. Undefined array key is an E_WARNING
  3. null on the right: silent nulls
  4. int, string, bool warn since 8.5
  5. plain object throws Error

basics

~20 s

Missing keys raise an E_WARNING and assign null. A null right-hand side assigns null silently; since PHP 8.5 an int, float, string or bool does the same but warns. A plain object throws an Error unless it implements ArrayAccess.

solid answer

~40 s

Destructuring never checks the whole shape; it looks up each key separately. A key that is missing raises `E_WARNING` "Undefined array key" and the variable gets `null`, so an associative `['lat' => …]` array under a positional pattern produces two warnings and two nulls. If the right-hand side is `null`, every variable becomes `null` with no diagnostic at all. PHP 8.5 added a warning for other scalars: destructuring an `int`, `float`, `string` or `bool` now emits "Cannot use int as array" (and so on) per variable, where earlier versions were silent. An object works only if it implements `ArrayAccess`; otherwise PHP throws an `Error`. Because most of these cases continue with `null`, validate a payload's shape before unpacking it.

code

php · 17 lines
php
<?php
declare(strict_types=1);

[$lat, $lng] = explode(',', '46.5580');
// Warning: Undefined array key 1  -> $lng is null

[$lat, $lng] = ['lat' => 46.558, 'lng' => 7.981];
// Warning: Undefined array key 0, then key 1 -> both null

[$lat, $lng] = null;   // both null, no diagnostic
[$lat, $lng] = false;  // PHP 8.5: Warning: Cannot use bool as array (twice)

try {
    [$lat, $lng] = new stdClass();
} catch (Error $e) {
    echo $e->getMessage(), PHP_EOL; // Cannot use object of type stdClass as array
}

go deeper

for a junior

Recall that a missing key gives null and a warning, and that a positional pattern needs keys 0 and 1 to exist.

for a middle

Walk through each right-hand side: short list, associative array, null, other scalars, plain object, ArrayAccess, and say which warn, which stay silent and which throw.

for a senior

Validate payload shape before unpacking, turn warnings into test failures, and flag the null and false cases in review, knowing the false case only warns from PHP 8.5.

for a principal

Set a boundary policy: external data is validated and mapped into typed objects once, so destructuring's silent-null behaviour never reaches business logic.

## Destructuring is a series of lookups A pattern like `[$lat, $lng] = $point;` is compiled into one read per variable: read key `0` into `$lat`, read key `1` into `$lng`. There is no step that checks the array has exactly the expected shape first. Each read succeeds or fails on its own, and most failures produce `null` plus, at most, a warning. Knowing which case gives which result is what separates a quiet bug from a loud one. ## Case by case | Right-hand side | Result in PHP 8.5 | Diagnostic | |---|---|---| | `[46.55, 7.98]` | both assigned | none | | `[46.55]` (too short) | `$lng = null` | `E_WARNING` Undefined array key 1 | | `['lat' => 46.55, 'lng' => 7.98]` | both `null` | two Undefined array key warnings | | `null` | both `null` | none | | `5`, `'46.55,7.98'`, `false` | both `null` | `E_WARNING` "Cannot use int as array" (or string, bool), once per variable | | a plain object (`stdClass`) | nothing assigned | `Error`: Cannot use object of type stdClass as array | | an `ArrayAccess` object | `offsetGet(0)`, `offsetGet(1)` | whatever the object does | Two lines in that table changed in recent releases. Missing keys were `E_NOTICE` before PHP 8.0 and are `E_WARNING` since. Scalars other than `null` were destructured silently into `null` before PHP 8.5; the 8.5 release added the warning. ## Where these cases come from in real code A hiking-route API gives each point as `"46.5580,7.9812"` in one field and as `{"lat": …, "lng": …}` in another. Typical failure paths: 1. **A short list.** `[$lat, $lng] = explode(',', $field);` on a field that contains only `"46.5580"` gives `$lng = null` and one warning. 2. **Wrong pattern for the shape.** A decoded JSON object becomes an associative array, so a positional pattern reads nothing. 3. **A failed call returning `false`.** Functions typed `array|false`, such as `getimagesize()`, return `false` on failure. In PHP 8.5 destructuring that result warns "Cannot use bool as array"; in older versions it was silent. 4. **A `null` result.** A decoder that returns `null` for invalid input leads to silent nulls, even in PHP 8.5, because `null` is deliberately exempt from the new warning. ## Things the compiler rejects outright Some shape assumptions cannot be written at all: - `[$head, ...$tail] = $items;` is a compile-time fatal error, "Spread operator is not supported in assignments". There is no rest element; use `array_slice()` for the tail. - Patterns have no default-value syntax. Defaults come from the source array (merge them in before unpacking) or from `??` applied afterwards. ## Making mismatches loud Because the common outcome is `null` with at most a warning, defensive code checks shape at the boundary: - **Check before unpacking.** `is_array($point)` and `count($point) === 2`, or `array_key_exists('lat', $point)`, before the destructuring line. - **Prefer keyed patterns for payloads.** `['lat' => $lat, 'lng' => $lng]` documents the contract and fails per missing field, not by silently reading the wrong position. - **Convert warnings into exceptions** in development and tests, so an `Undefined array key` fails the test instead of scrolling past in a log. - **Map to a typed object once.** A small class with `float $lat` and `float $lng` properties rejects `null` through its type declarations, turning the silent case into a `TypeError` at the edge. ## Reading the diagnostics The messages point straight at the case you hit: - **"Undefined array key 1"** — the array exists but lacks that key: a short list or a positional pattern on associative data. - **"Cannot use bool as array"** (or `int`, `float`, `string`) — the value was not an array at all; since PHP 8.5 this is reported once per variable in the pattern. - **"Cannot use object of type X as array"** — an `Error`, so execution stops unless caught. - **Nothing at all** — the value was `null`, or everything matched. Only a check you write yourself tells these two apart. ## Summary A destructuring assignment trusts the data. In PHP 8.5 it tells you about most mismatches with a warning, stays silent for `null`, and throws only for objects that cannot act as arrays. Treat each of these as a validation gap, not as a language guarantee.

  • Why is null exempt from the PHP 8.5 warning while false is not?
    The PHP 8.5 change covers non-array values other than `null`, so destructuring `null` stays silent exactly as before. Other scalars, including the `false` that many functions return on failure, usually mean the code expected an array and got something else, so they now warn once per variable.
  • How do you give a destructured variable a default when the key is missing?
    Patterns have no default syntax. Apply `??` after unpacking (`$lng ??= 0.0;`, which still leaves the warning) or, better, merge defaults into the source first, for example `['lat' => $lat, 'lng' => $lng] = $point + ['lat' => 0.0, 'lng' => 0.0];`, so every key exists.

saying these in an interview costs you the question

  • Destructuring throws an exception when a key is missing
  • Destructuring null raises a warning in PHP 8.5
  • A positional pattern reads associative arrays in insertion order
  • Destructuring a string splits it into characters
  • [$head, ...$tail] = $items is valid PHP 8 syntax