読者です 読者をやめる 読者になる 読者になる

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があつい!