一、pos()和current()是一回事
PHP手册里对pos()的说明很短:它是current()的别名。意思就是,这两个函数功能一样,参数一样,返回值一样,连底层实现都是同一个。
$news = array("aajtak", "zeenews", "ndtv", "CNBC");
echo pos($news); // aajtak
echo current($news); // aajtak
数组创建后内部指针停在第一个元素上,所以pos()和current()都返回"aajtak"。它们都不移动指针,只是读一下当前值。
函数签名:
mixed pos(array $array);
mixed current(array $array);
pos()的参数不是引用传递,传数组变量即可。返回值类型mixed,可能返回元素值,也可能返回false。
既然两者相同,为什么还要有pos()?历史原因。PHP早期从其他语言借鉴函数命名时,pos()是"position"的缩写,语义上更贴近“指针当前位置”。后来统一用current()作为主名,pos()保留为别名,兼容老代码。
二、返回false的两种情况
pos()返回false有两种可能:
-
指针指向的元素值本身就是false、0、""、null这类falsy值
-
指针已经越界,没有当前元素
$data = array(false, "code2", "code3");
var_dump(pos($data)); // bool(false)
这里指针在第一个元素,值是false,pos()返回false。但它并不是“没有当前元素”。
如果指针越界:
$arr = array("code1");
next($arr); // 指针移出数组
var_dump(pos($arr)); // bool(false)
指针在末尾之后再调next(),指针停在之后一个元素后面,pos()返回false。这时候用key()判断更清楚:
if (key($arr) === null) {
// 指针越界
}
key()在指针越界后返回null,这是区分“值就是false”和“没有元素”的可靠方式。我早期写遍历逻辑时用pos($arr)!==false判断是否还有元素,结果数组里一出现falsy值就提前退出,排查了好一阵才反应过来。后来改成看key(),问题消失。
三、pos()和next()、prev()的配合
单独用pos()看不出什么,把它放进指针函数组里才能理解它的位置。
$sports = array("archery", "badminton", "boxing", "baseball");
echo pos($sports), "\n"; // archery
echo next($sports), "\n"; // badminton
echo pos($sports), "\n"; // badminton
第一次pos()读第一个元素archery。next()把指针移到第二个元素并返回badminton。再调pos(),指针还在第二个元素上,所以还是badminton。
pos()不移动指针,这是它和next()、prev()的核心区别。next()和prev()移动指针并返回新位置的值;pos()和current()只读不动。
再看prev()加入:
echo pos($sports), "\n"; // archery
echo next($sports), "\n"; // badminton
echo pos($sports), "\n"; // badminton
echo prev($sports), "\n"; // archery
echo next($sports), "\n"; // badminton
prev()把指针退回第一个元素并返回archery,pos()如果这时调用也会返回archery。再next()又回到badminton。
这组函数的关系可以这样记:
| 函数 | 是否移动指针 | 返回什么 |
|---|---|---|
| pos()/current() | 否 | 当前元素值 |
| key() | 否 | 当前元素键 |
| next() | 前移一位 | 移动后的元素值 |
| prev() | 后移一位 | 移动后的元素值 |
| reset() | 移到开头 | 第一个元素值 |
| end() | 移到末尾 | 之后一个元素值 |
pos()在这个表里是“只读”的那一类。遍历时通常是current()读值、key()读键、next()推进,三者配合。
四、为什么开发项目里建议用current()
pos()能用,但我在代码审查时看到pos()会多问一句:这是有意选的,还是从老代码里带过来的?
原因有几个:
第一,current()是主名。PHP手册、社区文章、框架源码里,current()的出现频率远高于pos()。团队协作时,current()的可读性更好,别人不需要查“pos是不是current的别名”。
第二,命名语义更清晰。current对应“当前元素”,和key()、next()、prev()、reset()、end()这组命名是统一的。pos是position的缩写,单独看不够直观。
第三,静态分析工具和IDE对current()的支持更完整。一些代码检查工具会提示pos()是别名,建议替换。虽然不是错误,但统一用主名能减少这类提示。
如果你的项目有编码规范,通常会在“废弃别名”那一节列出pos()、sizeof()、chop()这类函数,建议用主名替代。pos()不算废弃,PHP8里还能用,但新代码没有理由选它。
五、项目场景实例
场景一:读取配置数组的第一项作为默认值。
$config = array("timeout" => 30, "retry" => 3);
reset($config);
$first = current($config); // 30
这里用current()比pos()更符合团队习惯。reset()确保指针在开头,current()读值。
场景二:在手动遍历中读取当前项。
$tokens = array("code1", "code2", "code3");
reset($tokens);
while (key($tokens) !== null) {
$token = current($tokens);
// 处理 $token
next($tokens);
}
这种“读值、处理、推进”的模式里,current()是标准写法。pos()能达到同样效果,但混在current()/next()/key()中间显得不协调。
场景三:解析时回退一步重读。
$lines = array("code1", "code2", "code3");
reset($lines);
while (($line = current($lines)) !== false) {
if (strpos($line, "code2") === 0) {
prev($lines); // 回退
$line = current($lines); // 重读
}
next($lines);
}
current()和prev()搭配,读取回退后的值。pos()在这个位置也能用,但和prev()放一起时,current()的命名更对仗。
一个踩坑记录。有次接手老项目,代码里大量用pos()读数组当前值。有个遍历循环写的是while(pos($arr)!==false),而数组里恰好有一个元素值是"0"。"0"!==false成立,所以没出错,但如果元素值是布尔false或空字符串,循环就会提前结束。当时我把它统一改成key($arr)!==null判断,顺带把pos()换成了current()。改完之后逻辑清晰很多,也顺手消除了falsy值带来的隐患。
六、本节课程知识要点
-
pos()是current()的别名,功能、参数、返回值一致,PHP8仍然可用。
-
pos()读取内部指针当前指向的元素值,不移动指针。
-
返回false有两种情况:元素值是falsy,或指针越界。判断是否越界用key()===null。
-
和next()、prev()的分工:pos()/current()只读不动,next()/prev()移动指针并返回新值。
-
开发项目建议用current(),因为它是主名,命名统一,工具支持更好,团队协作可读性更高。
-
遍历标准组合是reset()+current()+key()+next(),pos()在这个组合里没有额外优势。
-
不要用pos()返回值判断循环结束,falsy值会导致提前退出,用key()或count()更可靠。
七、小结
pos()本身没有复杂逻辑,它的存在是PHP历史命名的产物。理解它等于current(),就理解了它的全部行为。真正需要留意的是返回值false的歧义,以及遍历时该用哪个函数判断边界。新代码里写current(),老代码里看到pos()知道它是什么,够用了。内部指针这套东西在现在PHP里用得越来越少,foreach覆盖了大多数遍历需求,但解析token、手动回退、逐级处理这类场景,current()/next()/prev()这组函数还是绕不开。