Perl方法调用可以被拦截吗?

发布于 2024-08-04 00:50:06 字数 42 浏览 10 评论 0原文

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

Can you intercept a method call in Perl, do something with the arguments, and then execute it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

乖乖公主 2024-08-11 00:50:06

是的,您可以拦截 Perl 子例程调用。我在 Mastering Perl 中有一整章关于此类事情。查看 Hook::LexWrap 模块,它可以让您无需完成所有操作即可完成此操作的细节。 Perl 的方法只是子例程。

您还可以创建一个子类并重写您想要捕获的方法。这是一个稍微好一点的方法,因为这是面向对象编程希望您这样做的方式。然而,有时人们编写的代码不允许您正确执行此操作。 掌握 Perl 中也有更多相关内容。

Yes, you can intercept Perl subroutine calls. I have an entire chapter about that sort of thing in Mastering Perl. Check out the Hook::LexWrap module, which lets you do it without going through all of the details. Perl's methods are just subroutines.

You can also create a subclass and override the method you want to catch. That's a slightly better way to do it because that's the way object-oriented programming wants you do to it. However, sometimes people write code that doesn't allow you to do this properly. There's more about that in Mastering Perl too.

哭了丶谁疼 2024-08-11 00:50:06

简单地说,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 中进行方面分析,但我希望这可以演示该方法。

To describe briefly, Perl has the aptitude to modify symbol table. You call a subroutine (method) via symbol table of the package, to which the method belongs. If you modify the symbol table (and this is not considered very dirty), you can substitute most method calls with calling the other methods you specify. This demonstrates the approach:

# 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);

This also shows that, once someone takes reference of the subroutine and calls it via the reference, you can no longer influence such calls.

I am pretty sure there are frameworks to do aspectation in perl, but this, I hope, demonstrates the approach.

风向决定发型 2024-08-11 00:50:06

这看起来像是 Moose 的工作! Moose 是 Perl 的一个对象系统,可以做到这一点以及更多。 文档 在解释方面会比我做得更好,但是什么您可能需要一个 方法修饰符,特别是之前

This looks like a job for Moose! Moose is an object system for Perl that can do that and lots more. The docs will do a much better job at explaining than I can, but what you'll likely want is a Method Modifier, specifically before.

拥有 2024-08-11 00:50:06

您可以,并且帕维尔描述了一种很好的方法,但您可能应该首先详细说明为什么要这样做。

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

例如,Data::Dumper 通常将函数“Dumper”导出到调用命名空间,但您可以覆盖或禁用该函数并提供您自己的 Dumper 函数,该函数然后通过完全限定名称调用原始函数。

例如

use Data::Dumper;

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

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

Dumper($foo);

,这又是一个替代解决方案,根据原始问题,它可能更合适。使用符号表可以带来很多乐趣,但它可能有点过头了,并且如果您不需要它,可能会导致代码难以维护。

You can, and Pavel describes a good way to do it, but you should probably elaborate as to why you are wanting to do this in the first place.

If you're looking for advanced ways of intercepting calls to arbitrary subroutines, then fiddling with symbol tables will work for you, but if you want to be adding functionality to functions perhaps exported to the namespace you are currently working in, then you might need to know of ways to call functions that exist in other namespaces.

Data::Dumper, for example, normally exports the function 'Dumper' to the calling namespace, but you can override or disable that and provide your own Dumper function which then calls the original by way of the fully qualified name.

e.g.

use Data::Dumper;

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

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

Dumper($foo);

Again, this is an alternate solution that may be more appropriate depending on the original problem. A lot of fun can be had when playing with the symbol table, but it may be overkill and could lead to hard to maintain code if you don't need it.

狼性发作 2024-08-11 00:50:06

是的。

您需要三件事:

调用的参数位于 @_ 中,这只是另一个动态作用域变量。

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

最后,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 而不使用它的name,并且你不想使用eval,那么你可以通过操作%::来修改它,例如:

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

现在我们得到:

3 / 1 = 3

Yes.

You need three things:

The arguments to a call are in @_ which is just another dynamically scoped variable.

Then, goto supports a reference-sub argument which preserves the current @_ but makes another (tail) function call.

Finally local can be used to create lexically scoped global variables, and the symbol tables are buried in %::.

So you've got:

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

which of course prints out:

3 / 4 = 0.75

We can replace foo using local and go:

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

And now we get:

4 / 3 = 1.33333333333333

If you wanted to modify *foo without using its name, and you didn't want to use eval, then you could modify it by manipulating %::, for example:

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

And now we get:

3 / 1 = 3
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文