http://qs1969.pair.com?node_id=72928

SilverB1rd has asked for the wisdom of the Perl Monks concerning the following question:

I have been trying to get this script to work for hours. the problem is that it won't branch to anything but the FrontPage sub I have checked the param values and they are ok but but it still wont branch. I'm running it on a solarus::apache::mod_perl box.
#!/usr/bin/perl -w use CGI::Carp "fatalsToBrowser"; use CGI; use strict; $CGI::POST_MAX=1024 * 500; # max 500k post $CGI::DISABLE_UPLOADS = 1; # No uploads my ($q) = new CGI; my ($ThisScript, $DataFile, $i, $MyFile); my (%Subs); $ThisScript = 'Conf.cgi'; $DataFile = './data/list.txt'; %Subs=( 'FrontPage' => \&FrontPage(), 'ViewState' => \&ViewState()); if ( exists $Subs{$q->param('do')}) { $Subs{$q->param('do')}->(); } else { $Subs{$q->param('do')}->(); } exit; sub FrontPage { ... exit; } sub ViewState { ... exit; }


------
The Price of Freedom is Eternal Vigilance

Replies are listed 'Best First'.
Re: Won't jump to Sub!
by merlyn (Sage) on Apr 17, 2001 at 00:30 UTC
      Bingo. This is absolutely going to be a problem.

      For those who saw that comment go over their heads, in mod_perl it is absolutely essential that global variables be declared with vars and not with my. The latter leads to serious black magic. Try RE (3): BrainPain-Help for one attempt at an explanation.

      Here is another.

      The key to the problem is that the subroutine has a single global name. There can only be one sub with that name in that package. Ever. However there may be many different lexical variables running around with that name. (Look up closures.) So when the one global has to figure out which lexical it names...well what is the right answer?

      This is an issue in this case because Apache is just wrapping your entire script with a sub, and calling that. So that sub is called many times, leading to many copies of the lexical variable, and the brand new one being set each time isn't being seen where you want it to be.

      Merlyn's cryptic comment inspired me to find out what the "variable won't stay shared" message means.

      A subroutine can be named or anonymous, just as an array can be named or anonymous.

      Furthermore, a subroutine can include a lexical variable declared outside the subroutine if they are within the same lexical scope. (When a subroutine includes such a lexical variable it is called a closure.)

      But here is the catch. Normally perl only needs to compile a subroutine once. But when a subroutine is a closure, it potentially might need to recompile the subroutine more than once in case the externally-defined lexical variable changes its value.

      It is a Law of Perl that if a subroutine is both named and a closure then Perl will only compile it once. Powerful magicians like Merlyn are able to use this feature to practice Deep Mojo. For the rest of us, this feature is likely to produce unexpected results.

      So what Merlyn was saying is that he suspects that the named subroutines in the code in question contain lexical variables defined outside of the named subroutines. And he is sceptical that these completions were performed with the necessary knowledge of Perl magic needed to make them work.

Re: Won't jump to Sub!
by jwest (Friar) on Apr 17, 2001 at 00:42 UTC
    When assigning your %Subs, you're executing the FrontPage and ViewState subs and recording their return values, instead of getting the references that you seem to be after. Instead, use:
    %Subs = ( 'FrontPage' => \&FrontPage, 'ViewState' => \&ViewState, );
    --jwest
    -><- -><- -><- -><- -><-
    All things are Pefect
       To every last Flaw
       And bound in accord
           With Eris's Law
                   HBT 1:7
    
Re: Won't jump to Sub!
by Masem (Monsignor) on Apr 17, 2001 at 00:33 UTC
    Your code...
    if ( exists $Subs{$q->param('do')}) { $Subs{$q->param('do')}->(); } else { $Subs{$q->param('do')}->(); }
    ...screams that something's wrong in the logic here. It makes sense for the IF part, but if the 'do' parameter points to a non-existence entry in your Subs hash, the THEN part is going to fail out of the script terribly. You probably want THEN to be $Subs{'FrontPage'}->();

    This doesn't solve your problem, but this is a glaring error.


    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: Won't jump to Sub!
by Starky (Chaplain) on Apr 17, 2001 at 08:03 UTC
    If I may, I would like to summarize all of the insightful comments above:

    When coding in an Apache/mod_perl environment, always always always always always always always always always always always

      use strict;
    
    I was up until 7:00am one morning debugging someone else's code during a nightmare launch of a web application. The behavior of the application was utterly bizarre and confusing, and I acquired a goodly number of grey hairs before I noticed that a junior coder hadn't used strict in one of his modules. Let's just say I was a bit peeved.

    It's more of a mantra than a pragma for me: I use strict for everything I write that's over 10 lines of code.