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

I've got an html page consisting of a form with various types of input (a select box and a few text fields). I'm writing a perl program to parse the input of this form but I need the script to be dynamic; that is, I need it to work the same with various forms (basically, I'm opening a text file and writing whatever was input to it). I figure the best way to do this is to create an array with each of the variables and their corresponding values in it but I'm not quite sure how to do this. I've taken the variables from the form input:

if ($ENV{'REQUEST_METHOD'} eq 'POST') { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); my @pairs = split(/&/, $buffer); } else { &error; }

..., but now how do I make an associative array that holds each of the variable names and its corresponding value? (like color => blue, name => john, etc., or would there be an even easier way? remember, I won't know what the variables are when the script runs; it could be from any form, each of which has a different number/differently named variables) Thanks

Replies are listed 'Best First'.
(Ovid -- bug in your hand-rolled CGI code) Re: Pushing w/ an associative array?
by Ovid (Cardinal) on Dec 27, 2000 at 21:17 UTC
    Partly to join the chorus, I have to say "USE CGI.PM"!!! You don't want to do this on your own. In fact, you have a bug in the small snippet that you have posted -- well, you might have corrected for the bug elsewhere, but I have seen this so many times that I'm willing to wager a large sum of money that you haven't: you don't check to ensure that the length of data read from STDIN is the same as $ENV{'CONTENT_LENGTH'}

    What happens if the user hits the "stop" button on their browser? What if they have a power failure? What if the Web server screws up? For any of these and other conditions, the data that you read may be corrupted and you need to verify that it's not by checking its length. Pardon me for seeming pedantic, but a good programmer looks both ways before crossing a one-way street.

    If you'd like to learn more about the pitfalls of this, you can check out this link where I disect "hand-rolled" CGI parsers. I go into quite a bit of detail and by the time you're done reading it, you should have a good idea why writing your own is likely to have problems. Or, at the very least, you'll have a better idea of how to write your own (but I wouldn't recommend it -- you won't catch all of the browser anomolies).

    I am curious about one thing, though. Why would you want to write your own version? Were you unaware of the alternatives or were you concerned about performance? If the latter, you can also check out CGI::Lite.

    Cheers,
    Ovid

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

      How can I read the form input with CGI.pm, then? I still need to read in each variable name and put it into an array for sorting...

        Calling param with no arguments will return a list of all of the CGI arguments. You can therefore do something like this:

        use CGI qw(:standard); my %form; foreach (param) { my @args = param($_); if (@args == 1) { $form{$_} = $args[0]; } else { $form{$_} = [@args]; } }
        --
        <http://www.dave.org.uk>

        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

(jptxs) Re: Pushing w/ an associative array?
by jptxs (Curate) on Dec 27, 2000 at 21:01 UTC

    Your best bet is to look up and use CGI as quickly as possible. Not only will it accomplish the task you ask for it will give you a whole slew of power to play with in dealing with CGI and HTML forms related issues.

    Just type perldoc CGI on your server for info, or you can follow this link to the info about it here in the perlmonks own copy of the docs. Gook luck =)

    "A man's maturity -- consists in having found again the seriousness one had as a child, at play." --Nietzsche
Re: Pushing w/ an associative array?
by davorg (Chancellor) on Dec 27, 2000 at 21:00 UTC

    Use the param function from CGI.pm.

    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me

Re: Pushing w/ an associative array?
by Hrunting (Pilgrim) on Dec 27, 2000 at 21:26 UTC
    basically, I'm opening a text file and writing whatever was input to it

    And to join the chorus here ... use CGI.pm! I mean, even if you're not into all the param() stuff or the wonderful HTML abilities it has or the way it hides all the messy CGI stuff behind a very simple and intuitive interface, you can still take advantage of its save() method which does exactly what you're looking for (and, furthermore, allows you to reload that data into a CGI object if you want to at some later date).

Re: Pushing w/ an associative array?
by ichimunki (Priest) on Dec 27, 2000 at 22:06 UTC
    The answer which no one is willing to give you is this:
    my %pairs = split(/&|=/, $buffer);
    A hash can accept an ordered list of pairs which it will assign using a KEY VAL KEY VAL scheme.

    NOTE: I am only providing this answer because your method can be improved if you insist on ignoring the sound advice to use the CGI module for handling HTTP transactions. This sort of paired-list-to-hash assignment is very useful in creating arguments for subroutines, see here for more information.

    Update:This post was made in the spirit of solving the problem as stated, and is a great example of why using the CGI module is better than recoding parts of it. While this solution will work for the stated problem involving key/value pairs, it will have serious problems (as pointed out below) when you have checkboxes or empty (no default values) form fields.

    It will also likely break as soon as you change your form, unless you are overly restrictive in your form construction (and prohibit check boxes and enforce default values for all fields). All of this is an inefficient use of your time as a coder. Especially when working with CGI and web development, you open up a Pandora's box of problems for yourself or your clients if you do not take advantage of every well-tested solution (like CGI.pm) and get a solid understanding of the programmming you are doing before you build something that is so easily exposed for attack (that is, the web site).
      And this breaks if someone hands you the perfectly legitimate string of
      key1=val1&key2=val2&key3&key4&key5&key6=val6
      Please stop open-coding this. Cargo cult crap that breaks. And CGI is a pre-installed module (even if out of date)!

      -- Randal L. Schwartz, Perl hacker

        Good point.

        But look, it's great to point out the module (which I did), but is that a learning experience? Certainly CGI should be used for handling CGI, but no one answered this poor anon_monk's more general question, which was about handling lists and assigning values to hashes (and sadly, my answer assumed that we were feeding valid pairs in, something I would now know to change). Thus, this poor monk still has no idea how to write Perl to do this when there isn't a module involved. And so we'll have this really great use of modules strung together with the Perl equivalent of baby talk. This is good if the monk is going to immediately put something into production... but is this good for the personal growth of the monk?

      "The answer which no one is willing to give you is this"

      And the reason that no-one is willing to give this answer is that it has fundamental flaws.

      As well as the example that merlyn gives, your code also breaks on a query string like this:

      key1=val1&key2=val2a&key2=val2b

      Update: D'oh! Corrected example so the values are actually different! Thanks merlyn.

      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

        Um, even if the values are different, it doesn't really break. It just overwrites the first value for key2 with the second listed value for this. Shouldn't keys be unique if we expect them to actually be keys? How does CGI handle this exception?