多维数组是元素本身也是数组的数组。每一层用一对方括号读取:$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";
第二种形式,即由带命名字段的记录组成的列表,是你最常遇到的:数据库查询、解码后的 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。对于由记录组成的列表,一个循环通常就够了,因为你会按名称读取字段。对于网格,内层循环会依次访问每一行中的单元格。
<?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>。运行后,“页面”标签页会显示渲染出来的表格;修改一个值或添加一行,再运行一次。
<?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 保持可读。
从所有行中取出一列
array_column() 把每一行中的某个字段取出来,组成一个扁平的列表。传入第三个参数可以用另一个字段作为结果的键,这样就把记录列表变成了一张查找表。
<?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']));