PHP 8.2 大更新

PHP 官方在前不久发布了 PHP 8.2 的大更新,我粗浅地看了一下基本上没啥太过出彩的更新,基本上也就是常规的写 Class 比较方便了,然后支持了一些新的特性了,新增了一些不会用到的函数并且淘汰了一些也是不会用到的函数。不过这个随机数生成器还是让我比较感兴趣的,所以来说一下。

首先我们参考这次更新的网站可知,PHP 这次更新的随机数生成器分成两个部分,一个叫做 Engine (引擎),另外一个叫做 Randomizer随机器),但理解起来就是一个核心一个壳就行。引擎有四个不同的引擎,分别是 Secure (默认的),Mt19937PcgOneseq128XslRr64 以及 Xoshiro256StarStar,这几个引擎有啥不同就留给你们自己体会了。

具体实现

首先那必定是要先 use 一下的,我们这里用官方的 Xoshiro256StarStar 为例:

use Random\Engine\Xoshiro256StarStar;
use Random\Randomizer;

引用完了之后我们就可以调用了,首先我们先来初始化一下我们的核心,核心需要传入的是经过 SHA256 加密哈希后的种子:

$RndBluePrint = new Xoshiro256StarStar(
    hash("sha256", "This is the seed to be used", true)
);

接着官方这里会用到 Fiber 进行多个随机数的运算,至于 Fiber 是 PHP 8.1 新增的东西,详细的可以回去看 《PHP 8.1 使用 Fiber 实现伪多线程操作》,我们先初始化一下 Fiber 池,初始化完成之后我们就要开始添加我们的 Fiber 了。

我们每次添加 Fiber 的时候,我们都先复制一下我们的核心,然后命令我们的核心 jump 一下,按照官方的文档,这一 jump 就是往前 $2^{128}$ 个随机数,可以保证我们生成数字的随机性。复制完了核心之后我们在 Fiber 所引用的 Function 里面创建一个 Randomizer 随机器,创建的时候记得传入我们的核心,那样才能得到对应的随机数,否则的话就是使用默认的核心。

创建完了随机器之后我们可以通过调用 getInt 来进行随机数的生成,按照官方的文档,我们要传入随机数的下界与上界,返回的就是一个在上界和下界之间的随机数了,最后我们将他们输出一下:

$fibers = [];
for($i = 0; $i < 8; $i++) {
    $RndFiber = clone $RndBluePrint;
    $RndBluePrint->jump();

    $fibers[] = new Fiber(function() use ($RndFiber, $i) : void {
        $randomizer = new Randomizer($RndFiber);

        echo "{$i}: " . $randomizer->getInt(0, 1000) . ',' . $randomizer->getInt(0, 1000) . PHP_EOL;
    });
}

最后,我们也是用随机数生成器来打乱一下 Fiber 池,我们可以使用 shuffleArray 来对一个数组进行打乱,打乱了之后逐个 Fiber 进行激活:

$randomizer = new Randomizer();
$fibers = $randomizer->shuffleArray($fibers);
foreach($fibers as $fiber) {
    $fiber->start();
}

结果的验证

最后不妨来看一下我们生成的随机数是否一样,因为如果使用相同的核心以及相同的种子的话,生成的随机数也是一样的,或许因为打乱的 Fiber 的关系所以顺序不一样,但是每个 ID 的 Fiber 生成的随机数应该一样的:

5: 483,330
6: 424,713
0: 261,484
1: 700,395
2: 149,359
3: 685,449
7: 867,204
4: 665,715

完整代码

<?php

use Random\Engine\Xoshiro256StarStar;
use Random\Randomizer;

$RndBluePrint = new Xoshiro256StarStar(
    hash("sha256", "This is the seed to be used", true)
);

$fibers = [];
for($i = 0; $i < 8; $i++) {
    $RndFiber = clone $RndBluePrint;
    $RndBluePrint->jump();

    $fibers[] = new Fiber(function() use ($RndFiber, $i) : void {
        $randomizer = new Randomizer($RndFiber);

        echo "{$i}: " . $randomizer->getInt(0, 1000) . ',' . $randomizer->getInt(0, 1000) . PHP_EOL;
    });
}

$randomizer = new Randomizer();
$fibers = $randomizer->shuffleArray($fibers);
foreach($fibers as $fiber) {
    $fiber->start();
}