2006年10月20日(金) 23時53分47秒 [Perl/CGI ]
表示を自由自在にカスタマイズ可能なCGIでは、よく「スキン(テンプレート)」が使われます。スキンHTMLをCGIが読み込んで、CGI独自のタグを読んでCGIの機能を挿入し、それ以外の箇所はHTMLに書かれている内容をそのまま出力するスタイルです。こうすると、CGIの表示をユーザが楽に自由にカスタマイズできるようになります。
スケジュールCGI とか、私が公開してるCGIでもそのように実装しています。
で、今日もそんなCGIを(Perlで)書いてたんですけども、SSIの処理を加えたくなった んです。
元々SSIを使って構築してるWebで使うためのCGIだったので、CGIが読み込むスキンHTML内でもSSIを使いたいのですよね。(^_^;)
SSIというのは、HTMLの中身をWebサーバが解釈して、SSIの各コマンドを処理して元のHTMLに合成する機能です。特に使いたいのは、「別ファイルの中身を挿入する」機能。
スキンHTMLにSSIの記述を書いても、当然(読むのはCGIなので)SSIは有効になりません。有効にしたければ、CGI側でSSIの記述を解読して処理してやるしかないのですよね。なかなか面倒くさそうです。(^_^;)
なんかうまい処理方法がネットで公開されてるかも知れなかったのですが、ネット接続環境のないところでコーディングしてたので、自力で考えることに。(^_^;)
別ファイルの中身を自由に挿入できるようにできれば十分なので、SSIの記述方法とは異なりますが、以下のような(スキンHTML用の)独自の書き方を作りました。
(SSIの記述方法は汎用性が高くて解読が面倒くさそうだったので。^^;)
<<ssi:FILEPATH>>
スキンHTML中にこの記述を発見したら、FILEPATHの位置にあるファイルを読み込んで、スキンHTMLに挿入するような機能をCGIに加えることにしました。
この記述が登場するのが、スキンHTML中に1回だけなら話は簡単なんですけど、複数回登場しても毎回適切に挿入処理を行う必要があるのですよね。もちろん、回数に制限はなしで。
なんだか面倒くさそうです。
…が、いろいろうなっていると、良さそうな解決策が思い浮かびました。
それが以下のような感じのコード。(各行の先頭の数値は行番号)
※$skin変数には、スキンHTMLの中身がそのまま入ってる前提。
001: my @ssifiles = ($skin =~ m/<<ssi:(.*)>>/g);
002: my @ssiopened;
003: foreach my $fname ( @ssifiles ) {
004: push( @ssiopened, join("\n",&ssiopenfile( $fname )) );
005: }
006: my $sc = 0;
007: $skin =~ s/<<ssi:.*>>/$ssiopened[$sc++]/g;
008:
009: sub ssiopenfile
010: {
011: my $fname = shift @_;
012: my @ssi;
013: open(SSIFILE, $fname) || return("[SSI NoFile: $fname]");
014: @ssi = <SSIFILE>;
015: close SSIFILE;
016: return @ssi;
017: }
上記のコードだけで、SSIのように「指定されたファイルを(何度でも)HTML中に合成する」機能が実現できました。
まあ、もうちょっとスマートに何か書けそうな気もしますが(^_^;)、とりあえず思いついたのがこんなコードでした。
正規表現で「g」オプションを加えて抜き出した結果は、そのまま配列で受け取ることができるんですよね。だから、1行目の@ssifiles配列には、スキンHTML中に書かれたすべての(合成すべき)ファイル名が順番に格納されると。たった1行でこの処理ができるのが、正規表現(+Perl)の威力だなあと感心します。C++でも使いたいよ…。^^;
で、指定されたファイルの中身は、ssiopenfileサブルーチンで順番に読んで、@ssiopened配列に格納されます。
(ここのコードは、もっと短くできそうですね。考えてないけど。^^;)
最後に、読み込んだファイルの中身を、(スキンHTML中の)適切な位置に挿入する必要があります。それが7行目。
これが、なかなかうまく書けたんじゃないかと思ったとこなんですが。(^_^;)
$skin =~ s/<<ssi:.*>>/$ssiopened[$sc++]/g;
挿入すべき内容は、挿入順に@ssiopened配列の中に格納されているのですよね。
あとは、SSIモドキの記述がスキンHTML中に登場するたびに、その配列の中身を1つずつ置き換えていけば良いわけです。
正規表現の置換処理の中で、配列の添え字が果たして使えるのだろうかと心配だったんですけど、うまくいきました。
配列の中身を1つずつ進めるカウンタとして、6行目に$sc変数を用意しています。もし、Perlに、「変数++」というインクリメントの記述方法がなかったら、この処理は書けなかったよな…と思います。^^;
インクリメントの記述がなくても、「ただループさせれば良いだけでは?」と思うかも知れませんが、for文でループさせると、今度は正規表現の側(マッチする位置を探す側)で「何番目の記述か?」を知る処理が面倒くさそうな気がします。まあ、毎回、$skin変数の中を書き換えていって、1つずつSSIモドキの記述を($skin変数内から)なくしていけばいいかも知れませんが。^^;
というわけで、CGIでSSIっぽい処理をさせるPerlコードでした。
まあ、世の中にはもっと機能が高くてスマートな良いコードがあるでしょうけどね。(^_^;)
久しぶりに、ずいぶん楽しんでプログラミングできました。(笑)