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

时间:2012-02-23 14:51:19

标签: perl final moose monkeypatching

我想知道是否有可能确保我所制作的类中的方法不会被猴子修补(Monkey patch)。麋能实现这个吗?

请考虑以下事项:

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

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

Foo::hello()#bye

4 个答案:

答案 0 :(得分:4)

经过快速的网络研究后,我在Perlmonks上找到this线程,声明:

  

至于声明方法final,我不知道如果不做一些非常想要拦截符号表的所有添加的东西你会怎么做。 (甚至可以这样做吗?)。

我还认为这是不可能的。

使用Moose,您可以应用Method Modifiers,允许您定义在调用函数之前必须运行的函数。 我没试过,但也许你可以定义一个函数

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

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

但是我要补充一点,因为perl是一种解释性语言,任何使用你的软件包的人都可以查看和编辑源代码,从而使任何预防措施都可以避免。

答案 1 :(得分:3)

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通常会在重新定义子例程时发出警告。所以也许告诉你的用户use warnings已经足够了。正如拉里所说:

  

Perl对强制隐私没有迷恋。它会   因为你没有,所以你宁愿呆在客厅外面   邀请,不是因为它有霰弹枪。

答案 2 :(得分:1)

这是我在禁用Monkey Patching 的解决方案,正如我在评论中写到的那样。

#./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中的猴子补丁?”...... 也许更多阅读perlexwarn我们甚至可以实现最终功能......

答案 3 :(得分:1)

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

由于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/