ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PHP数组从入门到避坑:创建、遍历、排序与高阶函数实战详解

PHP数组从入门到避坑:创建、遍历、排序与高阶函数实战详解 一直觉得PHP里最被低估的语法特性就是数组。你随便打开一个PHP项目不管是老牌的ThinkPHP还是现在的Laravel十个函数里有八个的参数和返回值都跟数组脱不了关系。数据库查询结果是一组数组表单提交是一组数组配置文件也是一组数组。可以说搞懂数组PHP就入门了一半搞不透数组后面学什么框架都会觉得别扭。这篇东西不是官方文档的复读我按自己平时写代码的习惯把PHP数组从创建、遍历、排序到各种实用函数重新梳理了一遍顺手把踩过的坑也填在对应位置。不管你是刚学PHP的新手还是写了两三年代码想系统补一遍基础的老手这套内容都可以直接拿来对照着用。1. 数组的基础认知先搞明白它到底是什么1.1 PHP数组不是普通数组很多从C语言或者Java转过来的人一开始会用“固定长度的、同类型元素的集合”来理解数组这套思路放到PHP里完全行不通。PHP的数组是一个有序映射它把键和值关联起来键可以是整数也可以是字符串值可以是任何类型——整数、字符串、对象、数组本身都行。用大白话讲它既是列表又是字典是哈希表是栈是队列什么都能当。底层实现上PHP 7以上的数组使用的是zend_hash结构本质上是一张哈希表加上双向链表。哈希表负责快速按键取值双向链表负责记录插入顺序。所以PHP数组的遍历顺序就是元素被添加进去的顺序哪怕你用的是字符串键它依然按照插入先后输出。这一点在项目里非常实用比如从数据库查出来一组记录你什么都不用做顺序就是SQL里ORDER BY的顺序。这种设计带来的直接后果是PHP数组自带长度属性不需要单独维护size可以在任意位置插入元素键可以是任意标量。代价是内存占用比C数组大但PHP是脚本语言开发效率优先这个取舍完全值得。1.2 创建数组的六种姿势我知道你大概已经会用$arr array()或者$arr []但除了这两种PHP还提供了一些看起来冷门却相当省事的创建方式。最基本的// 老写法 $fruits array(apple, banana, cherry); // 短数组语法PHP 5.4以后推荐 $fruits [apple, banana, cherry]; // 指定键名 $user [ name 张三, age 28, city 北京 ];range()可以快速生成一个指定范围的数组做测试数据或者生成序号特别好用$numbers range(1, 10); // [1,2,3,4,5,6,7,8,9,10] $letters range(a, e); // [a,b,c,d,e] $steps range(0, 50, 5); // 0,5,10,15...50第三个参数是步长compact()能把变量名变成键、变量值变成值在做接口返回的时候很顺手$name 李四; $age 30; $result compact(name, age); // [name 李四, age 30]explode()把字符串拆成数组这个后面专门讲但创建数组时它出镜率也极高。比如把逗号分隔的标签变成数组$tags explode(,, php,数组,教程); // [php, 数组, 教程]从数据库查询结果创建数组就更常见了PDO的fetchAll()直接返回一个二维数组每一行是一条记录。这也是PHP数组能贯穿Web开发全流程的原因数据库返回值、中间处理、模板渲染全是数组。2. 数组的增删改查每一项都有讲究2.1 增加元素的方式不是只有$arr[]往数组里加元素新手最常用的是$arr[] $value。这种方式会把值追加到数组末尾键自动用下一个最大整数。但还有几个函数干的活类似细节各不一样。$arr [1, 2, 3]; // 追加到末尾 $arr[] 4; // [1,2,3,4] // array_push 等效于上面但多一个函数调用开销 array_push($arr, 5, 6); // [1,2,3,4,5,6] // array_unshift 插入到开头注意索引会重新编号 array_unshift($arr, 0); // [0,1,2,3,4,5,6] // 指定字符串键直接赋值即可 $user[age] 30;一个很容易踩的坑是$arr[] $value和array_push()是追加array_unshift()是头部插入后者的时间复杂度是O(n)因为所有已有元素都要往后挪。数据量小无所谓但如果你在一个循环里反复对一个大数组做array_unshift()性能会肉眼可见地变差。遇到这种情况先array_push()再array_reverse()或者用SplDoublyLinkedList都比硬着头皮往前插要好。往关联数组里增加值就很简单直接$arr[key] $value键不存在就是新增键存在就是修改。2.2 删除元素unset的坑和array_filter的妙用删除数组元素最容易翻车的是unset()。因为unset()不会重新整理索引数字键会留下空洞$arr [10, 20, 30]; unset($arr[1]); print_r($arr); // Array // ( // [0] 10 // [2] 30 // )如果你后面用for ($i 0; $i count($arr); $i)去遍历$arr[1]就是未定义索引会报警告甚至返回null。这时候用array_values()重建索引$arr array_values($arr); // [10, 30]删除末尾元素用array_pop()删除开头元素用array_shift()这俩会返回被删掉的值有时候顺手就拿来用了$stack [a, b, c]; $last array_pop($stack); // $last c$stack [a,b] $first array_shift($stack); // $first a$stack [b]还有一种批量删除的场景——按条件过滤元素。最土的办法是foreach里面unset其实array_filter()更优雅$numbers [1, 2, 3, 4, 5, 6]; // 保留偶数 $evens array_filter($numbers, function($n) { return $n % 2 0; });注意array_filter()默认会保留原来的键名如果不想保留记得外面套一层array_values()。2.3 遍历的几种姿势和引用陷阱PHP遍历数组foreach是绝对的主流但每种写法适用场景不一样。$users [ [id 1, name 张三], [id 2, name 李四], ]; // 只取值 foreach ($users as $user) { echo $user[name]; } // 取键和值 foreach ($users as $id $user) { echo $id . : . $user[name]; }用for遍历数字索引数组也行但前提是索引必须连续所以上面说的unset()之后用array_values()很重要for ($i 0; $i count($arr); $i) { // 每次循环都调用count()数据量大时建议先存到变量 }array_walk()可以让你不用foreach就把回调函数作用到每个元素上适合处理一些需要同时修改键和值的场景$arr [1, 2, 3]; array_walk($arr, function($value, $key) { $value $value * 2; });这里必须重点说一个foreach引用遍历的经典坑。很多人想在foreach里修改原数组会这么写$arr [1, 2, 3]; foreach ($arr as $value) { $value $value * 2; }这样写确实能改原数组但隐患巨大循环结束后$value仍然是数组最后一个元素的引用。如果你后续代码里不小心复用了$value这个变量就会意外修改到原数组的最后一个元素而且这种bug非常隐蔽排查起来极其痛苦。正确做法是循环结束后立刻unset($value)foreach ($arr as $value) { $value $value * 2; } unset($value); // 断开引用一定要做我真实项目里就因为这个在排查了一个多小时最后发现是之前某段foreach的$value引用没unset导致后面完全无关的逻辑把数组最后一个值悄悄改了。这个习惯一定要养成。3. 数组去重与元素查找千锤百炼的实用技能3.1 array_unique去重的几个层级热词里“数组去重”出现频率很高这是实际开发里最常见的需求之一。最简单的是array_unique()对一维数组去重$arr [1, 2, 2, 3, 3, 3]; $result array_unique($arr); // [1, 2, 3]注意保持了键名这个函数有几个特点需要注意。第一它判断相等的方式是(string) $elem1 (string) $elem2也就是说1和1会被当成同一个值。第二去重后保留的是第一个出现的键名所以键名可能不连续。第三它做的是字符串比较之后的值比较对浮点数等场景偶尔会有意外。如果要去重的是一组关联数组二维数组去重array_unique()就不好使了因为数组之间比较的是元素的值和顺序不是对象标识。常见的做法是用serialize()或者json_encode()把每个子数组变成字符串再去重$users [ [id 1, name 张三], [id 2, name 李四], [id 1, name 张三], // 重复 ]; $unique array_unique(array_map(serialize, $users)); $unique array_map(unserialize, $unique);这个方法实测很稳原理是先把子数组序列化成字符串去重完成后再反序列化恢复成数组。要注意键的顺序会影响serialize的结果所以如果你希望“内容相同但顺序不同”的也算重复得先对子数组做ksort()统一键顺序。3.2 查找元素别总是in_array有时候array_flip更快in_array($needle, $haystack)和array_key_exists($key, $arr)是最常见的两个查找函数但这两个函数的行为差别很大。in_array检查值array_key_exists检查键。很多人会把它们搞混导致判断失效。$user [name 张三, age 28]; if (array_key_exists(name, $user)) { echo 有这个键; } if (in_array(张三, $user)) { echo 有这个值; }再看一个性能点。in_array()在非严格模式下是弱比较而且它是线性查找数组一大就慢。如果你要频繁判断某个值是否存在而且不关心键名可以先用array_flip()把值和键互换再用isset()判断$arr [apple, banana, cherry]; $flipped array_flip($arr); // 判断banana是否存在 if (isset($flipped[banana])) { echo 存在; }isset()是哈希查找比in_array()线性遍历快得多。这个技巧在数据量大、判断次数多的时候效果极其明显。另外要注意区分isset($arr[$key])和array_key_exists($key, $arr)。如果键存在但值是nullisset()返回falsearray_key_exists()返回true。这也是一个经典踩坑点。比如从数据库取了一条记录某个字段值是NULL你用isset($row[field])去判断就判断不出来必须用array_key_exists()或者!empty()配合array_key_exists()。3.3 array_count_values统计出现次数统计数组里每个值出现了多少次array_count_values()一句搞定$words [php, 数组, php, 教程, 数组, php]; $counts array_count_values($words); // [php 3, 数组 2, 教程 1]返回数组的键是原数组的值值是出现次数。这个函数在日志分析、投票统计、词频统计场景里特别常用。要注意它只对字符串和整数有效如果是数组或对象作值会报警告。4. 数组排序sort函数族与usort自定义排序4.1 内置排序函数怎么选PHP的排序函数非常多光是原生就有一大排sort、rsort、asort、arsort、ksort、krsort、natsort、natcasesort、shuffle以及对应的usort、uasort、uksort。记不住很正常我平时也就记几个核心的其他用到了再查文档。核心规则其实就一条看你想按值排还是按键排。sort系列按值排ksort系列按键排。看你想保不保留键名。带a的asort/arsort保留键名不带a的sort/rsort排完直接重新编号。我列个表方便你对照函数排序方向是否保留键名适用场景sort()升序否索引数组重新排序rsort()降序否索引数组降序asort()升序是关联数组按值排序且保留键arsort()降序是关联数组按值倒序且保留键ksort()按键升序是按键排序的关联数组krsort()按键降序是按键倒序的关联数组natsort()自然排序是文件名、版本号等混合字符串shuffle()随机打乱否洗牌、随机展示看一个实际场景。你有一个按城市名做键、人口数做值的数组要按人口从高到低输出用arsort()最合适$population [ 北京 2189, 上海 2487, 广州 1530, ]; arsort($population); // 键值对应关系不变顺序变成上海、北京、广州4.2 自定义排序的usort三兄弟内置排序处理不了复杂规则比如按对象属性排、按字符串长度排、按二维数组某个字段排这时候用usort()。$users [ [name 张三, age 30], [name 李四, age 25], [name 王五, age 35], ]; usort($users, function($a, $b) { return $a[age] $b[age]; }); // 按age升序这里用到了PHP 7引入的太空船运算符它返回-1、0、1三种值正好符合排序回调的要求。如果你写的是老代码得手动判断usort($users, function($a, $b) { if ($a[age] $b[age]) { return 0; } return ($a[age] $b[age]) ? -1 : 1; });usort()排序后索引会被重新编号如果你操作的是关联数组并且想保留键名用uasort()如果想按键名自定义排序用uksort()。这三兄弟的区别就是前面表格里那套规则。排序回调的返回值必须是负整数、零、正整数PHP在比较的时候要求逻辑一致如果$a排在$b前面返回负数相等返回0$a排在$b后面返回正数。写反了排序结果就反了不报错但结果全部错这个只能靠测试数据来验证。4.3 排序的经典坑sort()会重排索引sort()和rsort()排序后会把键名丢掉重新用0、1、2…编号。这对索引数组来说没问题但如果你对一个关联数组用了sort()键就直接没了$scores [张三 90, 李四 85, 王五 95]; sort($scores); print_r($scores); // Array // ( // [0] 85 // [1] 90 // [2] 95 // )人名全丢了。所以处理关联数组一定要用asort()或arsort()。另外一个比较隐蔽的坑是natsort()对中文不友好中文按拼音排序不能依赖它得用collator扩展或者转拼音后再排。还有一个日常很容易遇到的需求就是给二维数组按某一列排序。PHP有一个专门为此设计的函数叫array_multisort()但它语法有点绕要配合array_column()一起用$users [ [name 张三, age 30], [name 李四, age 25], [name 王五, age 35], ]; $ages array_column($users, age); array_multisort($ages, SORT_ASC, $users); // 这样 $users 就按 age 升序排列了原理是先取出所有age组成一个独立数组用它作为排序基准再同步调整原数组顺序。如果你要按两个字段排序比如先按age再按name就多取一列多传一次。这个方法实测很好用比在usort里写复杂嵌套更简洁。5. 数组与字符串互转一套动作搞定格式化输出5.1 implode和explode的日常数组转字符串最常用的是implode()它的作用就是把数组元素用指定字符串连接起来$tags [PHP, 数组, 教程]; $str implode(,, $tags); // PHP,数组,教程写SQL查询的时候经常用到这个。比如你要查一组ID的数据但又不能用参数绑定一次传数组就先拼成IN子句$ids [1, 3, 5, 7]; $ph implode(,, array_fill(0, count($ids), ?)); // ?,?,?,? // 或者直接用逗号拼接 $idStr implode(,, $ids); // 1,3,5,7反向操作是explode()把字符串按分隔符拆成数组$str PHP,数组,教程; $arr explode(,, $str); // [PHP, 数组, 教程]explode()第三个参数可以限制返回数组长度explode(,, $str, 2)只返回前2个元素第二个元素里包含剩余全部内容。处理“键值对字符串”时这个功能很有用比如解析name张三age28这种格式。5.2 json_encode时中文被转义怎么办数组转JSON是接口开发里躲不开的一步。json_encode($arr)默认会把中文转成\uXXXX的Unicode转义形式比如$arr [name 张三]; echo json_encode($arr); // {name:\u5f20\u4e09}保存到数据库或者返回给前端其实都能用浏览器会自动解析但如果你想把JSON直接存文件、拼进日志、或者给别人看满屏\u就非常难受。解决办法是在第二个参数加JSON_UNESCAPED_UNICODEecho json_encode($arr, JSON_UNESCAPED_UNICODE); // {name:张三}PHP 5.4之后才支持这个常量所以老项目里看到中文全变\u也是版本限制。还有一个经常一起用的常量是JSON_PRETTY_PRINT输出格式化后的JSON调试接口时用特别好。json_decode()反向解析时要注意第二个参数。默认返回对象传true返回数组$json {name:张三,age:28}; $obj json_decode($json); // 对象 $arr json_decode($json, true); // 数组实际开发中建议用true转成数组因为数组的操作函数更多、更顺手而且不会出现对象属性访问时$obj-name和$obj[name]混用的问题。还有一个细节json_decode()解析失败返回null所以拿到结果后先判断是不是null不要直接相信解析结果。5.3 序列化中文和serialize/unserialize热词里提到“php序列化中文”这块确实有人会困惑。serialize()和json_encode()都能把数组变成字符串但原理完全不同。serialize()是PHP内部专用的序列化格式生成的字符串里英文和数字是明文中文会原样保留不转义$arr [name 张三]; echo serialize($arr); // a:1:{s:4:name;s:6:张三;}所以很多人以为“序列化中文会乱码”其实不会。真正容易出现“乱码”感觉的是urlencode()或者HTTP传输过程中编码不一致导致的问题不是serialize()本身。但serialize()格式是PHP私有的跨语言时不能用来交换数据跨系统传递数组建议统一用JSON。存储到Session或者缓存里用serialize()没问题但要注意一个问题序列化后的字符串里包含了键值对的字节长度和类型信息如果数组内容被更改过结构再去反序列化容易产生陈旧数据。缓存这些序列化数据时建议加上版本字段或者依赖数组的hash值做校验。6. 二维数组与高阶函数让你写代码短一半6.1 二维数组的核心操作热词“二维数组”也是搜索高频词。PHP的二维数组其实就是“数组的数组”每一行本身是一个数组。从数据库查出来的结果集、Excel导入的数据、爬虫抓下来的数据基本全是二维数组。创建一个二维数组$products [ [id 1, name 手机, price 2999], [id 2, name 电脑, price 5999], [id 3, name 耳机, price 399], ];遍历二维数组基本就是双层foreachforeach ($products as $product) { foreach ($product as $key $value) { echo $key: $value . PHP_EOL; } }但实际开发里你往往不需要全部字段只想取某一列的多个值。这个需求用array_column()一行解决$names array_column($products, name); // [手机, 电脑, 耳机]array_column()还有第三个参数可以指定用哪个字段做返回数组的键$result array_column($products, name, id); // [1 手机, 2 电脑, 3 耳机]这个函数在整理接口返回数据时极其好用。比如你有一个订单列表想按订单号快速查找对应的客户名用array_column($orders, customer, order_id)生成一个以订单号为键的数组后面直接$map[$orderId]取值比每次都遍历整个数组快得多这也是典型的空间换时间思路。6.2 数组指针current、next、reset的正确打开方式热词里有个“指针数组”虽然说的是C语言但PHP数组内部确实有指针的概念。每个数组都有一个内部指针默认指向第一个元素。相关函数有current()、next()、prev()、reset()、end()、key()等$arr [a, b, c]; reset($arr); // 指针移回开头 echo current($arr); // a当前元素 echo next($arr); // b同时指针后移 echo next($arr); // c echo prev($arr); // b指针回退 echo end($arr); // c指针移到末尾这套操作在很多老代码里用来遍历数组for (reset($arr); $key key($arr); next($arr)) { // 处理 $arr[$key] }但说实话foreach已经完全可以替代这种写法而且在PHP 7以后foreach是直接在内部哈希表上遍历性能更好也更安全。数组指针函数最大的用途是处理“当前指向哪个元素”的状态场景比如分页游标、逐行读取大数组然后断点续传。不过如果你发现自己在写比较复杂的指针操作通常意味着应该换用迭代器或者Generator了。PHP 8里each()函数已经被移除它以前用来返回当前键值对并且移动指针。如果你在维护老代码看到while (list($key, $value) each($arr))这类写法就要改成foreach。6.3 array_map、array_filter、array_reduce三件套如果说PHP数组函数是工具箱那array_map、array_filter、array_reduce这三兄弟就是最常用的三把主力工具。它们的思想都是“不要自己写循环”而是把对数组的操作抽象成处理逻辑。array_map把回调函数作用在每个元素上返回新数组原数组不变$numbers [1, 2, 3, 4]; $squares array_map(function($n) { return $n * $n; }, $numbers); // [1, 4, 9, 16]它还能同时接收多个数组回调函数会依次取每个数组的同位置元素。处理两个数组对应元素相加、合并等操作很方便$names [张三, 李四]; $ages [28, 30]; $users array_map(function($name, $age) { return [name $name, age $age]; }, $names, $ages); // 两个数组按位置配对array_filter按照回调过滤数组保留返回true的元素$numbers [1, 2, 3, 4, 5, 6]; $evens array_filter($numbers, function($n) { return $n % 2 0; }); // [2, 4, 6]注意这里和array_map不同array_filter保留的是原数组的键名。array_reduce把数组“折叠”成单个值类似累加器$numbers [1, 2, 3, 4]; $sum array_reduce($numbers, function($carry, $item) { return $carry $item; }, 0); // 10$carry是上一次回调的返回值初始值为第三个参数0。如果你要计算订单总金额、统计某个条件出现的次数用array_reduce比foreach更清爽。这三个函数的组合能处理很多复杂需求。举个例子要从订单列表里找出金额大于100的订单并把订单号收集成一个数组$orders [ [order_id 1001, amount 99], [order_id 1002, amount 299], [order_id 1003, amount 150], ]; // 用array_filter筛出大额订单再用array_map提取订单号 $bigOrders array_filter($orders, function($order) { return $order[amount] 100; }); $orderIds array_map(function($order) { return $order[order_id]; }, $bigOrders); // [1002, 1003]这套写法读起来就像在描述业务规则而不是描述“怎么循环”代码可读性高了很多。7. 常见问题速查与避坑指南7.1 高频踩坑问题整理我把平时在群里看到的新手问题和自己踩过的坑汇总成一个速查表方便你以后对照排查。问题现象原因解决方法sort()后关联数组键全没了sort默认重排索引改用asort()或arsort()in_array()在大数组中特别慢线性遍历用array_flip()isset()isset($arr[$key])明明键存在却返回false键的值是null用array_key_exists()判断键unset()后索引出现空洞unset不重建索引用array_values()重新编号foreach用$value后其他代码悄悄改了数组引用没断开循环后unset($value)json_encode()中文全变成\u默认转义Unicode加JSON_UNESCAPED_UNICODEin_array比较0和0不严格弱类型比较加第三个参数true开启严格比较usort()回调写反了返回正负号返回值与排序顺序搞反测试数据验证方向array_filter不加array_values键不连续保留原键外层套array_values()判断数组是否为空用empty($arr)没问题但$arr null判断不可靠类型比较用empty()或count($arr) 07.2 PHP 8环境下数组相关的变化如果你已经开始用PHP 8有几个细节要留意。一是字符串和数字之间的自动转换行为更严格了。比如PHP 8里访问$arr[123]和$arr[123]在某些边界情况下行为有差异旧代码里依赖这种混用的地方可能出现意外。二是sort()家族在排序时对混合类型值的比较逻辑有一些调整。如果数组里既有数字又有字符串比较规则更严格了原来能跑的排序结果可能发生变化。遇到排序结果和预期不符时优先检查数组里是否混入了不同标量类型。三是对未定义数组键的访问PHP 8默认还会报warning这一点没变。但PHP 8.0引入的str_contains()、str_starts_with()、str_ends_with()这些新函数不会直接改数组行为却会在你处理字符串时减少老式写法里strpos() ! false的判断出错概率。四是PHP 8.1开始支持枚举enum它本质上还是一个类常量集合但在处理状态码、配置项映射的时候你可以用Enum::cases()直接拿到数组这个配合array_column处理下拉选项特别方便。7.3 我在实际项目里常用的数组调试手段数组本身很难直接在浏览器里看结构新手最常犯的错就是echo $arr只输出Array然后一脸懵。调试数组的常规手段有这么几个。print_r()输出人类可读的数组结构但要注意它是HTML友好的直接输出到浏览器可以但如果在命令行环境下高亮标签反而影响阅读。var_dump()会带上类型和长度信息更精确。var_export()会输出合法的PHP代码你可以直接把输出结果复制回代码里当作数组定义这个在做测试数据的时候特别方便。// 用print_r调试 print_r($arr); // 想看类型信息用var_dump var_dump($arr); // 输出合法的PHP代码 $exported var_export($arr, true); file_put_contents(debug.php, ?php return . $exported . ;);var_export的第二个参数传true会返回字符串而不直接输出这样你就能把它写到日志文件里。配合日志文件调试线上问题时这个方法比直接输出到页面靠谱得多。如果你用PHPStorm或者VS Code断点调试数组也很方便。在循环内部打个断点变量面板里能看到数组展开的完整结构。我个人建议早点习惯断点调试比var_dump加die的方式高效不少。真要输出调试信息时我习惯用error_log(print_r($arr, true))把数组写到PHP错误日志里而不是直接输出到页面避免破坏接口返回的JSON结构。8. 最后再分享两个实战技巧第一个技巧用数组函数替代循环会让代码简短很多。比如要把一组用户ID查出来转换成字符串很多人会这么写$ids []; foreach ($users as $user) { $ids[] $user[id]; } $idStr implode(,, $ids);用数组函数可以缩成一行$idStr implode(,, array_column($users, id));这两种写法结果完全一样但第二种明显更清爽。代码越短出bug的地方就越少别人看代码也更容易理解你的意图。第二个技巧构建快速查找表。如果每次循环里都要in_array判断一个元素是否在另一个数组里性能会非常差。先建一个以值为键的查找表然后在循环里用isset判断时间复杂度从O(n)降到O(1)。$allowedIds [1001, 1002, 1003]; $lookup array_flip($allowedIds); foreach ($orders as $order) { if (isset($lookup[$order[id]])) { // 这个订单ID在允许列表里 } }数组这个东西越深入越觉得它不只是“存数据的容器”更是一套完整的操作思维。学PHP数组最好的方式就是多写、多拆、多对比把foreach写熟了把array_map用顺了后期学框架、写接口都会顺畅很多。我自己每次遇到数据处理的需求都会先想一遍“PHP哪个原生函数能直接干这个事”实在没有合适的才动手写循环。养成这个习惯之后你的代码量至少能少三分之一而且更不容易出错。
返回列表