如何在不使用eval的情况下动态包含Perl模块?

时间:2009-12-16 19:59:31

标签: perl module eval

我需要动态地包含一个Perl模块,但是如果可能的话,由于工作编码标准,我们希望远离eval。这有效:

$module = "My::module";
eval("use $module;");

但是如果可能的话,我需要一种方法来做到eval。所有谷歌搜索都会导致eval方法,但没有任何其他方式。

是否可以在没有eval的情况下执行此操作?

6 个答案:

答案 0 :(得分:48)

使用require在运行时加载模块。如果无法加载模块,通常最好将其包装在一个块(而不是字符串)eval中。

eval {
    require My::Module;
    My::Module->import();
    1;
} or do {
   my $error = $@;
   # Module load failed. You could recover, try loading
   # an alternate module, die with $error...
   # whatever's appropriate
};

eval {...} or do {...}语法和复制$@的原因是因为$@是一个全局变量,可以由许多不同的东西设置。您希望尽可能原子地获取值,以避免竞争条件,其他东西将其设置为不同的值。

如果您在运行时之前不知道模块的名称,则必须手动执行模块名称(My :: Module)和文件名(My / Module.pm)之间的转换:

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};

答案 1 :(得分:16)

如何使用核心模块Module::Load

用你的例子:

use Module::Load;
my $module = "My::module";
load $module;

“Module :: Load - 运行时需要模块和文件”

“加载无需知道您是要求文件还是模块。”

如果失败,它将会死于类似的东西“无法在@INC中找到xxx(@INC包含:......”

答案 2 :(得分:10)

嗯,

中始终有require
require 'My/Module.pm';
My::Module->import();

请注意,在编译时调用import而不是运行时,可能会失去任何效果。

编辑:这与eval方式之间的权衡是:eval允许您使用普通模块语法,如果模块名称无效(而不是仅仅找不到),则会给出更明确的错误。 OTOH,eval方式(可能)更容易受到任意代码注入。

答案 3 :(得分:4)

不,没有eval是不可能的,因为require()需要裸字模块名称,如perldoc -f require所述。但是,它并不是eval的恶意使用,因为它不允许注入任意代码(假设您可以控制文件的内容,当然是require。)

编辑:下面修改了代码,但我要完成第一个版本。

我使用 我曾经使用这个小糖模块在运行时进行动态加载:

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;
    eval "require $class" or do { die "Ack, can't load $class: $@" };
}

1;

PS。我正盯着这个定义(我很久以前写过)并且我在考虑添加这个: $class->export_to_level(1, undef, @imports); ... 工作,但未经过测试。

编辑:版本2现在,没有评估就更好了(感谢ysth)::)

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;

    (my $file = $class) =~ s|::|/|g;
    $file .= '.pm';
    require $file;  # will die if there was an error
}

1;

答案 4 :(得分:1)

CPAN上的Class :: MOP有一个load_class方法: http://metacpan.org/pod/Class::MOP

答案 5 :(得分:0)

我喜欢做像......这样的事情。

require Win32::Console::ANSI if ( $^O eq "MSWin32" );