Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The @ISA variable is a package variable that is used by all classes to indicate the class's parent (or parents). While this variable can be safely read to learn a class's inheritence hierarchy, it must not be modified at runtime [Conway 05].

Noncompliant Code Example (@ISA)

This noncompliant code example defines a base class and an object class with simple methods:

Code Block
bgColor#ffcccc
langperl
{
  package Base;

  sub new {
    my $class = shift;
    my $self = {}; # no parent
    bless $self, $class;
    print "new Base\n";
    return $self;
  };

  sub base_value {return 1;}
}


{
  package Derived;
  our @ISA = qw(Base);  # establishes inheritence

  sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);  # relies on established inheritence
    print "new Derived\n";
    return $self;
  };

  sub derived_value {return 2;}
}


BEGIN {
  my $derived = Derived->new();
  my $b = $derived->base_value();
  my $d = $derived->derived_value();
  print "base_value = $d\n";
  print "derived_value = $d\n";
}

When run, we get a program error:

Code Block
Can't locate object method "new" via package "Derived::SUPER" at ...

This error occurs because the BEGIN block is evaluated at the beginning of runtime, before the @ISA statement can be evaluated. Therefore, when the Derived::new() constructor is invoked, the Derived class has an empty parents list and therefore fails to invoke Base::new().

Compliant Solution (base)

This compliant solution uses the base module rather than directly modifying the @ISA variable.

Code Block
bgColor#ccccff
langperl
# ... package Base is unchanged

{
  package Derived;
  use base qw(Base);

  sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);  # relies on established inheritence
    print "new Derived\n";
    return $self;
  };

  sub derived_value {return 2;}
}

# ... The rest of the code is unchanged

The base module establishes the inheritence hierarchy at parse time, before any runtime code, including the BEGIN block, is evaluated. Therefore, when the Derived::new() constructor is invoked, Perl knows that Derived is an instance of Base, and the program produces the correct output:

Code Block
new Base
new Derived
derived_value = 2
base_value = 1

Risk Assessment

Modifying class inheritence at runtime can introduce subtle bugs, and is usually a sign of poor design.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

OOP00-PL

low

unlikely

low

P3

L1

Automated Detection

Tool

Diagnostic

Perl::Critic

ClassHierarchies::ProhibitExplicitISA

Bibliography

[Conway 05] pg. 360 "Inheritance"
[CPAN] Shank, Elliot, Perl-Critic-1.116 ClassHierarchies::ProhibitExplicitISA
[CPAN] Dolan, Chris. base