perl の require を知る
今、perlがあつい!よって require について知るしかない!
"普通" のrequire
require(もしくはuse)関数は Module をロードしたいときに使う。
例えば、
require Foo::Bar; # もしくは use Foo::Bar
とすると、perlは@INC
に入っているディレクトリ以下のFoo/Bar.pmファイルを探しload(eval)しようとする。
すなわち仮に@INCが
@INC = ("/path/to/dir1", "/path/to/dir2", "/path/to/dir3")
ならば、perlは
候補1: /path/to/dir1/Foo/Bar.pm 候補2: /path/to/dir2/Foo/Bar.pm 候補3: /path/to/dir3/Foo/Bar.pm
の順に Foo/Bar.pm を探し、見つかった時点でそれをloadし探索を終了する。 もし見つからなかったら、
Can't locate Foo/Bar.pm in @INC (you may need to install...
のメッセージとともにdieする。
よって自分の書いたモジュールMy::Moduleが lib/My/Module.pmに置いてあるなら @INC にlibディレクトリを突っ込むことで My::Module を使えるようになる。
BEGIN { unshit @INC, "lib" } use My::Module; # OK
@INCにはサブルーチンリファレンス、オブジェクトも入れられる
上記、"普通"のrequireの仕組みは特定のディレクトリにファイルとしてモジュールを配置することを強制する。
しかし実際のところ、@INCにはディレクトリだけでなく、サブルーチンレファレンス、オブジェクトも入れられる!すなわち
my $sub = sub { ... }; unshit @INC, $sub;
みたいにやってよろしい。このハックによってモジュールロードの仕方を自由に変えられる。
まず例を出すと、
BEGIN { my $sub = sub { my ($self_sub, $module_file) = @_; return if $module_file ne "My::Module"; my $module_content = q{ package My::Module; sub new { bless {}, shift } sub hello { print "hello!\n" } 1; }; return \$module_content; }; unshift @INC, $sub; } use My::Module; # OK!!
のようにすれば My::Module は $sub から読み込まれる。物理的なディレクトリやファイルの制約から解放された!
@INCに入れるサブルーチンリファレンス詳細
@INCに入れられる$subの詳細は以下である。
仮に @INC が
@INC = ($sub, "/path/to/dir1", "/path/to/dir2");
だとしよう。このとき require Foo::Bar
が実行されると
まず@INCの先頭の$subが実行され、それがFoo::Barを"load"させたなら
そこで終了。もし$subがFoo::Barを"load"しなかった場合は
"/path/to/dir1"から通常のディレクトリ探索が始まる。
さて$subは
第一引数:自分自身 ($sub) 第二引数:モジュールファイル名 (Foo/Bar.pm)
で実行される。そして$subはもしそれをloadさせたければ 以下の9種類のうちどれかを返り値として返せばよろしい(おそらく)。
1. $scalar_ref 2. ($scalar_ref, $fh) 3. ($scalar_ref, $fh, $filter_sub) 4. ($scalar_ref, $fh, $filter_sub, $filter_sub_arg) 5. $fh 6. ($fh, $filter_sub) 7. ($fh, $filter_sub, $filter_sub_arg) 8. $line_generator_sub 9. ($line_generator_sub, $line_generator_sub_arg)
もし、$subがそれをloadさせたくなければ return だけすればいい。
9種類の返り値のうち、1, 5 ぐらいしか見たことがないのでそれだけ説明する。
1.の $scalar_ref とはコード(文字列)のレファレンスである。つまり
my $code = <<'...'; use strict; use warnings; sub foo { ... }; sub bar { ... }; 1; ... my $scalar_ref = \$code;
のようなものである。これを返り値として返せば、それがFoo/Bar.pmのコードだとしてload(eval)される。
5.の $fh とはファイルハンドルである。例えば
open my $fh, "<", "/path/to/awesome/code.pm"; $fh;
などを返せば、$fhの中身がFoo/Bar.pmのコードだとしてload(eval)される。
@INCに入れるオブジェクト詳細
@INCにはオブジェクトも入れられる。動作はほぼサブルーチンの場合と一緒である。違うのはサブルーチンの場合はモジュール探索時単に実行されたが、 オブジェクトの場合はINCメソッドが実行される点である。
例えば以下のような感じ。
BEGIN { { package My::Hook; sub new { bless {}, shift } sub My::Hook::INC { my ($self, $module_filename) = @_; return if $module_filename ne "Foo/Bar.pm"; my $content = 'sub hello { warn "hello" } 1;'; open my $fh, "<", \$content; return $fh; } } my $hook = My::Hook->new; unshift @INC, $hook; } use Foo::Bar;
返すべき値はサブルーチンの場合と一緒である。
どんなハックができるか
FatPacker, Carmel, lib::xi あたりを見ざるを得ない。
SEE ALSO
- perldoc -f require
サブルーチンが返すべき値のところの文章がわかりにくい。よってこの説明があってるか微妙。
まとめ
perlがあつい!