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

Morning All,

I'm responsible for a small community website, being in the middle of a redesign to allow us to provide some member-specific content, and moving from an SHTML/PHP-based solution to a Perl/CGI one.

I've implemented a fairly simple application to handle the majority of user requests and information processing. My main niggle with this at the minute (and the point of this post) is the script's event handling (requests for pages, user login/logouts, this sort of thing). This currently takes the form of a set of if ... then ... else statements, but seems to run quite slowly when there's load on the site, and I'm generally not happy with it - the code is ugly, and already a nightmare to make changes to.

I'm considering implementing a system whereby CGI parameters directly map to subs, probably through a hash (as eval &$c->param is obviously insecure), but I'm not 100% convinced that this is the most sensible way to go.

I've also looked at the CGI::Application module, but I can't help but think that this would be overkill for what I'd be doing with it.

I'd be interested to know what suggestions people have for event handling with CGI, and opinions of the "best" way to go with this.

Thanks in advance,
-- Foxcub
A friend is someone who can see straight through you, yet still enjoy the view. (Anon)

Replies are listed 'Best First'.
Re: Event Handling in CGI
by Coruscate (Sexton) on Mar 18, 2003 at 09:00 UTC

    ... probably through a hash ...

    Yep, you've got it. Here's one way to go about it. The following snippet assumes that the action to act upon is passed via the 'q' parameter (ie: index.pl?q=members):

    #!/usr/bin/perl -w use strict; use CGI qw/:standard/; sub main { print header(), start_html('Welcome'), p( strong('Welcome to my website!') ), end_html(); } sub login { print header(), start_html('Member Login'), p( strong('Member Login') ), p('Login below:'), end_html(); } sub members { print header(), start_html('I am a member-only page!'), p( strong('Members Page') ), end_html(); } sub _invalid { print header(), start_html('Invalid Function'), p( strong('Invalid Script Function') ), p('That fucntion is not valid!'), end_html(); } my $actions = { main => \&main, login => \&login, members => \&members, _invalid => \&_invalid }; my $q = param('q') || 'main'; $actions->{ exists $actions->{$q} ? $q : '_invalid' }->();


    If the above content is missing any vital points or you feel that any of the information is misleading, incorrect or irrelevant, please feel free to downvote the post. At the same time, please reply to this node or /msg me to inform me as to what is wrong with the post, so that I may update the node to the best of my ability.

•Re: Event Handling in CGI
by merlyn (Sage) on Mar 18, 2003 at 12:56 UTC
      I haven't played with OpenInteract, but I have played with SPOPS, which is the database layer OpenInteract is based on, and personally I found it rather undocumented, and not entirely bugfree. (For example,

      $g = SPOPS::DBI->fetch(1); $f = SPOPS::DBI->fetch(1); # same as $g $f->{field} = "oldvalue"; $g->{field} = "newvalue"; print $f->{field}; # prints "oldvalue"! # now you have to guess which will be saved first :-(

      )

      andramoiennepemousapolutropon

(jeffa) Re: Event Handling in CGI
by jeffa (Bishop) on Mar 18, 2003 at 15:34 UTC
    Hi Foxcub. Personally, i don't think CGI::Application is overkill for this at all (well ... maybe just a tad). If Coruscate will allow me to lift some code:
    #!/usr/bin/perl -T package WebApp; use strict; use warnings; use base qw(CGI::Application); use CGI qw(:standard); sub setup { my ($self) = @_; $self->start_mode('main'); $self->mode_param('q'); $self->run_modes( main => 'main', login => 'login', members => 'members', AUTOLOAD => '_invalid', ); } sub main { return start_html('Welcome') . p( strong('Welcome to my website!') ) . end_html() ; } sub login { return start_html('Member Login') . p( strong('Member Login') ) . p('Login below:') . end_html() ; } sub members { return start_html('I am a member-only page!') . p( strong('Members Page') ) . end_html() ; } sub _invalid { return start_html('Invalid Function') . p( strong('Invalid Script Function') ) . p('That fucntion is not valid!') . end_html() ; } package main; use strict; use warnings; my $webapp = WebApp->new(); $webapp->run();
    I actually find CGI::App to be more elegant than a dispatch table, and for the record, i have only recently discoverd CGI::App. I used dispatch tables plenty in the past. In essence, CGI::App is a dispatch table, but one that is tested and ready to go.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Event Handling in CGI
by benn (Vicar) on Mar 18, 2003 at 11:26 UTC
    I doubt if your speed problems are solely due to your dispatch method. Obviously, neater dispatching would help with your readability etc., but I would be surprised if a bunch of 'if' statements were slowing things down dramatically, unless you were doing complicated comparisons with every statement. I suspect that there may be other areas that are taking their merry time - get those Benchmark modules out :)
Re: Event Handling in CGI
by robartes (Priest) on Mar 18, 2003 at 10:02 UTC
    Warning: This code is hazardous to your health. This is an example how not to do it.

    Right, now that's said, how's this for fun and giggles:

    #!/usr/local/bin/perl -w use strict; sub a { print "Hi from a\n"; } sub b { print "Hi from b\n"; } doit('a'); doit('b'); $main::c=1; $main::c++; doit('c'); sub doit { my $func=shift; unless (exists($::{$func}) && defined(&{$::{$func}})) { print "Invalid function called: $func\n"; return 1; }; no strict 'refs'; &{$func}; } __END__ Hi from a Hi from b Invalid function called: c
    This is ugly code. It uses symbolic references, it hacks the symbol table, and the sheer amount of curlies and parentheses induces headaches in anybody having to maintain it. But it works.

    For educational purposes only. (And I admit, I did it to play around with symbol tables a bit, to get a feel for the things).

    Update: Not only is this ugly, if called as a CGI (and the sub to call is provided as a parameter), it is also terribly insecure in this form: it's letting the CGI user call any function that is defined in your script. Bad programmer.

    CU
    Robartes-