|
落書き張で学ぶPerl講座 |
- 書き込みボードを生成する方法は、大きく分けて2つ有ります。
1つは、入力データをHTMLコードに変換して、HTMLドキュメントに追加する方法。
もう1つは、入力されたデータをデータファイルに保存し、ブラウザから呼び出されたとき、HTMLドキュメントを生成する方法です。
当サイトで紹介するスクリプトは、すべて後者のデータベース形式を採用しています。
前者は、書き込まれるHTMLドキュメントファイルが必要なため、入力フォームと、表示するドキュメントファイルが別々に必要です。
しかも、書き込み後、結果が「すぐに」反映されません。ブラウザが一度読み込んだドキュメントは、キャッシュに保存し、
二度目からはキャッシュのドキュメントを展開するからです。(リロードすれば話しは別)
また、ブラウザからの条件を取得し、表示するデータや、形式を変更できないのでデータ互換を持つバージョンアップも行なえません。
ここで紹介する「落書き帳」も、rakugaki.cgi スクリプトと、rakugaki.txt データファイルで動作し、
ブラウザから呼び出されるのは、スクリプトだけです。
rakugaki.cgi
- メインルーチン
基本的なデータの取得や、処理の流れを決めています。
あなたが加入しているプロバイダのperlが実行できるパスを指定します。
一般的に、#!/usr/bin/perや、#!/usr/local/bin/perlが多いようです。
ここからが本文。この上に空行を1行開けましょう。
日本語変換ライブラリの宣言
登録ボタンが押され、データを書き込み後ブラウザをリフレッシュするために「落書き帳」を設置するURLを指定します。
$rakugakiurl = 'http://www.tryhp.net/cgi-bin/rakugaki.cgi';
#データを保存するファイルの名前を指定します。
$rakugakifile = 'rakugaki.txt';
#現在日付と時刻をシステムから取得し、フォーマットを整えます。
#localtime関数が返す値をそれぞれ変数に代入します。
($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
#取得した数値を2桁に統一します。2桁に統一するのは、見た目をよくするだけでなく、
#日付による昇順、降順の並び替えを行なうためです。
#並び替えは、アスキーコードで行なわれますので、
#「2」と「10」では「10」が小さいと判断されるからです。
#「月」のデータは、0から11なので、1プラスする。
$mon = sprintf("%02d", $mon + 1);
$day = sprintf("%02d", $day);
$hour = sprintf("%02d", $hour);
$min = sprintf("%02d", $min);
#フォーマットを整えます。
#ダブルクォート「”」で囲まれた定数の中の変数は自動で展開されます。
$date_now = "$year年$mon月$day日 $hour時$min分";
|
|
フォームからのデータを変数$formdataに取得
メソッドプロパティーによって取得方法が異なりますので、どちらでも取得できるようにします。
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $QUERY_DATA, $ENV{'CONTENT_LENGTH'});
} else { $formdata = $ENV{'QUERY_STRING'}; }
|
|
フォーから送られてきたデータは、name=京子&email=kyouko@abc.biglobe.ne.jp&comment=始めまして京子です。
のように、「&」で区切られて送られてきます。このデータを分解し、ペアにします。
@pairs = split(/&/,$QUERY_DATA);
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
#不必要な文字を削除し、デコードします。
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
#タグが入力されていれば、それを無効にします。
$value =~ s/</</g;
$value =~ s/>/>/g;
$value =~ s/\n//g;
$value =~ s/\,//g;
#最初に宣言した日本語変換ライブラリを呼び出し、シフトjisに変換
&jcode'convert(*value,'sjis');
#分解したデータをキーで呼び出せるよう、連想配列に格納します。
$FORM{$name} = $value;
}
|
|
このスクリプトが何処から呼び出されたかで処理を分岐します。
フォームから呼び出されたのであれば、$FORM{'action'}が「true」に、
初めて呼び出されたのなら$FORM{}変数に'action'という名前すら存在しない。
$FORM{'action'}が「true」ならフォームからなので書き込みルーチンへ、
if ($FORM{'action'} eq "true") { ®ist; }
#それ以外ならHTMLドキュメントを生成する
else { &html; }
|
|
- HTMLドキュメント生成ルーチン
sub html {
#データファイルを読み込みモードオープンし、
#すべてのデータを配列変数@DATAに格納します。
if (!open(NOTE,"$rakugakifile")) { &error(bad_file); }
@DATA = <NOTE>;
close(NOTE);
#読み込まれたデータは、登録順(古い順)に保存されているので、新しい順に並び替えます。
@DATA = reverse(@DATA);
#HTMLドキュメントを生成します。
print "Content-type: text/html\n\n";
print "<!DOCTYPE HTML PUBLIC -//IETF//DTD HTML//EN>\n";
print "<html>\n";
print "<head>\n";
print "<meta http-equiv=Content-Type content= text/html; charset=x-sjis>\n";
print "<title>落書き帳</title></head>\n";
#バックグランドと文字色を指定
print "<body bgcolor=#000000 text=#FFFFFF>\n";
#ドキュメントの上部、コメント入力フォームを生成
#[言いたい]ボタンが押されると入力されたデータを持ってrakugaki.cgiを再度呼び出します。
print "<form action=rakugaki.cgi method=POST>\n";
#1つのスクリプトで処理を分岐するため、見えないフラグを設定
print "<input type=hidden name=action value=true>\n";
print "<div align=center><center>\n";
print "<table border=1 cellspacing=1>\n";
print "<tr>\n";
print "<td align=center>ニックネーム</td>\n";
print "<td><input type=text size=29 name=name></td>\n";
print "</tr>\n";
print "<tr>\n";
print "<td align=center>E-mail</td>\n";
print "<td><input type=text size=29 name=email></td>\n";
print "</tr>\n";
print "<tr>\n";
print "<td align=center>言いたい</td>\n";
print "<td><textarea name=comment rows=4 cols=68></textarea></td>\n";
print "</tr>\n";
print "<tr>\n";
print "<td align=center colspan=2><input type=submit value=書いちゃえ></td>\n";
print "</tr>\n";
print "</table></center></div>\n";
print "</form>\n";
#ここまでが入力フォーム
#ここからが、落書き記事を生成します。
print "<div align=center><center>\n";
#保存されているデータがなくなるまで繰り返す。
foreach $line (@DATA) {
#ファイルから読み込んだデータの最後には改行文字がついているので除去
chop($line);
#1行のデータを各項目に分解します。
#日付、名前、メール、コメントの順に格納されます。
#それ以降の$d1〜$d4は将来のバージョンアップのためダミーのデータにします。
($date,$name,$email,$comment) = split(/\,/,$line);
#コメント変数の中の改行文字「\r」を「<br>」に変換して、
#訪問者が入力した通りに表示します。
$comment =~ s/\r/<br>/g;
print "<table border=0 width=80% bgcolor=#FFFFFF>\n";
print "<tr>\n";
print "<td><font color=#000000>\n";
#メールアドレスが入力されていればリンクを付けます。
if ($email ne "") {
print "<a href=mailto:$email><strong>$name</strong></a>\n";
} else { print "<strong>$name</strong>\n"; }
print " $date<br>\n";
print "<blockquote>$comment</blockquote>\n";
print "</font>\n";
print "</td>\n";
print "</tr>\n";
print "</table>\n";
print "<p>";
}
print "</center></div>\n";
print "</body></html>\n";
exit;
}
|
|
- データ書き込みルーチン
sub regist {
#名前か、コメントが入力されていなければエラーを出力します。
if ($FORM{'name'} eq "") { &error(bad_name); }
if ($FORM{'comment'} eq "") { &error(bad_comment); }
#データファイルを追加モードオープンして、入力データを最後の行に追加します。
if (!open(NOTE,">>$rakugakifile")) { &error(bad_file); }
$value = "$date_now\,$FORM{'name'}\,$FORM{'email'}\,$FORM{'comment'}\n";
print NOTE $value;
close(NOTE);
#追加したデータをブラウザに反映させるため落書き帳をリロードします。
print "Location: $rakugakiurl" . '?' . "\n\n";
}
|
|
- エラー処理
sub error {
#サブルーチンに渡された引数参照のための配列
$error = $_[0];
#引数によるエラーメッセージの生成
if ($error eq "bad_file") { $msg = 'ファイルのオープン、入出力に失敗しました。'; }
elsif ($error eq "bad_name") { $msg = 'ニックネームが記入されていません。'; }
elsif ($error eq "bad_comment") { $msg = 'コメントが記入されていません。'; }
else { $msg = '原因不明のエラーで処理を継続できません。'; }
#エラーメッセージ表示のHTMLドキュメントの生成
print "Content-type: text/html\n\n";
print "<html><head><title>落書き帳</title></head>\n";
print "<body bgcolor=#000000 text=#FFFFFF LINK=#FFAAAA VLINK=#FF8888>\n";
print "<p>\n";
print "<center><h2>error</h2><hr>\n";
print "<i>" . $msg . "</i></hr></center>\n";
print "</body></html>\n";
exit;
}
|
|
|
|
|