RPGの観点からみたPHP-パート2:PHPのブラウザ機能
PHPのブラウザ処理、制御構造の概要および配列の続き
PHP入門シリーズ「RPGの観点からみたPHP」のパート2にようこそ。今回はPHPでのブラウザへの入力処理の基礎、特にHTMLフォームの変数名をちょっと工夫するだけで処理を簡素化する方法について説明します。これからお読みいただけるとおわかりいただけますが、PHPのすべてについて、特にブラウザへの入力については何らかの形で配列を使用することになります。今月使用する例はPHPの配列のある側面を紹介するものです。
ロジックと表示用フォーマッティングの分離
PHPのロジックを表示用フォーマッティングのコード(つまりHTML)から分離することについてはよく質問されることであり、今月のトピックにも関連しているのでこれについて簡単に説明しましょう。私が主催するPHPの研修でRPGのプログラマによく尋ねられるのは、私が使用するPHPコードの例のほとんどでHTMLがスクリプトの中に埋め込まれているのはなぜかというものです。確かに図―4に挙げた今回の主となる例でもある程度まではそれが当てはまります。その理由は極めて単純なことだと思っています。PHPはRPGと違って、外部で記述する表示ファイルと一対一に対応するものがないからです。私がRPGの例を書くときコード中でEXFMTを使用すれば、それを読んだ人は私が何をしようとしているのか理解できるはずです。多くのモダン・アプリケーションでもそうですが、PHPはRPGと同様に画面フォーマットを外出しにすることができますが、そうするための方法がたくさんあるのです。その結果、そのようなたくさんの方法の中の特定の方法について読者が良く知っていることを前提に例を書くことができないのです。
このシリーズの後の方でこのアプローチについてもっと詳しく説明できる機会があると思います。当面の間は埋め込み型HTMLのアプローチを使用しますが、今回使用する例では「ひねり」を加えてあります。今回使用した主なテクニックは、echoやprintなどを使用してウェブ・ページ上に追加するのではなく、純粋なHTMLとしてHTMLフォームをメインのスクリプトに組み入れたという点です。このテクニックを使用すると、一般のHTMLエディタを使用してフォームをデザインすることができます。確かに、私がPHPでコードを書くほとんどの場合に使用しているZend StudioのPHPエディタはHTMLの文法を理解しチェックしてくれますが、WYSIWYGビューはありません。最近のWYSIWYG HTMLエディタ(DreamweaverやKompoZerなど)の多くは、PHPコードの塊を認識してWYSIWYGビュー中などで強調表示します。ただし、こうしたエディタはPHPの文法を認識しているわけではないので、私はZend Studioを使うことが多いです。
PHPのスクリプト中に静的なHTMLコードを組み入れるにはまず、PHPをどのように使用して静的HTMLの出力の条件を整えるのかを理解する必要があります。しかもそれを理解する前に、PHP中に基本的な条件ロジックをどのようにコーディングするのかについて知っておく必要があります。簡単な入力フォームのためにこれだけのことを知っておかなければならないのです。しかし関連する一連の単純なテクニックをどうやって説明しようかと数時間にわたって悪戦苦闘した結果、一つずつ段階を追っていくしかうまく説明できないという結論に達しました。正直言ってそんなに難しいことではないのですが、そこには単純な原理がいくつか含まれているのです。
役立つ実例
基本的なテクニックについて図―1で説明します。このスクリプトではまず、変数$xに値5を代入しています。次に(図―1のA)$xの値が5に等しいかどうかテストしています(もちろんこの時点では等しいに決まっているのですが、もう少し我慢してください)。$xの値が5に等しければ、"echo"を使用してその旨のメッセージを出力します。もし$xの値が5に等しくなければ、"else"条件が成立し条件不成立メッセージが出力されます。簡単ですよね。しかしPHPの"if"制御構造についてまず説明しなければなりません。制御構造の詳細については下記サイドバーの「PHPの条件付き制御構造」で説明しますが、現段階ではPHPとRPGのif文の使い方には主に3つの違いがあると理解していただければ十分です。RPGでのif/else文の基本的な構文は以下の通りです。
If expression;
code to execute if true;
else;
code to execute if false;
endif;
PHPでは以下のようになります。
if (expression) {
code to execute if true;
} else {
code to execute if false;
}
{}文字の位置を揃えているのはスタイル上そうしたわけですが、基本的な構成要素はそのままです。ご覧になっておわかりの通り、主に以下の3つの違いがあります。
- 評価される式は、PHPでは()で囲まなければなりませんが、RPGでは囲むと良いという意味に過ぎず、強制ではありません。
- PHPでは、実行されるコードを{}文字で囲み(「グループ文」と呼ばれています)、"if"や"else"の後には「;」(セミコロン)を付けません。
- PHPでは"endif"は必要ありません。}をコードに記述することで、実際にその代わりを果たしていることになります。ただし"endif"を使用するPHP構文の変形態もあります。詳細については後述の「PHPの条件付き制御構造」を参照してください。
図―1のBをご覧いただくと、1点を除いてAと全く同じ"if"文を使用しているのがおわかりいただけるでしょう。異なるのは、条件がtrueのときに実行されるグループ文で始まる{のすぐ後ろでPHPコードの塊の終わりに?>を書きました。ちょっと変に見えませんか。しかしこれにより、図―1のCでのHTMLテキストは、Bの式がtrueのときだけブラウザに出力されることになります。図―1のDでは、もう一つのコードの塊を記述しています。ここではtrueのグループ文をすぐに閉じて"else"を書き、PHPコードの塊を終える直前でグループ文を開いています。これにより、EでのHTMLはグループ文の一部とみなされます。当然のことながら、Eでのコンテンツはif文の条件がfalseになったときだけ出力されます。最後になったからと言って重要ではないという意味ではありませんが、図―1のFでは最後のコードの塊を開いてelseグループを閉じています。
このような例はPHPでは極めて一般的で、printやechoを使用せずにHTML出力を条件付きで制御することができます。
スクリプトを実行してみると、ブラウザには「true」というメッセージだけが出力されるのが確認できます。さてここでスクリプトを編集して、$xに(5以外の)別の値を代入してスクリプトをもう一度ブラウザ上で実行してみてください。今度は「false」のメッセージが表示されるはずです。
さてこれでこのメカニズムがおわかりいただけたかと思いますので、この原理がHTMLフォームの出力にどのように適用されるのかについて説明します。私が説明したいスクリプトの部分を識別できるように、関連するHTMLコンポーネントとPHPの関連するコードを、同じイニシャル文字を使ってグループ化しておきました。つまり、PHPスクリプト中のAはHTML中のA1、A2、A3と関係していることになります。
しかしHTMLとその背後にあるPHPコードを見る前に、今回私が使用する例のウェブ・フォーム(図―2)を見てください。このフォームは2つの入力フィールドと1つの複数選択リストからなる単純なフォームです。(図―2からのフォーム・エントリでこのスクリプトを実行した結果を図―3に示します。) ただし、フォーム・フィールドをPHPスクリプトで使いやすくするために、フォーム・フィールドの名前の付け方にいくつか工夫をしました(図―4)。
ウェブの入力処理
ウェブの入力は以下のフォームで行われます。
name1=value1&name2=value2&...&nameN=valueN.
PHPはこれを連想配列に変換し、その中身は皆さんがキー入力したものと同じ形式となっています。
array('name1'=>'value1', 'name2'=>'value2', ... , 'nameN'=>'valueN');
作成される実際の配列は、フォームの入力用に使用している入力方法に依存します。今回の例の場合はご覧の通り、GETメソッドを使用しました(図―4のA2)。
<form get action="WebInput1.php">
GETメソッドというフォームは、データがURL中に埋め込まれて渡されるということを意味しています。現実世界では必ずしも良いフォームの処理方法とは言えないのですが(POSTメソッドの方がはるかに安全です)、ウェブ・プログラミングを学習している段階ではデータが見える方がわかりやすいのです。また、スクリプトがフォーム入力処理用であると認識したアクションはWebInput1.phpであるという点に注意してください。興味深いことに、これがスクリプトの名前そのものになっています。言い換えれば、送信ボタンがクリックされた時に、データはこの自分と同じ名前のスクリプトに送られて処理されるということです。ちょっと変に聞こえるかもしれませんが、心優しい読者のみなさんどうかもう少し我慢してください。もうすぐすべてがはっきりしますので。
GETメソッドを使用しているため、フォーム・インプットは(もしあれば)すべて「超大域変数」の連想配列$_GET[ ]に格納されます。超大域変数については本稿で後述しますが、現段階では配列$_GET[ ]にすべてのデータが格納されているということだけ知っておいていただければ十分です。このスクリプト(図―4)はフォームの出力と、その出力を受けての任意の以後の入力の処理の両方に必要となるので、ブラウザのURLにスクリプトの名前が入力されたか、あるいはフォームの送信ボタンが押されたか、どちらによってスクリプトが起動されたのかを知る必要があるのです。これには、便宜的に送信ボタンに名前を付与する(name="submit")という方法で対処し、その値(「Submit Answers」)がその他のデータと一緒に入力として渡されるようにしました(図―4のA3のHTMLコード)。
<input type="submit" name="submit" value="Submit Answers">
さてこれはどれくらい便利なのでしょうか。つまり配列エントリ$_GET["submit"]に関連した値があるかをチェックして、もし値があれば送信ボタンが押されたので処理すべきデータがあるということを知ることができるのです。変数がセットされていなければ、それはフォーム自身への出力要求に過ぎないのです。こうしたやり方はPHPでは一般的なもので、私は変数「submit」はPHPでは特別な意味をもつものなのだとしばらくの間は信じ切っていました。しかしこれはたくさんの人が使っている単なる慣例に過ぎないのです。
静的HTMLの出力を制御するための前述したテクニックがここで使用されています。HTMLの出力を制御するテストは図―4のAです。
If (!isset($_GET["submit"])) {
スクリプトが送信ボタンのクリックによって起動されたのでなければ、このテストがtrueとなり、HTMLフォーム(図―4のA1で始まる部分)が出力されます。関数isset()の直前にある感嘆符はRPGのNOTキーワードに相当しますので、RPGで以下のようにコーディングしたのと等価になります。
IF NOT submitPressed;
// Output HTML
もちろん、RPGにはsubmitPressedのようなインジケータはありませんが、あると仮定することはできますし、その意味するところはわかっていただけると思います。
スクリプトが送信ボタンのクリックによって起動された場合は、配列$_GET[ ]中には氏名、職業、使用するコンピュータ言語など、いくつかのエントリがあるはずです。その結果、静的HTMLセクションはスキップされ、"else"節で始まるメインのスクリプト(図―4のC)が実行されます。
メインのPHPスクリプトが最初に行うのは、count()関数(図―4のB)を使用してそのユーザーがいくつの言語を選択したかを判別することです。Countは配列関数ですので、これを使用するには、選択された言語が配列の形式になっているかを確認しなければなりません。配列の形式になっているかの確認は、入力フォーム中の命名規則で簡単に行いました。図―4のB1をご覧ください。入力の名前はlanguages[ ]と指定されています。ここで[]括弧が名前の一部になっている点に注意してください。これにより、PHPはこの名前の入力値はすべて配列として扱うべしと解釈します。ですからもしユーザーが3つの言語を選択した場合、配列の要素は3つになります。
言語数をカウントできたら、最初のメッセージを出力できます(図―4のD)。 特に、名前や職業のエントリについてはこれ以上処理する必要がないので、これらについては配列$_GETから直接$_GET["name"]や$_GET["jobTitle"]のように参照して出力できる、という点に注意してください。
以下のコーディングを簡素化するため、図―4のEで言語の配列を、配列$_GETから配列変数$languageListへコピーしています。そしてわれらの旧友foreach()を適用して配列内の要素を一つずつ処理しています(図―4のF)。ただし、foreach()の処理に1つだけ知恵を入れたくて仕方ありませんでした。スクリプトがループを処理する際、現在の項目が「Other」なのかをチェックし、もし「Other」であれば言語名を出力する代わりに「その他の言語」と出力するようにしました。
このスクリプトについては以上です。
追考
以上おわかりいただけました通り、PHPを使用すると言語選択のような繰り返し処理する必要のある項目を「自動的に」配列として取り扱う機能など、ウェブの入力データを非常に簡単に取得することができます。実際、この配列の考え方をさらに応用することもできます。たとえば、住所用のエントリ・フィールドからなるフォームがあったとしましょう。そうしたフィールドのすべてが住所配列の一部として処理できたら便利でしょう。PHPでこれを実現するには、フォーム中で適切な名前を使用すれば良いのです。 たとえば、通り名のフィールドにはAddress[Street]を、市名にはAddress[City]といった具合です。このようなフォームを送信すると、Address[xxx]フィールドのコンテンツが連想配列要素中に格納され、そのキーが[]配列のサブスクリプトのデリミタで使用されている名前となります。言い換えれば、GETメソッドを使用するという前提で考えると、$_GET[Address]には2つの要素からなる連想配列があります。1つは「Street」というキーで、もう1つが「City」というキーです。同様に、こうした名前の付け方を使用して、チェック・ボックスやラジオ・ボタンなどを配列としてグループ化することもできます。
前述した通り、$_GETはPHPでは単なる「超大域変数」の1つの要素に過ぎません。PHPスクリプト中の任意の箇所で常に利用可能な変数はまだ他にもあります。ブラウザの基本入力に関連するその他の超大域変数は以下の3つです。
- $_POST POSTメソッドを使用したフォーム・リクエスト中に送信されたすべてのデータを含んでいます。
- $_COOKIE クッキー経由でスクリプトに渡されたすべての変数を含んでいます。クッキーについては本シリーズでいずれご説明します。
- and $_REQUEST すべてのソースからのすべての入力データを含んでいます。つまり、$_GETと$_POSTと$_COOKIEのデータの組み合わせです。
$_REQUEST ではデータがどのように渡されてくるかについて気にしなくてよいので$_REQUEST を使いがちになりますが、まさにそこに実運用環境で$_REQUESTを使用することの危険がはらんでいます。データがPOSTリクエストの一部としてフォームからやってきたのか、アプリケーション中の特定のフィールドの名前を知っている(あるいは推測した)誰かが、そのフィールド用のデータ(つまりGETデータ)をURL中に入れて渡すことでスクリプトの動作をおかしくしようとしているのか、区別ができないのです。$_REQUESTを使用すると、それを知る方法がないのです。ですから$_REQUESTはテスト環境かあるいは「遊び場」でしか使用すべきではないのです。