簡単ゲストブックプログラムの詳細について解説する。解説といっても実は覚書。書いておかないと忘れてしまうからだ。
前回の記事では Picasa にコメント欄をつける方法を紹介したが(nlog(n): Picasa の Web エクスポートに PukiWiki 風のコメント欄をつける),このスクリプトは Picasa だけではなく一般のページに使うことができる。PHP が動作しているサーバであれば設置可能な1行コメント用スクリプトである。
Very Simple Shoutbox in PHP v1.0 のプログラムの詳細について見ていくことにする。
このスクリプトは次の5つの部分で構成されている。
それぞれについて個別に見ていくことにする。
「//」から行末まではコメントとして解釈される。「/*」と「*/」で挟んでもコメントになる。
「basename(__FILE__);」で,このスクリプトが設置されている自身のファイル名が取得できる。ファイル名は $self 変数に代入され,コメント用データファイル名やフォームの送信先に使われる。
これはコメント用データのファイル名の指定。Picasa のテンプレートに使うときは下のファイル名を,それ以外の場合は,Picasa 用のファイル名をコメントアウトして,上の一般用のファイル名を使う。このスクリプトを設置したファイル名が「guestbook.php」だとすると,データファイル名は「guestbook.php.txt」になる。
当初は,Picasa 用ファイル名指定はなかった。一般用でも Picasa に使うことはできる。Picasa がエクスポートする各画像用ファイルは「target0.html」や「target1.html」など,画像のファイル名に関わらず連番になる。このファイル名にデータファイルを結びつけてしまうと,画像を増やしてエクスポートし直した場合に,違う画像のコメントが表示されてしまう可能性があるからである。そこで,画像のファイル名「<%itemName%>」に拡張子「txt」を追加したファイルをデータファイルにすることにした。これなら画像が増えてもメッセージがずれる心配がない。
$maxlen は投稿できるメッセージの長さの上限を設定している。投稿者の名前の長さの上限も,同時にこれで設定している。この上限値は数値として使っていないので,ダブルクォートで囲んである。ダブルクォートの代わりにシングルクォートでもよい。数値とするならクォーテーションマークを外す。
表示に使う日本語は,文字化けを起こさないように数値文字参照で指定した。PHP の内部コードと表示に使うコードが異なる場合があるからである。$yournamelabel と $submitlabel はフォームを表示するときに使っている。$week の曜日は,データファイルに書き込まれる。PHP の内部コードと関係があるのは $week だけである。
このセクションで設定した値は,他の箇所で変更されることはない。変数ではなく,定数にしてしまった方がいいのかも知れない。
メッセージ書込み部は,「コメント挿入」ボタンをクリックしたときだけ実行される。クリックされたかどうかは $_POST 変数で判断することができる。$_POST 変数 は PHP 4.1.0 で導入された(定義済の変数)。コメント挿入ボタンは「submit」という名前に設定しているので(後述のフォーム部),ボタンがクリックされたときは $_POST['submit'] に値が入ることになる。
の部分は,
と書いても動作する。ただし,Apache のエラーログに次のようなエラーが残る。
submit という添え字が定義されていないというメッセージである。このスクリプトは,ボタンをクリックしたときとしないときの両方で実行される。ボタンがクリックされない場合は $_POST['submit'] が定義されないからである。
投稿者の名前と投稿メッセージについては,サニタイジングを行っている(後述)。
この部分でメッセージをファイルに書き込んでいる。$name に値が入っているときだけに限定しているのは,「いたずら防止」ではなく「うっかり防止」が目的。日付,名前,メッセージはタブ区切りとした。名前にタブが含まれていると困るので,後述のサニタイズによりタブはスペースに変換している。
ファイルを開くのには fopen 関数を使う。上述の fopen では,モードを "a+" として開いたので,「読み込み・書き出し用」になった。ファイルが存在する場合は追記用,ファイルが存在しない場合は新規作成となった。ここではファイルを読むだけなので,モードを "r" として開いている。
fopen の前に @ をつけてあるのは,ファイルが存在しない場合のエラーを抑制するためである(@)。
1行ずつ読み込むようにするには fgets 関数を使うのが簡単である。
ただし,PHP: fgets - Manual には次のような注意がある。
陥りやすい罠:
C言語のfgetsの動作に慣れている人は、EOFを返す条件の違いについて 注意する必要があります。
C言語の fgets 関数は,EOF を検出すると NULL を返す。EOF を返す訳ではないので,どこまで違うかよく分からないが,PHP の fgets を使う場合に気をつけておいた方がよいことはある。ファイルの最後の行の行末が改行で終わっている場合,fgets はその行の改行までを読み込み,EOF を残す。そして,もう一度 fgets が呼ばれたときに EOF を読み込んで $line に空文字列をセットする。つまり,EOF を独立した1行の空行のように扱ってしまうということである。
fgets で読み込んだ行を,タブを区切り文字にして $date, $name, $msg に代入しているのだが,ここで上述の EOF のみの場合に問題が起こる。スクリプト自体は正常に実行される。しかし,Apache のエラーログに次のメッセージが出力されてしまう。
これは,split によってできた配列の,1と2の添え字部分が定義されていないことを意味している。配列の添え字は0から始まる。EOF を読み込んだ場合,$line の添え字0は定義され,空文字列が代入されるが,その後の文字列がないために添え字1と2は定義されないからである。
fgets 関数の第2引数は PHP 4.2.0 からオプションになった(つまり指定してもしなくてもよい)。4.2.0 より前のバージョンの場合,エラーを出さずに止まっているように見えるとのことなので(閑古鳥 -> 徒然草 -> 2002年12月),明示的に 1024 を指定している。指定しない場合も 1024 になる(PHP: fgets - Manual)。
ここでは,ヒアドキュメントを使って投稿用フォームを表示している(PHP: echo - Manual)。Perl のヒアドキュメントと似ているが,セミコロンの位置や「<」の数が違うので注意が必要である(基本的な構文 - perl使いのPHP)。
「コメントの挿入」というボタン (type="submit") に,「submit」という名前をつけている(name="submit") のは,書き手の趣味である。
一番初めに求めた $self を使って,自分自身に POST している。
POST された投稿者の名前とメッセージは,サニタイジング用関数で無害化する。まず,タブや改行をスペースに変換する。メッセージ用データファイルは区切り文字をタブにしているからである。
mb_strimwidth() はマルチバイト対応の関数で,指定した文字数で文字列をカットする(PHP: mb_strimwidth - Manual)。マルチバイト対応でない関数 (substr 等) を使うと,2バイト日本語文字が文字の中間で切られる可能性があり,文字化けの原因になる。
最後に htmlspecialchars() で,HTML タグで使う文字を実体参照文字に変換している。HTML タグが入力されても,変なことが起こらないようにしている。htmlspecialchars() 関数は,前の mb_strimwidth() より後ろでなければならない。前に使うと,mt_strimwidth() が実体参照の途中で切ってしまうことがあるからである。
clean() 関数の仮引数名には,呼び出し側の実引数名と同じ名前をつかっているが,スコープが違うので別のものである。同じ名前にしているのは書き手の趣味である。
2007年4月13日追記:
サニタイジングをしない形に書き直しました (nlog(n): Very Simple Shoutbox 1.1)。
Master Archive Index
Total Entry Count: 1957