一、array_pop()做了什么
PHP里要从数组末尾拿掉一个元素,array_pop()是常用手段。它把之后一个元素从数组里删掉,同时把这个被删的值返回给你。
函数原型:
mixed array_pop(array &$array);
参数是引用传递,必须传变量。返回类型mixed,可能返回数组元素的值,也可能返回NULL。
注意参数类型要求:如果传进去的不是数组,PHP8会抛TypeError,PHP7及以前返回NULL并触发警告。文档里说“空数组或非数组返回NULL”,那是旧版本的行为描述,新版本要按类型错误处理。
看个基础例子:
$flag = array("saffron", "white", "green");
array_pop($flag);
print_r($flag);
输出:
Array
(
[0] => saffron
[1] => white
)
green被删掉了,原数组就地改变。array_pop()不返回新数组,它直接操作原数组,返回值是被删的那个元素。
二、返回值要接住,别丢了
array_pop()的返回值就是这个函数存在的另一个意义——出栈。如果你只是删元素,unset()也能做。array_pop()的价值在于“删的同时把值给你”。
$lang = array('php', 'java', 'python', '.net');
$last = array_pop($lang);
echo $last; // .net
print_r($lang);
输出:
Array
(
[0] => php
[1] => java
[2] => python
)
$last拿到了.net,原数组剩三个。
这里有个判断:如果数组末尾元素的值本身就是NULL,返回NULL就分不清是“删了一个NULL”还是“数组空了”。这种情况要看数组元素个数,或者用array_key_last()先确认。
$arr = array("code1", null);
$val = array_pop($arr);
var_dump($val); // NULL
echo count($arr); // 1
元素数从2变成1,说明确实弹出了一个NULL。只盯返回值会误判。
三、空数组调用会怎样
对一个空数组调array_pop(),返回NULL,数组还是空的,不报错。
$empty = array();
$result = array_pop($empty);
var_dump($result); // NULL
print_r($empty); // Array ( )
这个行为让array_pop()在循环里用起来方便,不用先判断数组是否为空:
while (($item = array_pop($stack)) !== null) {
// 处理 $item
}
不过上面这个写法有隐患:如果栈里存了值为NULL的元素,循环会提前结束。更稳的写法是用!empty($stack)或count($stack)>0作为循环条件。
while (!empty($stack)) {
$item = array_pop($stack);
// 处理
}
我在处理任务队列时踩过这个坑。队列里允许存NULL表示“空任务占位”,结果用返回值判断循环结束,遇到NULL就直接停了,后面的任务全没处理。后来改成用count()判断,问题解决。
四、关联数组和数字键的处理
array_pop()删的是“之后一个元素”,不关心键名是什么。
$sports = array(1 => "1983", 2 => "2007", 3 => "2011");
array_pop($sports);
print_r($sports);
输出:
Array
(
[1] => 1983
[2] => 2007
)
键3对应的"2011"被删掉了,剩下键1和键2。原键名保留,不重新编号。
对字符串键的关联数组也一样:
$bank = array("a" => "RBI", "b" => "SBI", "c" => "ICICI");
array_pop($bank);
print_r($bank);
输出:
Array
(
[a] => RBI
[b] => SBI
)
删的是之后插入的那个元素,跟键是数字还是字符串无关。PHP数组内部维护着元素顺序,array_pop()就是拿掉顺序上的之后一个。
这里有个容易忽略的点:如果对数组做过unset()再array_pop(),结果可能和你以为的不一样。因为unset()只是删掉某个键,不改变其余元素的顺序。比如:
$arr = array("code1", "code2", "code3");
unset($arr[1]);
array_pop($arr);
print_r($arr);
输出是[0]=>code1。code3被弹出,code2早被unset了。数组的内部顺序是code1、code3,弹掉之后一个就是code3。
五、和array_shift()、unset()的分工
array_shift()删的是第一个元素,返回它,并把所有数字键重新编号从0开始。array_pop()删末尾,不重排数字键。这两个是数组两端的对称操作,常用来实现队列和栈。
$stack = array("code1", "code2", "code3");
array_pop($stack); // 栈:后进先出
array_shift($stack); // 队列:先进先出
unset()删指定键的元素,返回值是void,拿不到被删的值。如果你只需要删、不需要那个值,unset()更直接。
$arr = array("code1", "code2", "code3");
unset($arr[2]); // 删掉 code3,无返回值
array_slice()取子数组,返回新数组,原数组不变。它不修改原数组,这是和array_pop()的根本区别。
选择逻辑:要末尾的值且要删,用array_pop();要开头的值且要删,用array_shift();只删不要值,用unset();要一段但不动原数组,用array_slice()。
六、本节课程知识要点
-
array_pop()删除数组之后一个元素并返回该值,原数组就地改变。
-
参数是引用传递,必须传变量。PHP8起传非数组会抛TypeError。
-
空数组调用返回NULL,不报错,数组保持为空。
-
返回NULL有歧义:元素值本身是NULL和数组为空都会返回NULL,判断时用count()或empty()辅助。
-
关联数组和数字键都按内部顺序删末尾,原键名保留,数字键不重新编号。
-
和array_shift()对称:一个删尾一个删头,array_shift()会重排数字键,array_pop()不会。
-
只需要删除不需要返回值时,unset()更直接;需要保留原数组时用array_slice()。
七、项目里的一些用法和观察
栈结构。array_pop()和array_push()是PHP里实现栈的现成组合。表达式求值、括号匹配、深度优先遍历的非递归写法,都常用到。
$stack = array();
array_push($stack, "code1");
array_push($stack, "code2");
$top = array_pop($stack); // code2
递归改迭代。树形结构遍历写成递归容易栈溢出,用数组当显式栈,array_pop()取节点,array_push()压子节点,代码可控。
处理路径。拆分URL路径后,逐段弹出末尾,常用于面包屑或逐级回退:
$path = explode("/", "code/learn/php/basic");
while (!empty($path)) {
$segment = array_pop($path);
// 逐级处理
}
别在foreach里对同一数组array_pop()。foreach遍历的是数组副本(PHP7+),你在循环里pop原数组,foreach不受影响,但逻辑会变得难懂。要边遍历边删,用while+array_pop()更清楚。
性能上,array_pop()比array_shift()快。array_shift()删头之后要重排所有数字键,元素多时开销明显。如果业务不要求先进先出,用array_pop()处理末尾更省。这个差异在几千个元素的循环里能感觉出来。
一个主观建议:把array_pop()当成“取值+删除”的组合操作,而不是单纯的删除函数。用之前先想清楚返回值要不要接、接了之后怎么判断NULL。如果只是清理数组,unset()或array_slice()往往更合适。
array_pop()的行为简单,但返回值的NULL歧义和引用传参这两个点,决定了它在代码里需要配合判断使用。删末尾不重排键,这是它和array_shift()的关键差异。栈式操作、逐级回退、递归改迭代,是它比较自然的用武之地。记住:它是“弹出”,不是“删除”;值被拿走了,原数组短了一个。