Một hack là cần thiết bởi vì require
(và do đó use
) cả biên dịch và thực thi mô-đun trước khi quay trở lại.
Cùng đi cho eval
. eval
không thể được sử dụng để biên dịch mã mà không thực thi nó.
Giải pháp ít xâm phạm nhất mà tôi tìm thấy sẽ là ghi đè DB::postponed
. Điều này được gọi trước khi đánh giá một tập tin yêu cầu biên dịch. Thật không may, nó chỉ được gọi khi gỡ lỗi ( perl -d
).
Một giải pháp khác là đọc tệp, sửa đổi và đánh giá tệp đã sửa đổi, giống như sau:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Ở trên không được đặt đúng %INC
, nó làm rối tên tệp được sử dụng bởi các cảnh báo và do đó, nó không gọi DB::postponed
, v.v ... Sau đây là một giải pháp mạnh mẽ hơn:
use IO::Unread qw( unread );
use Path::Class qw( dir );
BEGIN {
my $preamble = '
UNITCHECK {
no warnings qw( redefine );
*Foo::bar = sub { 7 };
}
';
my @libs = @INC;
unshift @INC, sub {
my (undef, $fn) = @_;
return undef if $_[1] ne 'Foo.pm';
for my $qfn (map dir($_)->file($fn), @libs) {
open(my $fh, '<', $qfn)
or do {
next if $!{ENOENT};
die $!;
};
unread $fh, "$preamble\n#line 1 $qfn\n";
return $fh;
}
return undef;
};
}
use Foo;
Tôi đã sử dụng UNITCHECK
(được gọi sau khi biên dịch nhưng trước khi thực thi) vì tôi đã thêm phần ghi đè (sử dụng unread
) thay vì đọc toàn bộ tệp trong và nối thêm định nghĩa mới. Nếu bạn muốn sử dụng phương pháp đó, bạn có thể lấy một tệp xử lý để quay lại sử dụng
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Kudos đến @Grinnz để đề cập đến @INC
móc.
Foo::bar
, nhưng nóuse Foo
sẽ chạy cả giai đoạn biên dịch (xác định lại thanh nếu có bất kỳ điều gì được xác định trước đó ở đó) và giai đoạn thời gian chạy của Foo. Điều duy nhất tôi có thể nghĩ đến sẽ là một@INC
cái móc sâu sắc để sửa đổi cách Foo được tải.