はじめに
こんにちは、結城浩です。
Perlでシンプルなアクセスカウンタを作ったので紹介します。アクセスカウンタはGIFの画像として出力しますが、GDなどの外部モジュールや、GIFファイルは使いません。そのため、設置がとても簡単です。
対象読者
この記事は、Perlで作ったCGIを設置できる読者を対象としています。
必要な環境
Perl 5.6以降を使ってCGIが動作するWebサーバが必要です。Perlの標準モジュール以外には、モジュールは不要です。
設置方法
今回作ったアクセスカウンタ「counter.cgi」の動作を説明する前に、設置方法を説明します。
1. counter.cgiを修正
まず、counter.cgiの中で以下の点を読者の環境に合わせて修正します。
1行目、Perl処理系のパスを修正します。「#!」に続いて、読者のWebサーバにインストールされているPerl処理系のパスを記述します。たとえば以下は「/usr/bin/perl」にインストールされている場合です。
#!/usr/bin/perl
16行目、カウンタファイルのパスを修正します。変数$counter_file
にアクセス数を保持するカウンタファイルのパス名を記述します。フルパスで記述したほうがトラブルが少ないでしょう。このカウンタファイルは前もって作っておきます。中身は空でかまいません。たとえば以下は「/usr/local/htdocs/mydir/counter.dat」にファイルを作った場合です。
my $counter_file = "/usr/local/htdocs/mydir/counter.dat";
2. HTMLファイルを修正
counter.cgiにアクセスするためのHTMLファイルを用意します。サンプルとして「sample.html」を用意しました。以下のimg
タグの中にあるsrc
属性に、counter.cgiにアクセスするためのURLを記述します。
以下は、「http://example.com/counter.cgi」でアクセスする場合の記述例です。
<img width="64" height="24" alt="(access counter)"
src="http://example.com/counter.cgi" />
もしも、ブログから使う場合には、表示させる場所に上記のようなimg
タグを埋め込んでください。
3. ファイルの設置
counter.cgiは、テキストモードでWebサーバに送信し、パーミッションは「0755(rwxr-xr-x)」にします。Webサーバの設定によっては「0705(rwx---r-x)」でなければ動かないときもあります。Webサーバ管理者に問い合わせてください。
カウンタファイルは、テキストモードでWebサーバに送信し、パーミッションは「0666(rw-rw-rw-)」にします。HTMLファイルは、テキストモードでWebサーバに送信し、パーミッションは「0644(rw-r--r--)」にします。
4. HTMLファイルを表示
以上で設置は完了です。imgタグを含むHTMLファイルにWebブラウザでアクセスしてください。Fig1のように表示されれば完成です(この図は6回実行した様子です)。Webブラウザのリロードボタンを使って、数が増加していくことを確認してください。
Fig2のように表示された場合には、counter.cgiが正しく動作していません。Perlのパスやパーミッションなどを確認してください。
Fig3のように「99」がいつも表示されるのは、カウンタファイルのオープンに失敗した場合です。カウンタファイルのパスやパーミッションを確認してください。
アクセスカウンタの動作
アクセスカウンタcounter.cgiの動作を簡単に説明します。
counter.cgiは、Perlで書かれた150行足らずのスクリプトで、Webサーバ上でCGIとして動作します。すなわち、Webクライアントからのリクエストに対して、標準出力にコンテンツを出力するプログラムです。
掲示板やアンケート、会議室など「Webページ」として動作するCGIは、出力のContent-typeが「text/html」ですが、counter.cgiは出力が画像ですので、Content-typeは「image/gif」になります。
counter.cgiには、アクセス数をカウントしているカウンタファイルがあります。といっても、その内容はこれまでのアクセス数がテキストとして記録されているだけです。
counter.cgiは、実行するたびに、アクセス数をファイルから読み取り、そこに書かれているアクセス数を画像に変換して標準出力に出します。そして、アクセス数を1増加させてカウンタファイルに書き戻します。
アクセスカウンタの基本的処理は、counter.cgiの中のget_access_counter
関数で行っています。
sub get_access_counter { my ($fname) = @_; if (!-e($fname)) { open(FILE, "> $fname"); close(FILE); } if (open(FILE, "+< $fname")) { flock(FILE, 2); # 2=LOCK_EX seek(FILE, 0, 0); my $count = <FILE> + 1; seek(FILE, 0, 0); print FILE "$count\n"; flock(FILE, 8); # 8=LOCK_UN close(FILE); return $count; } else { # File open error. return 99; } }
画像出力部分
画像で表示するアクセスカウンタは珍しくありませんが、多くの場合、PerlのGDモジュールを使います。そのため、GDモジュールがインストールされていなければなりません。
今回作ったcounter.cgiは、GDモジュールを使わず、自前でフォントデータをスクリプト中にハッシュ%image
として持ち、動的にGIFを合成して出力しています。このフォントデータは筆者がペイントツールで8x24の大きさの文字を描き、その内容を16進ダンプして作りました。
my %image = ( '[' => "\x08\x00\x18\x00\x00\x02\x17\x8C\x8F\xA9\x0A\xDD\x07 \x56\x64\xB2\xD2\x9B\x66\xB6\x1B\x23\xFD\x71\x9F\x03\x2E \xE6\x89\x14\x00", '0' => "\x08\x00\x18\x00\x00\x02\x1A\x8C\x8F\xA9\x07\xED\xBD \xA2\x9C\x0E\x01\x86\xCD\xD5\x39\x6C\xDF\x7D\x1E\x34\x95 \xE6\xF8\x90\xE7\x7A\x14\x00", '1' => "\x08\x00\x18\x00\x00\x02\x17\x8C\x8F\xA9\x07\xED\xBD \xA2\x9C\x01\x24\xB7\xAC\xD2\x37\xDF\x47\x85\xD4\x03\x8A \x26\x52\x00\x00", # 中略 '9' => "\x08\x00\x18\x00\x00\x02\x1A\x8C\x8F\xA9\x07\xED\xBD \xA2\x9C\x0E\x01\x86\xCD\x65\x55\xED\xF0\x7D\x20\x34\x95 \xE6\xF8\x90\xE7\x7A\x14\x00", ']' => "\x08\x00\x18\x00\x00\x02\x17\x8C\x8F\xA9\x07\x0D\xFB \x54\x4C\x13\x55\x06\xB3\xD4\x94\x5B\x8F\x6D\x8C\xB3\x94 \xE6\x69\x14\x00", );
文字列を画像に変換して組み立てるのはconcat_gif
関数です。ここで呼んでいるmake_header
関数とmake_footer
関数は、それぞれGIFのヘッダとフッタを作るものです。詳しくはGIFの仕様とスクリプトを参照してください。
sub concat_gif { my ($string) = @_; my $result = ''; $result .= &make_header(8 * length($string), 24); $result .= &make_image($string); $result .= &make_footer; return $result; }
make_image
関数では、与えられた文字列を1文字ごとにsplit
で分解します。そして、1文字分の画像データを作り出すmake_subimage
関数を繰り返し呼び出します。
# Create images. sub make_image { my ($string) = @_; my $step = 8; my $result = ''; my $top = 0; my $left = 0; foreach (split(//, $string)) { $result .= &make_control(); $result .= &make_subimage($left, $top, $_); $left += $step; } return $result; }
make_subimage
関数は、位置情報と%image
のフォント情報を連結して返すだけです。
# Create a GIF subimage. sub make_subimage { my ($left, $top, $name) = @_; # introducer my $result = "\x2C"; # image left position, top position $result .= &pack_xy($left, $top); $result .= $image{$name}; return $result; }
カスタマイズ
字体の変更
conter.cgiではフォントデータを内部に抱えていますので、字体を変更することはできません。
桁数の変更
アクセスカウンタとして表示する数字の桁数は、デフォルトで6桁になっています。これは、main
関数の「%06」の部分を変えれば変更できます。
my $string = sprintf("[%06d]", $counter);
複数カウンタの設置
counter.cgiが読み書きするカウンタファイルを切り替えれば、複数のアクセスカウンタを設置することもできます。これは、変数$counter_file
に与えるファイル名を外部から与えることで可能になります。たとえば、アクセスカウンタのURLの後に「?カウンタ名」を記述することにします。カウンタ名として「sports」を与えた例を以下に示します。
<img width="64" height="24" alt="(access counter)"
src="http://example.com/counter.cgi?sports" />
以下にcounter.cgiの修正例を示します。
my $counter_name = $ENV{QUERY_STRING}; if ($counter_name !~ /^[a-z]+$/) { $counter_name = 'counter'; } my $counter_file = "/usr/local/htdocs/mydir/$counter_name.dat";
上の例ではカウンタ名として「英小文字の列」だけを許し、それ以外は「counter」というカウンタ名にしています。この処理はセキュリティ上重要です。外部から与えられたカウンタ名をそのままファイル名としてしまうと、こちらが予期しないファイルを勝手に外部から作られてしまう恐れがあるからです。
まとめ
Perlでシンプルなアクセスカウンタを作りました。フォントデータをスクリプト中に持ち、GIFを自前で合成しますので、特別な外部モジュールを必要としません。スクリプトそのものは、Perlのライセンスにしたがって公開します。フォントデータそのものも筆者が作りましたので、自由に使ってかまいません。
Enjoy!
参考リンク
「GIF画像連結ライブラリ」は、外部ファイルとして用意されたGIF画像を連結するライブラリです。今回のcounter.cgiではこのライブラリは使いませんでしたが、その考え方は参考にさせていただきました。感謝します。