Perl provides no mechanism to hide variables or functions. Although it provides mechanisms such as my()
that limit the scope of variables, it does nothing to prevent code from accessing any variable or method that is available and dereferenceable from its current scope.
By convention, packages may indicate that a method or variable is not to be used outside the class by prefixing the method or variable name with an underscore (_
) [Conway 2005]. Perl provides no inherent enforcement of this convention; however, it is followed by many modules in CPAN and other developers. This convention must not be violated.
Noncompliant Code Example
This noncompliant code example provides a Car
package that registers cars and keeps track of all cars that it creates.
{ package Car; my %_all_cars; sub new { my ($class, $type) = @_; my $self = {type=>$type}; bless $self, $class; $_all_cars{$self} = 1; return $self; }; sub DESTROY { delete $_all_cars{shift()}; }; sub type { my $self = shift; return $$self{type}; } sub _get_all_cars { return %_all_cars; } } my $mine = Car->new("Transam"); my $type = $mine->type(); print "I drive a $type.\n"; my $yours = Car->new("Corvette"); $type = $yours->type(); print "You drive a $type.\n"; my %cars = Car::_get_all_cars(); my @all = keys( %cars); my $count = $#all + 1; print "There are $count cars on the road.\n";
This program behaves as expected, correctly reporting 2 cars on the road. However, it clearly violates encapsulation, because the _get_all_cars()
method is considered private within the Car
class.
Compliant Solution
This compliant solution adds a public method and invokes it instead of any private method.
{ package Car; my %_all_cars; sub count_cars { my @all = keys( %_all_cars); return 1 + $#all; } # ... other methods of Car } # ... my $count = Car::count_cars(); print "There are $count cars on the road.\n";
Exceptions
OOP31:EX0: A class may access private subroutines in classes it is inherited from.
OOP31:EX1: This rule does not apply to classes or modules that do not obey this convention. For instance, the POSIX package provides an API to the system calls endorsed by the POSIX standard. Many of these calls begin with _
but are not meant to be private.
Risk Assessment
Using deprecated or obsolete classes or methods in program code can lead to erroneous behavior.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
OOP31-PL | Medium | Probable | Medium | P8 | L2 |
Automated Detection
Tool | Diagnostic |
---|---|
Perl::Critic
| Subroutines::ProtectPrivateSubs |
Variables::ProtectPrivateVars |
Bibliography
[Conway 2005] | |
---|---|
[CPAN] | Elliot Shank, Perl-Critic-1.116 Subroutines::ProtectPrivateSubs, Variables::ProtectPrivateVars |
[CPAN] | Ragwitz, Florian, POSIX |
[Open Group 2008] |
4 Comments
Anonymous
The destructor is buggy. First, the method should be named
DESTROY
to be eligible for automatic invocation. Then,$hash{shift}
accesses the element calledshift
, but doesn't execute the built-in function of the same name. The codeperl -Mstrict -Mwarnings -E'my %hash = ("foo"=>1, "shift"=>1); sub del{delete $hash{shift}} del("foo"); say for keys %hash'
prints only
foo
(i.e. the "shift" element was deleted). To execute the function, do$hash{+shift}
or$hash{shift()}
. If a bareword appears in a hash subscript, it is used as a string. To force the interpretation as an expression, it has to be made an invalid bareword.David Svoboda
Fixed, thanks.
Anonymous
The POSIX link should probably be version agnostic link ( ordered from most preferred to least preferred )
If you want a truly authoritative link it would be to http://perl5.git.perl.org/perl.git/blob/HEAD:/ext/POSIX/lib/POSIX.pod
Since (I think) that core is upstream, it should probably be http://perldoc.perl.org/POSIX.html
It should definitely not specify any one person http://perl5.git.perl.org/perl.git/history/HEAD:/ext/POSIX.
If it did, it should specify "Wall, Larry" (I think) http://perl5.git.perl.org/perl.git/commit/463ee0b2acbd047c27e8b5393cdd8398881824c5
(It is difficult to determine who was responsible for any given change back in those days)
David Svoboda
I updated the POSIX link to point to perldoc.perl.org.