可以拦截Perl方法调用吗?

时间:2009-09-01 21:26:16

标签: perl

你能拦截Perl中的方法调用,用参数做一些事情,然后执行吗?

5 个答案:

答案 0 :(得分:14)

是的,您可以拦截Perl子例程调用。我在Mastering Perl中有关于这类事情的整章。查看Hook::LexWrap模块,您可以在不执行所有详细信息的情况下执行此操作。 Perl的方法只是子程序。

您还可以创建子类并覆盖要捕获的方法。这是一种稍微好一点的方法,因为这是面向对象编程希望你做的事情。但是,有时人们编写的代码不允许您正确执行此操作。在Mastering Perl中还有更多相关内容。

答案 1 :(得分:7)

简单描述一下,Perl具有修改符号表的能力。您可以通过该方法所属的包的符号表调用子例程(方法)。如果修改符号表(并且这不被认为非常脏),则可以使用调用指定的其他方法替换大多数方法调用。这证明了这种方法:

# The subroutine we'll interrupt calls to
sub call_me
{
    print shift,"\n";
}

# Intercepting factory
sub aspectate
{
    my $callee = shift;
    my $value = shift;
    return sub { $callee->($value + shift); };
}
my $aspectated_call_me = aspectate \&call_me, 100;

# Rewrite symbol table of main package (lasts to the end of the block).
# Replace "main" with the name of the package (class) you're intercepting
local *main::call_me = $aspectated_call_me;

# Voila!  Prints 105!
call_me(5);

这也表明,一旦有人参考了子程序并通过引用调用它,你就不能再影响这样的调用了。

我很确定在perl中有框架可以进行方面化,但我希望这能证明这种方法。

答案 2 :(得分:6)

这看起来像是Moose的工作! Moose是Perl的一个对象系统,可以做到这一点,还有更多。 docs会比我更好地解释,但你可能想要的是Method Modifier,特别是before

答案 3 :(得分:5)

你可以,帕维尔描述了一个很好的方法,但你应该详细说明为什么你想要这样做。

如果您正在寻找拦截对任意子例程的调用的高级方法,那么摆弄符号表将对您有用,但如果您想为可能导出到您当前正在使用的命名空间的函数添加功能,那么您可能需要知道调用其他命名空间中存在的函数的方法。

例如,

Data :: Dumper通常会将函数'Dumper'导出到调用命名空间,但您可以覆盖或禁用它,并提供您自己的Dumper函数,然后通过完全限定名称调用原始函数。 / p>

e.g。

use Data::Dumper;

sub Dumper {
   warn 'Dumping variables';
   print Data::Dumper::Dumper(@_);
}

my $foo = {
   bar   => 'barval',
};

Dumper($foo);

同样,这是一种替代解决方案,根据原始问题可能更合适。使用符号表时可以获得很多乐趣,但如果您不需要它可能会过度使用并且可能导致难以维护代码。

答案 4 :(得分:3)

你需要三件事:

调用的参数在@_中,这只是另一个动态范围的变量。

然后,goto支持reference-sub参数,该参数保留当前@_但是进行另一个(尾部)函数调用。

最后local可用于创建词法范围的全局变量,符号表隐藏在%::中。

所以你有:

sub foo {
    my($x,$y)=(@_);
    print "$x / $y = " . ((0.0+$x)/$y)."\n";
}
sub doit {
    foo(3,4);
}
doit();

当然打印出来:

3 / 4 = 0.75

我们可以使用local替换foo并转到:

my $oldfoo = \&foo;
local *foo = sub { (@_)=($_[1], $_[0]); goto $oldfoo; };
doit();

现在我们得到:

4 / 3 = 1.33333333333333

如果您想修改*foo而不使用其名称,并且您不想使用eval,那么您可以通过操纵%::来修改它,例如:

$::{"foo"} = sub { (@_)=($_[0], 1); goto $oldfoo; };
doit();

现在我们得到:

3 / 1 = 3