•Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by merlyn (Sage) on Sep 23, 2002 at 14:09 UTC
|
Yes. There are quite a few variations of "privateness" documented in theDamian's excellent Object Oriented Perl. You can check the file and line number of caller, for example.
However, the moment you do that, I would not want to maintain your code. Every attempt I've seen at privacy has made the code more unmaintainable.
Just name your "private" methods with a leading underscore, and put "do not call outside of this class"
in a comment. People will get the idea, and they deserve what they get when they violate it. Why is that so hard to understand?
-- Randal L. Schwartz, Perl hacker | [reply] [Watch: Dir/Any] |
|
I hear ya dude :) However, you never know when management or a client might ask you to do the ridiculous ;)
| [reply] [Watch: Dir/Any] |
|
However, you never know when management or a client might ask you to do the ridiculous ;)
And it's your job to explain why not to do that.
Don't cave. If you do, you end up increasing the cost
of the software overall. It's bad ethics.
If your boss/client is so pigheaded that they won't see your well-reasoned point, quit. Get another job.
-- Randal L. Schwartz, Perl hacker
| [reply] [Watch: Dir/Any] |
|
|
|
|
Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by Zaxo (Archbishop) on Sep 23, 2002 at 14:11 UTC
|
Sure, closures, the same technique that's used to make other lexicals really private, can be used for anonymous subroutines:
{
my $private_method = { foo => sub { stuff },
bar => sub { mostuff },
};
sub public_method {
my @args = @_;
$private_method->{foo}(@args);
$private_method->{bar}(@args);
}
}
The references within public_method keep the lexical $private_method alive after the name has gone out of scope.
After Compline, Zaxo | [reply] [Watch: Dir/Any] [d/l] |
Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by broquaint (Abbot) on Sep 23, 2002 at 14:13 UTC
|
The only way you can have truly private methods is through lexically scoped subrefs. This is because you can always fake being in another package with package. This doesn't bode well for inheritance, but as clintp suggests you can get around it. There's also miyagawa's wonderful Attribute::Protected, but that can be bypassed if you really want to.
HTH
_________ broquaint | [reply] [Watch: Dir/Any] |
|
And that only works until your attacker starts using magic (generally via B (something like (this is off the top of my head) B::svref_2object($obj)->PADLIST->ARRAY) to get access to your lexicals anyway. From what I can tell - if you can get a code ref to it then you can get it's lexicals. At that point there isn't any protection unless...
Ok, maybe... if you fork your process and orphan your objects via circular references and maybe if you are doing threading (which I gather is mostly a perl 5.8ism) you can still keep running code in those objects in a different process. Yeah, that's the ticket. (keep in mind this is just speculation)
Not that I think that would actually work but you have to go to some interesting lengths to keep your lexicals really, really private.
Update On further consideration just forking the process and allowing the private stuff to fall out of scope in the original process is enough to hide your stuff. Now you just have to do some sort of IPC to communicate back to the mother ship. ;-)
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |
Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by clintp (Curate) on Sep 23, 2002 at 14:05 UTC
|
One cheesy idea I've exploited: keep your methods as subrefs in a lexical hash in the module's file. Have AUTOLOAD take care of actually dispatching to the methods when they're called. The subs are unreachable outside of the module unless you pass a reference outside somehow.
It can make your code hard to read (and subclass, and...), but if all you want is privacy... | [reply] [Watch: Dir/Any] |
Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by dws (Chancellor) on Sep 23, 2002 at 17:23 UTC
|
Is there an eligant way to make methods in perl really private?
You're got some good answers above. Here's a different approach.
You can get the delayed effect of private methods by adopting a simple convention: Start all private methods with an underscore, and teach people not to invoke these methods from outside of the class hierarchy1. To enforce this, it's a simple matter to spider your code base looking for references to private methods that are not preceded by "$self->".
Having a script that traverses the code base looking for certain patterns can be very useful. In team settings, I've set up scripts to look for special code patterns (e.g., "TBD" or "FIXME" comments, correct copyright strings, etc.) The scripts ran every night, and produced a report (an HTML page) that we would check every morning. It also gave us an easy way to chart our code growth over time.
The question you'll need to ask yourself is whether you can live with the delay. I've found that the convention was good enough.
1 Purists will note that this really enforced "protected" methods.
| [reply] [Watch: Dir/Any] |
|
Great idea! Care to share your script?
-sam
| [reply] [Watch: Dir/Any] |
|
Care to share your script?
My ex-employers own the last ones I wrote, but they're pretty simple. Use File::Find to traverse a hierarchy, then ignore file types that aren't interesting (like .doc or .exe), and finally processing files that are. Stripped of extraneous detail, they look something like:
use File::Find;
my $ignore = '(?:\.exe|\.doc)$';
find(\&consider, $root);
emit_report();
sub consider {
my $path = $File::Find::path;
return if $path =~ m/$ignore/o;
process($path);
}
Processing consists of slurping the file into memory and
running a set of regexs against it, recording which files match which regex. Then sort the hashes and spit out HTML. It's just bookkeeping at this poing. Being a potential memory pig isn't a serious issue for a script that runs once a day at 2am.
| [reply] [Watch: Dir/Any] [d/l] |
|
Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by casiano (Pilgrim) on Jan 18, 2008 at 13:41 UTC
|
There is more than an answer to this question.
-
One way is to use a lexical reference to a method:
pl@nereida:~/LEyapp/examples$ cat localwithlocal3.pl
#!/usr/local/bin/perl -w
use strict;
{
package Tutu;
my $_tutu = sub {
my $self = shift;
"Inside tutu. My class is ".ref($self)."\n"
};
sub plim {
my $self = shift;
print "plim can access _tutu as a private method:\n"
.$self->$_tutu;
}
sub new {
bless {}, $_[0];
}
}
package main;
my $obj = Tutu->new();
$obj->plim();
Now the $_tutu method is visible only inside the closure enclosing the Tutu package.
-
Most of the time the sort of privacy we need is
to avoid a descendant class accidentally
overwriting a private method. In such case,
calling the method as a subroutine Class::_private_method($self)
instead of using
the $self->_private_method
syntax will suffice.
-
You can have a sort of dynamic visibility of methods beteween classes in the same hierarchy (but only
one level of inheritance)
using the strategy of naming
a lexical CODE reference inside the client
methods
#!/usr/local/bin/perl -w
use strict;
package Tutu;
my $protected = sub {
my $self = shift;
"inside Tutu::tutu!\n"
};
sub plim {
my $self = shift;
local *protected = $protected;
print "Accessing 'protected' as a method: "
.$self->protected();
}
sub new {
bless {}, $_[0];
}
package SubTutu;
our @ISA = qw{Tutu};
# SubTutu can overwrite 'protected'
sub protected {
my $self = shift;
"inside overwritten tutu!\n"
};
package main;
my $obj = SubTutu->new();
$obj->plim();
print(Tutu->can('protected')?
(Tutu::protected()."\n")
: "main does not even know of 'Tutu::protected'\n"
);
$obj->plim();
# Let us provoke an exception
Tutu::protected();
When executed gives the following output:
$ attributeprotectedwithlocal.pl
Accessing 'protected' as a method: inside overwritten tutu!
main does not even know of 'Tutu::protected'
Accessing 'protected' as a method: inside overwritten tutu!
Undefined subroutine &Tutu::protected called at ./attributeprotectedwi
+thlocal.pl line 49.
-
A problem with Attribute::Protected is that the universal method "can" still see the methods qualified as private.
But certainly the purpose of Attribute::Protected
is to warn you as soon as possible of the crash rather than
hide the method
- Unfortunately -at the time of this writing -
Sub::Lexical seems to be broken with modern versions of Perl. See the test results at CPAN
- Let me tell you a story:
I wrote Parse::Eyapp a LALR compiler compiler
(s.t. similar to Parse::RecDescent).
From a grammar specification produces an abstract syntax tree. The tree can then be manipulated using an attribute grammar like the one provided by Luke Palmer Language::AttributeGrammar.
Language::AttributeGrammar was written with
Parse::RecDescent in mind and assumes that node children are accesed by name instead than by ordinal number, which is the way used by Parse::Eyapp.
Language::AttributeGrammar access to children
is through a private method called _get_child.
The fact that the private method wasn't really hidden allowed me to
overwrite the method and to restore it after the modification so that if later in the program
an attributed grammar - let us say for a Parse::RecDescent generated tree - needs
the old version of
_get_child
it will work. See the pertinent fragment of code
for a small calculator:
94 my $attgram = new Language::AttributeGrammar <<'EOG';
95
96 # Compute the expression
97 NUM: $/.val = { $/->{attr} }
98 TIMES: $/.val = { $<0>.val * $<1>.val }
99 PLUS: $/.val = { $<0>.val + $<1>.val }
100 MINUS: $/.val = { $<0>.val - $<1>.val }
101 UMINUS: $/.val = { -$<0>.val }
102 ASSIGN: $/.val = { $::s{$<0>->{attr}} = $<1>.val; $<1>.val }
103 EOG
104
105 {
106 # rewrite _get_child, save old version
107 no warnings 'redefine';
108 *Language::AttributeGrammar::Parser::_get_child = sub {
109 my ($self, $child) = @_;
110
111 $self->child($child);
112 };
113
114 my $res = $attgram->apply($t, 'val');
115 }
116 # Restored old version of Language::AttributeGrammar::Parser::_ge
+t_child
Paraphrasing Larry Wall's quote,
This is like going into the living room, changing a bit
the things while none is using them, and
cleaning and restoring them after use so that the next visitors will find them where they expect.
If the living room were totally locked (I.e. if Luke Palmer
decided to use a solution like the lexical closure reference above for _get_child) we couldn't do it.
As someone else said, Perl's motto
"Don't enter my private spaces unless invited to do so"
carries an invisible caveat: "or you really need to".
| [reply] [Watch: Dir/Any] [d/l] [select] |