Perl と CGI |
| CGIスクリプトの仕組み |
 |
- CGIは、UNIX上で動作する言語なら何を使って作成してもかまいませんが、
C言語ではサーバのOSごとにコンパイル(機械語化)する必要があり、
一度コンパイルしてしまうと変更することもできません。
他のサイトから無料でダウンロードしてもご自分では何も変更できません。
Cのソースを公開する方法もありますが、これはまた厄介で、ある程度Cの知識が必要なだけでなく、
コンパイラが必要になります。 これらの問題をすべてカバーしてくれるのがPerl(以下Perl、パールと読む)言語です。
Perlは、テキストベースで動作するコンパイラ言語ですので、作成するのはテキストファイルだけです。
このテキストページにアクセスがあればPerlが自動でコンパイルして実行されます。
しかも、Perlは、C言語の長所とBASICの長所を合わせ持つ高級言語なのです。
これらの理由から現在CGIとして世界中で最も使用されているのがPerlです。
本サイトは、すべてPerlで作成するCGIを解説しています。
それでは、Perl言語が理解できればCGIは作成できるかと言えば、答えはNOです。
Perlはインターネットで最も使用されている開発言語ですが、
Perlそのものはインターネットの為に開発された言語でもなければ、CGIの為に開発された言語でもありません。
本来、レポートを作成する為の言語として開発されたドキュメント記述言語で、
Perlだけの知識ではCGIを開発する事ができません。
しかしながら、幸いWebサーバには環境変数というCGIにとって都合のいい代物が存在します。
Perlは、この環境変数と言語を組み合わせる事でCGIを実現します。
|
| 主な環境変数 |
 |
- 訪問者に関する情報
REMOTE_HOST |
訪問者のホスト名がセットされています。何もセットされていない場合や、
IPアドレスがセットされている場合は、ドメイン変換ルーチンを使って変換できます。
|
REMOTE_ADDR |
訪問者のIPアドレスがセットされています。 |
REMOTE_USER |
ユーザー認証サーバで、保護ディレクトリ内にプログラムがある場合、
認証ユーザ名がセットされています。ほとんどのプロバイダでは何もセットされていません。
|
HTTP_USER_AGENT |
訪問者のブラウザ名がセットされています。 |
HTTP_ACCEPT |
訪問者のブラウザがサポートするMIMEタイプのリストがセットされます。 |
HTTP_REFERER |
訪問者が何処から来たか、参照元の完全なアドレスがセットされています。
(ブックマークや、直接アドレスが入力された場合は何もセットされない)
現在ではプライバシー保護の為、ほとんど取得できない。
|
|
- サーバに関する情報
SERVER_NAME |
サーバのドメイン名か、サーバのIPアドレスがセットされています。 |
SERVER_ADMIN |
サーバ管理者のメールアドレスがセットされています。
(大変忙しい方々です。むやみにメールを出すのは控えましょう。)
|
SERVER_SOFTWARE |
Webサーバのソフトウェアの名前と、バージョンがセットされます。 |
GATEWAY_INTERFACE |
サーバ上で実行されているCGIのバージョンがセットされます。 |
DOCUMENT_ROOT |
サーバのドキュメントルートディレクトリのパスがセットされます。 |
SERVER_PROTOCOL |
サーバへのアクセスに使用されたプロトコルの名前とバージョンがセットされます。 |
SCRIPT_NAME |
現在実行されているスクリプトの相対パスがセットされています。 |
|
- フォームとクッキーに関する情報
REQUEST_METHOD |
<form>タグのmethodアトリビュートが返されます。'GET' / 'POST' |
CONTENT_LENGTH |
POSTクエリーにより送られてきた文字数がバイト単位で格納されています。 |
QUERY_STRING |
GETクエリーにより送られてきたフォームからのデータが格納されています。 |
HTTP_COOKIE |
訪問者のブラウザにセットされ、 サーバからアクセスできるCOOKIEの値がセットされています。 |
|
環境変数は$ENV{'環境変数名'}で簡単に取得できます。
たとえば、訪問者が使用しているブラウザを知るには$ENV{'HTTP_USER_AGENT'}を調べます。
|
| 初期設定 |
 |
- perlスクリプトの最初の行には、
#!usr/local/bin/perl
のようなperlが存在するパスを書く必要があります。
1行目が空白行でも、先頭にスペースがあってもエラーになります。
多くのUNIXサーバでは、#!usr/local/bin/perlに置かれ、近年よく使用されるようになったLinuxでは、
#!usr/bin/perlに設定されています。
Windowsのローカルサーバでは必要なく無視されますので、
ローカルで問題なく動作しているCGIがサーバにアップするとエラーになる場合のほとんどが、
このperlのパスに原因があります。
(WindowsでもWebサーバにApachiを使用している場合は必要)
|
| 初期化 |
 |
- 前述のとおりperlの先頭行にはperlのパスを記述する必要があります。
2行目以降、メインルーチンまでの前方には挿入するライブラリ、
環境変数を含めた動作環境を設定する変数を初期化(値の代入)するコードを置くのが一般的です。
#日本語コード変換モジュール
require 'jcode.pl';
これは、文字列を日本語コードに変換するライブラリを読み込んでいます。
- 日付の設定
世界中のサーバを自由に操作できる事、これがインターネットの最大の特徴です。
日本語のページでも外国のサーバに置かれているサイトも大変多いのです。
そこで問題になるのが時差です。perlには環境変数を操作して時差を修正することができます。
$ENV{'TZ'} = "JST-9";
$DATE = time;
これは、環境変数$ENV{'TZ'}国際標準時間に9時間プラスして日本時間にしてシリアル値を取得しています。
しかしながらサーバの中には$ENV{'TZ'}環境変数が機能しないものもあります。
このような場合を想定してすべての時刻を世界標準から算出しておけば問題なく動作させることができます。
$timelag = 9;
$ENV{'TZ'} = "GMT";
$DATE = time + $timelag * 3600;
|
| クッキー |
 |
- 通常のCGIではデータをサーバに保存しますが、これらの情報はすべての訪問者に共通に使用され、
訪問者ごとに記憶されることはありません。
時として訪問者ごとに保存しておきたい情報もあります。
たとえば、インターネットを利用した通信販売では訪問者ごとに購入しようとする商品が異なります。
これらをデータファイルに保存することはできません。このような個人情報を一時的に記憶させる機能がクッキーです。
クッキーは訪問者の情報をサーバが訪問者のブラウザに書き込む事で実現しています。
- クッキーの書式
クッキーにデータを保存するには「Set-Cookie」ヘッダーを使用します。
Print "Set-Cookie:クッキーの名称 = 保存するデータ ; expires = 保存期限 \n";
- クッキーの名称
名称は英数字で自由に付けることができます。他のCGIスクリプトと重複しないことだけには気をつける必要があります。
- 保存するデータ
保存するデータもプログラマが自由に作成できます。筆者の場合は分解しやすいように
Key!値, Key!値, Key!値, Key!値, Key!値
という書式で記録しています。
それぞれの項目は","で区切り、キーと値は通常あまり使用しない"!"で区切っています。
【例】
name!Terra,email!terra@xx.xxxx.or.jp,hp!http://www.xxxx.or.jp/terra/
|
|
- 保存期限
クッキーには保存しておく期限を設定する必要があり、書式にも厳密な規則があります。
"曜日, 日-月-西暦 時:分:秒 GMT";
曜日は、'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'から選択
日は、0を省略せずに'01','02','03'のように現します。
月は、1月から'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' に置き換える必要があります。
時分秒も0を省略することはできません。次のサンプルでは30日間の期限を指定し、書式を設定しています。
local(@date) = localtime(time + 30 * 86400);
$date[5] += 1900;
$date[3] = sprintf("%02d",$date[3]);
$date[2] = sprintf("%02d",$date[2]);
$date[1] = sprintf("%02d",$date[1]);
$date[0] = sprintf("%02d",$date[0]);
local($wday) = ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday') [$date[6]];
local($month) = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec') [$date[4]];
local($date_gmt) = "$wday, $date[3]\-$month\-$date[5] $date[2]:$date[1]:$date[0] GMT";
local($cookievalue) = join(",","name!$QUERY{'name'}","email!$QUERY{'email'}","pw!$QUERY{'pw'}");
print "Set-Cookie: $CookieName=$cookievalue; expires=$date_gmt\n";
|
|
- 汎用的な書き込み、読み込みルーチン
汎用的に利用できるサブルーチンにしておけば、張りつけるだけで、CGIを作成するたびに書き換える必要がなくなります。
#クッキー書き込み
sub cookie_regist {
my($cookiename, $cookielist, $date) = @_;
!$date && ($date = 30);
my(@COOKIELIST) = split(/\,/, $cookielist);
my(%COOK);
my(@DATE) = localtime(time + $date * 86400);
$DATE[5] += 1900;
$DATE[3] = sprintf("%02d",$DATE[3]);
$DATE[2] = sprintf("%02d",$DATE[2]);
$DATE[1] = sprintf("%02d",$DATE[1]);
$DATE[0] = sprintf("%02d",$DATE[0]);
my($wday) = ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday') [$DATE[6]];
my($month) = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec') [$DATE[4]];
my($date_gmt) = "$wday, $DATE[3]\-$month\-$DATE[5] $DATE[2]:$DATE[1]:$DATE[0] GMT";
my($cookievalue, $key, $value);
foreach (@COOKIELIST) {
($key, $value) = split(/=/, $_);
$key =~ s/\,/&comma/g;
$key =~ s/:/&colon/g;
$key =~ s/;/&semicolon/g;
$value =~ s/\,/&comma/g;
$value =~ s/:/&colon/g;
$value =~ s/;/&semicolon/g;
if ($cookievalue) {
$cookievalue .= ",$key:$value";
} else {
$cookievalue = "$key:$value";
}
$COOK{$key} = $value;
}
print "Set-Cookie: $cookiename=$cookievalue; expires=$date_gmt\n";
%COOK;
}
#クッキー読み込み
sub cookie_read {
my($cookiename) = @_;
my($key, $value, %COOK);
my($cookies) = $ENV{'HTTP_COOKIE'};
my(@pairs) = split(/;/,$cookies);
my(@DUMMY);
foreach $pair (@pairs) {
($key, $value) = split(/=/, $pair);
$key =~ s/ //g;
$DUMMY{$key} = $value;
}
@pairs = split(/\,/,$DUMMY{$cookiename});
foreach $pair (@pairs) {
($key, $value) = split(/:/, $pair);
$key =~ s/&comma/\,/g;
$key =~ s/&colon/\:/g;
$key =~ s/&semicolon/\;/g;
$value =~ s/&comma/\,/g;
$value =~ s/&colon/\:/g;
$value =~ s/&semicolon/\;/g;
$COOK{$key} = $value;
}
%COOK;
}
|
|
- 訪問回数の記録
自分のホームページへのアクセス数を知る代表的なものにアクセスカウンターがあります。
このアクセスカウンターは、すべての訪問者の累計を数えていますので、
どなたが何回訪れたのかは把握できません。クッキーを利用して、
それぞれの訪問者のブラウザにカウントを記録しておけば、訪問者ごとにカウントする事も可能になります。
$timelag = 9;
$ENV{'TZ'} = "GMT";
#$timelagに設定した世界標準時からの時差をプラスして本日の日付を取得
(@DATE) = localtime(time + $timelag * 3600);
$date = sprintf("%04d/%02d/%02d", $DATE[5] + 1900, $DATE[4]++, $DATE[3]);
#クッキーの名前を指定してクッキーを読み込み;
&cookie_read('cookie_count');
#日付を調べて同じ日なら1プラスする
#初めての訪問や、日付が変わっていれば本日分を1にする
if ($COOKIE{'date'} eq $date) { $COOKIE{'today'}++; } else { $COOKIE{'today'} = 1; }
$COOKIE{'total'}++;
#クッキーに書き込むデータのフォーマットを整える
$value = "date!$date,today!$COOKIE{'today'},total!$COOKIE{'total'}";
#クッキーの名称、書き込むデータ、日数を指定して書き込みルーチンを呼び出す
&cookie_regist('cookie_count', $value, 30);
print "Content-type: text/html\n\n";
print "<html><body>\n";
print "<p>本日$COOKIE{'today'}回、合計$COOKIE{'total'}回目のご訪問ありがとうございます。</p>\n";
print "</body></html>\n";
exit;
|
|
- フォームの入力支援
掲示板やチャットなど、よく利用するページで、名前やメールアドレスを毎回記入するのは厄介なものです。
クッキーを使用すれば2回目以降は、予め入力されていて訪問者は省略することができます。
クッキーに書き込むコードは、データをファイルに書き込んだ直後に置きます。
#クッキーに書き込むデータのフォーマットを整える
#記憶させるのは、お名前、E-Mail、ホームページ、パスワード
$value = "name!$QUERY{'name'},email!$QUERY{'email'},hp!$QUERY{'hp'},pw!$QUERY{'pw'}";
#クッキーの名前を「bbs」、保存期間を30日にし、クッキー書き込みルーチンを呼び出す
&cookie_regist('bbs', $value, 30);
書き込まれたクッキーの情報をフォームを作成するときに展開します。
#クッキーの名称に「bbs」を指定してクッキーを読み込む
&cookie_read('bbs');
print "<form name=inputform action=note.cgi method=POST>\n";
print "<input type=hidden name=action value=regist>\n";
#連想配列%COOKIEの値をプロンプトとして代入
print "お名前:<input type=text name=name value=$COOKIE{'name'}><br>\n";
print "E-mail:<input type=text name=email value=$COOKIE{'email'}><br>\n";
print "題名:<input type=text name=subject><br>\n";
print "Hp:<input type=text name=hp value=$COOKIE{'hp'}><br>\n";
print "Password:<input type=password name=pw value=$COOKIE{'pw'}><br>\n";
print "<textarea name=comment rows=4 cols=80></textarea><br>\n";
print "<input type=submit value=新規投稿/更新>\n";
print "</form>\n";
|
| 関数とサブルーチン |
 |
- perlでは関数とサブルーチンを区別しません。基本的にすべてが関数です。
一般的には処理だけを記述し、戻り値を帰さないものがサブルーチン、処理後の結果を帰すものを関数と呼びますが、
perlではすべてのサブルーチンで最後に処理された変数を帰します。
その帰された値を使用するもしないもプログラマ次第です。
使用すれば関数で使用しなければサブルーチンということになります。
- 関数の記述
関数の宣言にはsubキーワードで始まるブロックで記述します。
Sub function {
local($a) = 2;
local($b) = 3;
local($c) = $a * $b;
$c;
}
|
|
functionは英数字でプログラマが自由に付けることができます。
関数内ではlocalキーワードを使用して関数内だけのスコープ(有効範囲)を持つローカル変数を使用できます。
ローカル変数は、関数のブロック内だけで有効ですから
メインルーチンで同名の変数名が使用されていても値に影響を与える心配がありません。
したがって汎用的な関数を記述する事が可能になるのです。
- 関数の呼出し
関数や、サブルーチンを呼び出す場合は、関数名の前に「&」を付けます。
サブルーチンの場合は戻り値を必要としません。次のような記述だけで制御をサブルーチンに移すことができます
&function;
関数は戻り値を受け取りますので次のようにします。
$value = &function;
この場合、戻り値は$valueに格納されます。
計算式に含めて
$value = &function * 2;
print "<p>合計 = ", &function , "</p>\n";
for ($index = &start_roop; $index <= &end_roop; $index++) {
・・・・・・・
}
|
|
と書くこともできます。
関数は、関数内で他の関数を呼び出す事も、自分自身を呼び出すこともできます。
- 引数を渡す
関数、サブルーチンに引数を渡し、その引数を利用して計算させることもできます。
サブルーチンに引数を渡し、制御を移すには、
&function(3);
関数に引数を渡し、計算結果を受け取るには、
$value = &function(3);
とします。
もちろん、スカラ変数だけでなく配列変数を渡すこともできます。
$value = &fuction(@DATA);
- 引数を処理する関数
関数や、サブルーチンは引数を受け取り計算、処理することもできます。
呼び出し時に指定された引数は、特殊変数@_に格納されています。
sub function {
local($pi) = $_[0];
$pi * 3.14159;
}
|
|
この例では引数に円周率を掛けて帰しています。
複数のスカラ値を処理するには、
sub function {
local($a, $b) = @_;
$a * $b;
}
|
|
この例は、2つの引数を受け取り、その積を帰しています。
配列を受け取るには、次のように書きます。
sub function {
local(@PRM) = @_;
local($max) = 0;
foreach (@PRM) {
($_ > $max) && $max = $_;
}
$max;
}
|
|
これは、受け取った配列変数の数値を1つずつ調べ、最大値を帰しています。
次のサンプルでは、ディレクトリないのサブディレクトリや、ファイル名、ファイルのサイズを表示しています。
ディレクトリの場合は、自分自身を呼び出しその内部も表示します。
sub search {
local($dir) = $_[0];
#ディレクトリが指定されていなければ現在のディレクトリから開始
if (!$dir) { $dir = './'; }
#ディレクトリを移動
chdir $dir;
#サブディレクトリを含めディレクトリないのファイルを取得
local(@FILENAMES) = <*>;
foreach $file (@FILENAMES) {
#ファイルが読み込み可能なディレクトリなら
if (-d $file && -r _) {
#ディレクトリ名を表示
print " <b>[$file]</b>\n";
print "<ul>";
#このディレクトリの内部を調べるため自分自身を呼び出す
&search($file);
#元のディレクトリに戻る
chdir("../");
print "</ul>";
} else {
#ファイルが普通のファイルなら
if (-f $file) {
#ファイルのサイズを調べる
$size = int((-s $file) / 1024 + 0.9);
print "$file - $size K<br>\n";
}
}
}
}
|
|
|
|
|