01 · THE IDEAREAD → TRY → LEARN
Arrays inside arrays
A PHP array is an ordered map, and its values can be any type, including other arrays. That is all a multidimensional array is: a value that happens to be an array, holding another value that is an array, and so on. Records from JSON, configuration trees and table rows all take this shape.
You reach a nested value by chaining square brackets, one key per level: $user['address']['city']. Each bracket works on the result of the one before it.
Reading safely
Chained access is fine when the structure is guaranteed. When a key is missing, PHP issues a warning and the result is null. Reading through a missing level is worse, because the next bracket then runs on null.
The null coalescing operator ?? handles this. In $a['x']['y'] ?? 'fallback', PHP checks each step quietly and uses the fallback if any level is missing or null. No warning is raised.
$user = [
'name' => 'Ada',
'address' => ['city' => 'London', 'geo' => ['lat' => 51.5]],
];
echo $user['address']['city'], PHP_EOL;
echo json_encode($user['address']['geo']['lat'] ?? null), PHP_EOL;
echo json_encode($user['address']['geo']['lng'] ?? null), PHP_EOL;
echo json_encode($user['phone']['home'] ?? 'none'), PHP_EOL;London 51.5 null "none"
The phone key does not exist at all, yet the last line is safe and prints the fallback. One caveat: ?? also replaces a key that exists but holds null. Use array_key_exists() if you must tell those cases apart.
Updating nested values
Assignment uses the same chain of brackets. You can change a value, use compound operators such as +=, or add new keys. If an intermediate level does not exist, PHP creates it as an array when you assign through it. unset() removes a key at any depth and does not reindex the array.
$inventory = [
'north' => ['apples' => 4, 'pears' => 2],
'south' => ['apples' => 1],
];
$inventory['north']['apples'] += 3;
$inventory['south']['pears'] = 5;
$inventory['east']['plums'] = 9;
unset($inventory['north']['pears']);
echo json_encode($inventory), PHP_EOL;{"north":{"apples":7},"south":{"apples":1,"pears":5},"east":{"plums":9}}
Notice that east did not exist before the assignment. Creating levels this way is convenient, but a typo in a key will silently create a new branch instead of failing. Be careful with the spelling of keys.
Looping through nested data
Use foreach for each level. Array destructuring lets you pull the fields you need directly into the loop header, which is easier to read than repeated bracket chains.
$rows = [
['id' => 1, 'tags' => ['php', 'array']],
['id' => 2, 'tags' => []],
['id' => 3, 'tags' => ['json']],
];
$counts = [];
foreach ($rows as ['id' => $id, 'tags' => $tags]) {
$counts[$id] = count($tags);
}
echo json_encode($counts), PHP_EOL;{"1":2,"2":0,"3":1}
The result keys start at 1, not 0, so JSON encodes the array as an object. Destructuring a missing key gives null and a warning, so for uncertain data prefer ?? inside the loop body.
If you want to change elements while looping, write back through the key, as in $rows[$i]['seen'] = true inside foreach ($rows as $i => $row). The loop variable holds a copy, so changing $row alone does not alter the original array.
Common mistakes
- Chaining brackets on data that may be incomplete, instead of using
??. - Using
??when a falsy value such as0is valid.??only replacesnull, so this is actually safe, but?:would wrongly replace0. - Editing the loop copy instead of the original array.
- Forgetting that
unset()leaves gaps in integer keys.
Exercise contract
Write cityOrderTotal(array $orders, string $city): int. Each order may contain customer.address.city and an items list. Each item may contain price and qty.
- Only count orders whose city equals
$cityexactly (case-sensitive). - An order with a missing customer, address or city is skipped.
- A missing
itemslist adds nothing. - A missing
pricecounts as 0 and a missingqtycounts as 1. - Return the sum of
price * qtyover all matching items.
Recap
Nested arrays are chains of keys. Read them with ?? when the shape is uncertain, write through the full key path, and use foreach with destructuring to walk each level.
Functions · Arrays · Types
- Read and update nested in-memory records safely
- Implement and test a deterministic PHP 8.4 function