有没有一种方法可以在Perl中模拟内置的require函数?

5
我正在开发一个应用程序,需要逐步替换一堆混乱的代码。为了实现这个目标,我有一个调度器,当URI匹配时运行所需的HTTP资源,否则使用旧的HTTP资源类。
因此,这个旧的HTTP资源必须要require旧系统的入口文件,我正试图找出如何测试这个过程。我现在想到的方法是,将原始的require函数替换为模拟子例程,并检查它是否已被调用以及是否传递了适当的文件名。
这种做法可行吗?如果不行,也许有更好的方法吗?
3个回答

14

要在单个包中覆盖require,可以使用以下方法:

use subs 'require';  # imports `require` so it can be overridden

sub require {print "mock require: @_\n"}

全局覆盖 require

BEGIN {
    *CORE::GLOBAL::require = sub {print "mock require: @_\n"}
}

然后:

require xyz;           # mock require: xyz.pm

require Some::Module;  # mock require: Some/Module.pm

那样做会只为该模块模拟 require 函数吗?我需要它对所有其他模块也进行模拟。 - Igor Zinov'yev

11
更好的全局覆盖 require 的方法可能是在 @INC 中安装钩子。这个鲜为人知的功能在 require 文档 的末尾进行了描述。
以下是一个简单的示例,拦截任何以 HTTP 开头的模块名称的请求:
BEGIN {
  unshift @INC, sub {
    my ($self, $file) = @_;
    return unless $file =~ /^HTTP/;
    print "Creating mock $file\n";
    my @code = "1"; # Fake module must return true
    return sub { $_ = shift @code; defined $_ };
  }
}

require HTTP::Foo;
use HTTPBar;
请注意,这也会模拟 use,因为它基于 require

3
钩子可以作为代码引用添加到@INC路径中。然后这些钩子将全局应用于userequire语句。
引用perldocrequire
“您还可以通过直接将Perl代码放入@INC数组中来将钩子插入导入功能中。” “有三种形式的钩子:子例程引用、数组引用和blessed对象。” “子例程引用是最简单的情况。当包含系统遍历@INC并遇到子例程时,此子例程将使用两个参数调用,第一个是对自身的引用,第二个是要包含的文件的名称(例如,“Foo / Bar.pm”)。子例程应该返回以下顺序的空或最多三个值的列表:” “1.文件句柄,从中读取文件。” “2.对子例程的引用。如果没有文件句柄(前一项),则此子例程应每次调用生成一行源代码,将该行写入$ _并返回1,然后最终在文件结束时返回0。如果有文件句柄,则将调用子例程以充当简单的源过滤器,并将该行作为在$ _中读取。同样,在返回所有有效行后,返回1,并在返回所有行后返回0。” “3.子例程的可选状态。状态作为$_[1]传入。对子例程本身的引用作为$_[0]传入。”
以下是示例:
#!/usr/bin/perl

sub my_inc_hook {
    my ($sub_ref, $file) = @_;

    unless ($file =~ m{^HTTP/}) {
        warn "passing through: $file\n";
        return;
    }

    warn "grokking: $file\n";
    return (\*DATA);
}

BEGIN {
    unshift(@INC, \&my_inc_hook);
}

use strict;
require warnings;
require HTTP::Bazinga;

HTTP::Bazinga::it_works();

__DATA__
package HTTP::Bazinga;

sub it_works {warn "bazinga!\n"};

1;

生成:

$ perl inc.pl
passing through: strict.pm
passing through: warnings.pm
grokking: HTTP/Bazinga.pm
bazinga!

我相信这适用于Perl 5.10.0及以上版本。

感谢您的详细解释,我已经接受了一个类似的答案,我相信这个答案是最先发布的。 - Igor Zinov'yev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接