Name lookup in Perl hooks

Variables in Perl are of several types. Common types include:

  • my variables, which are local to the subroutine in which they are declared.
  • Local variables, which are local to the file in which they are declared (but global to all subroutines within that file).
  • Global variables, which are not declared explicitly.

You must specify global variables with unique names. You can also use local variables, using the my convention. For example:


my ($uvComponent);
Note: When setting a variable to be local in a Perl hook, use the my declaration.

It is possible for existing Perl hooks to have name look-up problems. A HCL Compass action, Submit for example, uses a single Perl interpreter to execute all the action hooks, like Action Initialization, and all the field hooks, like Field Value Changed, that are written in Perl. If the hook code does not declare local variables with the my keyword before it uses them, the variable can be shared between hooks unintentionally.

In releases before version 2003.06.00, when one Perl hook called another, the second hook is compiled in a different Perl namespace. In release version 2003.06.00 and later, all hooks are compiled in the same Perl namespace. This can affect how different hooks can interfere with each other if global variables are used. For example:


sub defect_Initialization

{

 $variable = "1";

 $entity->SetFieldValue("A", $variable);

}

sub a_ValueChanged

{

 $entity->SetFieldValue("B", $variable);

 $entity->SetFieldValue("C", $variable);

}

sub b_ValueChanged

{

 $variable = "3";

} 

The variable called $variable in these hook subroutines is a package scope variable. This means that subroutines in the same package share the same variable.

In releases before version 2003.06.00, nested hooks were in a different Perl package from the initial hook, and therefore did not share their global variables with the initial hook. This meant that the previous example was interpreted as if it had the following namespace qualifiers:


package main;

sub defect_Initialization

{

 $main::variable = "1";

 $entity->SetFieldValue("A", $main::variable);

}

package CQEntity;

sub a_ValueChanged

{

 # $CQEntity::variable is not set, so defaults to an empty string.

 $entity->SetFieldValue("B", $CQEntity::variable);

 $entity->SetFieldValue("C", $CQEntity::variable);

}

sub b_ValueChanged

{

 $CQEntity::variable = "3";

} 

A Field Value Changed hook is called immediately upon changing the associated field (that is, before returning from SetFieldValue), so the preceding code executes in this order:


 $main::variable = "1";    # From defect_Initialization;

       # $main::variable is set to "1"

 $entity->SetFieldValue("A", $main::variable); # From defect_Initialization

       # Sets A to "1"

       # HCL Compass calls a_ValueChanged before returning

 $entity->SetFieldValue("B", $CQEntity::variable); # From a_ValueChanged

       # $CQEntity::variable is uninitialized

       # Sets B to ""

       # HCL Compass calls b_ValueChanged before returning

 $CQEntity::variable = "3";   # From b_ValueChanged

       # $CQEntity::variable changes from "" to "3"

 $entity->SetFieldValue("C", $CQEntity::variable); # From a_ValueChanged

       # Sets C to "3" 

As a result, fields A, B and C are set to "1", "" and "3", respectively.

Beginning in release version 2003.06.00, Perl hooks are compiled into the same namespace. The preceding example is now interpreted as if it has the following namespace qualifiers:


package main;

sub defect_Initialization

{

 $main::variable = "1";

 $entity->SetFieldValue("A", $main::variable);

}

sub a_ValueChanged

{

 $entity->SetFieldValue("B", $main::variable);

 $entity->SetFieldValue("C", $main::variable);

}

sub b_ValueChanged

{

 $main::variable = "3";

} 

This code executes in this order:


 $main::variable = "1";    # From defect_Initialization;

       # $main::variable is set to "1"

 $entity->SetFieldValue("A", $main::variable); # From defect_Initialization

       # Sets A to "1"

       # HCL Compass calls a_ValueChanged before returning

 $entity->SetFieldValue("B", $main::variable); # From a_ValueChanged

       # Sets B to "1"

       # HCL Compass calls b_ValueChanged before returning

 $main::variable = "3";    # From b_ValueChanged

       # $main::variable changes from "1" to "3"

 $entity->SetFieldValue("C", $main::variable); # From a_ValueChanged

       # Sets C to "3" 

As a result, fields A, B and C are set to "1", "1" and "3", respectively.

To avoid unintentional sharing of variable values, you must declare those variables intended to be local to a hook function in this form:


sub d_ValueChanged

{

 my $temp = $entity->GetFieldValue("d")->GetValue();

 $session->OutputDebugString("d now set to $temp\n");

} 

where $temp is declared to be local to the d_ValueChanged function, and this assignment to $temp does not change the value of another variable of the same name. Using the my syntax makes the variable visible only within a specified block of code.

Note: $entity and $session are global variables defined by the HCL Compass core.