数组解构与展开
概述
PHP 7.1+ 引入了短数组解构语法,PHP 8.1+ 扩展为支持字符串键解构。解构使得从数组中提取值的代码更加简洁。同时 ... 展开运算符(splat operator)提供了便捷的数组展开能力。本章详细讲解数组解构与展开的各种用法。
基础概念
| 特性 | 版本 | 语法 | 说明 |
|---|---|---|---|
| 短数组解构 | PHP 7.1+ | [$a, $b] = $arr | 索引解构 |
| list 键解构 | PHP 7.1+ | list('k' => $v) = $arr | 字符串键解构 |
| 短语法键解构 | PHP 8.1+ | ['k' => $v] = $arr | 更简洁的字符串键解构 |
| 展开运算符 | PHP 7.4+ | ...$arr | 函数调用中展开 |
| 数组字面量展开 | PHP 7.4+ | [...$arr] | 合并数组 |
版本要求
字符串键解构需要 PHP 8.1+。在旧版本中只能使用 list() 的索引解构。
语法与代码
PHP 7.1+ 短数组解构
php
<?php
declare(strict_types=1);
// 基本解构
$colors = ['red', 'green', 'blue'];
[$r, $g, $b] = $colors;
echo $r; // red
echo $g; // green
echo $b; // blue
// 跳过元素
[, $second, $third] = $colors;
echo $second; // green
// 交换变量
$a = 'hello';
$b = 'world';
[$b, $a] = [$a, $b];
echo $a; // world
echo $b; // hello
// 解构嵌套数组
$data = ['name' => 'Alice', 'scores' => [90, 85, 95]];
['name' => $name, 'scores' => [$math, $english, $science]] = $data;
echo $math; // 90
echo $science; // 95
// 在 foreach 中解构
$users = [
['id' => 1, 'name' => 'Alice', 'age' => 30],
['id' => 2, 'name' => 'Bob', 'age' => 25],
];
foreach ($users as ['id' => $id, 'name' => $name]) {
echo "#{$id}: {$name}\n";
}
// 仅解构需要的字段
foreach ($users as ['name' => $name]) {
echo $name . "\n";
}PHP 8.1+ 字符串键解构
php
<?php
declare(strict_types=1);
// PHP 8.1+ 短语法字符串键解构
$user = ['name' => 'Alice', 'age' => 30, 'email' => 'a@b.com', 'role' => 'admin'];
// 选择性提取
['name' => $name, 'age' => $age] = $user;
echo $name; // Alice
echo $age; // 30
// 设置默认值(使用 ?? 或 + [])
['name' => $name, 'role' => $role] = $user + ['role' => 'guest'];
echo $role; // admin(数组中有则取值,没有则用默认值)
// PHP 7.1+ list() 也支持字符串键(语法较冗长)
list('name' => $name, 'age' => $age) = $user;
// 解构函数返回值
function getUser(): array
{
return ['id' => 1, 'name' => 'Alice', 'status' => 'active'];
}
['name' => $name, 'status' => $status] = getUser();foreach 中的解构
php
<?php
declare(strict_types=1);
// 实战:解析配置项
$items = [
['type' => 'input', 'name' => 'username', 'required' => true],
['type' => 'select', 'name' => 'country', 'required' => false],
['type' => 'input', 'name' => 'password', 'required' => true],
];
foreach ($items as ['type' => $type, 'name' => $name, 'required' => $required]) {
$mark = $required ? '*' : '';
echo "<{$type} name=\"{$name}\">{$mark}\n";
}
// 解构键值对
$translations = [
'en' => ['hello' => 'Hello', 'goodbye' => 'Goodbye'],
'zh' => ['hello' => '你好', 'goodbye' => '再见'],
];
foreach ($translations as $lang => $words) {
['hello' => $hello, 'goodbye' => $goodbye] = $words;
echo "[{$lang}] Hello: {$hello}, Goodbye: {$goodbye}\n";
}解构嵌套数据
php
<?php
declare(strict_types=1);
// 嵌套解构
$response = [
'status' => 200,
'data' => [
'user' => ['name' => 'Alice', 'id' => 1],
'token' => 'abc123',
],
'message' => 'OK',
];
// 同时提取多个层级的数据
[
'status' => $status,
'data' => [
'user' => ['name' => $userName, 'id' => $userId],
'token' => $token,
],
] = $response;
echo $status; // 200
echo $userName; // Alice
echo $userId; // 1
echo $token; // abc123详细说明
解构 vs 直接访问
php
<?php
declare(strict_types=1);
$user = ['name' => 'Alice', 'age' => 30, 'email' => 'a@b.com'];
// 直接访问
$name = $user['name'];
$age = $user['age'];
// 解构
['name' => $name, 'age' => $age] = $user;
// 解构的优势:
// 1. 一次提取多个值
// 2. 代码更紧凑
// 3. 在 foreach 中特别有用
// 4. 可以跳过不需要的值解构与展开配合使用
php
<?php
declare(strict_types=1);
// 解构 + 展开配合
$first = [1, 2, 3];
$second = [4, 5, 6];
// 解构第一个元素,展开剩余
[$head, ...$tail] = [...$first, ...$second];
echo $head; // 1
print_r($tail); // [2, 3, 4, 5, 6]解构不存在的键
解构不存在的键时,PHP 8.0+ 会产生 Warning。使用 $arr + ['key' => 'default'] 提供默认值。
实战示例
API 响应处理
php
<?php
declare(strict_types=1);
function handleApiResponse(array $response): string
{
[
'status' => $status,
'data' => ['items' => $items, 'total' => $total],
'error' => $error,
] = $response + [
'status' => 500,
'data' => ['items' => [], 'total' => 0],
'error' => null,
];
if ($status !== 200) {
return "Error: {$error}";
}
return "Found {$total} items, showing " . count($items);
}
$resp = ['status' => 200, 'data' => ['items' => [1, 2, 3], 'total' => 100]];
echo handleApiResponse($resp);
// Found 100 items, showing 3解构在函数参数中的应用
php
<?php
declare(strict_types=1);
// 解构作为函数参数
function processUser(array $userData): array
{
['name' => $name, 'age' => $age, 'role' => $role] = $userData + ['role' => 'guest'];
return [
'display_name' => $name . ' (' . $role . ')',
'is_adult' => $age >= 18,
];
}
echo json_encode(processUser(['name' => 'Alice', 'age' => 30]));
// {"display_name":"Alice (guest)","is_adult":true}
// 解构多个返回值
function getMinMax(array $numbers): array
{
return ['min' => min($numbers), 'max' => max($numbers)];
}
['min' => $min, 'max' => $max] = getMinMax([3, 1, 4, 1, 5, 9]);
echo "Min: {$min}, Max: {$max}\n"; // Min: 1, Max: 9
// 解构用于 switch/match
function classifyValue(array $data): string
{
['type' => $type, 'value' => $value] = $data;
return match ($type) {
'int' => $value > 0 ? 'positive' : ($value < 0 ? 'negative' : 'zero'),
'string' => strlen($value) > 10 ? 'long' : 'short',
default => 'unknown',
};
}解构嵌套配置
php
<?php
declare(strict_types=1);
// 深层嵌套配置解构
$settings = [
'database' => [
'connections' => [
'mysql' => [
'host' => 'localhost',
'port' => 3306,
'database' => 'myapp',
'credentials' => [
'username' => 'root',
'password' => 'secret',
],
],
],
],
'cache' => [
'default' => 'redis',
'prefix' => 'myapp_',
],
];
// 选择性解构
[
'database' => [
'connections' => [
'mysql' => [
'host' => $dbHost,
'port' => $dbPort,
'database' => $dbName,
],
],
],
'cache' => [
'prefix' => $cachePrefix,
],
] = $settings;
echo "DB: {$dbHost}:{$dbPort}/{$dbName}\n";
echo "Cache prefix: {$cachePrefix}\n";过度解构
嵌套解构的层级不宜过深(不超过 3 层)。过深的解构降低可读性,且一旦数据结构变化容易导致错误。
解构与默认值组合
php
<?php
declare(strict_types=1);
// 多层默认值
function getConfig(): array
{
return ['database' => ['host' => 'localhost']];
}
[
'database' => [
'host' => $host,
'port' => $port,
'name' => $name,
],
] = getConfig() + [
'database' => [
'host' => '127.0.0.1',
'port' => 3306,
'name' => 'app',
],
];
echo $host; // localhost(实际值)
echo $port; // 3306(默认值)
echo $name; // app(默认值)注意事项
PHP 8.1 之前
PHP 8.0 及之前不支持 ['key' => $var] 语法。需要使用 list('key' => $var) 或直接访问。
最佳实践
- PHP 8.1+ 用短语法解构:
['key' => $val]更简洁 - foreach 配合解构:大幅简化多维数组遍历
- 提供默认值:
$arr + ['key' => 'default'] - 解构返回值:函数返回多个值时用解构提取
- 不要过度解构:只解构需要使用的字段
解构的性能考虑
php
<?php
declare(strict_types=1);
// 解构 vs 直接访问性能对比
$user = ['name' => 'Alice', 'age' => 30, 'email' => 'a@b.com', 'role' => 'admin'];
$n = 1000000;
// 解构
$start = microtime(true);
for ($i = 0; $i < $n; $i++) {
['name' => $name, 'age' => $age] = $user;
}
$t1 = microtime(true) - $start;
// 直接访问
$start = microtime(true);
for ($i = 0; $i < $n; $i++) {
$name = $user['name'];
$age = $user['age'];
}
$t2 = microtime(true) - $start;
echo "解构: {$t1}s\n";
echo "直接访问: {$t2}s\n";
// 两者性能差异极小,可读性更重要PHP 8.1 前的限制
PHP 8.0 及之前版本不支持 ['key' => $var] = $arr 语法。需要使用 list('key' => $var) 或 foreach 中的解构替代。