ランダムな文字列生成
IDの発行などに、ランダムな文字列を生成するメモ
ランダムバイナリから変換する
openssl_random_pseudo_bytes()
は、暗号学的に強いアルゴリズムを使って疑似乱数バイナリを生成する。
/** * 数字+英小文字 * バイナリを36進数(=10数字+26小文字)に変換する */ function random($n = 8) { $str = base_convert(bin2hex(openssl_random_pseudo_bytes($n)), 16, 36); return substr(str_pad($str, $n + 1, '0', STR_PAD_LEFT), 1, $n); } // または /** * 数字+英大小文字+記号2種(_-) * バイナリをbase64_encode()した後、URLに使えない'/+'を'_-'に変換する */ function random($n = 8) { return strtr(substr(base64_encode(openssl_random_pseudo_bytes($n)),0,$n),'/+','_-'); }
36進数に変換する方法の諸注意
最上位の偏り
openssl_random_pseudo_bytes(n)
を数値に変換すると、進数とnの値によって最上位の数字に偏りが生じやすい。例えば36進数でn=8だと、最上位は1, 2, 3が全体の6割以上になる。最上位を省き、2桁目から取り出すと多少はランダム性が増すだろう。
文字数の不足
非常に低い確率だが、数値に変換するということは、先頭に0が続いた場合に文字数が小さくなる可能性があるということだ。文字数固定で使いたいならstr_pad()でゼロ埋めするなどの対処が必要。
通常はbase64の方法を使った方がよいだろう。諸般の事情で大文字や記号を使えず、36進数に変換する方法を取るなら、以上の点に留意する。
マスタを用意
候補文字マスタを定義する。PHPは、文字列strのn文字目をstr[n]
で取得できるため、マスタは候補文字を羅列した文字列でよい。
1文字ずつ選択・結合
- メルセンヌツイスタを利用して乱数生成
- 文字種は自由に決められる。(a-z,A-Z,0-9で62文字)
- 似た文字は除外するなど、柔軟な仕様も可
function makeRandStr($length = 8) { static $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789'; $str = ''; for ($i = 0; $i < $length; ++$i) { $str .= $chars[mt_rand(0, 61)]; } return $str; }
- PHPのメルセンヌツイスタにはバグがあるが、一応、乱数の品質としては問題ないらしい
- ただし、閾値によっては偶奇が偏るらしい
同じ文字を連続させない
- 需要があるか知らんけど、一応
function makeRandStr($length = 8) { static $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789'; static $chrLen = 62; $pos = mt_rand(0, $chrLen - 1); $str = $chars[$pos]; for ($i = 1; $i < $length; ++$i) { $pos = ($pos + mt_rand(1, $chrLen - 1)) % $chrLen; $str .= $chars[$pos]; } return $str; }
同じ文字を使わない
- str_shuffle()を使うことでシンプルに記述できる
function randStr($length = 8) { static $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789'; return substr(str_shuffle($chars), 0, $length); }