PHPで乱数を生成する方法について

採用はこちら

PHPでは、random_int()random_bytes()Random\Randomizerなどを使って乱数を生成できます。

どの方法を使うべきかは、整数を生成したいのか、ランダムな文字列を作りたいのか、セキュリティが必要なのかによって異なります。

特にパスワードリセット用のトークンや認証コードなど、第三者に予測されてはいけない値を生成する場合は、暗号学的に安全な乱数生成方法を選ぶことが重要です。

単純な整数を安全に生成するのであれば、random_int()が簡潔で使いやすい方法です。

また、PHP 8.2以降ではRandom\Randomizerが利用でき、乱数生成をより体系的に扱えるようになっています。

目次

random_int()で整数の乱数を生成する

random_int()の基本的な使い方

random_int()は、指定した最小値から最大値までの整数をランダムに生成する関数です。

基本的な書き方は次のとおりです。

random_int(最小値, 最大値);

例えば、1〜100の整数をランダムに生成したい場合は、次のように記述します。

<?php

$number = random_int(1, 100);

echo $number;

実行すると、例えば次のような値が返されます。

37

もう一度実行すると、次のように別の値になる可能性があります。

82

random_int(1, 100)では、1と100も生成対象に含まれます。

つまり、生成される範囲は1以上100以下です。

random_int()はセキュリティ用途にも利用できる

random_int()の大きな特徴は、暗号学的に安全な乱数を生成できることです。

そのため、単純なゲームや抽選だけでなく、第三者に予測されてはいけない値を必要とする処理にも利用できます。

例えば、次のような用途があります。

  • 認証コードの生成
  • 抽選番号の生成
  • ランダムなIDの一部
  • セキュリティ関連のランダム値
  • ゲーム内のランダム処理

ただし、長いトークンなどを生成する場合は、random_bytes()を利用する方法も適しています。

指定した範囲の乱数を生成する

0〜9の乱数を生成する

0〜9の整数をランダムに生成する場合は、次のように記述します。

<?php

$number = random_int(0, 9);

echo $number;

生成される値は、0〜9のいずれかです。

1〜6の乱数でサイコロを作る

サイコロのように1〜6の数字をランダムに生成したい場合は、次のように記述できます。

<?php

$dice = random_int(1, 6);

echo $dice;

例えば、実行結果は次のようになります。

4

HTMLと組み合わせる場合は、次のようにも記述できます。

<?php

$dice = random_int(1, 6);

?>

<p>サイコロの結果:<?= $dice ?></p>

ページを再読み込みするたびに、1〜6のいずれかの値が表示されます。

100〜999の乱数を生成する

3桁の整数をランダムに生成したい場合は、100〜999を範囲として指定します。

<?php

$number = random_int(100, 999);

echo $number;

例えば、次のような値が生成されます。

583

同じように、1000〜9999の4桁の整数を生成する場合は次のようにします。

$number = random_int(1000, 9999);

4桁のランダムな数字を生成する

1000〜9999の4桁整数を生成する

必ず先頭が1〜9になる4桁の数字でよければ、次のように記述できます。

<?php

$code = random_int(1000, 9999);

echo $code;

例えば、次のような結果になります。

7281

先頭の0を含む4桁の数字を生成する

「0042」のように先頭の0を許可したい場合は、0〜9999の整数を生成して4桁になるようゼロ埋めします。

<?php

$code = str_pad(
    (string) random_int(0, 9999),
    4,
    '0',
    STR_PAD_LEFT
);

echo $code;

例えば、次のような値が生成されます。

0042

この場合、値は数値ではなく4文字の文字列として扱うのが適しています。

random_bytes()でランダムな文字列を生成する

セキュアなトークンを生成する

パスワードリセット用トークンなど、第三者に予測されてはいけないランダムな文字列を作りたい場合は、random_bytes()を利用できます。

例えば、次のコードです。

<?php

$token = bin2hex(random_bytes(16));

echo $token;

実行すると、次のような文字列が生成されます。

f8c4e12a9330c5b71d8424c6ba4f5a92

random_bytes()は暗号学的に安全なランダムバイト列を生成します。

bin2hex()を使う理由

random_bytes()が返す値はバイナリデータです。

そのままではWebページやURL、データベースなどで扱いにくい場合があります。

そこで、bin2hex()を使って16進数の文字列へ変換します。

例えば、次のコードでは16バイトのランダムデータを生成しています。

random_bytes(16);

これをbin2hex()で変換すると、1バイトにつき2文字の16進数になるため、32文字の文字列になります。

bin2hex(random_bytes(16));

セキュリティトークンなどを生成する場合に、よく使われる方法です。

ランダムな英数字を生成する

random_int()を使って英数字を選択する

数字とアルファベットを組み合わせたランダムな文字列を作ることもできます。

例えば、8文字のランダムな英数字を生成するコードは次のとおりです。

<?php

$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

$result = '';

for ($i = 0; $i < 8; $i++) {
    $index = random_int(0, strlen($characters) - 1);
    $result .= $characters[$index];
}

echo $result;

実行すると、次のような文字列が生成されます。

A7kP2mQ9

関数として再利用する

ランダム文字列を何度も生成する場合は、関数にまとめると便利です。

<?php

function generateRandomString(int $length): string
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $result = '';

    for ($i = 0; $i < $length; $i++) {
        $result .= $characters[
            random_int(0, strlen($characters) - 1)
        ];
    }

    return $result;
}

echo generateRandomString(16);

文字数は引数で変更できます。

generateRandomString(8);
generateRandomString(16);
generateRandomString(32);

ただし、セキュリティトークンが目的であれば、独自に文字を選択するよりrandom_bytes()を使った方法のほうがシンプルです。

配列からランダムに要素を選ぶ

array_rand()を使う方法

PHPには、配列からランダムにキーを取得するarray_rand()もあります。

例えば、次の配列から果物を1つ選択できます。

<?php

$fruits = [
    'りんご',
    'みかん',
    'バナナ',
    'ぶどう'
];

$key = array_rand($fruits);

echo $fruits[$key];

実行すると、例えば次のように表示されます。

バナナ

array_rand()が返すのは値そのものではなく、選択された要素のキーです。

そのため、値を取得する場合は$fruits[$key]のようにアクセスします。

セキュリティが必要ならrandom_int()を使う

array_rand()は、暗号学的に安全なランダム選択を目的とした関数ではありません。

安全なランダム性が必要な場合は、配列の添字をrandom_int()で選択できます。

<?php

$fruits = [
    'りんご',
    'みかん',
    'バナナ',
    'ぶどう'
];

$key = random_int(0, count($fruits) - 1);

echo $fruits[$key];

この方法は、0から始まる連続した数値キーの配列を対象とする場合に利用できます。

複数の乱数を生成する

for文で複数生成する

1〜100の乱数を5個生成したい場合は、for文とrandom_int()を組み合わせられます。

<?php

for ($i = 0; $i < 5; $i++) {
    echo random_int(1, 100) . PHP_EOL;
}

例えば、次のような結果になります。

42
17
89
3
61

ブラウザ上で改行して表示するなら、<br>を使用できます。

<?php

for ($i = 0; $i < 5; $i++) {
    echo random_int(1, 100) . '<br>';
}

重複しない乱数を生成する

生成済みの数字をチェックする

通常のrandom_int()では、同じ値が複数回生成される可能性があります。

例えば1〜10の範囲から重複しない数字を5個取得する場合は、生成済みの数字を確認しながら追加します。

<?php

$numbers = [];

while (count($numbers) < 5) {
    $number = random_int(1, 10);

    if (!in_array($number, $numbers, true)) {
        $numbers[] = $number;
    }
}

print_r($numbers);

例えば、次のような結果になります。

Array
(
    [0] => 3
    [1] => 8
    [2] => 1
    [3] => 10
    [4] => 5
)

大量の値を生成する場合は効率に注意する

この方法は、少数の値を取得する場合には分かりやすい実装です。

一方で、例えば1〜100から99個の重複しない値を生成しようとすると、同じ数字が何度も生成されやすくなります。

その結果、ループ回数が増えて効率が悪化します。

大量の重複しない乱数を扱う場合は、必要なデータ量や範囲に応じて別のアルゴリズムを検討する必要があります。

小数の乱数を生成する

random_int()を利用して小数を作る

random_int()は整数を返す関数です。

そのため、小数を作りたい場合は、整数として生成してから割る方法があります。

例えば、0〜1までを0.0001刻みで生成する場合は次のようにします。

<?php

$number = random_int(0, 10000) / 10000;

echo $number;

例えば、次のような結果になります。

0.4732

このコードでは、0、0.0001、0.0002から1.0000までの値のいずれかを生成します。

連続した実数から無限の精度で乱数を選んでいるわけではない点には注意が必要です。

PHP 8.2以降ではRandom APIも利用できる

PHP 8.2以降では、Random\Randomizerを利用して浮動小数点数を扱う方法も用意されています。

そのため、新しいPHP環境で本格的に乱数処理を実装する場合は、Random拡張のAPIも選択肢になります。

mt_rand()で乱数を生成する

mt_rand()の基本的な使い方

mt_rand()でも指定範囲の整数を生成できます。

<?php

$number = mt_rand(1, 100);

echo $number;

基本構文は次のとおりです。

mt_rand(最小値, 最大値);

mt_rand()はMersenne Twisterを利用した擬似乱数生成関数です。

mt_rand()はセキュリティ用途には使用しない

mt_rand()による乱数は、暗号学的に安全な乱数ではありません。

そのため、次のような予測されてはいけない情報の生成には適していません。

  • パスワードリセット用トークン
  • 秘密鍵
  • セキュリティトークン
  • 認証に利用する秘密情報

こうした用途には、random_int()random_bytes()などを使用します。

ゲームやセキュリティを必要としないシミュレーションなどでは、mt_rand()が利用されることがあります。

rand()で乱数を生成する

rand()の基本的な使い方

rand()もPHPで昔から利用されている乱数生成関数です。

<?php

$number = rand(1, 10);

echo $number;

1〜10の整数がランダムに返されます。

PHP 7.1以降ではmt_rand()と同じ乱数生成器を利用する

現在のPHPを理解するうえで重要なのは、rand()mt_rand()の関係です。

PHP 7.1以降では、rand()mt_rand()と同じ乱数生成器を利用しています。

そのため、現在のPHPでは、rand()mt_rand()をまったく異なる乱数生成方式として考える必要はありません。

また、どちらも暗号学的に安全な値の生成には適していません。

新しくコードを書く場合は、用途に応じてrandom_int()Random\Randomizerなどを利用するほうが分かりやすいでしょう。

PHP 8.2以降はRandom\Randomizerも利用できる

Random\Randomizerとは

PHP 8.2以降では、Random拡張にRandom\Randomizerクラスが用意されています。

乱数生成の処理をオブジェクトとして扱えるため、複雑な処理を実装したり、乱数生成エンジンを切り替えたりしたい場合に便利です。

例えば、1〜100の整数を生成する場合は次のように記述できます。

<?php

$randomizer = new Random\Randomizer();

$number = $randomizer->getInt(1, 100);

echo $number;

getInt(1, 100)では、1〜100の整数が生成されます。

最小値と最大値はどちらも結果に含まれます。

デフォルトでは安全なエンジンが使用される

Random\Randomizerのコンストラクタで乱数生成エンジンを指定しなかった場合は、デフォルトでRandom\Engine\Secureが使用されます。

そのため、次のようなコードでは、安全な乱数生成エンジンが利用されます。

$randomizer = new Random\Randomizer();

PHP 8.2以降の環境でRandom APIを体系的に扱いたい場合に便利です。

ランダムなバイト列も生成できる

Random\Randomizerでは、getBytes()を利用してランダムなバイト列も生成できます。

<?php

$randomizer = new Random\Randomizer();

$bytes = $randomizer->getBytes(16);

echo bin2hex($bytes);

random_bytes()と同様、バイト列を16進数へ変換して扱うことができます。

単純な処理ならrandom_int()random_bytes()でも十分ですが、より高度な乱数処理ではRandom\Randomizerが便利です。

PHPで乱数を生成するときにシード値は必要なのか

random_int()では自分でシードを設定しない

古いPHPのサンプルコードでは、次のようなコードを見ることがあります。

srand(time());

または、

mt_srand();

しかし、random_int()random_bytes()を利用する場合、ユーザーが自分でシード値を設定する必要はありません。

セキュリティ用途で独自の方法によってシード値を作ろうとすると、逆に安全性を損なう可能性があります。

予測されてはいけない値が必要な場合は、PHPが用意している安全な乱数生成APIをそのまま利用するのが基本です。

PHPの乱数生成方法を用途別に使い分ける

単純な整数にはrandom_int()

1〜100や1〜6など、指定した範囲の整数を安全かつ簡単に生成したい場合は、random_int()が扱いやすい方法です。

$number = random_int(1, 100);

セキュアなトークンにはrandom_bytes()

長いランダム文字列や、第三者に推測されてはいけないトークンを作成する場合は、random_bytes()が適しています。

$token = bin2hex(random_bytes(16));

PHP 8.2以降ならRandom\Randomizerも選択肢

PHP 8.2以降では、より体系的に乱数を扱うためにRandom\Randomizerを利用できます。

$randomizer = new Random\Randomizer();

$number = $randomizer->getInt(1, 100);

単純な処理であればrandom_int()でも十分ですが、Random APIを活用した高度な設計ではRandom\Randomizerが有力な選択肢です。

mt_rand()とrand()はセキュリティ用途では避ける

mt_rand()rand()は、暗号学的に安全な乱数を生成するための関数ではありません。

そのため、セキュリティトークンなどには利用しないようにします。

用途を整理すると、次のようになります。

用途主な方法
指定範囲の安全な整数random_int()
サイコロや抽選番号random_int()
セキュアなランダムバイト列random_bytes()
セキュアなトークンbin2hex(random_bytes())
配列から簡単にランダム選択array_rand()
一般的な擬似乱数mt_rand()
PHP 8.2以降の高度な乱数処理Random\Randomizer

PHPで乱数を生成する際の注意点

セキュリティが必要かどうかを最初に判断する

乱数を生成するときは、単に値がバラバラに見えればよいのか、それとも第三者に予測されてはいけないのかを判断することが重要です。

ゲームの演出などではセキュリティが不要なケースもあります。

一方、認証やパスワードリセットなどでは安全な乱数が必要です。

後者では、random_int()random_bytes()、適切なRandom\Randomizerを利用します。

乱数だけをIDとして扱う場合は設計に注意する

ランダムな数字を生成しただけでは、必ずしも一意になるとは限りません。

例えば、

$id = random_int(100000, 999999);

としても、過去に生成した値と重複する可能性があります。

絶対に重複してはいけないデータベースの主キーなどでは、乱数だけに依存せず、一意性を保証できる仕組みを別途設計する必要があります。

用途に合った関数を選ぶ

PHPには複数の乱数生成方法があるため、単純に「どれが一番速いか」だけで選ぶのではなく、必要な安全性やPHPのバージョンに合わせて選ぶことが大切です。

単純な整数ならrandom_int()、セキュアなバイト列ならrandom_bytes()、PHP 8.2以降で高度な処理をするならRandom\Randomizerと覚えておくと分かりやすいでしょう。

PHPで乱数を生成する方法のまとめ

PHPで乱数を生成する場合、最も簡単な方法の一つがrandom_int()です。

例えば1〜100の整数であれば、次の1行で生成できます。

$number = random_int(1, 100);

セキュリティトークンなどのランダムな文字列を生成する場合は、random_bytes()を利用できます。

$token = bin2hex(random_bytes(16));

また、PHP 8.2以降ではRandom\Randomizerが利用でき、整数やバイト列などの乱数処理を統一的に扱えます。

$randomizer = new Random\Randomizer();

$number = $randomizer->getInt(1, 100);

一方、mt_rand()rand()は暗号学的に安全な乱数生成方法ではないため、認証やトークンなどのセキュリティ用途には使用しないようにしましょう。

PHPで乱数を生成するときは、「どのような値を作りたいか」と「第三者に予測されてはいけない値か」を考え、目的に合ったAPIを選択することが重要です。

以上、PHPで乱数を生成する方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次