You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

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:

{
  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:

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.

# ... 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:

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

OBJ30-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

      02. Expressions      FIO30-PL. Do not use the two-argument form of open()

  • No labels