Laravel:数字8桁の一意の乱数を生成する5つの方法
- 2021.10.05
- php備忘録
- Laravel, mt_rand(), openssl_random_pseudo_bytes(), PHP, rand(), random_bytes(), random_int(), スクリプト備忘録, 乱数

Laravel:数字8桁の一意の乱数を生成する5つの方法
例えば、サーバーが決めたIDでログインさせるなど「自動採番で8桁数字を生成してログインに使えるようにして」とか。
SMSで認証コードとして利用する乱数も数字になること多いですよね。
PHPでランダム値を生成する方法は全部で5通り
- rand()
- mt_rand()
- random_int()
- random_bytes()
- openssl_random_pseudo_bytes()
ちなみに私はopenssl_random_pseudo_bytes()
で書いたら「opensslは遅いから他ので」と言われた経験があります。
rand()の場合
8桁で調整しようとした場合次のようになります。
1 2 3 4 |
$code = str_pad(rand(0,99999999),8,0, STR_PAD_LEFT); echo 'rand:'.$code; #結果 => rand:48625668 |
rand($min, $max)
という形で最小値と最大値を指定するとその間の値をランダムに取得します。
上のコード例だと、0〜99999999までの値を取得するように指示しています。
当然ですが、6桁や7桁、1桁の数字も選択肢に入っています。
詳細は後半にまとめますが、str_pad
についてはゼロ埋めの指示です。8桁まで左からゼロで埋めてねと記載してあります。
公式を確認すると次の様なメッセージが記載されています。
警告 この関数が生成する値は、暗号学的に安全ではありません。そのため、これを暗号として使ってはいけません。暗号学的に安全な値が必要な場合は、random_int() か random_bytes() あるいは openssl_random_pseudo_bytes() を使いましょう。
mt_rand()の場合
【メルセンヌ・ツイスター乱数生成器を介して乱数値を生成する】乱数の生成方法です。
メルセンヌ・ツイスター乱数生成器の詳細についてはWikiにてご確認ください。(私は説明できません)
乱数生成器が異なるだけで、使用方法はrand()と同じです。
そのため、記載するコードも同じです。
1 2 3 4 |
$code = str_pad(mt_rand(0,99999999),8,0, STR_PAD_LEFT); echo 'mt_rand:'.$code; #結果 => mt_rand:79747812 |
ちなみに、先程の警告はこちらの公式でも同様に表示されています。
random_int()の場合
細い所はよくわかりませんが、公式曰く「暗号的にランダムな整数を生成」してくれる様です。
ログイン関係の実装でランダム数値が必要な場合はrand()
やmt_rand()
ではなくこちらを利用しましょう。
コードの書き方はrand()
/mt_rand()
と同じでrandom_int($min, $max)
です。
1 2 3 4 |
$code = str_pad(random_int(0,99999999),8,0, STR_PAD_LEFT); echo 'random_int:'.$code; #結果 => random_int:34857957 |
random_bytes()の場合
暗号論的に安全な、疑似ランダムなバイト列を生成する命令です。
バイト列(バイナリデータ)なので、出力するためには変換してあげる必要があります。
よくやる方法が16進数に変換【bin2hex()】した後に10進数に変換【hexdec()】するという方法です。(数字が欲しい場合)
1 2 3 4 5 6 7 8 9 10 |
$code = str_pad( ( hexdec( bin2hex(random_bytes(3)) ) ),8,0, STR_PAD_LEFT ) echo 'random_bytes:'.$code; #結果 => random_bytes:11086536 |
括弧の数わかりにくくなるので改行して書いてみました。
1行にしてしまうとこんな感じです。
1 |
$code = str_pad((hexdec(bin2hex(random_bytes(3)))),8,0, STR_PAD_LEFT) |
バイト数の指示は【3】で丁度8桁までが生成されます。
openssl_random_pseudo_bytes()の場合
【疑似ランダムなバイト文字列を生成する命令です】と公式には書いてありますが、理解はできていません。
random_bytes()
と同じく、バイナリデータとして出力される為、数字に直すためにはひと手間必要です。
1 2 3 4 5 6 7 8 9 10 |
$code = str_pad( hexdec( bin2hex( openssl_random_pseudo_bytes(3,$cstrong) ) ),8,0, STR_PAD_LEFT ) echo 'openssl_random_pseudo_bytes:'.$code; #結果 => openssl_random_pseudo_bytes:02436601 |
またrandom_bytes()
と比べて動作が遅いというデメリットがある様ですがrandom_bytes()
が利用されるのは重複な出力可能性が低いからなのだそうです。※低いだけで可能性はあるのでUniqueな値にしたい場合は回避手段の構築が必要になります。
ちなみに、生成するバイト数と数値の桁数の関係は次の通りです。※random_bytes()
とopenssl_random_pseudo_bytes()
は同じ
- hexdec(bin2hex(random_bytes(1))) => 3桁
- hexdec(bin2hex(random_bytes(2))) => 5桁
- hexdec(bin2hex(random_bytes(3))) => 8桁
- hexdec(bin2hex(random_bytes(4))) => 10桁
- hexdec(bin2hex(random_bytes(5))) => 12桁
- hexdec(bin2hex(random_bytes(6))) => 15桁
- hexdec(bin2hex(random_bytes(7))) => 17桁
8文字に足りない時は左からゼロ埋めする str_pad($int, 8,0, STR_PAD_LEFT)
str_pad
は【文字列を固定長の他の文字列で埋める】という命令です。(公式)
第3引数に埋める文字文字(数字)を入れ、第4引数に埋め方のタイプを記載します。(第4引数を指定しない場合は右梅になる)
第4引数pad_typeについて
3パターンの指示ができます。
STR_PAD_RIGHT: 左に詰めて右側を指定文字で埋める
STR_PAD_LEFT :右に詰めて左側を文字で埋める
STR_PAD_BOTH :中央に来るように左右を文字で埋めるこ※均等にならなない場合は右側増加
一意の値にするためにはチェッカーが不可欠
ログインの時に認証パスを入れたAuthorizationを書く事が多くなったような気がします。
認証パスの場合は重複を気にする必要ありませんが、ログインIDとして利用したり、uuid的な使い方をする場合には注意が必要です。
理由は、どの手段でも「値が重複しない事を保証はしていない」からです。
どの手段であっても、状況とタイミングによっては重複する値を生成する可能性があります。
そんな時は、重複チェック用のプロセスを1つ用意し「これを通ると誰かが使っている値」か否かの判断をつける事ができます。
簡単なのはIfの分岐ですが、Uniqueではない値を連続して取得してしまう可能性を考慮してwhileを利用するなど工夫してみましょう。
まとめ
自動採番と言っても手段はたくさんあるんですね。
ちなみに、この記事の元ネタは私がよく使っていたopenssl_random_pseudo_bytes()
でコードを書いた所「遅いから他のにして」と言われたことに起因しています。
-
前の記事
Laravel:5億越えのレコードをEloquentで消費メモリを抑えながら処理する方法 2021.10.04
-
次の記事
DynamoDBとの接続でlaravel-dynamodbを使ってみた感想 2021.10.06
コメントを残す