□Perl 講座 特別編 > パーツの記述

◆各パーツごとに考えよう

各パーツごとと言うのは、前のページでフローチャートをご紹介しましたが、その中の四角の部分のことです。『デコード処理』『書込み処理』『表示処理』の3つと『エラー処理』の合計4つのパーツを考えます。各パーツごとのフローチャートと、説明文、そして実際の記述までをここでご紹介いたします。

>パーツの記述>デコード処理

フローチャート ☆デコード処理☆まず、最初に考えなくてはいけないのがデコード処理です。我々が日本語を使う限りでてくる処理です。逆に言うとこれをよく理解しておくことによって、他のスクリプトに幾らでも利用できますので、ここで理解を深めてください。

フローチャートの流れを説明すると、まずフォームから送られてきたデータがPOST形式なのかGET形式なのかを振り分けます。振り分けたデータをそれぞれにあった方法でバッファに送ります。
別に自分のためだけでしたら、POSTだけとかGETだけの物を用意しておけば問題ないといえばないです。

送られたデータは『&』で区切られて入ってくるので『&』を区切りにまず一段階分割していきます。そして、分割した物は『=』で区切られているので『=』を区切りとしさっらに分割していきます。一番細かくなった時点で、それぞれを日本語化していこうという仕組みです。

全てのデータが分解・日本語化が終了したら終了です。
では、具体的に記述するとどうなるのか説明します。

#デコードするためのプログラム
sub decode {

    # プラウザからのデータ取込み
    if ($ENV{'REQUEST_METHOD'} eq "POST") {
        read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
    }
    else {
        $buffer = $ENV{'QUERY_STRING'};
    }

    # プラウザからのデータ変換
    $i = 0;  #※2
    @pairs = split(/&/,$buffer);
    foreach $pair (@pairs) {
        #1行毎に$name,$valueを取り出す
        ($name, $value) = split(/=/, $pair);
        # 変換演算子  tr  +  を  スペースに置き換え
        $value =~ tr/+/ /;
        # 変換演算子  s/// 単語の構成文字にマッチ
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

        # " を " に変換
        $value =~ s/"/"/g;
        #\n を "" に変換
        $value =~ s/\n//g;

        # 日本語に変換(デコード処理部分)
        &jcode'convert(*value,'sjis');
        &jcode'convert(*name,'sjis');
        $FORM{$name} = $value;    #※1
        @num[$i]=$value;    #※2
        $i=$i++;    #※2
    }
}
※1:INPUTタグ内のnameパラメータにセットした値とvalueパラメータにセットした値に関連をもたせたい場合。
※2:INPUTタグのvalueパラメータに入った値を上から順番に配列@num[0]…@num[データの数]で管理したい場合。

以上のような記述で、例えば
<INPUT type="text" name="email" size="20">
上記のフォームがあった場合にinfo@kakisiti.jpと言う値(value)があったとします。
※1の方法で記述すると
$FORM{'email'}と言う変数に『info@kakisiti.jp』が代入されることがわかりますか?これでひとまず、デコード処理が作れました!

初級編の『第5章 データを受け渡してみよう』でも解説しております。
参考ページ -> POSTとGET / jcode.pl / デコード方法 / 出力させてみよう

>パーツの記述>書込み処理

フローチャート ☆書込み処理☆次にファイルの書込み処理について説明します。左図のフローチャートの手順で動作させるわけですが、途中余計な機能を一つだけつけてありますので、注意して見てください。

ログ全部?とありますが、これは2重書き込みを見させるためにループさせています。2重書込みを気にしなければループ処理させる必要はありません。その他にも色々と試してみると良いでしょう。

まず、当然ですがログファイルから読込します。次にログの逆順処理をするんですが、これは新しい書き込みを最初に表示させるために行っています。古い書き込みが下の方が良い場合は上下にある逆順処理は省いてください。

そして、最後にログに入力されたデータを追加書き込みさせて処理は終了します。ここではあくまで最も単純に書込み処理をさせています。アクセスが多く同時書込み処理が起こってしまった場合の、ログの損傷などは、一切考慮しておりません。ファイルロックなんかを使うと良いのですが、簡易掲示板なのでここでも説明は割愛させて頂きます。

では、具体的に記述するとどうなるのか説明します。

#書込み処理プログラム
sub write {

    #ファイルのオープン  予め$fileにログファイル名が代入されている物とします。
    open(IN,"$file") || &error("Can't open $file");
    open(OUT,">$temp") || &error("Can't write Temp File");

    #ログファイルを配列@linesに代入
    @lines = <IN>;

    #ログの逆順処理
    @lines = reverse(@lines);

    #2重書込み禁止ルーチン
    foreach $line (@lines) {
        &jcode'convert(*line,'sjis');
        local($s1,$s2,$s3,$s4) = split(/<>/,$line);
        if (($FORM{'name'} eq $s1) && ($s4 eq $FORM{'msr'})){
            &error("二重書き込みは禁止です!!");
        }
    }
    #書き込むデータの順番を決定
    $newrecode="$FORM{'name'}<>$FORM{'email'}<>$FORM{'url'}<>$FORM{'msr'}<>\n";

    #配列@linesにデータをPUSH
    push (@lines,$newrecode);

    #ログの逆順処理
    @lines = reverse(@lines);

    #ログの書込処理
    print OUT @lines;
    close(OUT);
    close(IN);
    rename($temp,$file) || &error("Rename Error");
    chmod (0666,$file);
    if (-e $temp) { unlink($temp); }
}
2重書込みルーチンは特に必要なければ省いても問題ありません。
ログファイルに新たに追加されるデータは$name<>$email<>$url<>$msr<>\nです。

>パーツの記述>表示処理

フローチャート  ☆表示処理☆いよいよ、表示処理です。これが無いと始まりません。掲示板で表示される部分は大きく分けて3つあります。
  1. ヘッダ部分・・・書込みフォーム
  2. リピート出力部分・・・投稿記事
  3. フッタ部分・・・締めくくりです。
ヘッダは以前で用いた書込み部分の例をベースにとりあえず使います。皆さんはご自分で用意された物を使ってもらって構いません。また、リピート部分も投稿記事の例をベースに作っていきます。ヘッダ部分に関しては、単純に終了タグだけで今回は済ませてしまいます。あとで自分なりにアレンジしてもらっても構いません。

各書き込みのフォームの名前を
なまえ⇒name
E-mail⇒email
URL⇒url
メッセージ⇒msr
とするものとします。
尚、メッセージ部分は今回は改行を無視した形で出力させる物とします。改行させたい場合は改行文字を<BR>に変換させてあげる処理をすることによって可能になります。

それでは、メインの表示プログラムと、各3つに分けたヘッダ部分・リピート部分・フッタ部分のそれぞれについて記述しますので、流れを見てください。

#メイン表示プログラム
sub html {

    #ヘッダの読込
    &header;

    #ログの読込
    open(IN,"$file") || &error("Can't open $file");
    @lines = <IN>;
    close(IN);

    #ログ全部?
    foreach $line (@lines) {
        local($name,$email,$url,$msr) = split(/<>/,$line);
        &output;
    }

    #フッタの読込
    &footer;
exit;
}

#ヘッダ部分プログラム
sub header {
    print <<"EOT";
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<TITLE>簡易掲示板</TITLE>
</HEAD>
<BODY>
<P>簡易掲示板</P>
<FORM action="bbs.cgi" method="POST">
<INPUT type="hidden" name="mode" value="write">

なまえ:<INPUT size="20" type="text" name="name"><BR>
E-mail:<INPUT size="20" type="text" name="email"><BR>
URL:<INPUT size="50" type="text" name="url" value="http://"><BR>
メッセージ:<BR>
<TEXTAREA rows="5" cols="50" name="msr"></TEXTAREA><BR>
<INPUT type="submit" value="投稿する"><INPUT type="reset" value="クリア">
</FORM>
<HR>
EOT
}

#リピート部分プログラム
sub output {
    print <<"EOT";
投稿者:$name
E-mail:<A href="mailto:$email">$email</A><BR>
Homepage:<A href="$url" target="_blank">$url</A><BR>
投稿内容<BR>
$msr
<HR>
EOT
}

#フッタ部分プログラム
sub footer {
    print <<"EOT";
<HR>
</BODY>
</HTML>
EOT
}
注目して欲しいのが、ヘッダ部分の赤い文字にしてある部分です。こちらの具体的な説明は次の章でご説明します。覚えて置いてください。またリピート部分の記事内容にそれぞれ変数を記述することによって、出力します。

>パーツの記述>エラー処理

フローチャート ☆エラー処理☆最後にエラー処理プログラムです。別にこのルーチンを作らなくても、良いといってしまえばそれまでですが、動作確認するときや、入力制限などをするときに、一つ作っておくと非常に便利になってきます。

例えば、なまえを入力しないで空のまま投稿しようとするとエラープログラムが動くようにしてあげればいい訳です。こちらの簡単な説明も後ほど説明いたします。



#エラー処理プログラム
sub error {
    print <<"EOT";
<HTML>
<HEAD><TITILE>ERROR</TITLE></HEAD>
<BODY>
<CENTER>
<H3>ERROR !</H3>
<FONT color=red><B>$_[0]</B></FONT>
<HR width=\"90%\">
</CENTER>
</BODY>
</HTML>
EOT
    exit;
}
エラープログラム(サブルーチン)が呼び出されたとき、$_[0]に入ってくる値を決めてあげれば何通りものエラーメッセージを出力させることが出来ます。

□特別編 一緒に簡易掲示板を作ってみよう