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
You use the final keyword in a method declaration to indicate that the method cannot be overridden by subclasses. The Object class does this—a number of its methods are final .
We can declare a method as final, once you declare a method final it cannot be overridden. So, you cannot modify a final method from a sub class. The main intention of making a method final would be that the content of the method should not be changed by any outsider.
All methods in a final class are implicitly final.
If we declare a method as final, then it cannot be overridden by any subclasses. And, if we declare a class as final, we restrict the other classes to inherit or extend it. In other words, the final classes can not be inherited by other classes.
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.
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With