一、prev()做的是反方向操作
next()把数组内部指针往前推一格,prev()反过来,把指针往回拉一格,然后返回拉过去之后那个元素的值。
函数原型:
mixed prev(array &$array);
参数是引用传递,必须传变量。返回类型mixed,有前一个元素就返回它的值,没有就返回false。
数组刚创建时,内部指针默认停在第一个元素。这时候调prev(),没有前一个元素,返回false,指针不会跑到数组外面去,还停在第一个元素。
看个基础对照:
$language = array("Java", "php", "python", "javascript");
echo next($language), "\n"; // php
echo prev($language), "\n"; // Java
next()先把指针从Java移到php并返回php。prev()再把指针拉回Java并返回Java。一进一退,回到原点。
二、prev()和next()的对称性
把current()、next()、prev()放一起,指针的移动轨迹就清楚了。
$language = array("Java", "php", "python", "javascript");
echo current($language), "\n"; // Java
echo next($language), "\n"; // php
echo prev($language), "\n"; // Java
echo current($language), "\n"; // Java
current()只读不动,停在Java。next()移到php并返回。prev()移回Java并返回。再current(),指针还在Java,所以还是Java。
再看连续next()之后prev()的表现:
$ocean = array("indian", "arctic", "antarctic", "pacific");
echo next($ocean), "\n"; // arctic
echo next($ocean), "\n"; // antarctic
echo prev($ocean), "\n"; // arctic
指针从indian走到arctic,再到antarctic,prev()拉回arctic。prev()和next()的步长都是1,方向相反,来回调用会在相邻两个元素之间摆动。
echo next($ocean), "\n"; // arctic
echo prev($ocean), "\n"; // indian
echo next($ocean), "\n"; // arctic
echo current($ocean), "\n"; // arctic
之后current()读的是arctic,因为指针停在那里没动。
这组函数的关系:
| 函数 | 指针动作 | 返回值 |
|---|---|---|
| current() | 不动 | 当前元素值 |
| next() | 前移一位 | 移动后的值 |
| prev() | 后移一位 | 移动后的值 |
| reset() | 移到开头 | 第一个元素值 |
| end() | 移到末尾 | 之后一个元素值 |
prev()是这张表里唯一往回走的。它的存在让手动遍历有了“回退”的能力。
三、边界行为:两端会卡住
指针在第一个元素时调prev(),返回false,指针留在第一个元素。指针在之后一个元素时调next(),返回false,指针留在之后一个元素。这个设计意味着指针不会越界,多次调用prev()不会出错。
$arr = array("code1", "code2");
var_dump(prev($arr)); // false,指针还在 code1
var_dump(prev($arr)); // false,还是 code1
echo current($arr); // code1
prev()在开头卡住这件事,有个实际影响:如果你在循环里用prev()回退,回退到开头之后再回退就没有效果了,指针不会跑到负数位置。
用key()判断边界比看返回值可靠:
$arr = array("code1", "code2", "code3");
reset($arr);
while (key($arr) !== null) {
$val = current($arr);
// 处理
next($arr);
}
key()在指针越界后返回null,而prev()返回false的情况要复杂一些——元素值是falsy时也返回false。两者的判断语义不同,混用容易出错。
四、prev()返回false的歧义
和next()一样,prev()返回false有两种可能:指针没有前一个元素,或者前一个元素的值本身就是false、0、""、null。
$data = array("code1", false, "code3");
next($data); // 指针到 false
var_dump(prev($data)); // string(5) "code1"
这里prev()返回code1,没问题。但如果数组是:
$data = array(false, "code2", "code3");
next($data);
var_dump(prev($data)); // bool(false)
prev()返回false,但指针确实移到了第一个元素,只是那个元素的值是false。判断“是否真的没有前一个元素”不能只看返回值。
我在处理配置行解析时碰到过这个情况。配置数组里有一项是布尔false,表示某开关关闭。遍历时用prev()回退去读前一项,结果读到false就以为到头了,逻辑直接跳过。后来改成用key()判断指针位置,问题解决。
$config = array("debug" => false, "timeout" => 30, "retry" => 3);
end($config);
prev($config);
var_dump(key($config)); // "debug",指针确实在第一个元素
key()能明确告诉你指针在哪,比返回值靠谱。
五、项目里prev()的实际用法
解析带上下文的行。我做过一个读取自定义指令文件的小工具,每条指令可能引用上一行的变量。解析到某一行需要回看前一行时,prev()比较直接:
$lines = array("code1", "code2", "code3");
reset($lines);
while (($line = current($lines)) !== false) {
if (strpos($line, "code3") === 0) {
prev($lines);
$prevLine = current($lines); // 回看上一行
}
next($lines);
}
这里有个细节:prev()之后current()读的是回退后的位置,处理完还要next()把指针推回原位,否则下一轮循环会重复处理。
从末尾往前找。用end()把指针移到末尾,再反复prev()往前遍历。适合从后往前找第一个满足条件的元素:
$records = array("code1" => 10, "code2" => 20, "code3" => 30);
end($records);
while (key($records) !== null) {
if (current($records) > 15) {
echo key($records); // code3
break;
}
prev($records);
}
回退一步重新判断。表达式解析里,读到某个标记时可能需要回退到上一个token重新组合。prev()配合current()可以做这种“读一步、退一步”的控制。
一个踩坑记录。有次写遍历,逻辑是“遇到A就回退一步,把前一项和当前项合并处理”。代码里写的是while(($val=current($arr))!==false),数组里有一项值是"0","0"!==false成立,所以没出问题。但如果那一项是空字符串或布尔false,循环就会提前跳出。回退逻辑本身没错,错在把返回值当成“是否还有元素”的判断依据。后来统一改成key()判断,代码稳了。
不推荐用prev()做复杂遍历。内部指针是隐式状态,跨函数传递时容易被意外改动。如果遍历逻辑超过“读、判断、前进”三步,用索引变量或ArrayIterator更清楚。prev()适合局部、短距离的回退,不适合当遍历的主力。
六、和ArrayIterator的对比
PHP的SPL提供了ArrayIterator,它把指针状态封装在对象里,不会污染原数组的内部指针。
$arr = array("code1", "code2", "code3");
$it = new ArrayIterator($arr);
$it->next();
$it->prev();
echo $it->current(); // code1
ArrayIterator的current()、next()、prev()、key()和数组函数语义类似,但状态独立。同一个数组可以有多个迭代器同时遍历,互不干扰。数组内部指针做不到这一点——一个数组只有一个指针。
如果项目里需要多个游标、或者遍历状态要作为对象传递,ArrayIterator比prev()更合适。如果只是简单回退,prev()够用。
prev()的逻辑不复杂:指针回退一位,返回新位置的值,到头返回false。真正需要留意的是返回值false的歧义和指针在两端卡住的行为。判断是否还有前一个元素,用key()或指针位置,别只看返回值。实际项目里prev()多用于局部回退和从后往前找,不适合当遍历主力。内部指针是隐式状态,用得越少,代码越不容易出意外。