Practical mod_perl, from O'Reilly. | 19

Practical mod_perl: Chapter 6: Coding with mod_perl in Mind

Global Variable Persistence

Under mod_perl a child process doesn't exit after serving a single request. Thus, global variables persist inside the same process from request to request. This means that you should be careful not to rely on the value of a global variable if it isn't initialized at the beginning of each request. For example:

# the very beginning of the script
use strict;
use vars qw($counter);
$counter++;

relies on the fact that Perl interprets an undefined value of $counter as a zero value, because of the increment operator, and therefore sets the value to 1. However, when the same code is executed a second time in the same process, the value of $counter is not undefined any more; instead, it holds the value it had at the end of the previous execution in the same process. Therefore, a cleaner way to code this snippet would be:

use strict;
use vars qw($counter);
$counter = 0;
$counter++;

In practice, you should avoid using global variables unless there really is no alternative. Most of the problems with global variables arise from the fact that they keep their values across functions, and it's easy to lose track of which function modifies the variable and where. This problem is solved by localizing these variables with local( ). But if you are already doing this, using lexical scoping (with my( )) is even better because its scope is clearly defined, whereas localized variables are seen and can be modified from anywhere in the code. Refer to the perlsub manpage for more details. Our example will now be written as:

use strict;
my $counter = 0;
$counter++;

Note that it is a good practice to both declare and initialize variables, since doing so will clearly convey your intention to the code's maintainer.

You should be especially careful with Perl special variables, which cannot be lexically scoped. With special variables, local( ) must be used. For example, if you want to read in a whole file at once, you need to undef( ) the input record separator. The following code reads the contents of an entire file in one go:

open IN, $file or die $!;
$/ = undef;
$content = <IN>; # slurp the whole file in
close IN;

Since you have modified the special Perl variable $/ globally, it'll affect any other code running under the same process. If somewhere in the code (or any other code running on the same server) there is a snippet reading a file's content line by line, relying on the default value of $/ (\n), this code will work incorrectly. Localizing the modification of this special variable solves this potential problem:

{
  local $/; # $/ is undef now
  $content = <IN>; # slurp the whole file in
}

Note that the localization is enclosed in a block. When control passes out of the block, the previous value of $/ will be restored automatically.

STDIN, STDOUT, and STDERR Streams

Under mod_perl, both STDIN and STDOUT are tied to the socket from which the request originated. If, for example, you use a third-party module that prints some output to STDOUT when it shouldn't (for example, control messages) and you want to avoid this, you must temporarily redirect STDOUT to /dev/null. You will then have to restore STDOUT to the original handle when you want to send a response to the client. The following code demonstrates a possible implementation of this workaround:

{
    my $nullfh = Apache::gensym(  );
    open $nullfh, '>/dev/null' or die "Can't open /dev/null: $!";
    local *STDOUT = $nullfh;
    call_something_thats_way_too_verbose(  );
    close $nullfh;
}

The code defines a block in which the STDOUT stream is localized to print to /dev/null. When control passes out of this block, STDOUT gets restored to the previous value.

STDERR is tied to a file defined by the ErrorLog directive. When native syslog support is enabled, the STDERR stream will be redirected to /dev/null.

Redirecting STDOUT into a Scalar Variable

Sometimes you encounter a black-box function that prints its output to the default file handle (usually STDOUT) when you would rather put the output into a scalar. This is very relevant under mod_perl, where STDOUT is tied to the Apache request object. In this situation, the IO::String package is especially useful. You can re-tie( ) STDOUT (or any other file handle) to a string by doing a simple select( ) on the IO::String object. Call select( ) again at the end on the original file handle to re-tie( ) STDOUT back to its original stream:

my $str;
my $str_fh = IO::String->new($str);
 
my $old_fh = select($str_fh);
black_box_print(  );
select($old_fh) if defined $old_fh;

In this example, a new IO::String object is created. The object is then selected, the black_box_print( ) function is called, and its output goes into the string object. Finally, we restore the original file handle, by re-select( )ing the originally selected file handle. The $str variable contains all the output produced by the black_box_print( ) function.

print( )

Under mod_perl, CORE::print( ) (using either STDOUT as a filehandle argument or no filehandle at all) will redirect output to Apache::print( ), since the STDOUT file handle is tied to Apache. That is, these two are functionally equivalent:

print "Hello";
$r->print("Hello");

Apache::print( ) will return immediately without printing anything if $r->connection->aborted returns true. This happens if the connection has been aborted by the client (e.g., by pressing the Stop button).

There is also an optimization built into Apache::print( ): if any of the arguments to this function are scalar references to strings, they are automatically dereferenced. This avoids needless copying of large strings when passing them to subroutines. For example, the following code will print the actual value of $long_string:

my $long_string = "A" x 10000000;
$r->print(\$long_string);

To print the reference value itself, use a double reference:

$r->print(\\$long_string);

When Apache::print( ) sees that the passed value is a reference, it dereferences it once and prints the real reference value:

SCALAR(0x8576e0c)


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

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