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

I have some perl modules that I use for my CGI scripts, which:

use CGI::Carp qw(fatalsToBrowser);

to redirect errors to the browser. I want to use the same modules in my tty-oriented scripts too. But when I do, errors are reported with HTML tags embedded...

I understand why, of course, but there's another curiosity about this: because I use carpout() in my set_message() function to redirect error output to another file, perl complains that the file is non-existent when used from the tty scripts. (It works just fine from the cgi scripts.)

I would prefer that Carp somehow be more intelligent about what to output if STDOUT is a tty, but barring that, how can I conditionally choose to use CGI::Carp in the perl module that contains it?

Replies are listed 'Best First'.
Re: How to conditionally use CGI::Carp?
by jhourcle (Prior) on May 06, 2005 at 02:39 UTC

    From the documentation for use:

    Imports some semantics into the current package from the named module, generally by aliasing certain subroutine or variable names into your package. It is exactly equivalent to

    BEGIN { require Module; import Module LIST; }

    except that Module must be a bareword.

    So, given that, we can replace the 'use' with some form of logic structure

    BEGIN { if ( ... ) { # require 'CGI::Carp'; require CGI::Carp; CGI::Carp->import('fatalsToBrowser'); } }

    alternately, you can place the 'use' within an eval. (but you'll want it inside a BEGIN block, so it runs really early)

    As for what to test for, to compare if you're under CGI vs. on a console... I typically test for the existance of $ENV{'SERVER'} or any of the other environmental variables that are set by the webserver when it's running under CGI.

    Update: blah...no quotes in require when not giving direct paths to files.

      You said to use:

      BEGIN { if ( ... ) { require 'CGI::Carp'; CGI::Carp->import('fatalsToBrowser'); } }

      But my perl doesn't like the single quotes. And, since I want to import more than one function, I used "import" directly, resulting in:

      require CGI::Carp; import CGI::Carp qw(fatalsToBrowser set_message carpout); set_message(\&handle_error);

      Next, you said:

      I typically test for the existance of $ENV{'SERVER'} or any of the other environmental variables

      Problem is, $ENV is completely empty when I run either from the tty or as a cgi. This surprises me, too, frankly. I test for anything, and always get zilch.

        OK, now waitaminute.... I've got an update, and hence a modification to my previous reply to this. That is, I said that my %ENV had nothing in it. I was wrong--it DOES. The reason I was mistaken is because I was trying to be tricky. Follow me on this because it gets circuitous--thus, interesting. I used the following code:

        my $ip = $ENV{'REMOTE_ADDR'}; BEGIN { if ( $ip ) { [...] } }

        That didn't work, which is no surprise, because the BEGIN block runs before the code runs, so $ip has no value when the BEGIN block runs. But, if that's the case, why does a print statement print? I take up this question in What is the scope of BEGIN? Or... when does it "begin?"..

Re: How to conditionally use CGI::Carp?
by Errto (Vicar) on May 06, 2005 at 03:26 UTC
    I don't think CGI::Carp is lexically scoped. That is to say, if you put
    use CGI::Carp qw/fatalsToBrowser/;
    in your CGI scripts and not in your modules, it should work fine both ways when some piece of code in the module's methods happens to die.
      in your CGI scripts and not in your modules, it should work fine both ways

      well, actually, no... at least, not for me. I assumed this as well, but lots of experimentation has shown otherwise. For example, in February, I posted Inconsistent results from CGI::Carp handler, to which no one at all has replied. In my own research (which I haven't yet completed, but am getting close), I find that some/many perl errors inside MODULES are not caught by the CGI::Carp handler when the "use" statement is in the main (non-module) perl code (even within BEGIN blocks). However, if I duplicate the code within the modules, then the perl errors are caught and sent to the browser as expected (or to wherever you redirect).

      This is the reason for this current thread. If I didn't have to put the "use carp" stuff in the modules, none of this would be of concern.

      ...and the problems/confusions just seem to be adding up. For example, where are my $ENV environment variables?

        I think the bottom line here is that CGI::Carp is kind of brittle and unreliable, and only works well for simple things. Using real exception handling, where you wrap your calls in eval {} and then catch the exceptions in your CGI script or command-line script and handle them appropriately, is the next level. There is more discussion of this here.
Re: How to conditionally use CGI::Carp?
by tilly (Archbishop) on May 07, 2005 at 05:15 UTC
    You have CGI scripts running on production which use CGI::Carp? Bad idea. If someone finds a security flaw in your script, this will give them debugging information as they explore the breakage into an attack on your script.

    I strongly recommend finding a way to conditionally decide not to use CGI::Carp. Such as loading it at runtime, or doing as perrin suggested.