Multidimensional Arrays

An array element can itself be an array. That's how PHP represents tables of rows, grids, nested configuration and decoded JSON. This lesson shows how to build, read, loop over and transform nested arrays — skills you'll use every time you work with database results or API data.

A list of records

The most common shape in PHP applications is a list of associative arrays — exactly what a database query returns:

PHP
<?php

$employees = [
    ["name" => "Iris", "dept" => "Sales", "salary" => 52000],
    ["name" => "Omar", "dept" => "IT", "salary" => 61000],
    ["name" => "Lena", "dept" => "Sales", "salary" => 48000],
];

echo $employees[1]["name"], "\n";
echo count($employees), " employees\n";

foreach ($employees as $e) {
    printf("%-5s %-6s %8s\n", $e["name"], $e["dept"], number_format($e["salary"]));
}
Output
Omar
3 employees
Iris  Sales    52,000
Omar  IT       61,000
Lena  Sales    48,000

Chain the brackets to go deeper: $employees[1]["name"] is "element 1, then its name key".

Grids

A grid is a list of rows, each a list of cells:

PHP
<?php

$board = [
    ['X', 'O', 'X'],
    ['O', 'X', 'O'],
    [' ', ' ', 'X'],
];

foreach ($board as $r => $row) {
    echo ' ', implode(' | ', $row), "\n";
    if ($r < 2) {
        echo "---+---+---\n";
    }
}

$diagonal = [$board[0][0], $board[1][1], $board[2][2]];
echo count(array_unique($diagonal)) === 1 ? "Winner: {$diagonal[0]}\n" : "No diagonal win\n";
Output
 X | O | X
---+---+---
 O | X | O
---+---+---
   |   | X
Winner: X

Modifying nested elements

Assign through the full path. Missing levels are created automatically:

PHP
<?php

$config = ["db" => ["host" => "localhost", "port" => 3306]];

$config["db"]["port"] = 3307;
$config["cache"]["driver"] = "redis";        // creates "cache"
$config["cache"]["servers"][] = "10.0.0.5";  // creates "servers" and appends

print_r($config);
Output
Array
(
    [db] => Array
        (
            [host] => localhost
            [port] => 3307
        )

    [cache] => Array
        (
            [driver] => redis
            [servers] => Array
                (
                    [0] => 10.0.0.5
                )

        )

)

To change rows while looping, use the key ($rows[$i]['x'] = …), or array_map to produce new rows.

Reading safely

With nested data from outside (JSON, forms), any level might be missing. ?? works through the whole chain without warnings:

PHP
<?php

$response = ["user" => ["profile" => ["city" => "Porto"]]];

echo $response["user"]["profile"]["city"] ?? "unknown", "\n";
echo $response["user"]["address"]["street"] ?? "unknown", "\n";
echo $response["order"][0]["id"] ?? "no orders", "\n";
Output
Porto
unknown
no orders

Summarising a table

Group and total rows with a loop, or combine array_column, array_filter and friends:

PHP
<?php

$sales = [
    ["region" => "North", "product" => "Widget", "units" => 12, "price" => 2.5],
    ["region" => "South", "product" => "Gadget", "units" => 4, "price" => 10.0],
    ["region" => "North", "product" => "Gadget", "units" => 7, "price" => 10.0],
    ["region" => "East", "product" => "Widget", "units" => 20, "price" => 2.5],
];

$byRegion = [];
foreach ($sales as $row) {
    $byRegion[$row["region"]] = ($byRegion[$row["region"]] ?? 0) + $row["units"] * $row["price"];
}
ksort($byRegion);
foreach ($byRegion as $region => $revenue) {
    printf("%-6s %7.2f\n", $region, $revenue);
}

$widgetUnits = array_sum(array_column(array_filter($sales, fn($r) => $r["product"] === "Widget"), "units"));
echo "Widgets sold: $widgetUnits\n";
Output
East     50.00
North   100.00
South    40.00
Widgets sold: 32

Rows to columns and back

Passing null as the callback to array_map with several arrays "zips" them — which also transposes a matrix:

PHP
<?php

$matrix = [
    [1, 2, 3],
    [4, 5, 6],
];

$transposed = array_map(null, ...$matrix);
foreach ($transposed as $row) {
    echo implode(" ", $row), "\n";
}
Output
1 4
2 5
3 6

Walking nested data recursively

For structures of unknown depth, array_walk_recursive visits every leaf value. A recursive function (lesson 32) gives you full control:

PHP
<?php

$menu = [
    "Home" => [],
    "Products" => ["Laptops" => [], "Phones" => ["Android" => [], "iPhone" => []]],
    "About" => [],
];

function printMenu(array $items, int $depth = 0): void
{
    foreach ($items as $label => $children) {
        echo str_repeat("  ", $depth), "- $label\n";
        printMenu($children, $depth + 1);
    }
}

printMenu($menu);

$prices = ["a" => 1.5, "b" => ["c" => 2.0, "d" => ["e" => 3.5]]];
$total = 0;
array_walk_recursive($prices, function ($value) use (&$total) {
    $total += $value;
});
echo "Total: $total\n";
Output
- Home
- Products
  - Laptops
  - Phones
    - Android
    - iPhone
- About
Total: 7

Displaying nested data

print_r and var_dump show nested arrays, but for large structures json_encode with pretty printing is often easier to read:

PHP
<?php

$order = ["id" => 1001, "items" => [["sku" => "A1", "qty" => 2], ["sku" => "B7", "qty" => 1]]];
echo json_encode($order, JSON_PRETTY_PRINT), "\n";
Output
{
    "id": 1001,
    "items": [
        {
            "sku": "A1",
            "qty": 2
        },
        {
            "sku": "B7",
            "qty": 1
        }
    ]
}

Try it yourself

Print a class timetable from a nested array: days as keys, each with a list of lessons.

Try it yourself · main.php
Edit the code, copy it into main.php, then run it with php main.php (PHP 8.5). How to install PHP
Output
Mon  Maths, Physics, PE
Tue  English, PHP
Wed  PHP, Maths, Art, History
Most frequent: Maths (2 times)