mod_perlを5年以上触ってて初めてこんな現象に出会いました。ほんとひどい罠です。
コード
今回問題となったindex.plはこんな感じです。どこがまずいかわかりますか?
use strict; use warnings; use Apache2::RequestUtil (); use ModPerl::Util; sub main(){ my $r = Apache2::RequestUtil->request(); $r->content_type('text/plain'); print_hello($r); ModPerl::Util::exit(); } sub print_hello(){ my $r = shift; $r->print("It's bug!\n"); } main();
理由
print_helloはプロトタイプが指定されており、引数がないことになっています。しかし、main()の中では引数付きで呼ばれているのでエラーとなります。これが原因。
でも、これだけじゃあ納得できない現象も起こってますね。それは以下の理由で起こります。
なんでperl -cw で syntax OK なの??
プロトタイプのチェックはコンパイル時に行われます。が、このコードだとprint_helloのプロトタイプが宣言される前に使用しているため、このチェックが行われないわけです。よって、コンパイルエラーにはなりません。
なんでtouchでエラーが起こり始めるの?
touchすると、スクリプトの更新時間が変わるため、ModPerl::Registryはスクリプトを再コンパイルしようとします。しかし、この時パッケージ内で定義されている名前を奇麗にしたりしません。元の環境を残したまま再コンパイルが行われます。そのため、最初のコンパイルで定義された「print_helloは引数がない」と言うプロトタイプ情報が有効になった状態でコンパイルが行われるため、ここで引数の型チェックが有効となってエラーとなるわけです。
error_logにはこう残ります。
[Sat Apr 19 00:13:39 2008] [error] Too many arguments for ModPerl::ROOT::ModPerl::Registry::XXXXX_index_2epl::print_hello at /XXXXX/index.pl line 9, near "$r)"\n
対策
ダミアン先生がPerl Best Practiceで言っているように*1、プロトタイプは使わない方がいいってことでしょう。個人的には好きなんですけどね、プロトタイプ。
合わせて読みたい
せっかくなので、mod_perlの一癖も二癖もある挙動と戦った過去をまとめてみました。
- コンパイル済の関数を消す
- Class::DBIとStatINCは相性悪し
- StatINCとオーバーライド
- Class::DBIとキャッシュの問題
- mod_perl開発がキャッシュで行き詰ったら
- クラスの初期化(2)
- Apache::StatINC
- 障害はいつも定時前(6)
- 障害はいつも定時前(5)
雑誌連載の話があれば8回分は持ちそうですね≧(´▽`)≦。
*1:"Don't use subroutine prototypes ."