Peretasan diperlukan karena require(dan karenanya use) mengkompilasi dan mengeksekusi modul sebelum kembali.
Sama berlaku untuk eval. evaltidak dapat digunakan untuk mengkompilasi kode tanpa juga menjalankannya.
Solusi paling tidak mengganggu yang saya temukan adalah menimpanya DB::postponed. Ini disebut sebelum mengevaluasi file yang diperlukan dikompilasi. Sayangnya, ini hanya dipanggil saat debugging ( perl -d).
Solusi lain adalah dengan membaca file, memodifikasinya, dan mengevaluasi file yang dimodifikasi, seperti yang berikut ini:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Di atas tidak diatur dengan benar %INC, itu mengacaukan nama file yang digunakan oleh peringatan dan semacamnya, itu tidak memanggil DB::postponed, dll. Berikut ini adalah solusi yang lebih kuat:
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;
Saya menggunakan UNITCHECK(yang dipanggil setelah kompilasi tetapi sebelum eksekusi) karena saya mendahulukan override (menggunakan unread) daripada membaca di seluruh file dan menambahkan definisi baru. Jika Anda ingin menggunakan pendekatan itu, Anda bisa mendapatkan pegangan file untuk kembali menggunakan
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Kudos to @ Grinnz karena menyebutkan @INCkait.
Foo::bar, tetapiuse Fooakan menjalankan fase kompilasi (mendefinisikan ulang bar jika ada yang sebelumnya didefinisikan di sana) dan fase runtime dari Foo. Satu-satunya hal yang dapat saya pikirkan adalah@INCkait yang sangat dalam untuk memodifikasi bagaimana Foo dimuat.