data types and standard library
Randomness
Random values appear in password reset links, email verification tokens, API keys, CSRF tokens, invitation codes, temporary filenames, test data, lotteries, and games.
The most important distinction is security. If a value protects an account, session, payment, private file, or permission, use PHP's cryptographically secure functions: random_bytes() and random_int().
Generate secure tokens
Use random_bytes() when you need unpredictable bytes, then encode them into a string that can travel through URLs, emails, or databases.
<?php
declare(strict_types=1);
$token = bin2hex(random_bytes(32));
echo 'Token length: ' . strlen($token) . PHP_EOL;
echo 'Looks like hex: ' . (ctype_xdigit($token) ? 'yes' : 'no') . PHP_EOL;
// Prints:
// Token length: 64
// Looks like hex: yes
The actual token changes every run. The reliable checks are its length and format.
Generate secure integers
Use random_int() when you need an integer inside a range.
<?php
declare(strict_types=1);
$roll = random_int(1, 6);
echo 'Roll is valid: ' . ($roll >= 1 && $roll <= 6 ? 'yes' : 'no') . PHP_EOL;
// Prints:
// Roll is valid: yes
This is suitable for security-sensitive numeric choices too, such as backup code digits or short-lived verification codes.
Format numeric codes carefully
Verification codes often need leading zeroes. Generate a number, then format it as a fixed-width string.
<?php
declare(strict_types=1);
function verificationCode(): string
{
return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
}
$code = verificationCode();
echo 'Code length: ' . strlen($code) . PHP_EOL;
echo 'Only digits: ' . (ctype_digit($code) ? 'yes' : 'no') . PHP_EOL;
// Prints:
// Code length: 6
// Only digits: yes
Do not cast the final code back to an integer, because that would remove leading zeroes.
Avoid predictable functions for secrets
Older functions such as rand() and mt_rand() are not the right choice for tokens, passwords, sessions, or anything an attacker could benefit from guessing.
<?php
declare(strict_types=1);
function passwordResetToken(): string
{
return bin2hex(random_bytes(32));
}
$token = passwordResetToken();
echo strlen($token) . PHP_EOL;
// Prints:
// 64
The function name says what the random value is for. That helps reviewers spot when security-sensitive code is using the wrong tool.
Random filenames should not expose original names
Temporary and stored filenames often need randomness to avoid collisions and hide user-supplied names.
<?php
declare(strict_types=1);
function randomStorageName(string $extension): string
{
$extension = strtolower($extension);
if (!preg_match('/^[a-z0-9]+$/', $extension)) {
throw new InvalidArgumentException('Invalid extension.');
}
return bin2hex(random_bytes(16)) . '.' . $extension;
}
$filename = randomStorageName('pdf');
echo str_ends_with($filename, '.pdf') ? 'pdf name' : 'wrong extension';
echo PHP_EOL;
// Prints:
// pdf name
The original filename can be stored as metadata for display, but it should not control the storage path.
Test random code by properties
Random output changes, so tests should check promises that are always true.
<?php
declare(strict_types=1);
function inviteCode(): string
{
return strtoupper(bin2hex(random_bytes(4)));
}
$code = inviteCode();
$isValid = strlen($code) === 8 && ctype_xdigit($code) && strtoupper($code) === $code;
echo $isValid ? 'valid invite code' : 'invalid invite code';
echo PHP_EOL;
// Prints:
// valid invite code
For code that needs deterministic randomness in tests, inject a small generator service or use a dedicated randomizer abstraction. Do not weaken production randomness just to make tests easier.
What to remember
Use random_bytes() for secure tokens and random_int() for secure numbers. Check random output by length, format, and range. Keep generated secrets out of logs, store only hashes when appropriate, and avoid predictable generators for anything security-sensitive.
Practice
Task: Generate account recovery values
Write a small example that generates values for an account recovery flow.
Requirements
- Use
declare(strict_types=1);. - Generate a password reset token using
random_bytes(). - Encode the token as hexadecimal.
- Generate a six-digit verification code using
random_int(). - Preserve leading zeroes in the verification code.
- Print checks for token length, token format, code length, and code format.
- Include the expected output as comments in the same PHP code block.
Do not print the real token as the main proof. Randomness should be checked by properties that stay true every run.
Show solution
<?php
declare(strict_types=1);
function passwordResetToken(): string
{
return bin2hex(random_bytes(32));
}
function verificationCode(): string
{
return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
}
$token = passwordResetToken();
$code = verificationCode();
echo 'Token length: ' . strlen($token) . PHP_EOL;
echo 'Token is hex: ' . (ctype_xdigit($token) ? 'yes' : 'no') . PHP_EOL;
echo 'Code length: ' . strlen($code) . PHP_EOL;
echo 'Code is digits: ' . (ctype_digit($code) ? 'yes' : 'no') . PHP_EOL;
// Prints:
// Token length: 64
// Token is hex: yes
// Code length: 6
// Code is digits: yes
The real values change every run, so the example verifies stable properties instead of comparing exact strings. In a production recovery flow, the raw reset token should be shown to the user once and a hash should be stored for later comparison.