Skip to content

数组访问与修改

概述

PHP 提供了多种方式来访问和修改数组元素,包括方括号语法、isset() 检查、array_key_exists() 验证、unset() 删除等。本章详细讲解数组元素访问与修改的各种操作方式及安全访问技巧。

基础概念

操作一览

操作语法说明
读取$arr['key']通过键读取元素
写入$arr['key'] = 'val'修改或添加元素
追加$arr[] = 'val'末尾追加元素
删除unset($arr['key'])移除元素
检查isset($arr['key'])键存在且不为 null
检查键array_key_exists('key', $arr)键存在(含 null)
默认值$arr['key'] ?? 'default'PHP 7.0+ 空合并运算符

语法与代码

基本访问与修改

php
<?php

declare(strict_types=1);

// 索引数组
$fruits = ['apple', 'banana', 'cherry'];
echo $fruits[0];      // apple
$fruits[1] = 'blueberry';  // 修改
$fruits[] = 'date';        // 追加

// 关联数组
$user = ['name' => 'Alice', 'age' => 30];
echo $user['name'];   // Alice
$user['age'] = 31;    // 修改
$user['role'] = 'admin';  // 添加

// 多维数组
$matrix = [[1, 2, 3], [4, 5, 6]];
echo $matrix[0][1];   // 2
$matrix[1][0] = 40;   // 修改

isset vs array_key_exists

php
<?php

declare(strict_types=1);

$arr = ['name' => 'Alice', 'age' => null];

// isset - 键存在且值不为 null
isset($arr['name']);           // true
isset($arr['age']);            // false(值为 null)
isset($arr['nonexistent']);    // false

// array_key_exists - 仅检查键
array_key_exists('name', $arr);          // true
array_key_exists('age', $arr);           // true(键存在)
array_key_exists('nonexistent', $arr);   // false

// PHP 7.0+ 空合并运算符
$arr['age'] ?? 'unknown';          // 'unknown'
$arr['name'] ?? 'unknown';         // 'Alice'
$arr['nonexistent'] ?? 'unknown';  // 'unknown'

unset 删除元素

php
<?php

declare(strict_types=1);

$user = ['name' => 'Alice', 'age' => 30, 'email' => 'a@b.com'];

// 删除单个
unset($user['email']);  // ['name' => 'Alice', 'age' => 30]

// 删除多个
unset($user['name'], $user['age']);  // []

// 注意:删除后索引不连续
$nums = [0 => 'a', 1 => 'b', 2 => 'c'];
unset($nums[1]);
// [0 => 'a', 2 => 'c']

// 重新索引
$nums = array_values($nums);
// [0 => 'a', 1 => 'c']

多维数组安全访问

php
<?php

declare(strict_types=1);

$company = [
    'departments' => [
        'engineering' => ['head' => 'Alice'],
    ],
];

// PHP 7.0+ 空合并运算符链
$head = $company['departments']['engineering']['head'] ?? 'N/A';  // Alice
$ceo = $company['departments']['executive']['head'] ?? 'N/A';    // N/A

// 安全获取嵌套值
function getNestedValue(array $array, string $key, mixed $default = null): mixed
{
    $keys = explode('.', $key);
    $current = $array;

    foreach ($keys as $k) {
        if (!is_array($current) || !array_key_exists($k, $current)) {
            return $default;
        }
        $current = $current[$k];
    }

    return $current;
}

echo getNestedValue($company, 'departments.engineering.head');  // Alice

数组引用赋值

php
<?php

declare(strict_types=1);

// 值复制(默认)
$a = [1, 2, 3];
$b = $a;
$b[0] = 99;
echo $a[0];  // 1(不受影响)

// 引用赋值
$a = [1, 2, 3];
$b = &$a;
$b[0] = 99;
echo $a[0];  // 99(被修改)

详细说明

isset vs empty vs array_key_exists

php
<?php

declare(strict_types=1);

$arr = ['a' => 0, 'b' => '', 'c' => null, 'd' => false];

isset($arr['a']);    // true
isset($arr['c']);    // false

empty($arr['a']);    // true(0 是 falsy)
empty($arr['b']);    // true('' 是 falsy)
empty($arr['c']);    // true(null 是 falsy)

array_key_exists('a', $arr);  // true
array_key_exists('c', $arr);  // true
array_key_exists('z', $arr);  // false

PHP 8.0+ 未定义键行为

版本访问不存在的键
PHP < 8.0Notice,返回 null
PHP 8.0+Warning,返回 null
php
<?php

declare(strict_types=1);

$arr = ['a' => 1];

// PHP 8.0+: Warning: Undefined array key "b"
// echo $arr['b'];

// 安全写法
$val = $arr['b'] ?? null;

实战示例

点号语法配置读取器

php
<?php

declare(strict_types=1);

class Config
{
    public function __construct(private array $data = []) {}

    public function get(string $key, mixed $default = null): mixed
    {
        $keys = explode('.', $key);
        $current = $this->data;

        foreach ($keys as $k) {
            if (!is_array($current) || !array_key_exists($k, $current)) {
                return $default;
            }
            $current = $current[$k];
        }

        return $current;
    }

    public function set(string $key, mixed $value): void
    {
        $keys = explode('.', $key);
        $this->data = $this->setNested($this->data, $keys, $value);
    }

    private function setNested(array $array, array $keys, mixed $value): array
    {
        $key = array_shift($keys);
        if (empty($keys)) {
            $array[$key] = $value;
        } else {
            $array[$key] = $this->setNested($array[$key] ?? [], $keys, $value);
        }
        return $array;
    }
}

$config = new Config(['db' => ['host' => 'localhost', 'port' => 3306]]);
echo $config->get('db.host');        // localhost
echo $config->get('db.port');        // 3306
echo $config->get('db.name', 'app'); // app

数组切片操作

php
<?php

declare(strict_types=1);

// array_slice - 截取数组的一部分
$fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];

// 从偏移 1 开始取 3 个
$slice = array_slice($fruits, 1, 3);
// ['banana', 'cherry', 'date']

// 负偏移
$slice = array_slice($fruits, -3, 2);
// ['cherry', 'date']

// array_splice - 截取并替换(修改原数组)
$colors = ['red', 'green', 'blue', 'yellow', 'purple'];
$removed = array_splice($colors, 1, 2, ['cyan', 'magenta']);
// $colors: ['red', 'cyan', 'magenta', 'yellow', 'purple']
// $removed: ['green', 'blue']

// array_chunk - 将数组分割为多个块
$numbers = range(1, 10);
$chunks = array_chunk($numbers, 3);
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

// array_chunk 保留键名
$chunks = array_chunk($numbers, 3, true);
// [[0 => 1, 1 => 2, 2 => 3], [3 => 4, 4 => 5, 5 => 6], ...]

// array_pad - 填充数组到指定长度
$arr = ['a', 'b'];
$padded = array_pad($arr, 5, 'x');
// ['a', 'b', 'x', 'x', 'x'](右侧填充)

$padded = array_pad($arr, -5, 'x');
// ['x', 'x', 'x', 'a', 'b'](左侧填充)

数组值检查

php
<?php

declare(strict_types=1);

// in_array - 检查值是否存在
$fruits = ['apple', 'banana', 'cherry'];

// 严格比较(推荐)
in_array('banana', $fruits, true);    // true
in_array('BANANA', $fruits, true);   // false(严格比较)
in_array('BANANA', $fruits, false);  // true(松散比较,不推荐)

// array_search - 查找值并返回键
$key = array_search('cherry', $fruits, true);
echo $key;  // 2

// 注意:array_search 返回 false 或键(可能是 0)
$key = array_search('apple', $fruits, true);
if ($key !== false) {
    echo "Found at key: {$key}";  // 0
}

// in_array 的陷阱
in_array(0, ['foo', 'bar']);   // false(严格模式)
in_array(0, ['foo', 'bar'], false);  // true!(松散模式,0 == 'foo' 为 false... 实际为 true 取决于 PHP 版本)

数组唯一化与去重

php
<?php

declare(strict_types=1);

// array_unique - 去除重复值
$data = [1, 2, 3, 2, 1, 4, 5, 3];
$unique = array_unique($data);
// [1, 2, 3, 4, 5](保留键名)

// array_unique 标志
$unique = array_unique($data, SORT_REGULAR);
// SORT_REGULAR: 比较值(默认)
// SORT_STRING: 作为字符串比较
// SORT_NUMERIC: 作为数字比较
// SORT_FLAG_CASE: 与 SORT_STRING 组合时忽略大小写

// 多维数组去重(基于某个字段)
$users = [
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 2, 'name' => 'Bob'],
    ['id' => 1, 'name' => 'Alice Duplicate'],
];

$seen = [];
$unique = array_filter($users, function (array $user) use (&$seen): bool {
    if (in_array($user['id'], $seen, true)) {
        return false;
    }
    $seen[] = $user['id'];
    return true;
});

print_r(array_values($unique));
// [['id' => 1, 'name' => 'Alice'], ['id' => 2, 'name' => 'Bob']]

in_array 松散比较

in_array 默认使用松散比较。in_array('test', [0]) 在 PHP 8.0 以前返回 true(因为 0 == 'test')。PHP 8.0+ 修正了部分行为。始终使用 in_array($needle, $haystack, true) 进行严格比较。

数组切片与提取

php
<?php

declare(strict_types=1);

// array_slice 截取
$colors = ['red', 'green', 'blue', 'yellow', 'purple'];
echo implode(', ', array_slice($colors, 1, 3));
// green, blue, yellow

// array_splice 替换(修改原数组)
$items = ['a', 'b', 'c', 'd', 'e'];
$removed = array_splice($items, 1, 2, ['X', 'Y', 'Z']);
// $items: ['a', 'X', 'Y', 'Z', 'd', 'e']
// $removed: ['b', 'c']

// array_column 提取列
$users = [
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 2, 'name' => 'Bob'],
];
$ids = array_column($users, 'id');       // [1, 2]
$map = array_column($users, 'name', 'id'); // [1 => 'Alice', 2 => 'Bob']

注意事项

PHP 8.0+ 未定义键

PHP 8.0+ 将未定义数组键从 Notice 升级为 Warning。使用 ?? 运算符或 isset() 检查。

unset 后索引不连续

删除元素后索引不自动重排。需要连续索引时使用 array_values()

最佳实践

  1. 用 ?? 获取带默认值元素:比三元运算符更简洁
  2. null 值用 array_key_exists:区分 null 和不存在的键
  3. 删除后用 array_values():重建连续索引
  4. 多维数组链式 ??:避免报错
  5. 避免引用赋值:除非确实需要

参考链接