なーんか難しいんだか変なんだかよくわからない事してるコードですね。
$chars[ord($bytes[$i]) % $charsLen]
の部分はどういう意味になるのでしょうか?
これはですね、ややこしいです。まず、$charsですが、これは
PHP
1$chars = preg_split('//', $elem, -1, PREG_SPLIT_NO_EMPTY);
ここで生成していて、$elemに格納されている文字列を1文字ずつ区切って連番配列に格納しています。$elemが10文字あったら、10個要素のある配列になります。たとえば、ここから渡されてくる文字列などですね。
PHP
1echo generateSecureRandomString(15, 'abcde_!#$@') . '</p>';
結果はこんなんになっています。
PHP
1Array
2(
3 [0] => a
4 [1] => b
5 [2] => c
6 [3] => d
7 [4] => e
8 [5] => _
9 [6] => !
10 [7] => #
11 [8] => $
12 [9] => @
13)
そして、その長さは、
PHP
1$charsLen = count($chars);
で、測っています。上記の場合、abcde_!#$@ で10文字ですから$charsLenには10が入ります。
次に$bytesですが、
PHP
1$bytes = openssl_random_pseudo_bytes($length);
ここで生成していて「希望するバイト長($length)の疑似乱数バイト文字列を生成」しています。つまり、ここで命題の「ランダム」な要件を満たしているわけですね。
で、ここではランダムなバイト文字列を生成しただけなので、使用して良い文字とかは全く考慮されていないです。
ですから、最初に定義してある$chars配列の要素をこのランダムさを利用して必要文字数分だけ取り出そう、というわけです。
$bytesは長さ$lengthのバイト文字列だったので、for文を使って$bytesの最初の文字から最後の文字までを1つずつ取り出し、ord()します。
int ord(string $string)
$stringで渡された文字列の最初の1文字目のASCIIコードを整数で返す関数です。
ordで先頭を指定する必要はないのではないのでしょうか?
についてですが、ここでord()を使っている意図は先頭を取り出すことにあるのではなく、ランダムな整数 を得る事にあります。
取り出された要素は一つしかない以上、その要素は必ず先頭になるので
についてですが、ord()に渡しているのは$bytesではなく $bytes[$i] つまり文字列ではなくバイト文字1文字であって、for()ループの毎に、0文字目から最後の文字まで1文字ずつ変わっていくわけです。
※コマンドラインでこんな事を数回繰り返してみると、ランダムな数字が10個生成されていて、それが得られるのがわかります。バイト文字なので、0から255までのランダムな数字になります。
# php -r "$bytes = openssl_random_pseudo_bytes(10); for($i=0; $i < strlen($bytes); $i++){ echo ord($bytes[$i]) . \"\n\"; }"
130
160
69
116
180
250
89
198
21
248
さて、これで、ランダムな整数が取得できました。それがこの太字の部分です。
$chars[ ord($bytes[$i]) % $charsLen]
ですが、これではまだ、ランダムな整数なだけで文字ですらなく、使って良い文字も全く考慮していません。
そこでこのランダムな整数を、使ってよい文字の範囲に制限したいわけですが、ここで出てくるのが % です。余りを求める記号(合同算術のmodular)ですね。
ある整数Aを別の整数Bで割った余りは、必ず0から(B-1)の範囲に収まる、という性質を利用しています。
それが、以下の太字の部分です。$charsLenには、使用してよい文字種の個数が入っていましたね?
$chars[ ord($bytes[$i]) % $charsLen ]
これで、0から(使用してよい文字種の数 - 1)までのランダムな数 が求められたので、晴れて、使用してよいランダムな文字が求まるわけです。$charsは使用してよい文字の配列でしたね。
$chars[ord($bytes[$i]) % $charsLen]
あとはこれを、必要な文字数分だけfor文で繰り返すことで、必要な文字数の使用してよいランダムな文字列 を得るだけです。
というわけで、提示のコードは OpenSSLの乱数を使ったとても高度なランダム文字列の生成 を行うコードなわけですが、たかがランダム文字列を生成するためにこんな盛大に難解な事する必要どれ程あるの? っつーのが、正直な感想です。どうせ、仮パスワードに使うくらいの事なんでしょ?って。
またpreg_split('//は何にでもマッチするという意味になるのでしょうか?
preg_split() なので、マッチを求めているのではなく、分割しています。
パターンに空文字列( // )を指定しているので特定の文字・パターンに関係なく区切られ、結果は、文字が1文字ごとにバラバラにされた配列として得られます。