random_int, never rand, for anything that matters

rand and mt_rand are seeded generators whose output is predictable from a small number of samples, and they are still the first result for “php random number”.

// predictable. fine for a jitter delay, never for a token.
rand(1, 1000);
mt_rand(1, 1000);
shuffle($array);
array_rand($array);

// cryptographically secure, and throws rather than
// silently degrading if no source is available
random_int(1, 1000);
bin2hex(random_bytes(16));

The failure mode of the insecure functions is that everything works: a password reset token generated with mt_rand is a valid token that an attacker can predict, and no test will distinguish it from a good one. random_int throwing when no entropy source is available is the correct behaviour and is why it must not be wrapped in a try that falls back — a fallback here is the vulnerability.