一、回顾与本篇目标
上一篇你学会了 PHP 的条件判断和循环。你已经能写出有逻辑的程序了——根据条件执行不同的代码,用循环重复处理数据。
这一篇我们要学的是 PHP 中最重要、最强大、最常用的数据结构——数组。在 PHP 中,数组不是 C 语言那种只能存相同类型元素的连续内存块,也不是 Python 中需要区分 list 和 dict 的两种独立类型。PHP 的数组一个顶俩——它同时承担了”列表”和”字典”两种角色,甚至还可以混用。
PHP 的数组功能之强大,在主流编程语言中是罕见的。它内置了排序、过滤、映射、合并、去重等几十个函数,不需要引入任何库就能完成复杂的数据处理。这是 PHP 在 Web 开发中如此高效的重要原因之一。
本篇的目标:
- 理解 PHP 数组的两种类型:索引数组和关联数组
- 掌握数组的创建、访问、增删改查
- 学会 foreach 遍历数组的各种写法
- 掌握多维数组的用法
- 学会最常用的数组函数:排序、过滤、合并、去重等
- 理解 PHP 数组和 JavaScript 数组/Python 列表字典的对应关系
二、PHP 数组的本质
PHP 的数组本质上是一个有序映射——它把键映射到值。每个数组元素都由一个键和一个值组成。
如果你不指定键,PHP 会自动分配数字键(从 0 开始递增)——这就是索引数组,相当于 Python 的 list 或 JavaScript 的数组。
如果你指定了字符串键——这就是关联数组,相当于 Python 的 dict 或 JavaScript 的对象。
关键区别:在 JavaScript 中,数组和对象是两种不同的类型。在 Python 中,list 和 dict 是两种不同的类型。在 PHP 中,它们是同一种类型——都是 array。
<?php
// 索引数组(PHP 自动分配数字键 0, 1, 2)
$fruits = ["苹果", "香蕉", "橘子"];
// 关联数组(你指定字符串键)
$user = [
"name" => "张三",
"age" => 28,
"city" => "上海"
];
// 用 var_dump 看看它们的实际结构
var_dump($fruits);
// array(3) { [0]=> string(6) "苹果" [1]=> string(6) "香蕉" [2]=> string(6) "橘子" }
var_dump($user);
// array(3) { ["name"]=> string(6) "张三" ["age"]=> int(28) ["city"]=> string(6) "上海" }
?>
看到 var_dump 输出的方括号了吗?索引数组的键是 [0]、[1]、[2],关联数组的键是 ["name"]、["age"]、["city"]。结构完全一样,只是键的类型不同。
三、创建数组的多种方式
3.1 用 [] 语法(PHP 5.4+,最推荐)
<?php
// 空数组
$empty = [];
// 索引数组
$numbers = [10, 20, 30, 40, 50];
// 关联数组
$person = [
"name" => "张三",
"age" => 28,
"city" => "上海"
];
// 混合数组(键既有数字也有字符串)
$mixed = [
"title" => "产品列表",
10, 20, 30, // 自动分配键 0, 1, 2
"total" => 3
];
?>
3.2 用 array() 函数(旧版语法,仍广泛使用)
<?php
$fruits = array("苹果", "香蕉", "橘子");
$user = array(
"name" => "张三",
"age" => 28
);
?>
[] 和 array() 完全等价。[] 更简洁,但 array() 在老代码中非常常见,需要能看懂。
3.3 用 range() 创建数字序列
<?php
$nums = range(1, 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
$odds = range(1, 10, 2); // [1, 3, 5, 7, 9] —— 第三个参数是步长
$letters = range('a', 'z'); // ['a', 'b', 'c', ..., 'z'] —— 字母也可以
?>
3.4 用 explode() 从字符串创建数组
<?php
$str = "苹果,香蕉,橘子,葡萄";
$fruits = explode(",", $str);
print_r($fruits);
// Array ( [0] => 苹果 [1] => 香蕉 [2] => 橘子 [3] => 葡萄 )
?>
explode(分隔符, 字符串) 把字符串按分隔符切开,返回数组。这是处理 CSV 数据或用户输入时非常常用的函数。
对应的反向操作是 implode(分隔符, 数组),把数组元素拼接成字符串:
<?php
$fruits = ["苹果", "香蕉", "橘子"];
echo implode("、", $fruits); // 苹果、香蕉、橘子
?>
四、访问和修改数组元素
4.1 用方括号访问
<?php
$fruits = ["苹果", "香蕉", "橘子"];
echo $fruits[0]; // 苹果 —— 第一个元素
echo $fruits[2]; // 橘子 —— 第三个元素
$user = ["name" => "张三", "age" => 28];
echo $user["name"]; // 张三
echo $user["age"]; // 28
?>
注意:PHP 数组的键区分大小写。$user["name"] 和 $user["Name"] 是两个不同的键。
4.2 添加元素
<?php
$fruits = ["苹果", "香蕉"];
// 方式一:用 [] 追加到末尾(自动分配下一个数字键)
$fruits[] = "橘子"; // 现在键是 2
// 方式二:指定键添加(如果键已存在,则覆盖)
$fruits[5] = "葡萄"; // 键 5
// 方式三:关联数组添加
$user = ["name" => "张三"];
$user["age"] = 28; // 新增键 age
$user["city"] = "上海"; // 新增键 city
print_r($fruits);
// Array ( [0] => 苹果 [1] => 香蕉 [2] => 橘子 [5] => 葡萄 )
?>
注意:当你用 $arr[] = 值 追加时,PHP 会使用当前最大整数键 + 1。上面例子中,添加”橘子”时最大整数键是 1,所以新键是 2。添加”葡萄”时指定了键 5。如果再追加 $fruits[] = "西瓜",键会是 6(而不是 3)。
4.3 修改元素
<?php
$fruits = ["苹果", "香蕉", "橘子"];
$fruits[1] = "草莓"; // 把"香蕉"改成"草莓"
$fruits["new"] = "芒果"; // 添加一个字符串键的元素
print_r($fruits);
// Array ( [0] => 苹果 [1] => 草莓 [2] => 橘子 [new] => 芒果 )
?>
4.4 删除元素
<?php
$fruits = ["苹果", "香蕉", "橘子", "葡萄"];
// 用 unset() 删除指定元素
unset($fruits[1]); // 删除"香蕉"
print_r($fruits);
// Array ( [0] => 苹果 [2] => 橘子 [3] => 葡萄 )
// 注意:键 1 被删除了,但其他键不会重新编号!
// 用 array_values() 重新索引数组
$fruits = array_values($fruits);
print_r($fruits);
// Array ( [0] => 苹果 [1] => 橘子 [2] => 葡萄 )
?>
unset 不会重新索引数组。删除键 1 后,数组的键变成了 0、2、3。如果需要重新按 0、1、2 编号,用 array_values()。
4.5 检查键是否存在
<?php
$user = ["name" => "张三", "age" => 28];
// 用 array_key_exists() 检查键是否存在
var_dump(array_key_exists("name", $user)); // true
var_dump(array_key_exists("email", $user)); // false
// 用 isset() 检查键是否存在且值不为 NULL
var_dump(isset($user["name"])); // true
var_dump(isset($user["email"])); // false
?>
array_key_exists() 和 isset() 的区别:如果键存在但值为 NULL,array_key_exists() 返回 true,isset() 返回 false。
4.6 检查值是否存在
<?php
$fruits = ["苹果", "香蕉", "橘子"];
var_dump(in_array("香蕉", $fruits)); // true
var_dump(in_array("西瓜", $fruits)); // false
?>
五、遍历数组
5.1 foreach 遍历值
<?php
$fruits = ["苹果", "香蕉", "橘子"];
foreach ($fruits as $fruit) {
echo $fruit . "<br>";
}
?>
5.2 foreach 同时获取键和值
<?php
$user = ["name" => "张三", "age" => 28, "city" => "上海"];
foreach ($user as $key => $value) {
echo "{$key}: {$value}<br>";
}
// name: 张三
// age: 28
// city: 上海
?>
5.3 foreach 遍历时修改数组
遍历值时修改——不会影响原数组:
<?php
$nums = [1, 2, 3];
foreach ($nums as $num) {
$num = $num * 10; // 修改的是 $num 副本,不影响原数组
}
print_r($nums); // Array ( [0] => 1 [1] => 2 [2] => 3 ) —— 没变
?>
遍历时通过引用修改——会影响原数组:
<?php
$nums = [1, 2, 3];
foreach ($nums as &$num) { // 注意 & 符号——引用传递
$num = $num * 10;
}
unset($num); // 重要!遍历结束后取消引用,防止后续意外修改
print_r($nums); // Array ( [0] => 10 [1] => 20 [2] => 30 ) —— 变了
?>
foreach 引用遍历后必须 unset 引用变量。如果不 unset($num),$num 仍然指向数组最后一个元素,后续代码中对 $num 的任何操作都会意外修改那个元素。
5.4 用 for 遍历索引数组
<?php
$fruits = ["苹果", "香蕉", "橘子"];
$count = count($fruits); // 获取数组长度
for ($i = 0; $i < $count; $i++) {
echo "第 " . ($i + 1) . " 个水果:{$fruits[$i]}<br>";
}
?>
count($arr) 返回数组的元素个数,相当于 JavaScript 的 arr.length 或 Python 的 len(arr)。
六、多维数组
数组的元素本身也可以是数组——这就是多维数组。最常见的是二维数组,用来存储表格数据:
<?php
$students = [
["name" => "张三", "age" => 20, "score" => 85.5],
["name" => "李四", "age" => 21, "score" => 92.0],
["name" => "王五", "age" => 19, "score" => 78.5],
["name" => "赵六", "age" => 22, "score" => 95.0],
];
// 访问二维数组元素
echo $students[0]["name"]; // 张三
echo $students[2]["score"]; // 78.5
// 遍历二维数组
foreach ($students as $index => $student) {
echo "学生 " . ($index + 1) . ":";
echo "{$student["name"]},";
echo "{$student["age"]}岁,";
echo "{$student["score"]}分<br>";
}
?>
七、常用数组函数
PHP 内置了超过 80 个数组处理函数。下面是最常用的十几个,按功能分类。
7.1 统计类
<?php
$nums = [10, 20, 30, 40, 50];
echo count($nums); // 5 —— 元素个数
echo sizeof($nums); // 5 —— count 的别名
echo array_sum($nums); // 150 —— 所有元素的和
echo array_product($nums); // 12000000 —— 所有元素的乘积
echo max($nums); // 50 —— 最大值
echo min($nums); // 10 —— 最小值
?>
7.2 查找类
<?php
$fruits = ["苹果", "香蕉", "橘子", "香蕉"];
// 检查值是否存在
var_dump(in_array("香蕉", $fruits)); // true
// 查找值的位置(返回第一个匹配的键)
echo array_search("香蕉", $fruits); // 1
// 获取所有键
print_r(array_keys($fruits)); // [0, 1, 2, 3]
// 获取所有值
print_r(array_values($fruits)); // ["苹果", "香蕉", "橘子", "香蕉"]
// 检查键是否存在
var_dump(array_key_exists(2, $fruits)); // true
?>
7.3 增删改类
<?php
$arr = ["a" => "苹果", "b" => "香蕉"];
// 在开头添加
array_unshift($arr, "草莓");
// 在末尾添加
array_push($arr, "橘子", "葡萄");
// 从开头删除
$first = array_shift($arr);
// 从末尾删除
$last = array_pop($arr);
// 用 print_r 而不是 var_dump 获得更简洁的输出
print_r($arr);
// 删除重复值
$with_dups = [1, 2, 2, 3, 3, 3, 4];
$unique = array_unique($with_dups);
print_r($unique); // [1, 2, 3, 4]
// 反转数组
$reversed = array_reverse([1, 2, 3]);
print_r($reversed); // [3, 2, 1]
// 随机抽取一个或多个元素
$random_key = array_rand(["a" => 1, "b" => 2, "c" => 3]); // 随机返回一个键
$random_keys = array_rand(["a" => 1, "b" => 2, "c" => 3], 2); // 返回两个键的数组
// 打乱数组
$nums = [1, 2, 3, 4, 5];
shuffle($nums);
print_r($nums); // 顺序随机(注意:键会被重新索引)
?>
7.4 排序类
PHP 的排序函数非常多,按”是否保留键”和”升序/降序”组合出多种变体:
| 函数 | 排序方式 | 是否保留键 | 说明 |
|---|---|---|---|
sort($arr) |
升序 | ❌ 重新索引 | 最常用 |
rsort($arr) |
降序 | ❌ 重新索引 | reverse sort |
asort($arr) |
升序 | ✅ 保留键 | 关联数组排序用这个 |
arsort($arr) |
降序 | ✅ 保留键 | 关联数组降序 |
ksort($arr) |
按键升序 | ✅ | 按 key 排序 |
krsort($arr) |
按键降序 | ✅ | 按 key 反向排序 |
<?php
$fruits = ["橘子", "苹果", "香蕉"];
sort($fruits);
print_r($fruits); // [0] => 苹果 [1] => 橘子 [2] => 香蕉 —— 升序排列
$scores = ["张三" => 85, "李四" => 92, "王五" => 78];
asort($scores); // 按值升序,保留键
print_r($scores); // [王五] => 78 [张三] => 85 [李四] => 92
?>
7.5 过滤和映射类
<?php
$nums = [10, 15, 20, 25, 30, 35, 40];
// array_filter:筛选出满足条件的元素
$even = array_filter($nums, function($n) {
return $n % 2 == 0; // 保留偶数
});
print_r($even); // [10, 20, 30, 40]
// array_map:对每个元素应用函数
$doubled = array_map(function($n) {
return $n * 2;
}, $nums);
print_r($doubled); // [20, 30, 40, 50, 60, 70, 80]
// 只取某一列(二维数组中非常常用)
$students = [
["name" => "张三", "score" => 85],
["name" => "李四", "score" => 92],
["name" => "王五", "score" => 78],
];
$names = array_column($students, "name");
print_r($names); // ["张三", "李四", "王五"]
?>
7.6 合并和拆分
<?php
// array_merge:合并两个或多个数组
$arr1 = ["a" => "苹果", "b" => "香蕉"];
$arr2 = ["b" => "蓝莓", "c" => "橘子"];
$merged = array_merge($arr1, $arr2);
print_r($merged);
// ["a" => "苹果", "b" => "蓝莓", "c" => "橘子"]
// 注意:相同的字符串键,后面的覆盖前面的
$arr3 = [10, 20];
$arr4 = [30, 40];
$merged2 = array_merge($arr3, $arr4);
print_r($merged2); // [10, 20, 30, 40] —— 数字键会重新索引
// array_slice:截取数组的一部分
$nums = [10, 20, 30, 40, 50];
$slice = array_slice($nums, 1, 3); // 从索引 1 开始,取 3 个
print_r($slice); // [20, 30, 40]
// array_chunk:把数组分成多个小块
$chunks = array_chunk([1, 2, 3, 4, 5, 6, 7], 3);
print_r($chunks); // [[1,2,3], [4,5,6], [7]]
?>
7.7 集合运算
<?php
$a = [1, 2, 3, 4];
$b = [3, 4, 5, 6];
// 差集:在 $a 中但不在 $b 中
print_r(array_diff($a, $b)); // [1, 2]
// 交集:同时在 $a 和 $b 中
print_r(array_intersect($a, $b)); // [3, 4]
?>
八、PHP 数组和 JavaScript/Python 的对照表
| 操作 | PHP | JavaScript | Python |
|---|---|---|---|
| 创建列表 | [1, 2, 3] |
[1, 2, 3] |
[1, 2, 3] |
| 创建字典/对象 | ["a" => 1] |
{a: 1} |
{"a": 1} |
| 访问元素 | $arr[0] 或 $arr["key"] |
arr[0] 或 obj.key |
arr[0] 或 d["key"] |
| 追加到末尾 | $arr[] = 值 |
arr.push(val) |
arr.append(val) |
| 删除末尾 | array_pop($arr) |
arr.pop() |
arr.pop() |
| 长度 | count($arr) |
arr.length |
len(arr) |
| 检查值存在 | in_array(val, $arr) |
arr.includes(val) |
val in arr |
| 合并 | array_merge($a, $b) |
[...a, ...b] |
a + b(list)或 {**a, **b}(dict) |
| 过滤 | array_filter($arr, fn) |
arr.filter(fn) |
filter(fn, arr) 或列表推导式 |
| 映射 | array_map(fn, $arr) |
arr.map(fn) |
map(fn, arr) 或列表推导式 |
| 排序 | sort($arr) |
arr.sort() |
arr.sort() 或 sorted(arr) |
九、综合演示:学生成绩管理系统
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>学生成绩管理</title>
<style>
body { font-family: sans-serif; padding: 40px; background: #f0f4f8; }
.container { max-width: 700px; margin: 0 auto; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #e0e0e0; }
th { background: #4a90d9; color: white; }
.excellent { color: #27ae60; font-weight: bold; }
.good { color: #2980b9; }
.pass { color: #f39c12; }
.fail { color: #e74c3c; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h2>📊 学生成绩管理系统</h2>
<?php
// 学生数据(通常来自数据库,这里用数组模拟)
$students = [
["name" => "张三", "math" => 85, "english" => 78, "chinese" => 92],
["name" => "李四", "math" => 92, "english" => 88, "chinese" => 95],
["name" => "王五", "math" => 58, "english" => 72, "chinese" => 65],
["name" => "赵六", "math" => 95, "english" => 90, "chinese" => 88],
["name" => "孙七", "math" => 45, "english" => 55, "chinese" => 60],
];
// 计算每个学生的总分和平均分
foreach ($students as &$student) {
$student["total"] = $student["math"] + $student["english"] + $student["chinese"];
$student["average"] = round($student["total"] / 3, 1);
// 判断等级
if ($student["average"] >= 90) {
$student["grade"] = "优秀";
$student["class"] = "excellent";
} elseif ($student["average"] >= 80) {
$student["grade"] = "良好";
$student["class"] = "good";
} elseif ($student["average"] >= 60) {
$student["grade"] = "及格";
$student["class"] = "pass";
} else {
$student["grade"] = "不及格";
$student["class"] = "fail";
}
}
unset($student); // 取消引用
// 按总分降序排列
usort($students, function($a, $b) {
return $b["total"] - $a["total"];
});
?>
<table>
<thead>
<tr>
<th>排名</th>
<th>姓名</th>
<th>数学</th>
<th>英语</th>
<th>语文</th>
<th>总分</th>
<th>平均分</th>
<th>等级</th>
</tr>
</thead>
<tbody>
<?php foreach ($students as $index => $s): ?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= $s["name"] ?></td>
<td><?= $s["math"] ?></td>
<td><?= $s["english"] ?></td>
<td><?= $s["chinese"] ?></td>
<td><?= $s["total"] ?></td>
<td><?= $s["average"] ?></td>
<td class="<?= $s["class"] ?>"><?= $s["grade"] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
// 班级统计
$all_scores = array_merge(
array_column($students, "math"),
array_column($students, "english"),
array_column($students, "chinese")
);
$class_avg = round(array_sum($all_scores) / count($all_scores), 1);
$class_max = max($all_scores);
$class_min = min($all_scores);
$pass_count = count(array_filter($all_scores, function($s) { return $s >= 60; }));
$pass_rate = round($pass_count / count($all_scores) * 100, 1);
?>
<div style="margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h3>班级统计</h3>
<p>班级平均分:<strong><?= $class_avg ?></strong></p>
<p>最高分:<strong><?= $class_max ?></strong> | 最低分:<strong><?= $class_min ?></strong></p>
<p>及格率:<strong><?= $pass_rate ?>%</strong>(<?= $pass_count ?>/<?= count($all_scores) ?>)</p>
</div>
</div>
</body>
</html>
代码解析:
&$student:引用传递,让 foreach 循环内部修改数组元素时能直接修改原数组。循环结束后必须unset($student)。usort($arr, fn):用自定义函数排序。这里用匿名函数按总分降序排列。$b["total"] - $a["total"]为正时 $b 排在前面(降序)。array_merge():把三个科目的分数合并成一个大数组,用于班级统计。array_column():提取二维数组中某一列的所有值。round($val, 1):四舍五入保留一位小数。
十、本篇动手练习
练习 1:购物车计算
新建 practice4-1.php,创建一个购物车数组,每个商品包含 name、price、quantity。计算总金额、平均单价、最贵和最便宜的商品。用 foreach 遍历输出购物清单。
练习 2:单词频率统计
新建 practice4-2.php,给定一段英文文本,用 explode() 把文本拆成单词数组,用关联数组统计每个单词出现的次数,按出现次数降序排列输出。
练习 3:数组函数练习
新建 practice4-3.php,创建两个数组,分别测试以下函数并输出结果:array_merge、array_diff、array_intersect、array_filter(筛选出大于 50 的值)、array_map(每个值乘以 1.1)。
练习 4:学生管理系统加强版
修改第九节的综合演示代码:增加”删除成绩最低的学生”功能(用 usort 排序后用 array_shift 删除);增加”按单科成绩筛选”功能(用 array_filter 筛选数学大于 80 分的学生)。
十一、本篇小结
这一篇你系统学习了 PHP 最强大的数据结构——数组:
- PHP 数组的本质:有序映射。同一个 array 类型同时承担”列表”和”字典”两种角色。
- 创建数组:
[]语法(推荐)、array()函数、range()创建序列、explode()从字符串创建。 - 访问和修改:方括号访问、
[]追加、unset()删除、array_values()重新索引。 - 遍历数组:
foreach ($arr as $val)遍历值、foreach ($arr as $key => $val)同时获取键和值。引用遍历后必须unset。 - 多维数组:数组的元素也是数组,用多重方括号访问。
- 常用数组函数:统计类(
count、sum、max)、查找类(in_array、array_search)、排序类(sort、asort、ksort)、过滤映射类(array_filter、array_map、array_column)、合并拆分(array_merge、array_slice)。
PHP 的数组功能之多,一篇写不完。但这篇覆盖了日常开发中 90% 的数组操作。把这些函数练熟,你处理任何表格数据、JSON 数据、数据库查询结果都会游刃有余。
下一篇预告
下一篇——《PHP 函数——封装可复用的代码》:定义和调用函数、参数传递(传值和传引用)、默认参数、可变参数、返回值、匿名函数和闭包、变量作用域、常用内置函数一览。
PHP 零基础入门,每周更新。














暂无评论内容