数组类型
概述
PHP 数组实际上是有序映射(ordered map),可以包含整数键、字符串键或两者混合。本章讲解索引数组、关联数组、混合数组、多维数组、对象数组以及 PHP 中的 array 类型声明。
基础概念
| 数组类型 | 键类型 | 示例 |
|---|---|---|
| 索引数组 | 整数(自动) | ['a', 'b', 'c'] |
| 关联数组 | 字符串 | ['name' => 'Alice'] |
| 混合数组 | 整数 + 字符串 | [0 => 'a', 'key' => 'b'] |
| 多维数组 | 任意(嵌套) | [[1,2], ['a'=>'b']] |
| 对象数组 | 整数/字符串(值为对象) | [new User()] |
语法与代码
索引数组
php
<?php
declare(strict_types=1);
$letters = ['a', 'b', 'c', 'd'];
foreach ($letters as $index => $letter) {
echo "{$index}: {$letter}\n";
}
// range() 生成
$numbers = range(1, 10);
$alphabet = range('a', 'z');
$steps = range(0, 100, 10);
// array_fill 填充
$filled = array_fill(0, 5, 'default');
// ['default', 'default', 'default', 'default', 'default']关联数组
php
<?php
declare(strict_types=1);
$user = ['id' => 1, 'name' => 'Alice', 'email' => 'a@b.com', 'isActive' => true];
// 遍历
foreach ($user as $key => $value) {
echo "{$key}: ";
echo is_bool($value) ? ($value ? 'true' : 'false') : $value;
echo "\n";
}
// 获取键或值
$keys = array_keys($user);
$values = array_values($user);
// 翻转键值
$flipped = array_flip(['a' => 1, 'b' => 2]);
// [1 => 'a', 2 => 'b']混合数组
php
<?php
declare(strict_types=1);
$mixed = [
0 => 'zero',
'key' => 'value',
1 => 'one',
'another' => 'test',
];
// PHP 会统一整数键,字符串数字键被转换
$arr = [0 => 'first', '0' => 'second', 1 => 'third'];
// [0 => 'second', 1 => 'third']('0' 覆盖了 0)
// 遍历保持插入顺序
foreach ($mixed as $key => $value) {
echo "{$key} => {$value}\n";
}多维数组
php
<?php
declare(strict_types=1);
// 二维(表格数据)
$students = [
['name' => 'Alice', 'score' => 95],
['name' => 'Bob', 'score' => 82],
];
// 三维
$grades = [
'class1' => [
'Alice' => ['math' => 95, 'english' => 88],
'Bob' => ['math' => 82, 'english' => 90],
],
];
echo $grades['class1']['Alice']['math']; // 95
// 遍历多维
foreach ($grades as $class => $students) {
foreach ($students as $name => $scores) {
foreach ($scores as $subject => $score) {
echo "{$class} - {$name} - {$subject}: {$score}\n";
}
}
}对象数组
php
<?php
declare(strict_types=1);
class User
{
public function __construct(
public string $name,
public int $age,
public string $role
) {}
}
$users = [
new User('Alice', 30, 'admin'),
new User('Bob', 25, 'user'),
new User('Charlie', 35, 'user'),
];
foreach ($users as $user) {
echo "{$user->name} ({$user->role})\n";
}
// 提取属性
$names = array_map(fn(User $u): string => $u->name, $users);
$admins = array_filter($users, fn(User $u): bool => $u->role === 'admin');array 类型声明
php
<?php
declare(strict_types=1);
function sum(array $numbers): int
{
return array_sum($numbers);
}
echo sum([1, 2, 3]); // 6
// 注意:array 类型不检查元素类型
// PHP 8.0+: 联合类型
function processId(int|string $id): void {}
// typed arrays 需要 PHPDoc
/**
* @param int[] $numbers 整数数组
*/
function sumIntegers(array $numbers): int
{
return array_sum($numbers);
}详细说明
数组内部实现
PHP 数组使用 Zend HashTable 实现:有序、双链表、哈希表 + 链表、自动扩容。平均查找/插入/删除 O(1)。
性能要点
array_merge、array_values 等需要重建数组的操作在大数组时比较耗时。
isset vs empty 对比
| 函数 | null | 0 | '' | false | 未定义 |
|---|---|---|---|---|---|
isset | false | true | true | true | false |
empty | true | true | true | true | true |
array_key_exists | true | true | true | true | false |
实战示例
数据表格渲染
php
<?php
declare(strict_types=1);
function renderTable(array $headers, array $rows): string
{
$html = '<table><thead><tr>';
foreach ($headers as $header) {
$html .= '<th>' . htmlspecialchars($header) . '</th>';
}
$html .= '</tr></thead><tbody>';
foreach ($rows as $row) {
$html .= '<tr>';
foreach ($headers as $key => $_) {
$value = is_array($row) ? ($row[$key] ?? '') : '';
$html .= '<td>' . htmlspecialchars((string)$value) . '</td>';
}
$html .= '</tr>';
}
return $html . '</tbody></table>';
}
$headers = ['姓名', '年龄', '城市'];
$rows = [['张三', 25, '北京'], ['李四', 30, '上海']];
echo renderTable($headers, $rows);可迭代类型与数组
php
<?php
declare(strict_types=1);
// iterable 类型(PHP 7.1+)
function forEachItem(iterable $items, callable $callback): void
{
foreach ($items as $key => $item) {
$callback($item, $key);
}
}
// 可以接受数组和 Traversable 对象
forEachItem([1, 2, 3], fn(int $v, int $k): void => echo "{$k}: {$v}\n");
// 生成器也是 iterable
function rangeGen(int $start, int $end): \Generator
{
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
forEachItem(rangeGen(1, 5), fn(int $v): void => echo $v . ' ');
// 1 2 3 4 5
// 返回 iterable
function getItems(): iterable
{
return [1, 2, 3]; // 也可以返回生成器
}特殊数组类型
php
<?php
declare(strict_types=1);
// 空数组
$empty = [];
// 嵌套空数组(用于默认参数)
function search(array $criteria = [], array $options = []): array
{
// ...
return [];
}
// 数组作为栈
$stack = [];
$stack[] = 'a'; // push
$top = array_pop($stack); // pop -> 'a'
// 数组作为队列
$queue = [];
$queue[] = 'a'; // enqueue
$front = array_shift($queue); // dequeue -> 'a'
// 数组作为集合
$set = array_flip(['a', 'b', 'c']); // ['a' => 0, 'b' => 1, 'c' => 2]
isset($set['a']); // O(1) 查找typed 数组实现
php
<?php
declare(strict_types=1);
// PHP 不支持原生 typed arrays,但有几种替代方案
// 方案 1:使用泛型类(PHP 8.0+)
/**
* @template T
*/
class TypedArray
{
/** @var array<T> */
private array $items = [];
private string $type;
public function __construct(string $type)
{
$this->type = $type;
}
public function add(mixed $item): void
{
if (!($item instanceof $this->type || gettype($item) === $this->type)) {
throw new \InvalidArgumentException("Expected type: {$this->type}");
}
$this->items[] = $item;
}
public function toArray(): array
{
return $this->items;
}
public function count(): int
{
return count($this->items);
}
}
$intArray = new TypedArray('integer');
$intArray->add(1);
$intArray->add(2);
// $intArray->add('string'); // InvalidArgumentException
// 方案 2:使用 DS\Vector
$vector = new \Ds\Vector();
$vector->push(1, 2, 3);
// DS 不检查类型,但语义更清晰
// 方案 3:使用 SplFixedArray(固定长度)
$arr = new SplFixedArray(5);
$arr[0] = 'a';
$arr[1] = 'b';
// $arr[5] = 'c'; // RuntimeException(越界)PHP 8.0+ 类型改进
PHP 8.0+ 对联合类型的支持使得函数参数可以更精确地声明 int|float、string|null 等类型。但 typed arrays 仍然需要使用 PHPDoc 或封装类。
数组类型检查工具
php
<?php
declare(strict_types=1);
// 检查是否为关联数组
function isAssociativeArray(array $arr): bool
{
if (empty($arr)) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
// 检查是否为顺序索引数组
function isIndexedArray(array $arr): bool
{
return $arr === array_values($arr);
}
echo isAssociativeArray(['a' => 1, 'b' => 2]); // true
echo isAssociativeArray([1, 2, 3]); // false
echo isIndexedArray([1, 2, 3]); // true
echo isIndexedArray([0 => 'a', 2 => 'b']); // false
// PHP 8.1+ array_is_list
echo array_is_list([1, 2, 3]); // true
echo array_is_list(['a', 'b', 'c']); // true
echo array_is_list([0 => 'a', 1 => 'b']); // true
echo array_is_list([1 => 'a', 0 => 'b']); // false
echo array_is_list([]); // true注意事项
array 类型不检查元素
array 类型声明只检查参数是否为数组,不检查元素类型。使用 PHPDoc 或自定义类封装。
最佳实践
- 索引数组用于列表,关联数组用于映射
- PHPDoc 注解类型:
@param string[] - 避免混合键类型
- 多维数组保持结构统一
- 对象数组考虑 DTO 封装