如何使方法成为“最终”方法在 Perl 中?

发布于 2025-01-08 16:22:20 字数 258 浏览 0 评论 0原文

我想知道是否可以确保我创建的类中的方法不会被猴子修补(猴子修补) 。穆斯能做到这一点吗?

考虑以下几点:

{
  package Foo;
  sub hello{print "HI"}
1;
}

package main;
sub Foo::hello {print "bye"}

Foo::hello()#bye

I was wondering if it is possible to make sure a method in a class I make will NOT be monkey patched (Monkey patch). Can Moose achieve this?

Consider the following:

{
  package Foo;
  sub hello{print "HI"}
1;
}

package main;
sub Foo::hello {print "bye"}

Foo::hello()#bye

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

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

发布评论

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

评论(4

回忆躺在深渊里 2025-01-15 16:22:20

经过快速的网络研究后,我发现 Perlmonks 上的这个线程指出:

至于将方法声明为final,我不确定如果不做一些非常奇特的事情来拦截符号表中的所有添加,您将如何做到这一点。 (这可以做到吗?)。

我也认为这是不可能的。

使用 Moose,您可以应用方法修饰符允许您定义必须在调用函数之前运行的函数。
我还没有尝试过这个,但也许你可以定义一个函数,

before "hello" => sub{ # check if hello has been tampered with
                   }

我不知道如何检查它以及它是否有效,但看起来值得一试!

不过我想补充一点,由于 perl 是一种解释语言,任何使用你的包的人都可以查看和编辑源代码,从而可以规避任何预防措施。

After a quick web research i found this thread on Perlmonks that states:

As for declaring methods final, I'm not sure how you would do it without doing something really fancy to intercept all additions to the symbol table. (Can that even be done?).

I would also assume that it is impossible.

Using Moose you can apply Method Modifiers that allow you to define functions that must run before a function is called.
I have not tried this but maybe you could define a function

before "hello" => sub{ # check if hello has been tampered with
                   }

I don't know exactly how to check it and if it even works, but it looks like it's worth a try!

However I would add, that since perl is an interpreted language anyone who uses your package can view and edit the source, making any precaution circumventable.

暗恋未遂 2025-01-15 16:22:20

Perl 不太喜欢 final 子例程的概念,但您可以尝试。给出以下内容:

BEGIN {
    package final;
    $INC{'final.pm'}++;
    use Variable::Magic qw(wizard cast);
    sub import {
        my (undef, $symbol) = @_;
        my ($stash, $name)  = $symbol =~ /(.+::)(.+)/;
        unless ($stash) {
            $stash  = caller().'::';
            $name   = $symbol;
            $symbol = $stash.$name;
        }
        no strict 'refs';
        my $glob = \*$symbol;
        my $code = \&$glob;
        my ($seen, @last);

        cast %$stash, wizard store => sub {
            if ($_[2] eq $name and $code != \&$glob) {
                print "final subroutine $symbol was redefined ".
                      "at $last[1] line $last[2].\n" unless $seen++
            }
            @last = caller
        }
    }
}

然后您可以编写:

use warnings;
use strict;
{
    package Foo;
    sub hello {print "HI"}
    use final 'hello';
}

package main;
no warnings;
sub Foo::hello {print "bye"}

Foo::hello();

它将打印类似以下内容:

final subroutine Foo::hello was redefined at filename.pl line 9.
bye

警告是在第一次调用重新定义的子例程之前打印的,而不是在实际重新定义它时打印的(由于 perl 和 Variable::Magic 方式的限制)工作)。但总比没有好。

no warnings; 在那里,因为当重新定义子例程时,perl 通常会抛出警告。因此,也许告诉您的用户使用警告就足够了。正如拉里所说:

Perl 并不热衷于强制隐私。它会
我宁愿你呆在客厅外面,因为你不是
被邀请,不是因为它有猎枪。

Perl doesn't really like the concept of final subroutines, but you can try. Given the following:

BEGIN {
    package final;
    $INC{'final.pm'}++;
    use Variable::Magic qw(wizard cast);
    sub import {
        my (undef, $symbol) = @_;
        my ($stash, $name)  = $symbol =~ /(.+::)(.+)/;
        unless ($stash) {
            $stash  = caller().'::';
            $name   = $symbol;
            $symbol = $stash.$name;
        }
        no strict 'refs';
        my $glob = \*$symbol;
        my $code = \&$glob;
        my ($seen, @last);

        cast %$stash, wizard store => sub {
            if ($_[2] eq $name and $code != \&$glob) {
                print "final subroutine $symbol was redefined ".
                      "at $last[1] line $last[2].\n" unless $seen++
            }
            @last = caller
        }
    }
}

You could then write:

use warnings;
use strict;
{
    package Foo;
    sub hello {print "HI"}
    use final 'hello';
}

package main;
no warnings;
sub Foo::hello {print "bye"}

Foo::hello();

Which will print something like:

final subroutine Foo::hello was redefined at filename.pl line 9.
bye

The warning is printed right before the redefined subroutine is first called, not when it is actually redefined (due to limitations of the way perl and Variable::Magic work). But it is better than nothing.

The no warnings; is in there since perl will normally throw a warning when subroutines are redefined. So maybe telling your users to use warnings is good enough. As Larry has said:

Perl doesn't have an infatuation with enforced privacy. It would
prefer that you stayed out of its living room because you weren't
invited, not because it has a shotgun.

貪欢 2025-01-15 16:22:20

这是我在问题评论中所写的禁用猴子补丁的解决方案。

#./FooFinal.pm
package FooFinal;
use strict;
use warnings;
sub import { warnings->import(FATAL => qw(redefine)); }
sub hello { print "HI" }
1;

#./final_test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use FooFinal;
sub FooFinal::hello {print "bye".$/}

FooFinal->hello();#bye

print 'still living!!!'

结果是 ./final_test.pl 在打印“仍然活着!!!”之前就死掉了。
是的,这使得所有方法“不可修补”,但仍然允许继承/扩展模块。
是的,该模块的用户可以随时更改其酸味或说“无警告”:)
但我们还是大声说“不邀请你!”

也许问题标题应该是“如何在 Perl 中禁用 Monkey Patching?”...
也许通过更多地阅读 perlexwarn 我们甚至可以实现最终的功能......

Here is my solution to disable Monkey Patching as I wrote in the comment to my question.

#./FooFinal.pm
package FooFinal;
use strict;
use warnings;
sub import { warnings->import(FATAL => qw(redefine)); }
sub hello { print "HI" }
1;

#./final_test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use FooFinal;
sub FooFinal::hello {print "bye".$/}

FooFinal->hello();#bye

print 'still living!!!'

The result is that ./final_test.pl dies before printing "still living!!!".
Yes this makes all the methods "un-patchable", but still allows the module to be inherited/extended.
And yes user of the module can always change its sours or say "no warnings":)
But still we said loudly "You are not invited!"

Maybe the question title needed to be "How to disable Monkey Patching in Perl?"...
Maybe with more reading of perlexwarn we could implement even a final feature...

你的笑 2025-01-15 16:22:20

致命的重新定义警告会给你你想要的。
但它无法实现语言实现者希望能够优化最终方法调用的优化,例如索引或内联。

由于 p5p 拒绝考虑这种优化可能性(perl6 和 p2 会这样做),
这是不切实际的。但您只需要只读包哈希 %Foo:: 及其键即可。
方法重定义将引发编译时错误,编译器可以优化方法调用。

我在 github 上有一个分支,它在语言级别上实现了 const,用于词法和包。
https://github.com/rurban/perl/blob /typed/const/pod/perltypes.pod#const

这是计划,但没有支持:
http://blogs.perl.org/users /rurban/2012/09/my-perl5-todo-list.html
所以我分叉了perl5。 http://perl11.org/p2/

Fataling the redefine warnings will give you what you want.
But it will not enable the optimizations a language implementor will want to be able to optimize finalized method calls, such as indexing or inlining.

Since p5p refuses to think about such optimization possibilities (perl6 and p2 does it),
this is not practical. But you simply need to readonly the package hash %Foo:: and its keys.
Method redefinitions will throw compile-time errors then, and the compiler can optimize the method calls.

I have a branch on github which implements const on the language level, for lexicals and packages.
https://github.com/rurban/perl/blob/typed/const/pod/perltypes.pod#const

This was the plan, but there was no support for it:
http://blogs.perl.org/users/rurban/2012/09/my-perl5-todo-list.html
so I forked perl5. http://perl11.org/p2/

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