多次元配列とは、要素が配列である配列です。階層ごとに角括弧を1組ずつ付けて読みます。$grid[1][2]は1行目の2列目、$users[0]['name']は最初のレコードの名前です。
<?php
$grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
echo $grid[1][2], "\n"; // row 1, column 2
$users = [
['name' => 'Ada', 'role' => 'admin'],
['name' => 'Linus', 'role' => 'editor'],
];
echo $users[0]['name'], "\n";
echo $users[1]['role'], "\n";
$grid[2][0] = 70; // change a nested value
echo implode(' ', $grid[2]), "\n";
2つ目の形、つまり名前付きのフィールドを持つレコードのリストが、いちばんよく出会う形です。データベースのクエリ、デコードしたJSONのレスポンス、解析したCSVファイルは、たいていこの形で返ってきます。各レコードは連想配列です。
ネストした配列を作る
ネストは必要なだけ深くでき、階層ごとに数値のキーと文字列のキーを混ぜられます。設定の配列はよくある例で、名前付きのセクションがそれぞれ設定やリストを持ちます。
<?php
$config = [
'db' => [
'host' => 'localhost',
'port' => 3306,
],
'admins' => ['ada', 'grace'],
'features' => [
'search' => ['enabled' => true, 'limit' => 20],
],
];
echo $config['db']['host'], "\n";
echo $config['admins'][1], "\n";
echo $config['features']['search']['limit'], "\n";
行とネストした値を追加する
$rows[] = [...]は行全体を追加します。$arr['key'][] = $valueはネストしたリストの中に追加し、PHPは最初のときに内側の配列を作るので、値のグループ分けに準備は要りません。
<?php
$orders = [
['customer' => 'Ada', 'item' => 'book'],
['customer' => 'Linus', 'item' => 'pen'],
['customer' => 'Ada', 'item' => 'lamp'],
];
$orders[] = ['customer' => 'Grace', 'item' => 'mug']; // add a row
$byCustomer = [];
foreach ($orders as $order) {
$byCustomer[$order['customer']][] = $order['item']; // inner array created on first use
}
print_r($byCustomer);
多次元配列をループする
階層ごとにforeachを1つ使います。レコードのリストなら、フィールドを名前で読むので、たいていループは1つで十分です。グリッドなら、内側のループが各行のセルを順に処理します。
<?php
$students = [
['name' => 'Ada', 'scores' => [90, 85, 99]],
['name' => 'Linus', 'scores' => [70, 88, 79]],
];
foreach ($students as $student) {
$average = array_sum($student['scores']) / count($student['scores']);
echo $student['name'], ": ", round($average, 1), "\n";
}
$grid = [[1, 2, 3], [4, 5, 6]];
foreach ($grid as $r => $row) {
foreach ($row as $c => $cell) {
echo "[$r][$c]=$cell ";
}
echo "\n";
}
配列からHTMLの表を作る
行の配列はそのままHTMLの表に対応します。最初の行のキーが見出しになり、各行が<tr>になります。実行するとPageタブに描画された表が表示されます。値を変えたり行を足したりして、もう一度実行してみてください。
<?php
$products = [
['name' => 'Keyboard', 'price' => 49.99, 'stock' => 12],
['name' => 'Mouse', 'price' => 19.50, 'stock' => 0],
['name' => 'Monitor', 'price' => 189.00, 'stock' => 4],
];
?>
<style>
table { border-collapse: collapse; font-family: sans-serif; }
th, td { border: 1px solid #ccc; padding: 6px 12px; text-align: left; }
.out { color: #b00; }
</style>
<table>
<tr>
<?php foreach (array_keys($products[0]) as $heading): ?>
<th><?= htmlspecialchars(ucfirst($heading)) ?></th>
<?php endforeach; ?>
</tr>
<?php foreach ($products as $product): ?>
<tr class="<?= $product['stock'] === 0 ? 'out' : '' ?>">
<td><?= htmlspecialchars($product['name']) ?></td>
<td><?= number_format($product['price'], 2) ?></td>
<td><?= $product['stock'] ?></td>
</tr>
<?php endforeach; ?>
</table>
foreach (...): ... endforeach;の形はテンプレート向けに書いた同じループなので、HTMLが読みやすく保たれます。
すべての行から1つの列を取り出す
array_column()は、すべての行から1つのフィールドを取り出して平らなリストにします。3つ目の引数を渡すと別のフィールドを結果のキーにでき、レコードのリストが検索用の表になります。
<?php
$users = [
['id' => 3, 'name' => 'Ada', 'email' => 'ada@example.com'],
['id' => 7, 'name' => 'Linus', 'email' => 'linus@example.com'],
['id' => 9, 'name' => 'Grace', 'email' => 'grace@example.com'],
];
print_r(array_column($users, 'name'));
print_r(array_column($users, 'email', 'id')); // id => email
$byId = array_column($users, null, 'id'); // id => whole row
echo $byId[7]['name'], "\n";
フィールドで行を並べ替える
usort()は比較関数で行を並べ替えます。宇宙船演算子<=>は-1、0、1を返し、これはusort()が期待するものそのものです。降順にするには$aと$bを入れ替えます。
<?php
$players = [
['name' => 'Ada', 'points' => 120],
['name' => 'Linus', 'points' => 95],
['name' => 'Grace', 'points' => 140],
];
usort($players, fn($a, $b) => $b['points'] <=> $a['points']); // highest first
foreach ($players as $rank => $player) {
echo $rank + 1, ". {$player['name']} ({$player['points']})\n";
}
echo count($players), " rows, ", count($players, COUNT_RECURSIVE), " elements in total\n";
count($players, COUNT_RECURSIVE)は3つの行とその中の6つのフィールドを数えるので、9と表示されます。
よくあるミス:存在しないかもしれない深いキーを読む
cacheキーがないのに$config['cache']['ttl']を読むと、Warning: Undefined array key "cache"が表示され、次の階層でWarning: Trying to access array offset on nullが続きます。??演算子は連鎖全体を一度に確認し、決して警告を出さないので、任意のネストした値を読む安全な方法です。
<?php
$config = ['db' => ['host' => 'localhost']];
$ttl = $config['cache']['ttl'] ?? 60; // missing at the first level
$port = $config['db']['port'] ?? 3306; // missing at the second level
$host = $config['db']['host'] ?? '127.0.0.1'; // present
echo "$ttl $port $host\n";
var_dump(isset($config['db']['host']));