mod_perlハンドラを書く
Rev.2を表示中。最新版はこちら。
このテキストは
mod_perl2.0ドキュメントの"Writing mod_perl Handlers and Scripts"(http://perl.apache.org/docs/2.0/user/coding/coding.html)の和訳です。
現在編集中
目次
# Code Developing Nuances* Auto-Reloading Modified Modules with Apache2::Reload
# Integration with Apache Issues
* HTTP Response Headers
o Generating HTTP Response Headers
o Forcing HTTP Response Headers Out
* Sending HTTP Response Body
* Using Signal Handlers
# Perl Specifics in the mod_perl Environment
* BEGIN Blocks
* CHECK and INIT Blocks
* END Blocks
* Request-localized Globals
* exit
# ModPerl::Registry Handlers Family
* A Look Behind the Scenes
* Getting the $r Object
# Threads Coding Issues Under mod_perl
* Thread-environment Issues
* Deploying Threads
* Shared Variables
# Maintainers
# Authors
説明※
この章ではmod_perlのコーディングの詳細、通常のPerlコーディングとの違いを扱います。その他のほとんどのPerlコーディングに関する情報はperlのmanページや書籍で取り扱われています。
前提※
原文なし
Where the Methods Live
mod_perl 2.0では多くのモジュールに跨ってメソッドが存在します。これらのメソッドを使うためにメソッドを含むモジュールは最初にロードしておかないといけません。そうでないと、mod_perlは「メソッドを見つけられない」とエラーにします。どのモジュールが必要かを探すにはModPerl::MethodLookup モジュールが使えます。
Techniques
メソッドハンドラ※
関数ハンドラに加えてメソッドハンドラを使うことができます。メソッドハンドラは継承のメリットを生かしたコードを書きたい場合に便利です。mod_perl2のメソッドとしてハンドラを作る場合、method属性を使ってください。
属性の詳細についてはPerlのmanページを参照してください(perldoc attributes)。
例:
package Bird::Eagle; @ISA = qw(Bird); sub handler : method { my ($class_or_object, $r) = @_; ...; } sub new { bless {}, __PACKAGE__ }
そして、これを以下のようにして登録します。
PerlResponseHandler Bird::Eagle
mod_perlはハンドラがmethod属性を持っている場合は、2つの引数をハンドラに渡します。上記のコードにもあるようにオブジェクトもしくはクラス名(どのように呼び出されたかに依存します)とRequestオブジェクトです。
以下のようにClass->methodのような形式が使われた場合は、
PerlResponseHandler Bird::Eagle->handler;
method属性は必要ありません。上の設定例ではhandler()メソッドはクラス(静的)メソッドとして呼び出されます。
メソッドを呼び出すのにオブジェクトを使うこともできます。例えば、
<Perl> use Bird::Eagle; $Bird::Global::object = Bird::Eagle->new(); </Perl> ... PerlResponseHandler $Bird::Global::object->handler
この例では、hanlder()メソッドはグローバルオブジェクト$Bird::Global::objectのインスタンスメソッドとして呼び出されます。
クリーンアップ※
さまざまなフェーズの終わりで発生するクリーンアップ処理をアレンジできます。これを行うのにENDブロックは使えません。なぜなら、ENDブロックはインタプリタが終了するまで実行されないからです(Registryハンドラは例外です)。
モジュール作成者は各HTTPリクエストの後にクリーンアップ処理が必要な場合は、PerlCleanupHandlerを使うべきです。
その他のタイミングでクリーンアップ処理を行う必要がある場合は、プールオブジェクトのcleanup_registerメソッドでクリーンアップコールバックを登録することができます。使用例をいくつか示します。
サーバーのシャットダウンやリスタート時に何か行いたい場合は、startup.plでserver_shutdown_cleanup_register()で登録したクリーンアップハンドラを使ってください。
#PerlPostConfigRequire startup.pl use Apache2::ServerUtil (); use APR::Pool (); warn "parent pid is $$\n"; Apache2::ServerUtil::server_shutdown_cleanup_register((\&cleanup); sub cleanup { warn "server cleanup in $$\n" }
この方法は、サーバーが停止したり、再起動する時に行うべきサーバー全体のクリーンアップ処理を行うのに便利です。
接続フェーズの終わりにクリーンアップを行う場合は、コネクションプールオブジェクトにクリーンアップコールバックを割り当てます。
use Apache2::Connection (); use APR::Pool (); my $pool = $c->pool; $pool->cleanup_register(\&my_cleanup); sub my_cleanup { ... }
あなた自身のプールオブジェクトを作成してコールバックを登録することもできます。これはオブジェクトが破壊された時に呼び出されます。
use APR::Pool (); { my @args = 1..3; my $pool = APR::Pool->new; $pool->cleanup_register(\&cleanup, \@args); } sub cleanup { my @args = @{ +shift }; warn "cleanup was called with args: @args"; }
この例ではクリーンアップコールバックは$poolがスコープの外に出て破壊された時に呼び出されます。これはオブジェクト指向のDESTROYメソッドに非常に似ています。
Goodies Toolkit
環境変数※
mod_perlは次の環境変数を設定します。
- $ENV{MOD_PERL} - mod_perlのバージョンが設定されます
例:
mod_perl/2.000002
$ENV{MOD_PERL}が存在しなければ、たぶんmod_perl配下で動作していません。
die "I refuse to work without mod_perl!" unless exists $ENV{MOD_PERL};
どのバージョンが使われているのかチェックするには、次のテクニックを使うとよいです。
use mod_perl; use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); # die "I want mod_perl 2.0!" unless MP2;
mod_perlは次の環境変数をエクスポートします(それらが設定されていれば)。
- PATH - 実行可能ファイルのパス
- TZ - タイムゾーン
これらの環境変数は%ENVでアクセスできます。
スレッド化MPMか否か※
実行環境がスレッド化されたMPMかそうでないかでコードが異る動作をする必要があるならApache2::MPM->is_threadedクラスメソッドが使えます。
例:
use Apache2::MPM (); if (Apache2::MPM->is_threaded) { require APR::OS; my $tid = APR::OS::current_thread_id(); print "current thread id: $tid (pid: $$)"; } else { print "current process id: $$"; }
このコードはスレッド化されたMPM配下ではカレントスレッドIDを表示します。そうでなければプロセスIDを表示します。
MPM固有のコードを書く※
あなたがCPANモジュールを書くなら、全てのMPMで動作しないコードを書くのはまずい考えです。開発者は全てのMPMで動作するコードを書くよう努力すべきです。異るMPM環境で異ることを行うのはまったく立派なことです。
あなたがCPANモジュールを開発しないなら、特定のMPMで動作すればあなたのプロジェクトにとって十分でしょう。
use Apache2::MPM (); my $mpm = lc Apache2::MPM->show; if ($mpm eq 'prefork') { # prefork-specific code } elsif ($mpm eq 'worker') { # worker-specific code } elsif ($mpm eq 'winnt') { # winnt-specific code } else { # others... }