Practical mod_perl, from O'Reilly. | 14

Practical mod_perl: Chapter 6: Coding with mod_perl in Mind

Example 6-3: Book/Counter.pm

package Book::Counter;
 
my $counter = 0;
 
sub run {
    $counter = 0;
    for (1..5) {
        increment_counter(  );
    }
}
 
sub increment_counter {
    $counter++;
    print "Counter is equal to $counter !<BR>\n";
}
 
1;
_  _END_  _

Example 6-4: counter-clean.pl

use strict;
use Book::Counter;
 
print "Content-type: text/plain\n\n";
Book::Counter::run(  );

As you can see, the only difference is in the package declaration. As long as the package name is unique, you won't encounter any collisions with other scripts running on the same server.

Another solution to this problem is to change the lexical variables to global variables. There are two ways global variables can be used:

use strict;
use vars qw($counter $result);
# later in the code
$counter = 0;
$result  = 1;

is similar to this code if use strict is not used:

$counter = 0;
$result  = 1;

However, the former style of coding is much cleaner, because it allows you to use global variables by declaring them, while avoiding the problem of misspelled variables being treated as undeclared globals.

The only drawback to using vars is that each global declared with it consumes more memory than the undeclared but fully qualified globals, as we will see in the next item.

  • Using fully qualified variables. Instead of using $counter, we can use $Foo::counter, which will place the global variable $counter into the package Foo. Note that we don't know which package name Apache::Registry will assign to the script, since it depends on the location from which the script will be called. Remember that globals must always be initialized before they can be used.
  • Perl 5.6.x also introduces a third way, with the our( ) declaration. our( ) can be used in different scopes, similar to my( ), but it creates global variables.

    Finally, it's possible to avoid this problem altogether by always passing the variables as arguments to the functions (see Example 6-5).

    Example 6-5: counter2.pl

    #!/usr/bin/perl -w
    use strict;
     
    print "Content-type: text/plain\n\n";
     
    my $counter = 0;
     
    for (1..5) {
        $counter = increment_counter($counter);
    }
     
    sub increment_counter {
        my $counter = shift;
     
        $counter++;
        print "Counter is equal to $counter !\n";
     
        return $counter;
    }
    

    In this case, there is no variable-sharing problem. The drawback is that this approach adds the overhead of passing and returning the variable from the function. But on the other hand, it ensures that your code is doing the right thing and is not dependent on whether the functions are wrapped in other blocks, which is the case with the Apache::Registry handlers family.

    When Stas (one of the authors of this book) had just started using mod_perl and wasn't aware of the nested subroutine problem, he happened to write a pretty complicated registration program that was run under mod_perl. We will reproduce here only the interesting part of that script:

    use CGI;
    $q = CGI->new;
    my $name = $q->param('name');
    print_response(  );
     
    sub print_response {
        print "Content-type: text/plain\n\n";
        print "Thank you, $name!";
    }
    

    Stas and his boss checked the program on the development server and it worked fine, so they decided to put it in production. Everything seemed to be normal, but the boss decided to keep on checking the program by submitting variations of his profile using The Boss as his username. Imagine his surprise when, after a few successful submissions, he saw the response "Thank you, Stas!" instead of "Thank you, The Boss!"

    After investigating the problem, they learned that they had been hit by the nested subroutine problem. Why didn't they notice this when they were trying the software on their development server? We'll explain shortly.

    To conclude this first mystery, remember to keep the warnings mode On on the development server and to watch the error_log file for warnings.

    Created: March 27, 2003
    Revised: July 23, 2003

    URL: http://webreference.com/programming/perl/mod_perl/chap6/1