PHP – ランダム文字列の生成


str_shuffleによる方法~文字重複なし

概要

この方法の場合、

  • ランダム文字列中の文字は全て異なる
  • ランダム文字列の最大長は36文字

数字とアルファベットで36文字を使う場合、文字列長に対するパターン数は以下の通り(36!/(36-n)!)。

  • 5文字→45,239,040通り(約45百万通り、107のオーダー)
  • 10文字→922,393,263,052,800通り(約900兆通り、1014のオーダー)
  • 16文字→152,901,072,685,905,223,680,000(約1500垓通り、1023のオーダー)

丁寧に書いた場合

コード

処理の流れ

  1. ランダム文字列に使う文字ソースの文字列を準備しておく。
    • const RANDOM_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
  2. str_shuffle()関数により、文字ソースの文字をシャッフルした文字列を得る($shuffled_chars)。
  3. substr()関数により、シャッフルされた文字列の先頭から必要な文字数分を切り出す($random_string)

1行で書いた場合

確率

16文字のケースで考えると、1つのパターンが発生する確率はおおよそ1÷1.5×1023 = 6.54×10-23

仮に1秒間に100万個のパターンが生成されるとすると、同じパターンの再帰年数は1/6.54×10-23÷106÷60÷60÷24÷365.25≒4.8×109年、約50億年になる。

str_shuffleによる方法~文字重複あり

概要

この方法の場合、

  • ランダム文字列中に同じ文字が出現し得る
  • ランダム文字列の長さは任意

数字とアルファベットで36文字を使う場合、文字列長に対するパターン数は以下の通り。

  • 5文字→60,466,176通り(約6千万通り、107のオーダー)
  • 10文字→3,656,158,440,062,976通り(約3600兆通り、1015のオーダー)
  • 16文字→7,958,661,109,946,400,884,391,936(約8𥝱通り、1024のオーダー)

丁寧に書いた場合

コード

処理の流れ

  1. ランダム文字列に使う文字ソースの文字列を準備しておく。
    • const RANDOM_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
  2. 文字ソースの文字列をランダム文字列の文字数分だけ繰り返した文字列を得る($chars)。
  3. その文字列をシャッフルする($random_chars)。
  4. シャッフルされた文字列の先頭から、ランダム文字列の文字数分だけ切出す($random_string)。

1行で書いた場合

確率

16文字のケースで考えると、1つのパターンが発生する確率はおおよそ1÷8×1024 = 1.25×10-25

仮に1秒間に100万個のパターンが生成されるとすると、同じパターンの再帰年数は1/1.25×10-25÷106÷60÷60÷24÷365.25≒2.5×1011年、約2500億年になる。

array_mapによる方法~文字重複あり

概要

この方法による結果は、str_shuffleによる方法と等価。

丁寧に書いた場合

コード

処理の流れ

  1. ランダム文字列に使う文字ソースの文字列を準備しておく。
    • const RANDOM_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
  2. ランダム文字列のための配列を準備する($source_array)。文字列長分の長さの配列を確保すればよいので、ここではarray_fill()を使っている。
    • [0, 0, 0, ..., 0]
  3. 文字ソース文字列からランダムに1つの文字を返すコールバック関数を定義する($random_char)。
  4. ランダム文字を要素とする配列を得る($random_chars)。最初に準備した$source_arrayの各要素にコールバックをマッピングしている。
    • ['a', '9', 'i', ..., 's']
  5. ランダム文字配列をimplode()関数で文字列に変換する($random_string)。
    • 'a9i...s'

1行で書いた場合

先のコードを1行で書くとこうなる。

1行が長くなりすぎるので複数行にすると以下のようになる。

確率

この方法による確率は、str_shuffleによる方法と同じ。

 

PHP – foreachでの参照渡しとunset

概要

  • foreachは値渡しなので、要素の変更が反映されない
  • 要素を参照渡しにすると変更ができる
  • ただしループ終了直後にunsetが必要

雛形としては以下のようになる。

PHP-Manual: foreach

警告

foreach ループを終えた後でも、 $value は配列の最後の要素を参照したままとなります。 unset() でその参照を解除しておくようにしましょう。 さもないと、次のような目に遭うことになるでしょう。

・・・・・

foreachでは要素の変更ができない

以下のように、foreachで配列の要素などを変更しようとしても、値渡しのため変更が反映されない。

参照渡しで変更ができる

以下のように要素を参照で渡すと(要素の変数の前にに'&'を付けると)変更ができるようになる。

注意点

以下のように、その後に再びforeachで内容を参照すると、最後の要素が繰り返し呼ばれてしまう。

unsetによる参照のクリア

参照でforeachを実行した後にunset()でその参照変数をクリアすることで、その後のループは正常に回る。

補足

参照でforeachを回した後、unset()せずに通常のforeachを実行すると、その後にunset()しても最後の要素が指された状態が残ってしまうようだ。

 

PHP/HTML – 同一フォームの複数送信ボタン

概要

1つのフォームに複数のsubmitボタンを配置して、ボタンごとの処理を分ける方法

  • $_POST変数で押されたボタンのname属性をキーとする連想配列が定義され、他のボタンは定義されない。
  • isset($_POST[処理するボタンのname属性値])がtrueの場合にボタンに対応する処理を書く。

確認コード

コード概要

  • 1つのFORMに3つのsubmitボタンを配置
  • 押されたボタンのname属性をキーとする連想配列が定義され、押されなかったボタンの連想配列は定義されない
  • 同じFORM内に配置されたINPUT要素は常に$_POSTに定義される
  • クリア用のボタンはGETを送信

処理結果

開始直後:ボタンのみ表示される。

送信1ボタンを押したとき:

送信2ボタンを押したとき:

クリアボタンを押したとき:表示はクリアされる

 

PHP – 雛形 – 複数の例外のチェーン

概要

複数のエラーを補足していって、まとめて表示するなどの処理をしたい場合がある。たとえばユーザーIDとパスワードの妥当性をチェックし、それぞれのエラーを全て表示したいような場合。

このようなケースで例外を使うと、例外が発生した場合や意図的にスローした場合、その時点で例外処理に移行するので、複数のエラーを扱い難い。

Exception::getPrevious()

Exceptionクラスのコンストラクターは、3つの引数をとることができる。

public __construct (string $message = "", int $code = 0, Throwable $previous = null )

3つ目の引数はThrowableで、この引数にExceptionオブジェクトを指定すると、新たに生成されたオブジェクトの前に発生したExceptionオブジェクトがスタックに保持される。

$new_ex = new Exception('this exception', 0, $previous_ex)

最終的に得られたExceptionオブジェクトのスタック上にそれ以前のExceptionオブジェクトがある場合、getPrevious()メソッドで直前のオブジェクトが得られる。直前のExceptionオブジェクトがない場合はgetPrevious()の戻り値はnullとなる。

基本形

これらのことを利用して、複数の例外を途中で抜け出さずに蓄積する方法は以下のようになる。

  • 2行目の初期化は、1番目・・・n番目のどこが最初の例外をスローするかが確定していないため必須。
  • 17~19行目、適切な位置で例外をスローしないと反映されないが、例外が発生していない場合はスローしない

実行例

以下は実行例。1つ目と3つ目の条件式がfalseなので例外が積み重ねられ、2つ目はtrueなのでそのまま表示、そのあとに積まれた例外が表示される。

関数化・配列への格納

Exceptionオブジェクトの積み込みと、例外発生時のみの例外スロー、スタックされた例外に対する操作を関数化しておくと、再利用しやすく可読性も上がる。

18行目で配列の順序を反転し、スタックに積まれた順番(LIFO)からFIFOのリスト順にしている。

これらの関数を使って先と同じ処理をするコード

関数での例外発生のトレース

最初の関数で発生した例外への過程をスタックに残すには、以下のように関数内で前の例外を登録してスローする。

 

 

 

PHP – 雛形 – セッション

概要

セッション開始時

  • セッション開始時、サーバー側でセッションIDを生成する
  • サーバーはセッションIDをサーバー内に保存し、それをクライアントにCookieで保存する
  • クライアントはセッションIDをサーバーに送り、継続中のセッションでの接続を要求する
  • サーバーは送られてきたセッションIDのセッションが生きていればそのセッションで接続を続ける

セッション処理中

  • サーバーはセッション変数を通して必要な情報を得たりセットしたりする
  • セッション継続中、セッション変数の内容は保持される

セッション終了時

  • セッション変数をクリアする
  • クライアント側のセッションクッキーを完全に削除する
  • session_destroy()を実行する

処理の流れ

継続中セッションの確認

セッション中かどうか確認する必要がある場合、以下で確認。

isset($_COOKIE[session_name()])

セッション開始

  1. session_start()を実行する
  2. セッションで使用しているセッション変数($_SESSION['キー'])が定義されているか確認する
    • 定義されていなければセッション変数を定義してセッション開始
    • 定義されていればセッション継続

セッション終了時

  1. session_start()を実行する
  2. セッション変数をクリア($_SESSION = [])
  3. setcookie()でクライアントのセッションクッキーを削除
  4. session_destroy()を実行

セッションクッキーの名前はsession_name()で得られる。

セッションクッキーの削除

一般的なクッキーの削除は第3引数まで設定すればよいが、セッションクッキーの削除は第4引数のpathまで指定する必要がある。通常、セッションクッキーのパスはドキュメントルート('/')。

setcookie(session_name(), '', 0, '/')

さらにクライアント上のサーバーに関するドメインやセキュリティー設定情報も確実に消すために、session_get_cookie_params()で取得したパラメーターをsetcookie()の全引数に設定するのがよい。

 

PHP – 文字列のトークンによる分割

概要

strtok()は文字列を指定したトークンで分割する。

イテレーターとして順次取り出す操作に注意。

処理の流れ

以下のように、最初に文字列を指定してstrtok()を実行した後、文字列指定なしで実行し、切り分けられる文字列がなくなった時に結果がfalseになる。

一般的な利用例

以下のように複数のトークン文字を指定できる。

 

 

PHP – 配列の先頭・末尾要素の追加・抜き出し(破壊的)

概要

以下の関数は、配列の先頭・末尾に要素を追加し、要素を抜き出す。これらの関数は元の配列を変更する。

  • unshift()は配列の先頭に要素を追加し、要素数を返す
  • shift()は配列の先頭から要素を1つ抜き出す
  • push()は配列の末尾に要素を追加し、要素数を返す
  • pop()は配列の末尾から要素を1つ抜き出す

ただし連想配列については、これらの関数がうまく機能しない、臨んだ値を返さないということがあり、別の処理が必要。

非破壊的に先頭・末尾の要素を取り出す方法はこちら

配列

unshift()/shift()~先頭要素の追加・抜き出し

unshift()は配列の先頭に要素を追加し、追加後の配列の要素数を返す。追加後の配列は変更される。追加する要素は複数指定加可能。

unshift($array, $value[, $value ...]) : $size

shift()は配列の先頭から要素を1つ抜き出して返す。抜き出した後の配列は変更される。

shift($array) : $value

push()/pop()~末尾要素の追加・抜き出し

push()は配列の先頭に要素を追加し、追加後の配列の要素数を返す。追加後の配列は変更される。追加する要素は複数指定加可能。

push($array, $value[, $value ...]) : $size

pop()は配列の先頭から要素を1つ抜き出して返す。抜き出した後の配列は変更される。

pops($array) : $value

連想配列

unshift()/push()はよくない

連想配列への追加は、要素としてではなく配列として追加されてしまう。

shift()/pop()は値だけ返る

キーの取得が必要ない場合は、これらを使える。

結合演算子’+’は使える

配列の結合演算子を使えば、先頭・末尾への追加が素直にできる。

array_slice()とunset()による書き方

連想配列のキー・値のセットで取り出したい場合に、array_slice()で先頭や末尾要素を取り出し、そのキーを使ってunset()する方法。reset()end()でもよい

 

PHP – 配列の要素を削除する

配列

配列の要素の削除はunset()関数を使う。このときインデックスが飛んでしまうので、振りなおす場合はarray_values()で新たな配列として取り出す。

連想配列

連想配列の要素の削除はキーを削除してunset()を実行すればよい。

 

PHP – 配列の先頭・末尾・部分配列の取出し(非破壊的)

概要

配列の先頭・末尾要素をunshift()pop()で取り出すと、元の配列が変更される。

これを変更せずに要素や部分配列を取り出したいときの方法。

配列

reset()/end()

配列の先頭・末尾を取り出すことに特化した関数はないが、ポインターを先頭に巻き戻すrest()関数、最後に移動させるend()の戻り値が移動後の要素を指していることを利用する。

ただし、これらの関数がポインターを移動させるため、独自にポインターを使っている中で使うと副作用が生じ得る。

array_slice()

array_slice()関数は、0から始まるオフセットと長さを指定して配列の任意の部分配列を取り出す。

array_list($array, $offset, $length)

連想配列

連想配列の場合、array_slice()は想定通りに機能するが、先頭要素や末尾要素の場合は少し面倒。

部分列~array_slice()

部分列が連想配列として得られる。

先頭要素

reset()の引数に連想配列を指定した場合の戻り値は連想配列の「値」のみ。キーを取得するには、reset()で先頭に巻き戻された元の配列のキーを取得する。

ポインターを移動させるため副作用が生じ得る。

array_slice()を使ってもよく、この場合は副作用は生じない。

末尾要素

末尾要素は、reset()と同じようにend()関数を使ってポインターを移動させる。

ポインターを移動させるため副作用が生じ得る。

array_slice()を使っても末尾要素を取り出せるが、少しややこしくなる。

 

PHP – 雛形 – JSの確認ダイアログとの連動

概要

PHPでHTMLを操作する際、formからの送信前にユーザーに確認して、OKの場合だけ値をPHPで受け取る方法。

  • form要素のonsubmit属性で確認用の関数のreturnを設定
  • JSで確認用の関数を準備(return true/false)
  • submitに対する処理を普通にPHPで記述

実装例

以下のHTMLでは、form要素の属性でonsubmit="return ask()“を設定。true/falseを返すask()関数は別途定義している。

HTML側でask()の結果がfalseのときは先へ進まず、trueの時にPOSTが実行される。

PHP側では、HTMLからPOSTされたときの処理を普通に書けばよい。

 

 

なお上の例では関数ask()を別に定義したが、以下のように直接confirm()関数を書き込んでも実行される。

<form onsubmit="return confirm('本気ですか?')">