一、为什么要单独讲array_push()
很多初学者对array_push()的印象停留在"往数组里加东西",但真正写项目时会遇到几个问题:
-
追加元素后数组的键名到底怎么变?
-
关联数组和索引数组混用时,追加行为是否一致?
-
只追加一个元素时,用array_push()和
$arr[]=$val到底该选哪个? -
array_push()的返回值是什么,能不能拿来做判断?
这些问题在手册里分散在不同位置,项目开发中一旦用错,轻则数组结构不符合预期,重则接口返回的JSON键名错乱。下面按实际使用顺序展开。
二、函数原型与参数说明
array_push(array &$array, mixed ...$values): int
| 参数 | 说明 | 是否必需 |
|---|---|---|
| array | 待操作的数组,以引用方式传入 | 必需 |
| values | 要追加的一个或多个值 | 至少一个 |
返回值:返回追加完成后数组的元素总个数,类型为int。
这里有两个细节值得注意:
第一个细节是引用传参。&$array表示函数内部直接修改原数组,不需要接收返回值再赋值。也就是说下面两种写法效果不同:
// 写法 A:原数组被修改
array_push($lang, 'laravel');
// 写法 B:原数组不变,$result 是元素个数
$result = array_push($lang, 'laravel');
写法B里$result拿到的是数字,不是数组。这一点在调试时容易看错。
第二个细节是可变参数。PHP4时代array_push()的签名只支持一次追加一个元素,后来才支持一次传多个值。所以一次追加多个元素时,不需要写多次调用。
三、键名变化规则
这是array_push()最容易被忽略的地方。它追加元素时,始终使用"下一个可用的整数键",不会去覆盖已有的字符串键,也不会去填补被unset掉的空洞。
<?php
$lang = array("a" => "php", "b" => "java");
array_push($lang, "laravel", "bluej");
print_r($lang);
输出:
Array
(
[a] => php
[b] => java
[0] => laravel
[1] => bluej
)
关联键a、b保留,新元素从整数0开始编号。这说明array_push()并不关心原数组是索引数组还是关联数组,它只认"下一个整数键"。
再看一个unset后的情况:
<?php
$car = array('tata', 'honda', 'nexon');
unset($car[2]);
array_push($car, 'city');
print_r($car);
输出:
Array
(
[0] => tata
[1] => honda
[2] => city
)
被删除的2号位置不会被跳过,新元素依然落在2号键上。原因是PHP内部维护了一个nextfreeindex,unset不会把这个指针往回退。如果项目里依赖键名做映射,这一点要特别小心。
四、与$arr[]=$val的对比
这是实际写代码时最常做的选择。两者都能往数组末尾加元素,但差异明显:
| 对比项 | array_push() | $arr[]=$val |
|---|---|---|
| 追加单个元素 | 可以 | 可以 |
| 追加多个元素 | 一次调用完成 | 需要写多行 |
| 函数调用开销 | 有 | 无 |
| 返回值 | 新长度 | 赋值表达式的值 |
| 可读性 | 明确表达"追加"语义 | 更简洁 |
个人经验:单个元素追加用$arr[]=$val,多个元素追加用array_push()。原因不是性能,而是语义清晰。前者是语言结构,后者是函数调用。在循环里逐条追加数据时,$arr[]=$row比array_push($arr,$row)少一次函数调用,在数据量大的场景下差距会体现出来。
有一个踩坑经历值得分享:曾经在一个数据导出脚本里,循环10万次用array_push()逐条追加,执行时间比改成$arr[]=多了将近一倍。后来查资料确认,array_push()每次调用都要做参数解析和引用处理,而[]是编译期直接生成opcode。数据量小看不出,量大就很明显。
五、项目实例
实例一:代码号学习编程·标签合并
<?php
$tags = ['php', 'mysql'];
$newTags = ['redis', 'nginx'];
foreach ($newTags as $tag) {
array_push($tags, $tag);
}
print_r($tags);
输出:
Array
(
[0] => php
[1] => mysql
[2] => redis
[3] => nginx
)
这个写法没问题,但如果只是合并两个数组,用array_merge()更直接。array_push()适合"逐个条件判断后再决定是否追加"的场景。
实例二:代码号学习编程·购物车追加商品
<?php
$cart = [
['id' => 101, 'name' => '机械键盘'],
];
$newItem = ['id' => 102, 'name' => '无线鼠标'];
array_push($cart, $newItem);
print_r($cart);
输出:
Array
(
[0] => Array
(
[id] => 101
[name] => 机械键盘
)
[1] => Array
(
[id] => 102
[name] => 无线鼠标
)
)
这里数组元素本身是关联数组,array_push()追加的是整个子数组,不会打散内部结构。这点和array_merge()的行为不同,后者在合并时会对外层数字键重新编号,但不会递归合并。
实例三:代码号学习编程·批量追加并获取数量
<?php
$subject = ['c', 'java'];
$count = array_push($subject, 'os', 'compiler');
echo $count; // 4
print_r($subject);
返回值4表示追加后总共4个元素。如果项目里需要判断"追加后是否达到某个阈值",可以直接用返回值,不必再调count()。
六、容易混淆的几个函数
-
array_merge():合并两个或多个数组,数字键重新编号,字符串键后者覆盖前者。适合"拼接"语义。
-
array_unshift():在数组开头插入元素,数字键全部重新编号,开销比array_push()大。
-
array_pop():从末尾弹出一个元素,与array_push()方向相反。
-
$arr[]=:语言结构,单元素追加的优选。
选择原则:末尾追加用array_push()或[];开头插入用array_unshift();数组合并用array_merge()。不要用array_push()去做合并,那样写出来的代码既长又慢。
七、本节课程知识要点
-
array_push()以引用方式修改原数组,返回新长度而非数组本身。
-
追加时使用下一个可用整数键,不覆盖字符串键,不填补unset空洞。
-
单个元素追加优先用
$arr[]=$val,多个元素追加用array_push()。 -
大数据量循环追加时,
[]的性能优势明显。 -
需要合并数组时用array_merge(),不要用array_push()循环追加。
-
关联数组追加后,新元素从0开始编号,接口输出前要确认键名结构。
八、常见问题
问:array_push()能不能追加一个数组进去?
可以,但追加的是整个数组作为一个元素。如果想要把数组里的每个值都追加进去,需要用循环或array_merge()。
问:array_push()对空数组有效吗?
有效。空数组追加后,元素从0开始编号。
问:为什么追加后键名不是从原来的较大值加一?
如果原来的键名不连续,PHP会使用内部维护的nextfreeindex,而不是"当前较大键+1"。unset操作不会回退这个指针,所以行为可能和直觉不一致。