2010.07.21
ジョン・パリス著

RPGの観点からみたPHP-パート4:PHPの詰め合わせ

データの取り出し/表示、エラー処理、セッション・データの保存について掘り下げる

RPGの観点からみたPHPシリーズの今回は、基本的なデータベースへのアクセスからエラー処理、セッション・データの保存などといった、いくつかの話題を取り上げます。何らかの形でデータベースへのアクセスをしないアプリケーションはほとんどありませんから、これ以上は余計なことは言わず、早速始めましょう。

最初の例ではSQLiteというデータベース・システムを使用します。たとえばDB2 for iやMySQL等の代わりにSQLiteを選択したのは、本シリーズをお読みになっている皆さんがお使いになっているPHPの実装がどのようなものであっても、SQLiteは2004年のPHPバージョン5以降のどのバージョンのPHPにも組み込まれているからです。(SQLiteはIBM i版のPHPには組み込まれていますが、どういうわけかデフォルトでは利用可能な状態になっていないという点に注意してください。コントロール・パネルを使用してSQLiteを利用可能な状態にする必要があります。この手順がよくわからない場合は、手順を追ったビデオを見るを参照してください。) SQLiteを使用するつもりが全くない場合でも逃げないでください。PHPデータベースへのアクセスに関する原理は、どのデータベースを使用しても大体同じだからです。

SQLiteについては聞いたことがないという方もいらっしゃるかもしれませんので、「本当の意味での」データベースではないという印象を持たないように、SQLiteのウェブ・ページをクイックに見るをご覧になってください。そこで登場する名前のいくつかをご覧になってきっと驚かれるのではないでしょうか。私も個人的にSQLiteを毎日使っているということを知った時はショックだったと認めざるを得ません。というのもSQLiteがMac OS Xオペレーティング・システム、Safariブラウザ、iTunesの一部だからです。確かにiPhoneを使用していれば(iPhoneのなかなかよい国際電話プランに加入することができるのなら使用するのですが)、ほとんど毎日1日中SQLiteを使うでしょう。この小さくて悪魔のようにかわいいデバイスでもSQLiteが使われているからです。

簡単なデータの取り出しと表示

最初のアプリケーションで使用しているCUSTOMERSというテーブルは、顧客番号、顧客名、顧客所在地、所在地の州の郵便番号から構成される単純なものです。最初のプログラムは単にデータベースをオープンして基本的なselect操作を実行し、その結果をテーブル形式で表示しています。この簡単なスクリプトを実行した結果を図―1に示します。

全スクリプトを図―2に示します。ではこのスクリプトがどのように動作するのかを見て、ここで使用されている機能について見てみましょう。このスクリプトでは、データを取り出して表示するのに必要なロジックに焦点を当てており、データ処理ロジックと表示ロジックを分離することにはあまり気を使っていません。(この例で使用しているSQLiteデータベースを作成するスクリプトに限らず、本稿に登場するスクリプトはすべてSystemiNetwork.com/codeからダウンロード可能です。)

本スクリプトは、データを表示するためのウェブ・ページの開始やテーブルのヘッダなどを設定する、基本的なHTMLで始まっています。お気づきの通り、ここではどちらかというと旧式のHTMLスタイルとなっていますが(テーブルのコントロールにはCSSを使うべきでした)、ほとんどの人にとってわかりやすいスタイルなので、今回の例ではこれが適当と判断しました。

実際の動作は呼び出しAで始まります。ここでは、関数ShowErrMsg()を定義しています。PHPスクリプトでは多くの場合、この種の基本的なエラー・レポート用にdie()関数が使用されているのをよく目にしますが、私はメッセージを表示してスクリプトに処理を継続させたいので、独自のエラー・メッセージをフォーマットしてテーブル中に表示できるような関数を使用することにしました。この関数は、単にechoを使用してメッセージ・テキストをフォーマットされたテーブルの行中に埋め込むだけです。この例では、echoの複数のパラメータを受け取れる機能を使用して、可変のメッセージ・テキストとHTMLを結合し、HTMLテキスト内の二重引用符の処理を簡素化している点に注意してください。$msgTextを文字列中に埋め込んだだけでは、HTML中に出てくる二重引用符を処理するのにエスケープ文字(\)も挿入しなければなりません。結果は以下のようになりますが、ちょっと読み解きづらいかもしれません。

echo "<tr><td colspan=\"4\"> $msgText </td></tr>";

本当のデータベースの処理作業は、sqlite_open()関数で始まります(B)。最初のパラメータには、オープンするデータベースの名前が入っています。この例のようにファイルのフルパス名を入れてもかまいません。次の2つのパラメータはオプションで、データベースのオープン・モードとエラー・テキストのパラメータです。モードは0666にセットしました。これはデフォルト値です。PHPのマニュアルにはこのパラメータは無視されると明確に描かれていますので、これ以上このパラメータについては説明しません。ここでオープン・モードのパラメータをコードに記述したのは、エラー・テキストのパラメータを使用したかっただけです。このパラメータは、テーブルをオープンする際に発生したエラーに対して、適切なエラー・メッセージを入れることができるので便利です。ただし気をつけなければいけないのは、ファイル名自体に間違いがあっても必ずしもエラーにならない点です。なぜなら、SQLiteのデフォルトの動きはテーブルが存在しなければ、そのテーブルを作成するというものだからです。その結果、アクセス権の問題や存在しないパス名の時だけエラーが発生します。

オープン関数の結果はリソース変数$dbconnに代入されます。リソースは特殊なタイプの変数で、外部リソースへの参照を保持するために使用されます。私の例では、$dbconnは、それ以後のすべてのデータベース操作で使用する特定のデータベース接続を識別するのに使用されています。RPGではこれに相当するものはありません。なぜなら、すべての入出力操作には、一対一に対応する特殊なファイルがあるからです。C言語をご存じであれば、あるいはRPGでIFS APIを使用したことがあれば、この考え方をご理解いただけるでしょう。そうした経験がなくても心配しないでください。そのうちに完全に理解できますから。

オープンを発行したすぐ後に、スクリプトではデータベースへの接続が確立されたか否かをテストしています(C)。そしてもし確立されていなければ、エラー・メッセージを発行します。接続がうまくいったかどうかをテストするこの方法については、このシリーズのパート3の「真実性とは何か?」に解説がありますので、本稿ではここまでとします。

次のステップは、SQLクエリー自体を発行することです(D)。sqlite_array_query()関数は、2番目のパラメータで指定されたSELECT文を、1番目のパラメータで渡されたリソース$dbconnに適用します。関数名から想像できる通り、クエリーの結果は配列として返され、配列変数$selectedに代入されます。この配列には、SQL SELECT文にマッチした各データの行が1つの要素に格納されています。この例では、テーブル中のすべての行が格納されます。各要素自体は行中のすべての列を含んだ配列になっています。実際、リクエストでSQLITE_ASSOCオプションを指定したので、返された配列は連想配列になっていて、その中ではキーが取り出す列の名前になっています。この後すぐお分かりになりますが、名前を調整することでどのフィールド(おっとごめんなさい、どの列)が出力されるのかを簡単に識別できるようにできます。クエリーの結果として何も返ってこない場合(つまり空の結果セットが返ってきた場合)は、その結果に対するメッセージが発行されます(E)。

結果が返ってきた場合は、おなじみの友達foreachを使用して行をテーブル形式で出力します(F)。ここで出力される文字列はちょっと違っている点に注目してください。特に、波括弧({と})を使って、出力中に取り入れる配列変数の名前を囲んでいる点に注目してください。二重引用符で囲まれた文字列の中では、PHPは常に変数への参照を置換しますが、配列要素を参照しているときは「複雑な構文」をお勧めします。今回の例では、一重引用符と波括弧を使用しないで済むように$row[CUSTOMER]とコーディングする方法もありました。しかしこれでは保守的なPHPになってしまい、詳しくは述べませんが良くない方法とみなされてしまいます。詳細はphp.netのウェブサイトを参照してください。経験則として、(今回の例のように)二重引用符で囲まれた文字列内で文字の配列インデックスを持つ配列要素を使用する際は、波括弧{}を使用することをお勧めします。この「複雑な構文」(実際はそれほど複雑ではないのですが)は、PHPの例でよく見かけるもう1つの習慣です。

すべての行を出力したら、残っているのはデータベースへの接続をクローズすることです(G)。ここでも、関数sqlite_close()に対して接続識別子$dbconnをパラメータとして使用して、データベースをスクリプトからリリースさせます。スクリプトの残りの部分は、テーブルとウェブ・ページを閉じるためのHTMLです。

この例はとても簡単な例ですが、これをどのように発展させることができるかお分かりいただけたと思います。まず、使用されているSQLクエリーは選択基準を適用しているわけではなく、取り出されたデータにシーケンスを適用する以外のことはしていません。どんなクエリーでもかまわないのです。別の例としては、取り出された列名を使用して、どの列をどういう順番で表示するのかをユーザーに要求させるのがいかに容易であるかを考えてみてください。こうしたことはPHPでは容易にできることかもしれませんが、RPGの固定ファイル関係があるために、緑色画面の5250アプリケーションではほぼ不可能なことなのです。組み込みSQLを使用してデータを取り出す方法は問題の一部には対処しますが、表示上の問題は解決できません。

「サブ・ファイル」のページング例

System iを使用しているほとんどの組織で、現在保有しているデータを表示するアプリケーションはサブ・ファイルを使用しています。5250端末は、ブラウザとは違って表示できる行数に制限があります。その結果、出力をページのサイズごとに常に分割しなければなりません。ブラウザにはそのような制限はありませんが、データをページ単位で見たいような場合もあります。しかしちょっとした問題があります。ブラウザ・アプリケーションは従来のアプリケーションとは異なり、ステートフルではありません。つまり1つのメッセージとその次のメッセージの間では、自動的に記憶されるものは何もありません。ステートフルではないということによる影響がどのようなものであるかという詳細な議論は、本稿のスコープ外です。その効果は、RPGのプログラムがディスプレイ・ファイルに出力するたびにLRを使用するのと同じです。もしそうなればすべてのファイルはクローズされ、プログラム内に保持されていた合計値やその他のデータは失われ、次にそのプログラムが実行されるときはあたかも初めて起動されたかのように動作することになります。5250のRPGアプリケーションは通常LRをせずに戻りますので変数、ファイルの位置などの値を保持したままにしており、上記のような状況を回避しています。

しかしウェブの世界では一般的に、そして特にPHPの世界では、上記のような選択肢はありません。したがってどこにいたのかを「覚えておく」ための別の方法が必要なのです。たとえば、データの1ページ目を表示した後、ユーザーが「次のページ」を要求してきたときに正しいレコード・セットを表示できるように、データベース内の位置をもう一度指定できなければなりません。こうした状況に対処するのに使用できる方法はたくさんありそれぞれ異なります。HTMLフォーム中に隠しフィールドを用意したり、クッキーを使ったり、データベース中のエントリをユーザーに関連付けたり等いろいろです。ウェブ志向の言語のほとんどは、このような操作を直接サポートするように設計された機能を提供しています。PHPでのそのような機能の1つがセッション・サポートと呼ばれているもので、今回の例では私はこれを使用しています。

ページングをどのように行っているのかを説明するために、ここでは使用した基本的なページングロジックに焦点を当てます。セッションをどのように開始して終了するのか、セッション中で変数はどのように保存されるのか、の詳細については後述の「PHPにおけるセッション・コントロール」でもう少し説明します。この「PHPにおけるセッション・コントロール」では、セッション変数を試しに使用する基礎として利用できる簡単なプログラムが提供されており、これによりセッション変数がPHP内でどのように使用されてどのようにコントロールされるかがわかるでしょう。さてそこで図―3を見てください。この図は、[次のページ]と[前のページ]ボタンが表示された「サブ・ファイル」アプリケーションのイメージを示しています。このスクリプトのロジックは、この2つのボタンが意味のある時だけ利用可能になることを保証しています。

さてアプリケーションがどのようなものかはおわかりいただけたでしょうから、スクリプト(図―4)の中身を見てみましょう。Aでは、まだ説明していないPHPの機能が使われています。これはdefine()関数で、その機能は定数を設定することです。ここではNEXTとPREVという2つの定数を定義しました。定数名が大文字になっている点に注意してください。これはPHPでの定数の名前の付け方の習慣です。「古き良き」RPG/400の時代を感じさせませんか。ただし、これは習慣に過ぎず規則ではありません。それぞれの2番目のパラメータは定数の値を定めています。今回の場合、「次のページ」と「前のページ」です。スクリプトの後の方でこれがどのように使われるかがわかります。ここで使用している主な理由は、考え方を説明するだけではなく、ボタンに表示される文言を変更したい場合にこの定数の値を変えれば簡単にできるからです。

このスクリプトと以前紹介したスクリプトとの実際上の重要な違いが最初に出てくるのはBのところで、session_start()関数が起動されているところです。この関数はブラウザのセッションとスクリプトの関連付けを開始し、セッション内で以前に保存されていた任意の情報へアクセスできるようにします。

Cでは、スクリプト中で使用される2つの変数に初期値が代入されています。$maxは1ページに表示される最大の行数をコントロールし、$countは現在のページに表示した行数を保持しています。この小さな例ではテストを簡単にするために10行しか使用していませんが、ページの最大サイズを変更する(つまり変数$maxの中身を変える)ことで、好きな時にいつでもページングのサイズを変更できるようなシナリオも設定したかったのです。これは5250表示端末ではできないことでした。実際、HTMLフォームに「ページのサイズはどれくらいにしますか?」という入力フィールドを追加するだけで、1ページの行数をユーザーにコントロールさせることもできます。

次に、スクリプトは最初に呼び出されたのか2回目以降の呼び出しなのかを判断する必要があります。2回目以降の呼び出しであれば、リクエストは次のページへの移動だったのでしょうか、前のページへの移動だったのでしょうか。これらはすべてDで始まるロジックで判断できます。このスクリプトはまず、送信ボタンの1つが押されたのか否かを調べます。このテクニックは本シリーズのパート3で解説されています。つまりisset()関数を使用して、このスクリプトはボタンが押されたことで起動されたのか否かを判断します。もしそうであれば、このスクリプトが起動されたのは初めてではなく、セッション中に保存されている開始値を取り出すことができます。この値がどのように設定されたのかは後述しますが、今のところは、ここには結果セット中の行番号が含まれていて、次のページを表示する際にはその行から表示されるものだと理解してください。ボタンが押されたのだと確認したら、次にテストするのはどのボタンが押されたのかです(呼び出しE)。先に定義したPREV定数を覚えていますか。この定数が最初に活躍する場所がここです。もし今回のリクエストがPREVリクエストであれば、現在のページの開始値($start)で使用されている値からページ・サイズ変数($max)を減じることで、データベースへのクエリーの新しい開始値を計算しなければなりません。後でわかりますが、この値は、ページが作成された時にセッション変数lastStartに保存されていました。もしここまでのところがわかりづらい場合は、各最初の数ページが作成された時のプログラム変数とセッション変数の値を示した表を載せておきました(図―5)。

呼び出しFでは、もし今回の呼び出しが確かに1回目の呼び出しであることがわかった場合は、開始値を0(ゼロ)に設定し表の先頭から開始するようにします。これで、ゼロで開始することがおかしいことではないとおわかりいただけたと思います。さて開始点に正しい値を設定したので、次の行の呼び出しGではこれをセッション変数lastStartに保存します。前述の通り、この値はページに戻った時に取り出すことができます。

その後の数行はデータベースのオープン処理などで、これは前の例と同様です。変更したのはLIMIT節を追加したことだけです(H)。この節を使用すると、取り出しの際にどの行から開始するのか、一度に何行取り出すのかを指定することができます。これには何通りかのやり方があります。その1つが、本シリーズの次回でDB2を使用して説明する方法です。しかしこのSQLiteの拡張は便利で、同様の構文をほとんどのデータベースがサポートしているので、再利用できるテクニックです。もちろん、各クエリーでデータが同じ順序で取り出されることを保証するためには、ORDER BY節を使用しなければならないということになります。ORDER BY節を使用しないと、SQLは取り出しの順番を保証できません。

呼び出しIでは、レコードが取り出されない場合に関連するもう少し複雑な条件を処理しています。もう少し複雑というのは、1行も取り出せなかったのか、結果セットの最後までたどり着いたのでクエリーが失敗したのかを区別する必要があるからです。幸いなことに、開始値を見ればこれが簡単に判断できます。開始値がゼロ以外の値であれば、結果セットの最後までたどり着いたことになり、開始値がゼロであれば、SQLのクエリーがデータを1つも取り出せなかったことになります。
取り出したデータを表示させるためのforeachループは、表示済みの行数を数える命令(J)を加えた他は、以前の例と同じです。$count ++という書き方にしたのは、PHPではこれがかなり一般的な書き方だからです。RPGではこれを$count += 1;と書きます。
呼び出しKは前述したところで、セッション値startが保存されています。この値は「次のページ」リクエストが来た時の開始位置として使用されますので、現在の行数($count)を現在のページの開始位置($start)に加えたものとして計算されます。

すべての行が出力されたら、ページに[次へ]ボタンと[戻る]ボタンを追加する処理を開始します(L)。[前のページ]ボタンを表示するか否かを判断するのは比較的簡単です。開始がゼロ以外の値であれば[前のページ]ボタンを表示することになります。ボタンの実際の値が、どのボタンが押されたかを判断する際の値と同じであることを保証するために、文字列を構築する際に定数PREVを使用している点に注意してください。[次のページ]ボタンを表示すべきか否かを判断するのは、結果セット中のすべての行が表示された場合だけそのボタンを表示しないという点で同様です。したがって、[次へ]ボタンはページ数がページの最大数と等しい時は必ず表示されます。もちろん、表示される行の総数がページ・サイズで割り切れるというシナリオもあり、そのような場合は[次へ]ボタンが表示されますが、このボタンを押すと「これ以上行はありません」というメッセージが表示されます。これが不快感を与えるやり方だということであれば、このような状況を処理するのはプログラミングで対処するだけのことになりますが、その実際の解法については皆さんにお任せします。

以上です

従来の5250のサブ・ファイルとはまた違ったロジックですが、それは主に、通常はサブ・ファイル自体にページの前後の移動を任せていたからです。PHPでコーディングする場合、そのちょっとした追加の作業が必要となります。もちろん、アプリケーション・フレームワークを使用する場合も同様のサポートを提供します。実際、Ajaxプログラミング・テクニックを使用する場合は、私がここで使用したようなHTMLの表ではなく、データ・グリッドを使用してサブ・ファイルのような情報を表示するのではないでしょうか。またAjaxは、追加のロジックを自分でコーディングすることなく、列の順序を並べ替えたり、列の幅や位置を変更したりするなどの機能も持っています。ただし、これらのテクニックは本稿の対象範囲を超えています。

本稿を終わりにする前に紹介しておきたいことがもう1つあります。php.netの関連するマニュアルのセクションの研究で、他のSQLite関数についても紹介されていますので、是非ご覧になってください。パフォーマンスについて懸念があるのであれば、特にsqlite_popen()を見てください。前述した通り、ウェブ・アプリケーションではトランザクションの最後でファイル、接続などをクローズするのが通常です。その結果、次にリクエストがまたそれらを開くのです。バリアント_popen()は裏で接続を保持しておき、以後同じデータベースへの接続を試みる_popen()が再利用できるようにすることで、こうしたオーバーヘッドを最小化します。他のデータベースAPIを調べるとおわかりになりますが、特にIBM i固有の関数の場合は、オープンなどの接続関数に「p」が付いたバージョンがあることが多いです。

PHPにおけるセッション・コントロール

本稿の本文で述べました通り、ブラウザ・アプリケーションはステートレスですので、スクリプトの実行が完了しブラウザからの出力が送り出されてしまうと、変数の中身、ファイルの状態などはすべて失われます。緑色画面のアプリケーションはすべてこのように動作すると覚えていらっしゃる経験豊富な方もいらっしゃるでしょう。たとえば、S34やS36ではほとんどの対話型アプリケーションは、複数リクエスター端末(MRT: Multiple Requestor Terminal)用プログラムとして記述されていました。メインフレームやIBM iシステムの一部では、そうしたアプリケーションはカスタマー情報コントロール・システム(CICS: Customer Information Control System)を使って記述されるのが一般的です。MRTシステムとCICSシステムに共通している特徴は、ユーザーから次のメッセージが送られてきたときに、その特定のユーザーのための変数の値を保存したり回復したり、ファイル中の位置を覚えておいたりなどといったことはプログラマ側の責任であるということです。

PHPはセッションという形でこの「記憶」するという要件を直接サポートしています。セッション・コントロールはサーバーによって管理され、通常はクッキーと組み合わされて使用されます。クッキーを使用しない場合は、必要なセッションIDをリクエスト・データ中に(つまりGETデータやPOSTデータの一部として)組み入れて渡すことができますが、クッキーを使用する方がお勧めです。セッションがどのように動作するか以下で説明します。

  • ブラウザから最初のリクエストが到着すると、サーバーは新しいセッションを作成しセッションIDを割り当てます。実際はIDを作成するだけでなくセッション・オブジェクトも確立し、後でこのオブジェクトを使ってセッション情報を保存します。データをどのように保存してどのように取り出すかについては後述します。
  • サーバーは、セッションIDをクッキーとしてブラウザ側に戻します。
  • ブラウザはIDクッキーを保存し、以後のリクエストの一部としてこのIDをサーバーに送り返します。サーバーはこのデータをどこに保存するのでしょうか。実際のところいろいろな方法があります。通常は、セッション・データはsess_の後ろにセッションIDを付けたファイル名が付けられたファイル中に保存されます。実際のファイルの格納場所は、php.ini中のsession.save_pathの値で決まります。この値がどのように決まるかの詳細については、後述の「PHPセッション・コントロールの設定」のセクションを参照ください。別の方法としては、セッション関数session_save_path()を使用して現在のファイルの位置を取り出すこともできます。保存されたファイルは単純なテキスト・ファイルで、これをテキスト・エディタで編集すれば長いテスト・シナリオを最初から最後まで実行しなくても、このファイル中の特定の値に関してテストすることが可能です。

セッション・データが有効であることを保証するために、セッションにはタイムアウト時間(調整可能)が関連付けられています。セッションがタイムアウトになると、それに関連したIDでブラウザがリクエスト送った時に、サーバーが「無効なセッション」エラーを送り返してきます。このエラーをご覧になったことがあるのではないでしょうか。セッションがタイムアウトになってもう一度最初からやり直してくださいとブラウザに表示される、あのイライラさせられるポップアップ・メッセージのことです。

PHPセッション・コントロールの設定

セッションをコントロールするのにクッキーを使用するか否かは、PHPの構成ファイルphp.ini中の設定で決まります。どのような設定になっているのかを調べる一番簡単な方法は、phpinfo()関数を含んだスクリプトを実行するという方法です。そのスクリプトはphpinfo()関数を呼び出すだけのもので十分です。

<?php
phpinfo();
?>

このスクリプトを実行した結果、表示されるのは(長い表示になります)「セッション」セクション中の関連するすべての設定です。クッキーの使用を支配しているのはsession.use_cookiesです。デフォルトの設定はOnで、PHPに対してクッキーを使用してセッションIDを管理するように指定しています。デフォルトのクッキー名はPHPSESSIDで、このクッキーの値はphpinfo()関数の実行結果の表示中のsession.nameのところにあります。特定のセッションに関しての情報を複数「セット」で保存したい場合は、PHPスクリプト中でこの名前を動的に変更することができます。

簡単な例

セッション処理の基本的なメカニズムはおわかりいただけたと思いますので、セッションについていろいろ試すための基本として使える、簡単な「遊び場」のスクリプトをみてみましょう(図―A)。なぜ「遊び場」というかというと、既存のスクリプトに機能を追加していくだけで新しい領域についていろいろ試すのはかえって逆効果だと思うからです。新しい機能を試す以外には何もしない、簡単なスクリプトに触れる方がずっと良いのです。そしてその機能がどのように動作するのかがわかれば、実運用のスクリプトに適用できるのです。私はこのやり方をすべてのプログラミングに適用していますし、皆さんにもこのやり方をお勧めします。

さて演説はこれくらいにして、私たちの「遊び」スクリプトを見てみましょう。このスクリプトを実行した結果の出力を図―Bに示します。現在のセッションIDとセッション名に続いて、セッション用に保存してあるデータ項目の一覧をスクリプトが表示しているのがおわかりいただけると思います。この後に2つのオプションを提供する短いフォームが続きます。名前やフォームに入力した値を「Remember」(記憶)するか、現在保存されているものすべてを「Forget」(忘れる)するかのいずれかのオプションです。

図―Bのイメージ中では、現在2つの値が保存されている点に注意してください。1つは“JonsData”で、これは手で入力したもので、フォームを使用して保存しています。もう1つは“count”で、スクリプト内で自動的に更新されるものです。これについてはこの後すぐに説明します。
さて、このスクリプトがどのように動作するのかを見てみましょう。図―Aのスクリプトをよく見てみると、もしセッションが存在すれば呼び出しAで関数session_start()を呼び出すところから始めて現在のセッションを継続させ、存在していなければ新しいセッションを開始します。スクリプトの中でこの処理から開始しなければならないというわけではありませんが、この処理を行うまではセッション変数へはアクセスできません。セッションが開始されるとグローバル連想配列$_SESSION[ ]を利用することが可能になり、セッション・データの保存、取り出し、更新、削除が可能となります。

次に呼び出しBで、スクリプトがフォーム上のどちらかの[送信]ボタン(RememberまたはForget)から起動されたのかを判断するための、もはやおなじみとなったタスクを実行しています。もしそうであれば、このセッションに対して今までにcountの値が保存されたか否かをチェックしたいからです。これには、セッション配列$_SESSION[ ]中のcount要素に対して関数isset()を実行します。count要素が存在する場合はそれを1つだけ増やします(つまり$_SESSION['count']++;)。count要素が存在しなければ、値1とcountキーを関連付けて新しい値を簡単に作成します。

次のチェックは、Cにおいて現在のリクエストが新しい項目を「Remember」するというリクエストなのかを調べます。もしそうであれば、そしてその項目のIDに対して値が提供されていれば、その名前を保存するために変数$idが作成されます。そしてこの変数は配列$_SESSION[ ]へのインデックスとして使用され、それに関連付けられた値が保存されます。

スクリプトの次の部分は呼び出しDで、入力フォーム用のHTMLに過ぎないのですが、ここには現在のセッションIDと名前を表示するための2つのPHPのコードの塊が含まれています。セッションIDと名前はそれぞれ、関数session_id()と関数session_name()を使用して取り出します。

呼び出しEでは、「Forget」が要求された場合を除き、おなじみのお友達foreach()を呼び出して、配列$_SESSION[ ]中の各エントリを順番に処理して結果をテーブル形式で出力します。

スクリプトが「Forget」(F)を要求された場合はelse部分のロジックが適用され、関数session_unset()を呼び出したあとに関数session_destroy()を呼び出すだけとなります。php.netにあるドキュメントをご覧になると、この2つの関数はほとんど同じで、必要なのは1つだけ(このスクリプトの場合はどちらでもうまく動作するというのがわかったのですが)という印象をお持ちになるかもしれませんが、PHPコミュニティ内ではこの2つの関数を組み合わせて使用した方が、すべてのブラウザにおいてより一貫した結果を生成するということで意見が一致しています。現在までのところ私自身は問題に出くわしていませんが、特にユーザーをログアウトしようとしている場合に万が一問題が生じた場合は、マニュアルにある例を調べてみてください。そこに記載されているユーザーが提供してくれたスクリプトのいくつかは、特定のブラウザやフレームなどで発生する多くの問題について対処しています。

スクリプトのバランス(G)は、「Remember」関数に対するエントリー・フィールドを提供するHTMLフォームとRemember送信ボタン、Forget送信ボタンで構成されています。

いろいろと試してみてください

先ほど、新しい値はIDが提供された時にのみ保存されると説明したことを思い出してください。IDを入力せずに「Remember」送信ボタンを押そうとすると、count値が1つ増えるのがわかるはずです。同様に、既存のIDを入力したのに異なる値を入力した場合は、保存されている値が更新されているのがわかるでしょう。

実際(図―4の呼び出しE)、通常は配列$_SESSION[ ]の要素の中身を使用して正規の変数を設定し、以後のロジックを簡素化します。結局のところ、ロジック中で$variableを参照する方が、$_SESSION['variable']を参照し続けるよりずっと「綺麗」なやり方なのです。このように処理するセッション変数がいくつかある場合は、配列関数extract()を使用すると便利かもしれません。この関数は指定された配列中の各要素に対して変数を作成しますが、その際に要素のキーを変数の名前に使用します。この関数を使用する前に、既存の変数の上書きなどに関する関数のオプションの設定を必ずよく調べてください。extract()関数の正反対のことをする関数はありません。compact()関数を使用することはできますが、この関数は一度に1つの変数しか処理しないので、変数の現在の値を適切な$_SESSION[ ]配列要素に割り当てるだけの単純な処理には効率的ではありません。

セッションに関連した関数はこの他にも多数ありますので、他にどんなものがあるのかマニュアルを参照してください。「悪評あり」とされている関数の使用は避けるようにしてください。今の時点では動作するかもしれませんが、PHP 6がリリースされると動作しなくなる確率が高いからです。

ページトップ

ボタン