今回のPHPポケモンでは内部の作り込みをしていきます。見た目への反映は無いので、プレイを楽しみにしている人や、ポケモンが好きで毎日チェックしてくれているような人は、ブラウザをバックしてもらって問題ありません。
それでは、前々回辺りから保留にしていた「メッセージIDに重複回避対策」についてです。
引き継ぎ処理による重複対策
メッセージIDが重複しないように、セッションに生成した値を格納していましたが、処理が終わればすべて破棄して次の処理へ移行しています。その関係上、技習得で導入したレスポンスやメッセージ、モーダルの引き継ぎで「引き継いだデータで使用されているIDと重複する可能性」が出てきてしまいました。
接頭語を除けば16桁のランダム文字列を生成しているため、これが多くても10個程度の新しく生成したIDと重複する可能性はほぼ0%と言っても良いでしょう。しかし、問題は「0ではない」ということです。0ではない以上は対策が必要です。
それでは対策方法についてです。
メッセージの引き継ぎがあった場合、そのIDを重複チェック用のセッションに格納します。これをメイン処理の前に行うことで、メッセージIDの重複チェックの対象に引き継いだメッセージIDも含めていきましょう。
まずはコントローラーの親クラスのコンストラクタに以下の判定を追加しましょう。
コントローラー(/App/Controllers/Controller.php)
// メッセージIDの重複回避
if(
isset($_SESSION['__data']['before_reponses'])
|| isset($_SESSION['__data']['before_messages'])
|| isset($_SESSION['__data']['before_modals'])
){
$this->avoidMessageId();
}
issetを使って、引き継ぎがされているかをチェックしています。もし該当セッションにデータが格納されていれば、avoidMessageIdというメソッドを呼びだして、既に使用されているメッセージIDを取り出します。
array_filterを使った抽出
それでは、IDの抽出方法について見ていきましょう。これにはarray_filter関数を使用します。
array_filter(PHP.net)
第1引数に対象となる配列を指定し、第2引数でコールバック関数を指定します。
第1引数では、メッセージIDを値とした配列を指定する必要があるので、技習得時に引き継ぎ処理でも使用したarray_keysまたはarray_columnを使いましょう。
レスポンスからIDを抽出
array_keys($_SESSION['__data']['before_responses'])
メッセージからパラメーターを抽出
array_column($_SESSION['__data']['before_messages'], 1)
モーダルからパラメーターを抽出
array_column($_SESSION['__data']['before_modals'], 0)
レスポンスにはキーとして、メッセージには配列の2番目(添字1)、モーダルには1番目(添字0)にメッセージIDが格納されています。なので、それぞれに合わせた方法で取り出しました。
ですが、これではレスポンスの場合はメッセージID以外の添字やキー、メッセージとモーダルには空の値などが含まれることになります。なので、ここからメッセージIDのみを抽出するため、array_filterの第2引数のコールバック関数で判定を行います。
// array_fillterコールバック用関数
function callback($msg_id){
// 行頭にmsgがついているかを判定
return preg_match('/^msg/', $msg_id);
}
preg_matchを使って正規表現で、行頭がmsgで始まるものだけを判定しました。もし一致すれば1、一致しなければ0が返り値となり、1が返った場合だけarray_filterの返り値として配列に格納されていきます。
例として、レスポンスからメッセージIDを抽出する処理を見てみましょう。
// 格納用の空配列
$results = [];
// array_fillterコールバック用関数
function callback($msg_id){
// 行頭にmsgがついているかを判定
return preg_match('/^msg/', $msg_id);
}
// レスポンス
if(isset($_SESSION['__data']['before_responses'])){
$results = array_filter(
array_keys($_SESSION['__data']['before_responses']),
'callback'
);
}
メソッド内でコールバック用の関数を事前に用意しておき、array_filterで呼び出しています。抽出結果は変数の$resultsに格納していきます。
array_diffによる差分チェック
次に、抽出したIDの差分チェックを行います。例えば、レスポンスとメッセージを引き継いだ場合、もちろんそこには同じIDが含まれることになります。重複チェック用のセッション内で既に重複した値が入っているのはおかしいので、もし取り出した値が既に抽出されていなければ結果へ追加するという処理が必要になります。
そのために配列の差分をチェックするarray_diff関数を使用します。
array_diff(PHP.net)
第1引数には比較元の配列、第2引数以降には比較対象の配列が入ります。今回のケースであれば、まずレスポンスからメッセージIDを抽出して$resultsに格納しています。メッセージからメッセージIDを抽出して、その結果が$resultsとかぶっているものは必要ありませんね。
array_diffでは、比較元(第1引数)の中で比較対象(第2引数以降)には無い値を配列として返り値にするので、第1引数に新しく取り出した配列、第2引数に$resultsを指定して差分を取り出します。
メッセージからIDを抽出するケースを見てみましょう。
$msg_ids = array_filter(
array_column($_SESSION['__data']['before_messages'], 1),
'callback'
);
$diff = array_diff($msg_ids, $results);
これで$diffに、既に取り出されたメッセージID以外のメッセージIDが格納することができます。
最後にこの結果を$resultsに追加するためのarray_mergeを使用しましょう。
// メッセージ
if(isset($_SESSION['__data']['before_messages'])){
$msg_ids = array_filter(
array_column($_SESSION['__data']['before_messages'], 1),
'callback'
);
// 結果の差分を格納
$results = array_merge($results, array_diff($msg_ids, $results));
}
これで差分だけを$resultsに追加することができました。
以下、avoidMessageIdメソッドの最終コードです。
/**
* メッセージIDの重複回避
*
* @return void
*/
private function avoidMessageId()
{
// 格納用の空配列
$results = [];
// array_fillterコールバック用関数
function callback($msg_id){
// 行頭にmsgがついているかを判定
return preg_match('/^msg/', $msg_id);
}
// レスポンス
if(isset($_SESSION['__data']['before_responses'])){
$results = array_filter(
array_keys($_SESSION['__data']['before_responses']),
'callback'
);
}
// メッセージ
if(isset($_SESSION['__data']['before_messages'])){
$msg_ids = array_filter(
array_column($_SESSION['__data']['before_messages'], 1),
'callback'
);
// 結果の差分を格納
$results = array_merge($results, array_diff($msg_ids, $results));
}
// モーダル
if(isset($_SESSION['__data']['before_modals'])){
$msg_ids = array_filter(
array_column($_SESSION['__data']['before_modals'], 0),
'callback'
);
// 結果の差分を格納
$results = array_merge($results, array_diff($msg_ids, $results));
}
// 重複回避用セッションに格納
$_SESSION['__message_ids'] = $results;
}
これで、引き継ぎも含めたメッセージIDの重複を回避することができるようになりました。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは、引き継ぎも含めたメッセージIDの重複回避方法をご紹介しました。
プログラミング上達のコツは「どうなっているか」がどれだけ理解出来ているかです。コピペでもサイトやシステムは動作しますが、それを本格的に自分のものにしていくためには、やはり「どの処理で、どの値が、どうなっているか」をしっかりと理解できていなければなりません。
また、便利な関数をどれだけ使いこなせるかも、よりスマートかつ負担の少ないシステムを作り上げるためには必要なことです。そして、関数すべてを覚える必要はありません。必要に応じて再現できる関数があるかどうかを公式マニュアルなどを参考にしながら探して、それを使えれば良いのです。
その中でも、コールバック関数を引数として必要とするような関数は、その結果や記述方法がややこしくなりがちです。そういったときは、オンラインエディターなどを使って、細かくどういった値が返却されるのかをチェックしていくと良いでしょう。
現在プログラミング学習に取り組んでいる方や、WEBプログラミングに興味がある人は、ぜひ参考にしてみてくださいね。