Home / Menu

CGI-Perlの基礎講座

CGI-Perlの基礎講座(p01)

(本ページは、KENT WEBのFantasyボードプログラムをステップ単位に説明しています。)

◆ 初期設定&メイン処理

・概要

FantasyボードのCGI設置方法の設定、カスタマイズ可能なパラメータの設定、
機能単位に分けた各サブルーチンへの分岐(メイン処理)です。

・1行目

#!/usr/local/bin/perl

通常 # 以下一行分はコメントになりますが、この文だけは例外でパールコマンドのパスを設定します。
また、ソースファイルの1行目の1カラムから必ず記述し、#!の前には空行や空白文字を入れないようにしてください。

・2から5行目

# なのでコメントです。

・6行目

$ver = 'Fantasy v0.82'; # バージョン情報(修正不要)

単なる代入文です。しかし、説明することは山ほどあります。
$verの様に $ から始まる変数をスカラー変数いいます。
スカラー変数には数値、文字列、配列(後に説明)の要素、ハッシュ(後に説明)の要素が格納されます。
また、C言語のように変数を宣言する必要はありません。

'Fantasy v0.82'のように ' で囲まれた文字は文字列といいます。また、" で囲まれた文字も文字列といいます。
違いは、変数が展開されるかいなかです。
尚、「=」は数学のとちょっとちがっていて、右辺を左辺に代入する意味です。

例、

$ver='Fantasy';

$AAA='AAA$verBBB'; -----> $AAAの内容はAAA$verBBB (変数が展開されない)
$BBB="AAA$verCCC"; -----> $BBBの内容はAAAFantasyBBB (変数が展開される)

あと、”# バージョン情報(修正不要)”の様に一カラム目でなくても # の右側はコメントになります。

・7から16行目

 # なのでコメントです。

・17行目

require '../bin/jcode.pl';

requireは関数で、この場合binディレクトリのjcode.plを読み込み実行します。
jcode.plは漢字コード変換モジュールで、ブラウザから入力された漢字コードの統一に利用します。
(ブラウザからの漢字コードが任意の為。漢字コードについてはここ参照してください。)

・18から133行目

コメントとスカラー変数への代入文です。
fantasyボードのカスタマイズ用パラメータの設定です。
詳細は、KENT WEBのfantasyボードの説明を参照して下さい。
ただ、98行目は目新しいので説明致します。

@icon1 = (
    'boy1.gif','boy2.gif','seinen.gif','girl1.gif','girl2.gif',
    'ol1.gif','ol2.gif','ol3.gif','china.gif','cook.gif','piero.gif',
    'dog.gif','cat.gif','mouse.gif','pig.gif','hiyoko.gif','flog.gif');

@ から始まる変数は配列変数といいます。
配列変数は一個のリストを格納するものです。
リストとは順序付けられた値の集合です

例、13 56 9 562.8 とか "ちゃいちゃん" "まちゃ" "やっくん"

の様に、順番に並んだ数値あるいは文字列の集合がリストです。

・134行目

$lockfile = "$lockdir\/$lockfile";

これはロックファイルのパスを編集していますが、 \/ が意味不明です。
たぶん、Cシェル等で正規表現時にディレクトリの / をエスケープすることのなごりかなぁ?

・137行目

$imgurl =~ s/\/$//;

=~ はパターン結合演算子で右辺処理の対象になり、右辺処理後の結果が代入されます。
s/// は置換演算子で、s/A/B/ の場合、AがBに置き換わります。
今回場合は、イメージディレクトリのパスで、最後($)の / を一つ削除する。
つまり、イメージディレクトリパスが 〜/img/ でも 〜/img でもいいってことです。

・139から150行目

&decode;                             # 標準入力されたデータのデコード処理
&axs_check;                          # アクセス制限の処理
if ($mode eq 'regist') { &regist; }  # モードが書き込みの時は書き込み処理へ
if ($mode eq 'find') { &find; }      # モードがワード検索の時はワード検索処理へ
if ($mode eq 'howto') { &howto; }    # モードが使い方の時は使い方処理へ
if ($mode eq 'admin') { &admin; }    # モードが管理の時は管理処理へ
if ($mode eq 'usrdel') { &usrdel; }  # モードが削除の時は削除処理へ
if ($mode eq 'image') { ℑ }    # モードがイメージgif紹介の時はイメージ表示へ
if ($mode eq 'res') { &res_msg; }    # モードが返信の時は返信レスフォーム処理へ
&html;                               # 記事表示の処理

&decode; の & はdecodeサブルーチンを呼び出します。
サブルーチンとは、機能単位に処理をまとめたものです。

標準入力されたデータとは、ブラウザのフォームで入力されたデータや予めフォームで設定されたデータをいいます。
また、このデータはURLエンコーディングされていますので、デコードしなくてはいけません。
URLエンコーディングに関しては後で説明致します。

if文の文字列の比較は lt,gt,le,ge,eq,ne で、数値の比較は <,>,<=,>=,==,!= です。
C言語得意の人は文字列の比較は要注意です。はじめのうちは、結構、はまります!!

アクセス制限処理

・概要

Fantasyボードに書き込んで欲しくない人のホストを締め出す処理です。

・151〜167行目

# アクセス制限(拒否するホスト名を記述)

@deny = (
    "anonymizer",
    "cache*.*.interlog.com",
    "",
    "",
    "",
    "",
    "",
    "",
    ""
    );
sub axs_check {
    if ($deny[0]) {
        # ホスト名を取得
        &get_host;

        $flag=0;
        foreach (@deny) {
            if ($_ eq '') { last; }
            $_ =~ s/\*/\.\*/g;
            if ($host =~ /$_/) { $flag=1; last; }
        }
        if ($flag) { &error("アクセスを許可されていません","no") }
    }
}

まず、sub はサブルーチンであることを表します。先に説明した &で始まるサブルーチンのコール先です。
この場合、axs_check はサブルーチン名になり、その後の {(151行) に対応した }(165)目までがaxs_checkサブルーチン本体です。

では、ロジックを見ていきましょう。

if ($deny[0]) { は $deny[0] の値が真(NULL以外)の時、直後の処理を行います。
$deny[0]は配列変数 @deny の一番目の値です。
このように、perlでは配列変数をスカラー変数で表現する時は鍵括弧中にインデックスで表現します。
真(NULL以外)とは、変数に任意の値が設定されている状態のことで、偽(NULL)はNULLが設定されています。
&get_host; はサブルーチンコール、$flag=0; は単なる代入文、そして、目新しい、foreach文です。

foreach(フォーイーチ)文はperl特有のループ文で、C言語得意の人は初めは馴染めないと思います。(私がそーでした)
perl特有のリストに関する概念を理解すると、foreach文は結構使えます。
つまり、リストの要素単位のループ処理が簡単にできます。
今回は、@deny配列変数の値の個数分のループ処理を行っています。

if ($_ eq '') { last; }の $_ はパターンマッチと入力処理でデフォルトになる変数です。
今回の場合は @deny配列変数の値がforeach文によってリスト毎に設定されます。
last; はループ文の強制終了です。今回はforeach文です。

$_ =~ s/\*/\.\*/g; は $_ に格納されたホスト名中のすべての * を .* に置き換えることですが、意味的には良く分かりません。

if ($host =~ /$_/) { $flag=1; last; } は ホスト名($host)中に $_ の値が存在したら、$flag=1; last;を実行します。

if ($flag) { &error("アクセスを許可されていません","no") } はもうわかりますねぇ!

記事表示処理の前半

・概要

まさに、fantasyボードの記事表示処理です。(ゲストブックをクリックした時の初めての表示のところ)
ブラウザ情報、クッキー情報、記事(ログファイル)情報をfantasy風に編集して表示します。

・168〜176目行

&get_agent;&get_cookie;&header;

は各々、ブラウザ情報を取得、クッキー情報を取得、ヘッダー部を表示で各サブルーチンをコールします。各サブルーチンは後で説明いたします。

・177〜203目行

print <<"EOM";
<center>
<font color="$t_color" face="$t_face" size="5"><b><span>$title</span></b></font>
          :
          :
<tr>
  <td><b%gt;居住地</b></td><>td><select name=area>
EOM

上記は、print文のヒヤドキュメントいい、<<の直後に「EOM」のような文字列(終端文字列)を指定すると、次の行からその終端文字列「EOM」が現れる行の直前の行までは一連の文字列とみなされます。
また、<<と"EOM"の間に空白を入れてはいけませんし、ヒヤドキュメント中にコメント(#)を入れることはできません。

・204〜212目行

    # 居住地の選択フォームを表示
    if ($c_area eq "") { $c_area = "関東"; }
    foreach (@areas) {
        if ($_ eq "$c_area") { print "<option value=\"$_\" selected>$_\n"; }
        else { print "<option value=\"$_\">$_\n"; }
    }

    print "</select>   \n";

ここは、居住地のプルダウンメニューを作成しています。(foreach文で@areasのリスト分の処理)
また、前回入力されてクッキーにから持ってきた居住地の値($c_area)をselectedにしています。
あと、print文の文字列中の \ は 文字列表現に " を使用したので、 文字列中に " を記述する時、文字列表現の " との区別で使用します(通常エスケープといいます)

・213〜243目行

    # イメージの選択フォームを表示
    print "<b>イメージ</b> <select name=icon>";
    foreach (0 .. $#icon1) {
        if ($icon1[$_] eq "$c_icon") {
            print "<option value=\"$icon1[$_]\" selected>$icon2[$_]\n";
        } else {
            print "<option value=\"$icon1[$_]\">$icon2[$_]\n";
        }
    }

    print <<"EOM";
    </select> [<a href="$script?mode=image" target="_blank">アイコン参照</a>]
  </td>
                  :
                  :

ここは、イメージの選択のプルダウンメニューを作成しています。(foreach文で0から$#icon1までのリスト分の処理)
また、前回入力されてクッキーにから持ってきた値($c_icon)をselectedにしています。
あと、$#icon1 は @icon1 の最後のインデックスが格納されています。(perlの場合はゼロオリジン。(ゼロ始まり))
あとはおまけで、先ほど説明したヒヤドキュメントでHTMLソースを標準出力しています。

◆ 記事表示処理の後半

・244〜380目行

    # ページ区切り処理
    $start = $page + 1;
    $end   = $page + $p_log;

    open(IN,"$logfile") || &error("Open Error : $logfile","no");

    $i=0; $j=0;
    while ($_ = <IN>) {
        $i++; $j++;
        if ($i < $start) { next; }
        if ($i > $end) { next; }

        ($no,$date,$name,$mail,$area,$icon,
            $icon2,$com,$res,$url,$host,$pw) = split(/<>/, $_);

                :
                :
        # 反転対象部分1
        if (!$flag) {
                :
                :
    }
    close(IN);
          :
          :
    exit;

$startと$endは表示ページが何ページか管理するスカラー変数です。
また、nextはこの場合、whileへの先頭へ制御が移ります。

open関数は今回の場合、書き込みされたデータファイル(ログファイル)をオープンします。IN はファイルハンドルといいます。

オープン時のエラーの場合、openは偽(NULL)を返しますので、短絡論理和演算子 || で &errorサブルーチンが実行されます。
|| は 左側のオペランドが(今回の場合 open)が偽場合のみ右オペランド(今回の場合&error)が評価されます。
つまり、左側のオペランドが真の場合は右オペランドが評価されません、実行されません。

while($_ = <IN>) { はログファイルから一行分データを $_ へ読み込みます。
while文でログデータがなくなるまで以下の処理します
データがなくなると <IN> は偽(NULL)を返しますのでwhile文から脱出します。

split(/<>/, $_); は split関数で、読み込みデータを分解して各変数へ格納しています。
読み込みデータは<>デリミタのCSV形式(AAA<>BBB<>111等)になっています。(KENTさんのデータ形式は<>がお好き)

if (!$flag) { の ! は逆を意味し、真とき偽で偽のとき真になります。

close(IN);はオープンしたログファイルをクローズします。使ったら後始末は必ずしましょう!!

exit;はこのCGIプログラムの終了です。

Home / Menu