尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PHP range() 函数

PHP range() 函数 前言range()用来生成一个连续序列的数组是 PHP 里少数几个「天生就返回数组」的函数之一。正因为它看着简单很多人在用它的时候带着三个不准确的印象第一以为它只能生成数字。它同样能生成字符序列range(a, z)是常见的写法。但要注意两个边界都是字符串时生成的是字节序列——range(A, z)会顺带把[ \ ] ^ _这几个符号也包进来因为它们正好落在A和z的 ASCII 区间之间。第二以为负的步长能自动得到降序。PHP 8.3.0 起如果区间是递增的起点小于等于终点却给了负步长会直接抛ValueError。而在 PHP 8.2 及以前range()会去「猜」方向结果常常与预期相反。第三以为它像别的语言那样是左闭右开区间。PHP 的range()是闭区间结果里包含终点。本文按官方手册讲清range()的类型推导规则、PHP 8.3 引入的错误与警告、字符序列的坑以及大区间下的内存问题。一、签名与返回类型推导range(string|int|float $start, string|int|float $end, int|float $step 1): array返回的是数组但元素的类型取决于参数类型规则写在手册里条件结果两个边界都是字符串且$step是整数字节序列通常是 ASCII 字符$start、$end、$step中至少有一个是浮点数浮点数序列其余情况整数序列方向由边界决定$start小于等于$end时递增否则递减。步长$step只负责「每两个元素之间差多少」不负责决定方向。?php // 适用于 PHP 8.0echo implode(, , range(0, 5)), PHP_EOL; // 0, 1, 2, 3, 4, 5echo implode(, , range(5, 0)), PHP_EOL; // 5, 4, 3, 2, 1, 0echo implode(, , range(0, 100, 25)), PHP_EOL; // 0, 25, 50, 75, 100echo implode(, , range(c, a)), PHP_EOL; // c, b, a注意最后一个元素手册说明结果数组的最后一个元素要么是$end要么是它的前一个元素取决于步长。range(0, 10, 4)得到[0, 4, 8]——12超了终点所以停在8。二、PHP 8.3 引入的错误与警告PHP 8.3.0 对range()做了一次行为规范化这是使用它时必须知道的分界线情况PHP 8.3.0 起的行为$step为0抛ValueError$start、$end、$step中存在非有限值INF、-INF、NAN抛ValueError$step为负但区间是递增的$start $end抛ValueError$start或$end是空字符串发出E_WARNING空串按0处理$start或$end是多字节的非数字字符串发出E_WARNING一个边界是数字导致另一个字符串边界被隐式转成整数发出E_WARNING$step是浮点但小数部分为 0如2.0视为整数2结果数组是整数两个边界都是字符串一律生成字节序列旧版本里若一边是数字串另一边会被隐式转成整数也就是说range(1, 5, -1)在 PHP 8.2 上是能跑出结果的方向被「猜」成升序在 PHP 8.3 上会直接报错。升级到 PHP 8.3 时这类调用是最典型的兼容性炸弹应该在迁移前全局搜一遍。三、字符序列好看但危险range(a, z)很直观但它生成的其实是字节区间。ASCII 里大写字母、小写字母被若干符号隔开?php // 适用于 PHP 8.0$a range(a, z);$A range(A, Z);$all range(A, z);echo count($a), , count($A), , count($all), PHP_EOL;echo implode(, $all), PHP_EOL;26 26 58ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyzrange(A, z)得到的 58 个元素里中间的[、\、]、^、_、反引号并不是字母。需要「全部字母」时必须分两段再合并?php // 适用于 PHP 8.0$letters array_merge(range(A, Z), range(a, z));echo count($letters), PHP_EOL; // 52四、实战示例1. 用range()生成分页页码窗口?php // 适用于 PHP 8.0declare(strict_types1);/*** 生成当前页附近的页码窗口例如当前第 5 页、总 20 页、窗口 5 → 3,4,5,6,7** return listint*/function pageWindow(int $current, int $total, int $size 5): array{if ($total 1) {return [];}$half intdiv($size, 2);$start max(1, $current - $half);$end min($total, $start $size - 1);// 靠近尾页时向左补齐保证窗口宽度尽量等于 $size$start max(1, $end - $size 1);return range($start, $end); // 闭区间两端都包含}var_dump(pageWindow(1, 20)); // [1, 2, 3, 4, 5]var_dump(pageWindow(10, 20)); // [8, 9, 10, 11, 12]var_dump(pageWindow(20, 20)); // [16, 17, 18, 19, 20]2. 大区间不要用range()range()会一次性构造完整的数组。生成 1000 万个整数就要在内存里放 1000 万个元素如果还要对它们做array_map()中间还会再产生一份数组。需要惰性序列时用生成器generator?php // 适用于 PHP 8.0declare(strict_types1);/*** 与 range($start, $end, $step) 等价的惰性版本内存占用与区间长度无关。** return Generatorint, int*/function intRange(int $start, int $end, int $step 1): Generator{if ($step 0) {throw new ValueError(步长必须是正整数);}if ($start $end) {for ($i $start; $i $end; $i $step) {yield $i;}} else {for ($i $start; $i $end; $i - $step) {yield $i;}}}$sum 0;foreach (intRange(1, 1_000_000) as $n) {$sum $n;}echo $sum, PHP_EOL; // 500000500000这段代码的内存占用是恒定的无论区间多大。需要数组时再iterator_to_array()转换这样「是否真的需要全部元素」就成了一个显式决定。3. 生成时间点时注意浮点误差?php // 适用于 PHP 8.0// 用整数运算生成「每 15 分钟一个时间点」避开浮点累加误差$base strtotime(2024-01-01 00:00:00);$slots array_map(static fn (int $i): string date(Y-m-d H:i, $base $i * 900),range(0, 95) // 一天 96 个 15 分钟时段);echo $slots[0], ... , $slots[95], PHP_EOL;常见坑点❌range(1, 5, -1)想得到降序 —— PHP 8.3.0 起会抛ValueError递增区间配负步长PHP 8.2 及以前方向靠猜结果与预期相反。✅ 方向交给边界决定range(5, 1)就是降序真要用负步长写成range(5, 1, -1)。❌range(1, 10, 0)想「不按步长、逐个取」—— 旧版本里是死循环式的告警PHP 8.3.0 起抛ValueError。✅ 步长恒为正整数需要逐个取就省略第三个参数。❌ 用range(A, z)当「所有字母」—— 会带进[、\、]、^、_、反引号这 6 个非字母字符。✅array_merge(range(A, Z), range(a, z))。❌ 以为range()是左闭右开、不含终点 —— PHP 是闭区间range(1, 3)得到[1, 2, 3]。✅ 想要「从 0 开始的 N 个值」写range(0, $n - 1)用for ($i 0; $i $n; $i)时要记得这里才是开区间。❌ 用range()生成很大的区间再array_map()—— 数组先完整构造出来内存峰值随区间长度线性上升通常直接 OOM。✅ 用for循环或生成器惰性产生确实要数组时才调用iterator_to_array()。❌ 用浮点步长累加并期望精确值 ——range(0, 1, 0.1)的元素受二进制浮点表示影响末位可能不是0.1的精确倍数用来做等值比较会失败。✅ 需要精确小数时用整数运算先乘成整数再除比较用abs($a - $b) 1e-9或先round()。❌ 依赖range()的元素类型 ——range(1, 5, 2.0)在 PHP 8.3.0 起会返回整数数组浮点步长若没有小数部分会被当成整数在旧版本上可能是浮点。✅ 需要浮点结果时显式转换array_map(floatval, range(1, 5))。❌ 用range()生成数组键后忘了 PHP 的键类型规则 ——array_combine(range(1, 3), $values)得到的键是整数1, 2, 3不是字符串1,2,3PHP 数组会把纯数字字符串键自动转成整数。✅ 需要字符串键就显式转换array_map(strval, range(1, 3))或直接在键里加前缀。总结要点结论签名range(string\区间闭区间包含$end最后一个元素可能小于$end步长不整除时方向由$start与$end的大小决定不由步长的正负决定元素类型双字符串 整数步长 → 字节序列含浮点 → 浮点序列否则整数序列PHP 8.3.0 新增错误步长为 0、非有限值、递增区间配负步长均抛ValueError字符序列是按字节区间生成的range(A,z)会带进 6 个非字母字符内存一次性构造整个数组大区间应改用生成器或for循环一句话总结range()的方向由两个边界决定$step只控制间距记住它是闭区间、记住 PHP 8.3 起对非法步长不再「猜」而是抛ValueError再用生成器处理大区间它就是一个可靠的工具。
返回列表