From PHP 8.2 you are able to use the new random extension.
This extension was designed to address some of the types of issue you are experiencing with the current random number generators in PHP. The details of it are:
It is object based
Because the state of the random number generators (called “Engines”) is stored in an object, they can be passed as parameters, and won't interfere with each other, as they are not dependent on global state.
So you can create multiple random engines which implement the Xoshiro256** algorithm with the same or different seeds:
$engine_1 = new \Random\Engine\Xoshiro256StarStar(1234);
$engine_2 = new \Random\Engine\Xoshiro256StarStar(4321); // generates a different sequence than (1).
$engine_3 = new \Random\Engine\Xoshiro256StarStar(1234); // generates the same sequence as (1).
Each of these engines will generate their own reproducible sequence of random data, without interfering with the internal state of each other.
Better algorithms
The Mersenne Twister random generator was state of the art for 1997, but mt_rand() fails several statistical tests for randomness, e.g. the BigCrush and Crush tests.
The following is the list of the engines available from PHP 8.2 on.
\Random\Engine\Mt19937
This engine implements the same Mersenne Twister that's currently available as mt_rand().
The Mt19937 engine still generates a 32 bit (4 byte) bytestring, as Mt19937 by definition is a 32 bit engine.
However combining it with the the Randomizer, which is the high-level API used to interact with the engines, the Mt19937 engine is capable of generating random 64 bit integers, by stretching the engine's randomness into whatever amount of randomness is required to span the requested range:
$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234));
$randomizer->getInt(0, 8_000_000_000); // 8 billion requires 64 bit integers
\Random\Engine\PcgOneseq128XslRr64
This engine implements a Permuted Congruential Generator (pcg_oneseq_128_xsl_rr_64).
PCG is a family of simple fast space-efficient statistically good algorithms for random number generation. Unlike many general-purpose RNGs, they are also hard to predict.
It is a pseudorandom number generator (PRNG) and so does not generate cryptographically secure random sequences.
More info can be found here: https://www.pcg-random.org/
\Random\Engine\Xoshiro256StarStar
This is another pseudorandom number generator and so again does not generate cryptographically secure random sequences.
It is called Xoshiro256StarStar in PHP, but in general the name of the algorithm is spelled as 'Xoshiro256**'. This is due to PHP not support the character * in class names
The full details of the algorithm can be found here: https://prng.di.unimi.it/
\Random\Engine\Secure
This engine implements a cryptographically secure PRNG (CSPRNG). It cannot be seeded, as its purpose is to generate the highest quality, unguessable randomness required for cryptographic applications (e.g. password reset links).
What engine to use
In order of preference:
Use Secure. This is the safe choice, unless you know you have specific requirements. That's why it's the default for the Randomizer if no engine is given.
Use either Xoshiro256StarStar or PcgOneseq128XslRr64 if your application does not require a CSPRNG and you have strict performance requirements, or if your application requires the random numbers to be repeatable, for reproducibility.
Use Mt19937 only for backwards compatibility. Both Xoshiro256** and PcgOneseq128XslRr64 are better in every metric possible.
You can also choose to use a reproducible random number generator in your development and testing environments, and then use a cyptographically secure generator in production:
$rng = $is_production
? new \Random\Engine\Secure()
: new \Random\Engine\PcgOneseq128XslRr64(1234);
Custom implementations
If the default engines are not sufficient to support your use case you can also implement your own engine in
userland PHP.
Here's a simple example of a SHA-1 based engine:
<?php
final class Sha1Engine implements \Random\Engine {
public function __construct(private string $state) { }
/**
* Return a random bytestring. The bytestring will be interpreted in little-endian order.
*/
public function generate(): string
{
$this->state = \sha1($this->state, true);
return \substr($this->state, 0, 8);
}
}
The randomness generated by this SHA-1 based engine should be fairly good, but is must not be used for security-critical applications. The Secure engine must be used!