一、array_rand()到底返回什么
初学者看手册容易产生一个误解:以为array_rand()返回的是数组里的值。实际上它返回的是键名。想拿到值,还得用返回的键去原数组里取一次。
这个设计并不多余。返回键名意味着你既可以通过键拿到值,也可以保留键名本身的业务含义,比如用用户ID做键时,抽出来的就是ID。理解这一点,后面的用法就顺了。
二、函数原型与参数
array_rand(array $array, int $num = 1): mixed
| 参数 | 说明 | 是否必需 |
|---|---|---|
| array | 待抽取的数组 | 必需 |
| num | 抽取的键名个数,默认为1 | 可选 |
返回值规则:
-
$num=1或不传时,返回一个键名,类型可能是int或string。 -
$num>1时,返回一个包含多个键名的数组,元素顺序是随机的。 -
$num超过数组长度时,会触发E_WARNING并返回null。 -
$num=0时会触发E_WARNING并返回null。
内部实现上,array_rand()使用的是伪随机数生成器,和rand()、mt_rand()属于同一类,不适合用于密码、令牌、验证码等安全场景。涉及安全的随机数要用random_int()或random_bytes()。
三、抽取一个键与抽取多个键的差异
抽取一个键时,返回的是标量:
<?php
$fruit = array("a" => "apple", "b" => "ball", "c" => "cat", "d" => "dog");
print_r(array_rand($fruit, 1));
输出类似:
b
抽取多个键时,返回的是数组:
<?php
$lang = array('php', 'java', 'python', 'ruby');
$randKeys = array_rand($lang, 2);
echo $lang[$randKeys[0]] . "\n";
echo $lang[$randKeys[1]] . "\n";
输出类似:
php
ruby
这里有个细节:$num>1时返回的键名数组,其内部顺序是随机的,不要假设第一个键就是原数组里下标小的那个。抽取多个键时不会重复,相当于无放回抽样。
四、关联数组下的表现
关联数组抽取返回的是字符串键:
<?php
$score = array("sachin" => "200", "rohit" => "300", "rahul" => "270");
print_r(array_rand($score, 2));
输出类似:
Array ( [0] => sachin [1] => rahul )
注意返回的数组下标是0、1,值是键名。想拿分数,需要$score[$randKeys[0]]这样取值。项目里如果直接把array_rand()的结果当值用,会得到键名而不是分数,这是新手常犯的错误。
五、与shuffle()、mt_rand()的对比
| 函数 | 用途 | 是否改变原数组 | 返回内容 |
|---|---|---|---|
| array_rand() | 随机抽取若干键名 | 否 | 键名或键名数组 |
| shuffle() | 打乱整个数组顺序 | 是 | bool |
| mt_rand() | 生成随机整数 | 否 | int |
选择原则:
-
只需要从数组里挑几个元素,用array_rand()。
-
需要把整个数组顺序打乱,用shuffle()。
-
从固定范围内取随机整数,用mt_rand()。
个人经验:曾经在一个"随机推荐三个商品"的需求里,同事先用shuffle()把整个商品列表打乱,再取前三个。商品列表有几千条,每次请求都要重排整个数组,性能浪费明显。改成array_rand()抽三个键名再去取值,开销小了很多。打乱是全局操作,抽样是局部操作,需求是抽样就别用打乱。
六、项目实例
实例一:代码号学习编程·随机抽一道题
<?php
$questions = [
'q1' => 'PHP 中 array_push 的返回值是什么?',
'q2' => 'array_rand 返回的是键还是值?',
'q3' => 'unset 后追加元素,键名会补位吗?',
];
$key = array_rand($questions);
echo $questions[$key];
这种单题抽取用默认的$num=1就够了,返回标量键名,取值直接。
实例二:代码号学习编程·随机展示三个标签
<?php
$tags = ['php', 'mysql', 'redis', 'nginx', 'docker', 'git'];
$randKeys = array_rand($tags, 3);
foreach ($randKeys as $k) {
echo $tags[$k] . "\n";
}
抽取三个不重复的标签。注意$num不能超过数组元素个数,否则会报警告。
实例三:代码号学习编程·带权重的抽奖思路
array_rand()本身不支持权重。如果项目里需要按概率抽取,比如一等奖概率低、参与奖概率高,做法是把权重展开成重复元素:
<?php
$pool = [];
$prizes = [
'一等奖' => 1,
'二等奖' => 5,
'参与奖' => 94,
];
foreach ($prizes as $name => $weight) {
for ($i = 0; $i < $weight; $i++) {
$pool[] = $name;
}
}
$key = array_rand($pool);
echo $pool[$key];
这种写法直观,但池子会随权重放大。权重数值很大时,内存占用会上去,此时更适合用mt_rand()配合累计概率区间来判断。选哪种取决于权重规模和调用频率。
七、踩坑记录
坑一:把返回值直接当值用。关联数组里array_rand($arr)返回的是键,不是值。直接输出会得到键名,看起来"对",实际业务数据错了。
坑二:$num超过数组长度。传5抽3个元素的数组,PHP会发警告并返回null。上线前要加一层判断:
$num = min($num, count($array));
坑三:用于安全场景。有人拿array_rand()抽验证码、抽token,这是不合适的。它的随机源是伪随机,可预测。安全相关的随机必须换成random_int()。
坑四:以为返回顺序和原数组一致。$num>1时返回的键名数组顺序是随机的,如果业务上要求"按原数组顺序输出抽中的元素",需要自己对返回的键名排序后再取值。
八、本节课程知识要点
-
array_rand()返回键名,不是值,取值要再查一次原数组。
-
$num=1返回标量,$num>1返回键名数组,且不重复。 -
$num超过数组长度会警告并返回null,使用前应做边界判断。 -
随机源是伪随机,不能用于密码、令牌等安全场景。
-
抽样用array_rand(),全局打乱用shuffle(),两者不要混用。
-
需要权重抽奖时,可展开权重池,或改用mt_rand()配合概率区间。
九、常见问题
问:array_rand()抽多个键会重复吗?
不会。抽取多个键时相当于无放回抽样,返回的键名互不相同。
问:空数组调用会怎样?
会触发E_WARNING,返回null。调用前应确认数组非空。
问:抽取结果每次都不一样吗?
不一定。随机本身就有概率抽到同一个键,尤其是数组元素少的时候。如果业务要求"连续两次不重复",需要自己记录上一次的结果并做排除。
问:能不能用array_rand()实现随机排序?
可以,但没必要。随机排序用shuffle()更直接。array_rand()的定位是抽样,不是排序。