rand() has been an alias of mt_rand() since PHP 7.1: one global Mersenne Twister, fast but predictable. random_int() and random_bytes() (PHP 7.0) read the operating system's secure generator. The Random extension (PHP 8.2) separates the algorithm, an engine, from Random\Randomizer, which turns its output into integers, floats, bytes and shuffles.
<?php
use Random\{Randomizer, IntervalBoundary};
use Random\Engine\Xoshiro256StarStar;
$r = new Randomizer(); // default engine: Random\Engine\Secure (CSPRNG)
echo get_class($r->engine), ' ', $r->getInt(1, 6), ' ',
$r->getBytesFromString('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', 10), ' ', // no 0/O or 1/I
json_encode($r->shuffleArray(['a', 'b', 'c', 'd'])), ' ',
round($r->getFloat(0, 1, IntervalBoundary::ClosedOpen), 4), "\n";
echo random_int(100000, 999999), ' ', bin2hex(random_bytes(16)), "\n";
$seeded = fn() => new Randomizer(new Xoshiro256StarStar(20260923)); // reproducible
[$a, $b] = [$seeded(), $seeded()];
echo json_encode([$a->getInt(1, 100), $a->getInt(1, 100)]), ' ',
json_encode([$b->getInt(1, 100), $b->getInt(1, 100)]), ' ',
json_encode($a->pickArrayKeys(['x' => 1, 'y' => 2, 'z' => 3], 2)), ' | ';
mt_srand(42); // the legacy global Mt19937 generator
echo mt_rand(1, 100), ' ', rand(1, 100), ' ', mt_getrandmax(), "\n";Random\Engine\Secure 1 YXDNGT5MT7 ["b","a","c","d"] 0.7686 105738 e7f06880306d9722efab543f6ab7e9e5 [44,31] [44,31] ["x","y"] | 43 68 2147483647
The first two lines change on every run; the third never does. Of the engines Secure, Mt19937, PcgOneseq128XslRr64 and Xoshiro256StarStar, only Secure is fit for secrets: tokens and reset codes never come from rand(), mt_rand() or uniqid(). Seeded engines give reproducible test data without touching the global state. getFloat() and getBytesFromString() (PHP 8.3) replace lcg_value(), deprecated in 8.4. CSRF Tokens and Auth Hardening build tokens this way.