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

I am maintaining a large CGI quoting script. Happily, all of the relation DB stuff seems to work just fine. However, there is one problem in the code that I have taken some time trying to debug with no success.

The problem is this: there are some global variables in the main program that are used for script user customization, such as the name of the company, how many days the quote is good for, and such like. These are used in presenting the final version of the quote, both in HTML for user review before submitting the quote as well as in HTML or text format for the quote that is actually emailed to the customer. The quote generation, display, and email are all handled by subroutines in a separate autoloaded module (autoloaded modules were used for various sections of the program to reduce resource utilization, since response times are quite acceptable as-is, but memory slamming would create a problem).

Most of the variables appear exactly as one would expect them to both in the web page preview and in the final quote emailed to the customer. However, one of the variables seems to vanish during the emailing phase for the HTML version only.

I thought the problem might have to do with a local variable substitution in the email subroutine (the $crlf variable, used for newlining, is explicitly changed to \x0d\x0a for SMTP compatibility independent of platform \n). However, even when this was eliminated, the problem remains.

To summarize: why, during one subroutine pass, does the variable exist, and during another it vanishes? Relevant code samples follow:

In main::

use autouse WQ_Finish => qw(EmailQuote QuotePage genHTMLquote genTextQ +uote zoneBreak); ... $QteExpires = '30 days'; $QtePostscript = 'Terms: Net 30 days. FOB Anytown, USA. Freight pre +paid & add. Subject to Company terms and conditions. ' . $crlf . 'Company makes every effort to meet acceptable del +ivery dates; however, the lead times quoted are only estimated ' . 'dates of delivery. Company disclaims liability fo +r delays in delivery. Contact the factory after placing your ' . 'order for information regarding current availibil +ity and shipping estimates. All lead times are ARO.'; ... QuotePage(); # Display quote for user preview ... EmailQuote(); # Send quote to customer


In module WQ_Finish, both QuotePage and EmailQuote call genHTMLQuote, which generates the quote in HTML format. However, Email quote does include:

local $crlf = "\x0d\x0a";
Note that $crlf is defined in the remainder of the script as "\n".

Located in WQ_Finish, genHTMLQuote includes the relevant lines:

$retval .= '<b><i>This quote is good for ' . $main::QteExpires . +' from the date above.</i></b><br><br>' . $crlf; if ($main::QtePostscript ne '') { $retval .= '<font size=-1>' . m +ain::esc2HTML($main::QtePostscript) . '</font>' . $crlf; }


Of course, $retval is the accumulator for the value returned by genHTMLQuote to the caller for output via the appropriate stream. This is also the section with the problem. When called from QuotePage, $main::QteExpires and $main::QtePostscript are visible and act correctly. When called from EmailQuote, however, $QtePostscript vanishes. It should also be noted that in a flat implementation of this script (no modules), $QtePostscript is visible during both calls. Also of note is that the call from QuotePage and from EmailQuote occurs during separate runs of the script in both the flat and the autoload modularized versions.

One other important code sample, in main::

sub esc2HTML { # Convert plain text to HTML (&s, < +s, >s, "s, double spaces, etc.) my $retval = shift; $retval =~ s/\&/\&amp\;/g; $retval =~ s/\</\&lt\;/g; $retval =~ s/\>/\&gt\;/g; $retval =~ s/\"/\&quot\;/g; $retval =~ s/ /\&nbsp\; /g; $retval =~ s/\n\n/\n/g; $retval =~ s/\n/\<br\>\<br\>\n/g; return $retval; }


Thanks in advance for your help! HyperZonk

Replies are listed 'Best First'.
(Ovid) Re: Obscure variable suicide problem in autoloaded module
by Ovid (Cardinal) on Jul 13, 2001 at 00:06 UTC

    Looks like you've stumbled on one of the many, many problems that occurs when using global variables. Since these variables do not need to be declared, it's relatively easy to misuse them. I don't think it's possible to tell, from the information you've listed, exactly what the problem is, but here are some guesses:

    • autouse eliminates a lot of compile time checking and can break things if there is code that needs to be initialized when the program starts. Check to see if the modules are initializing any variables. If the modules in question initialize things only when autoused (in other words, when you first use them, not when the program starts), perhaps they're overwriting the globals?
    • The way you initialize globals has me concerned:
      $QteExpires = '30 days';
      Since you have not explicitly stated a package name, this implies that you have not used strict. This means that your error could be virtually anywhere. All bets are off, then.
    • In EmailQuote, have you fully qualified $QtePostscript (i.e., referred to it as $main::QtePostscript)? If not, you may be referring to the wrong package instance of it, depending upon how it was used.
    • Why not eliminate globals and simply pass the data? Better yet, create an object that carries the data with it and pass the object?

    As a general rule, if I find that I'm worrying a lot about namespace issues, then I have some serious code problems and need to consider redesigning things, though I realize that you may not have this luxury. Good luck! Sounds like you've got a serious problem on your hands.

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Some notes -- I seem not to have been very clear in my original message (looking back at it, indeed, very muddy).

      First of all, EmailQuote and QuotePage neither refer to $QtePostscript. That variable is only referenced in that module by genHTMLQuote.

      No, it is not a strictish program. And, yes, globals are overused - this was a down and dirty script that needed to be completed in a very short time. (Excuses, excuses, may the Saints forgive me!) However, this program does work "flat" and does not work as autoloaded modules (the script was tested without autoload, BTW, to ensure no deferred run-time errors). Also, literally the only places in the code in which $QtePostscript appears is at its initialization in main:: and at genHTMLQuote in the WQ_Finish module (I searched the code, both flat and modulized).

      Thank you for your tips, of course. Fortunately, I will have an opportunity to take a lot of time to fix this messy script sometime in the near future, and your tips will be invaluable.
Re: Obscure variable suicide problem in autoloaded module
by Zaxo (Archbishop) on Jul 13, 2001 at 00:17 UTC

    In $main::QtePostscript, $crlf is inserted at compile time. Your return is already assembled by the time the local line-end is seen.

    One way to do this is to accumulate your return lines as an array @retary and:

    join $crlf, @retary;

    Another approach would be to set $\ and:

    print for @retary;

    A third is to s/\n/\r\n/g inside EmailQuote(), and forget about the local $crlf

    After Compline,
    Zaxo

    Update, corrected an error regarding use of $retval.

      Indeed, removing the local and doing s/\n/\r\n/g is the first thing I tried.

      Hmmm ... I hadn't noticed that there was a $crlf in the initialization. My bad.