Skip to content

数组解构与展开

概述

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) 或直接访问。

最佳实践

  1. PHP 8.1+ 用短语法解构['key' => $val] 更简洁
  2. foreach 配合解构:大幅简化多维数组遍历
  3. 提供默认值$arr + ['key' => 'default']
  4. 解构返回值:函数返回多个值时用解构提取
  5. 不要过度解构:只解构需要使用的字段

解构的性能考虑

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 中的解构替代。

参考链接