目次


便利なCGIスクリプト作成のためのヒント

CGI.pmモジュールを綿密に検討する

Comments

通常CGIプログラミングを行う場合は、CGIモジュールを使うか、手作業でスクリプトを記述するかのいずれかを選択できます。ここでは、CGI::FastのようなCGIモジュールとその派生クラスで何を行うことができるのか検討することにします。次のような場合は、作成済みのモジュールを使用せずに、スクリプトを記述しても特に不都合はないかもしれません。たとえば$ENV{QUERY_STRING} を構文解析する場合のように、CGIアプリケーションに必要なコードがほんの少量であったり、または、200 KBのモジュールをコンパイルする場合のように、スピードが最も重要になる場合です。しかし、たいていの場合は、CGIモジュールの方がはるかに適しているのです。

CGIモジュール

CGIモジュールを利用しようとする第1の理由は、これがファイル・アップロード機能にとって、他の代替方法がない唯一のものであるということですが、これは各種のブラウザーとプラットフォームで若干異なっています。CGIモジュールは、HTTPヘッダーとクッキーの処理、コマンド行からのスクリプトの実行、NPHスクリプトとファイル・アップロード機能のサポートなど、ファイルをアップロードするために必要なすべてのツールを提供してくれます。さらに、都合のよいことに、モジュールはオブジェクト指向と関数指向のいずれの方式でも利用できます。

CGIモジュール自体、無数のPerlの機能を備えた複雑なアプリケーションです (複雑過ぎるかもしれませんが)。CGI.pmモジュールを理解できればPerlも理解できると言い切る専門家さえいることも、驚くに値しません。LincolnSteinが作成したCGI.pmは、PerlでWebアプリケーションを構築する際にほとんどの開発者が利用するモジュールです。このモジュールはCPANから入手することができ、最新のドキュメンテーションはCGI.pm ページに掲載されています。

残念ながら複雑なライブラリーは、往々にして使いにくさを増してしまうような微妙な点をあわせ持つものです。これに反して、CGIモジュールでは、複雑さは一定の条件のもとでしか現れません。例えば、HTML形式でファイルをアップロードする機能を構築する場合を考えてみましょう。このとき、ドキュメンテーションでは機能の解明はできないとします。下のリスト1のコードで、ユーザーはファイルをWebサーバーにアップロードすることができます。フォームが処理されると、スクリプトはファイルの内容を取り出し、それをブラウザーに表示します。それは<pre></pre> のタグで囲まれています。

リスト1. ファイル・アップロードのサンプル・コード
#!/usr/bin/perl -Tw
use CGI ':standard';
use strict;
my $out.= start_multipart_form.filefield(-name.=> 'upload');
$out .= br.submit('submit','Send').end_form;
my$file.= param('upload');#the filename returned is also a file handle  
if(request_method eq 'POST' &&
    defined $file && ref $file && ref $file eq 'Fh')
{
    local $/.= undef; #read the whole file
    $out .= pre ||'';#takes care not to send uninitialized value
    close $file if $CGI::OS ne 'UNIX';#such as Win32 platforms
}
print header,start_html('Sample upload page'),$out,end_html;

"$out .= pre <$file>||'';" の行は、ここでは絶対に必要です。ユーザーが空ファイルを送信するか、または無効なファイル名を入力すると、ダイヤモンド・オペレーターは初期化されていない定義値を返し、pre 関数のクラッシュを招いてしまいます。

さらに、$file はかなり多くの確認ポイントが必要になります。POST メソッドがシンプルな形式に使用されると、もはや$file はファイル・ハンドルではなくなるからです。

また、一部のオペレーティング・システムでは、一時ファイルを明示的に閉じる必要があります。これはWin32の場合に当てはまりますが、UNIXプラットフォームについてはその限りではありません。

以上のことはすべて、ドキュメンテーションには示されていないCGIモジュールの微妙な特性で、そうした条件のもとでこのモジュールをいくぶん複雑なものにしているのです。

ブラウザーでスクリプト・エラーを表示

ブラウザー画面上で構文とランタイムのエラーを表示すると、コードのデバッグ作業中に非常に助かることがあります。例では、スクリプト・サイクルはスクリプト・ファイルの編集、その保存、ブラウザー画面の再ロードから構成されます。では、実際に見てみましょう。

リスト2. 最も完全なデバッグ出力コードの実施
#!/usr/bin/perl -Tw
use strict; #restrict unsafe constructs
use CGI ':standard';
use CGI::Carp qw/carpout fatalsToBrowser set_message/;
use diagnostics -verbose;#print warning diagnostics
BEGIN {
        local *LOG;
        my $size.= -s "my.log" || 0;
        open LOG, ">>my.log" or die "Can't open: $!";
        carpout(\*LOG);
        my $errors.= 0;
        sub handle_errors#will be called with the text of the error
        {
            $errors.= defined $_[0] && $_[0] || $errors, $size
        }
        set_message(\&handle_errors);
        }
END {
      my($errors, $size).= handle_errors;
      if($errors)
      {
        local *LOG;
        local $/.= undef;
        open LOG, "my.log" or die "Can't open: $!";
        seek LOG,$size,0;#skip previous error log
        local $_.= <LOG>;
        close LOG;
        s/&/&amp;/g;#replace special characters    s/"/&quot;/g;
        s/>/&gt;/g;
        s/</&lt;/g;
        print "<table><tr><td bgcolo.=linen><pre styl.='color:black'>";
        print "<b>$errors</b>$_</pre></td></tr></table>";
      }
}
print header,start_html('Test page'),'test',end_html;

以上、(使用されたモジュールに従って) 最も完全で定評あるコード・デバッグの方法について一例を見てきました。通常の状況では、すべてのテクニックを同時に利用することはほとんどありませんが、それぞれを単独で調べておく価値はあります。

ここでは-w フラグと"use strict" を巧みに使用していますが、スクリプト内のセキュリティー・ホールを強調してくれるので、-T フラグは絶対に必要です。

CGI::LogCarpの使用と欠点

診断モジュールとCGI::Carpモジュールを同時に使用すると、いくつかの興味深い結果が出ます。CGI::Carpは$::SIG{__WARN__} および$::SIG{__DIE__} シグナル・ハンドラーを概算で設定しますが、診断モジュールは、インストールされたシグナル・ハンドラーを拡張します。このため、モジュールがuse ステートメントに記述される順序が重要になってきます。さらに、このモジュールではブラウザー画面上にSTDERR 全体を表示することができないため、結果的にモジュールのコードのほんの一部だけが使用されます。

診断モジュールによって表示された追加エラー・データ表示は、繰り返される (しかもうるさい) エラー・メッセージであるため、必ずしも読みやすいものではありません。このことは特に、最初の開始時点でしか使用されない-verbose フラグの場合に当てはまります。

この例でもう1つ不備な点は、比較的長いコンパイル時間ですが、これはコード・フラグメントを別のモジュールにコピーすることによって防止できます。ファイルにSTDERR を書き込むよりも、連結されたファイル・ハンドルを使用してSTDERR を直接スカラー値に書き込み、続いてブラウザーにエラーを表示する方がよいでしょう。残念ながら、現時点ではPerlのバグのため、これを行うことはできません。

Tie::STDERRモジュールの作成者であるJan Pazdzioraは、「Tie::STDERRはコンパイル時エラーを見つけますが、その理由を突き止めることはしません。エラーがあったという事実だけを知らせます。これを修正する方法はわかりません」と認めています。ですから、現時点での唯一の解決策は、STDERR をファイルにリダイレクトすることです。

スクリプト設計とテストの段階を完了したら、デバッグ段階をスキップして、先にコードの断片をサーバーにアップロードするのがよいでしょう。Perl Diver (シンプルなPerlスクリプト)は、一部のサーバー機能 (インストール・モジュール、sendmailのロケーション、環境変数など) を判断するために使用できます。スクリプトの詳細な記述に目を通すこともできます。

CGI::* 型の標準モジュール

CGIモジュールは、その主な用途に加えて、さらにいくつかの機能を備えています。代表的なものとしてHTML書き込み機能がありますが、モジュールの作成者によれば「CGI::*モジュールを優先し、これは廃棄すべき」(HTTP::* およびHTML::* もここで追加すべき)とのことです。多くのモジュールの機能が必ずしも便利とは限りません。そのため、代わりにCGI.pmを使用することが多くなり、HTML書き込み機能は通常捨てておかれます。CGI::MinimalおよびCGI_Liteモジュールの方が、この目的に沿った機能を果たします。

複雑なCGIスクリプト記述には、時としてWeb関連のモジュールが必要になります。その実装は多くの場合、特定の作業や通常のHTML書き込みテクニックに依存しています。このためのモジュールのリストは、CPAN (参考文献を参照) に掲載されていますが、これはPerlプログラミング・アーカイブだけにとどまらず、着想の中核ともなるものです。複雑なスクリプトを記述するに先立ってCPANをご覧になることを強くお勧めします。手間をかけるだけの価値はあるはずです。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=226818
ArticleTitle=便利なCGIスクリプト作成のためのヒント
publish-date=06012001