| home / programming / perl / mod_perl / chap6 / 2 | [previous] [next] |
|
|
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 scriptuse 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 inclose 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.
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.
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.
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)
| home / programming / perl / mod_perl / chap6 / 2 | [previous] [next] |
Created: March 27, 2003
Revised: July 23, 2003
URL: http://webreference.com/programming/perl/mod_perl/chap6/2