2012年09月05日

mod_rewriteのRewriteMapでPHPのプログラムを使う

 現代の黒魔術、mod_rewriteは万能である。
 正規表現と置換だけでも万能だが、更に黒い魔術として、入力をユーザー独自のプログラムに渡して書き換えることができる、RewriteMapというのがある。

 厳密には、RewriteMapは独自プログラムに渡すと言うよりは、マップテーブルを用意しておいてそこから選ぶもので、その拡張機能としてプログラムに渡すというものがあるのだろう。まあ来歴はどうでもいいが、これを使うと正規表現では書けないような妙ちきりんな書き換えもできる。例えばUTF-8エンコードされたクエリーをデコードしてSJISに変えてまたエンコードしたものにする、とか、特定のファイルが存在していたら書き換え、とか。

 まず、Apacheのconfigファイルに例えばこのように書く。
RewriteEngine on
RewriteLock /tmp/rewritemap.lock
RewriteMap myprog prg:/usr/local/bin/url_rewriter.php
RewriteRule ^/rewrite/(.*)$ /test/${myprog:$1}
 RewriteLockはロックファイルの場所で、どこでも良い。但し、VirtualHostの中には置けないようなので、一番上に書く。
 "RewriteMap プログラム名 prg:プログラムのパス"で、プログラムの場所を指定し、RewriteRule中の ${プログラム名:入力} が、プログラムによって置換される。
 
 さて、問題のプログラムである。
 どういう仕組みになっているかと言うと、mod_rewrite側は標準入力に入力を入れてきて、プログラム側は標準出力に書き換え結果を出力する。のであるが、このプログラムは、Apacheの起動時に書き換え用のプロセスが1つ立ち上がり、プロセスはずっと走りっぱなしになっているのである。アクセス毎に起動されるわけではなく、プログラム中では、(一般的には)標準入力をブロッキングI/Oで読み続けるループにする必要があるのであった。
 従って例えば、url_rewriter.phpはこんな感じになる。
 
#!/usr/local/bin/php
<?php
$fin = fopen("php://stdin",'r');
while(!feof($fin)){
$read = fgets($fin);

$out = ここで$readに応じて書き換え処理...;

$fout = fopen("php://stdout",'w');
$out .= "\n";
fwrite($fout, $out);
fclose($fout);
}
fclose($fin);
// ※入出力はSTDINとechoを使ってもいいが、分かり易さでphp://とした。
?>
 mod_rewriteからは、stdinに入力内容+"\n"が来る。この入力をfgetsで取って、書き換えた結を"\n"で終端してstdoutに返す。終わったら次の入力に備える。と、こういう次第である。
posted by usoinfo at 21:13 | Comment(0) | 開発 | このブログの読者になる | 更新情報をチェックする