Skip to content

Random 统一随机数生成器(PHP 8.2+)

PHP 8.2 引入了全新的 Random\ 命名空间,提供了面向对象的随机数生成器(RNG)统一接口。新的 Random 扩展取代了旧的 rand()srand()mt_rand() 函数,提供了更好的安全性、可测试性和扩展性。

前置知识

阅读本节前,建议先了解:Math 数学函数BC Math 任意精度

基础概念

新旧对比

php
<?php
declare(strict_types=1);

// 旧方式(已不推荐)
// rand()     - 伪随机,不安全
// mt_rand()  - Mersenne Twister,较快但不安全
// random_int() - PHP 7.0+ 密码学安全随机数

// PHP 8.2+ 新方式 - Random 扩展
use Random\Randomizer;

$randomizer = new Randomizer();

// 密码学安全的随机整数
echo $randomizer->getInt(1, 100) . PHP_EOL;

// 随机字节
echo bin2hex($randomizer->getBytes(16)) . PHP_EOL;

Randomizer 类

基本用法

php
<?php
declare(strict_types=1);

use Random\Randomizer;

$randomizer = new Randomizer();

// 随机整数(包含边界)
$int = $randomizer->getInt(0, 100);
echo "随机整数 (0~100): {$int}" . PHP_EOL;

// 随机浮点数
$float = $randomizer->getFloat(0.0, 1.0);
echo "随机浮点: {$float}" . PHP_EOL;

// 随机字节
$bytes = $randomizer->getBytes(32);
echo "随机字节: " . bin2hex($bytes) . PHP_EOL;

// 从数组中随机选择
$items = ['苹果', '香蕉', '橙子', '葡萄'];
$random = $randomizer->pickArray($items);
echo "随机选择: {$random}" . PHP_EOL;

// 打乱字符串(PHP 8.3+)
$shuffled = $randomizer->shuffleBytes('hello world');
echo "打乱字符串: {$shuffled}" . PHP_EOL;

获取随机浮点数

php
<?php
declare(strict_types=1);

use Random\Randomizer;

$randomizer = new Randomizer();

// getFloat(min, max) - 返回闭区间 [min, max] 内的随机浮点数
echo $randomizer->getFloat(0, 1) . PHP_EOL;
echo $randomizer->getFloat(1, 100) . PHP_EOL;

// 获取指定精度的浮点数
$float = $randomizer->getFloat(0, 1000000, \Random\IntervalBoundary::ClosedOpen);

生成随机字符串

php
<?php
declare(strict_types=1);

use Random\Randomizer;

class RandomStringGenerator
{
    private Randomizer $randomizer;

    public function __construct()
    {
        $this->randomizer = new Randomizer();
    }

    /**
     * 生成随机字符串
     */
    public function generate(
        int $length,
        string $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    ): string {
        $charsetLength = strlen($charset);
        $result = '';

        for ($i = 0; $i < $length; $i++) {
            $index = $this->randomizer->getInt(0, $charsetLength - 1);
            $result .= $charset[$index];
        }

        return $result;
    }

    /**
     * 生成安全 Token
     */
    public function token(int $length = 32): string
    {
        return bin2hex($this->randomizer->getBytes($length));
    }

    /**
     * 生成密码
     */
    public function password(int $length = 16): string
    {
        $lower = 'abcdefghijklmnopqrstuvwxyz';
        $upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $digits = '0123456789';
        $symbols = '!@#$%^&*()_+-=';

        $charset = $lower . $upper . $digits . $symbols;
        $password = $this->generate($length, $charset);

        // 确保包含每种字符
        $password[0] = $lower[$this->randomizer->getInt(0, strlen($lower) - 1)];
        $password[1] = $upper[$this->randomizer->getInt(0, strlen($upper) - 1)];
        $password[2] = $digits[$this->randomizer->getInt(0, strlen($digits) - 1)];
        $password[3] = $symbols[$this->randomizer->getInt(0, strlen($symbols) - 1)];

        return $password;
    }

    /**
     * 生成 UUID v4
     */
    public function uuid(): string
    {
        $data = $this->randomizer->getBytes(16);

        // 设置版本号 (4) 和变体位
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);

        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }
}

$gen = new RandomStringGenerator();
echo "随机字符串: " . $gen->generate(20) . PHP_EOL;
echo "Token: " . $gen->token(32) . PHP_EOL;
echo "密码: " . $gen->password(16) . PHP_EOL;
echo "UUID: " . $gen->uuid() . PHP_EOL;

引擎(Engine)

可用引擎

php
<?php
declare(strict_types=1);

use Random\Engine;
use Random\Randomizer;

// Mersenne Twister(快速,不安全)
$mtEngine = new Engine\Mt19937(1234); // 可指定种子
$randomizer = new Randomizer($mtEngine);
echo $randomizer->getInt(1, 100) . PHP_EOL;

// Xorshift 128+(快速,不安全)
$xorEngine = new Engine\XorShift128Plus(5678);
$randomizer = new Randomizer($xorEngine);

// PCG64(快速,较好统计特性)
$pcgEngine = new Engine\PcgOneseq128XslRr64();
$randomizer = new Randomizer($pcgEngine);

// Secure(密码学安全,PHP 8.2+)
$secureEngine = new Engine\Secure();
$randomizer = new Randomizer($secureEngine);

// 从系统源生成(密码学安全)
$randomizer = new Randomizer(); // 默认使用 Secure 引擎

可种子引擎(可重现)

php
<?php
declare(strict_types=1);

use Random\Engine\Mt19937;
use Random\Randomizer;

// 使用固定种子,结果可重现
$engine1 = new Mt19937(42);
$engine2 = new Mt19937(42);

$r1 = new Randomizer($engine1);
$r2 = new Randomizer($engine2);

// 两者生成相同的随机序列
for ($i = 0; $i < 5; $i++) {
    echo "R1: " . $r1->getInt(1, 100) . ", R2: " . $r2->getInt(1, 100) . PHP_EOL;
}

序列化引擎

php
<?php
declare(strict_types=1);

use Random\Engine\Mt19937;
use Random\Randomizer;

// 序列化引擎状态
$engine = new Mt19937(42);
$randomizer = new Randomizer($engine);

// 生成一些随机数
echo $randomizer->getInt(1, 100) . PHP_EOL;

// 序列化当前状态
$serialized = serialize($engine);
echo "序列化: " . $serialized . PHP_EOL;

// 反序列化恢复状态
$restored = unserialize($serialized);
$randomizer2 = new Randomizer($restored);
echo "恢复后: " . $randomizer2->getInt(1, 100) . PHP_EOL;

测试中的使用

可预测的随机数(单元测试)

php
<?php
declare(strict_types=1);

use Random\Engine\Mt19937;
use Random\Randomizer;

class Lottery
{
    public function __construct(private readonly Randomizer $randomizer = new Randomizer()) {}

    public function draw(array $participants, int $winners): array
    {
        $shuffled = $participants;
        for ($i = count($shuffled) - 1; $i > 0; $i--) {
            $j = $this->randomizer->getInt(0, $i);
            [$shuffled[$i], $shuffled[$j]] = [$shuffled[$j], $shuffled[$i]];
        }
        return array_slice($shuffled, 0, $winners);
    }
}

// 单元测试中使用固定种子
$testEngine = new Mt19937(42);
$lottery = new Lottery(new Randomizer($testEngine));

$winners = $lottery->draw(['Alice', 'Bob', 'Charlie', 'Dave', 'Eve'], 2);
echo "中奖者: " . implode(', ', $winners) . PHP_EOL;
// 每次运行结果相同,便于断言

旧 API 兼容

php
<?php
declare(strict_types=1);

// random_int() - 密码学安全随机整数(PHP 7.0+)
$int = random_int(1, 100);

// random_bytes() - 密码学安全随机字节(PHP 7.0+)
$bytes = random_bytes(16);
echo bin2hex($bytes) . PHP_EOL;

// 这些函数在新 Random 扩展中仍然可用且推荐

注意事项

  • Randomizer 默认使用 Secure 引擎(密码学安全)
  • 单元测试使用固定种子的 Mt19937 引擎
  • 不要使用 rand()mt_srand(),已被弃用
  • Random\Engine\Secure 基于 CSPRNG

下一节

继续学习:cURL 详解

参考链接