Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Variable Variables

by marius (Hermit)
on Dec 05, 2000 at 07:25 UTC ( [id://44922]=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to save myself some time and typing (isn't that the perl way?) by using a loop to declare some variables.. however, it's bombing on my telling me it's unable to deref a scalar. Any ideas what's going on? Perl is runing with -wT, using strict and CGI::Pretty.

Here's a snippet that should give you an idea of what I'm trying to achieve:

my($key); foreach $key ("name","email","website","homeaddress","city","state","p +hone","college","company","password") { my(${$key}) = $query->param($key); }
Basically, I'm looking to avoid the following:
my($name)=$query->param("name"); my($email)=$query->param("email");

so on and so forth.. any ideas on how to fix this nagging error, or a better way to do it? Thanks monks!

Replies are listed 'Best First'.
(CGI::Vars) Re: Variable Variables
by mwp (Hermit) on Dec 05, 2000 at 07:41 UTC
    Doing something like this is bad practice, for a variety of reasons. Mostly because if someone adds a form field that you did not prepare for, it might affect a different part of the script. For example, if someone added a form field called "key" your script would die because it would try to set $key to the form value, which is also the loop iterator.

    If you want to avoid calling $query->param() each and every time you want to access the form values, and I sympathize, you should try something like this:

    use strict; use CGI; my $query = new CGI; # this will import the form values my %vars = $query->Vars(); # this will "export" the form values print "Name: ", $vars{name}, "\n";

    ...et cetera. See the CGI.pm manpage for more info about the Vars syntax.

    HTH

    'kaboo

      Actually, his code didn't have that particular flaw. He only creates the variables in his array. But your point is good, its something people need to think about.

      I also like the $query->Vars(); suggestion. never seen that before. cool :-)

        Thanks. Vars() is relatively new, around CGI.pm 2.50 if I remember correctly.

        I understand what you're saying... he only creates the variables in the list he defines for the foreach loop. I guess what I was trying to imply was that whomever edits the form and adds a field is going to have to come into the CGI script and modify that too, creating the situation I described. =) You would hope the programmer in question would realize their folly and change the formfield name so that it wouldn't break the script, but never forget O'Toole's postulate: Murphy was an optimist.

      I recall seeing a link to a PHP(?) bug report because the automatic action in that language is to create variables in main namespace named after the form elements. However people could write over package globals that people didn't want changed, compromising security.
      This is OK unless a parameter is specified more than once as in '?ar=3&ar=2' - in this case Vars loses a value. The following is what I use:
      my %args = (); # get cgi parameters into a hash. preserve multi-valued # parms for my $p ( $cg->param() ) { my @tmp = $cg->param($p); if ( scalar @tmp == 1 ) { $args{$p} = $tmp[0] } else { $args{$p} = [ @tmp ] } }
      Now %args has your params, w/ multi-valued ones as references to arrays. - Bob Niederman
        That's a good point. (Neat routine, too.) But consider this:

        Before coming here, and before CGI.pm matured, I was a notorious advocate of cgi-lib.pl. As a matter of fact, part of the reason that the Vars method in CGI.pm was added was to facilitate the transition of cgi-lib.pl users over to CGI.pm. In cgi-lib.pl you would do something like this:

        require "cgi-lib.pl"; &ReadParse(*input); # store form values in the glob "input" print &PrintHeader; # print HTML header print "Name: ", $input{name}, "<br>\n";

        This was very nice syntax and worked well for me for many moons. =) When the form parser would encounter multiple values for a single field name, it would string them together with the NUL character (by default), so you could do this:

        print "First value: ", $input{valueList}, "<br>\n"; print "All values: ", join("<br>\n", split(/\0/, $input{valueList}));

        And it would do exactly what you'd expect it to do. Neat, huh? Well, conveniently enough, CGI.pm inherited this behaviour. But don't take my word for it:

        #!/usr/bin/perl -w # /~alakaboo/cgi-perl/multi.pl use strict; use CGI; my $q = new CGI; my %v = $q->Vars(); print $q->header('text/plain'); { local $" = "\n"; my @tmp = split /\0/, $v{ar}; print "@tmp"; }
        Voila.
Re: Variable Variables
by Adam (Vicar) on Dec 05, 2000 at 07:33 UTC
    Your problems are many.
    First off, the my inside the foreach loop means your variables vanish right after you declare them. Not what you intended. Second, you are violating strict (${$key} is an unstrict ref). Third there are many better ways to do this, but I'm left wondering why you would want to. Why not just use $query->param("whatever") when you need it? Why duplicate it like that? To make your code more legible? Well then that loop isn't going to help. If you really want to get a copy of the params while shaking the $query then use a hash:
    use strict; my $q = CGI->new(); my %p; $p{$_} = $q->param($_) for qw( name email website andSoOn );
Re: Variable Variables
by merlyn (Sage) on Dec 05, 2000 at 07:34 UTC
Re: Variable Variables
by marius (Hermit) on Dec 05, 2000 at 11:03 UTC
    thanks for the insight, all.. ++'s all around. Valuable lessones when learning perl: hashes can be used for "darn near everything". I hadn't even thought of using hashes for the project, but it's obviously the superior approach.

    I think I'm gonna end up using alakaboo's $query->Vars() method. Didn't know about that, and ended up going from CGI.pm 2.46 to 2.76 anyway. I guess that needed an update anyhoo.

      I don't know if this will help at all, but you could try either:

      use strict; use CGI qw(header param); print header; print "The variable is: ", param('variable_name'), "\n";

      This will cut off a line, and you just have to reference param() everywhere.

      The other option requires that you don't use strict, but it's fairly nifty:

      use CGI qw(header param); print header; foreach (param) { $$_ = param($_); }

      With this, you'll end up with all the variables translated into Perl variables. For example, a form with item named item will become $item in your script. Someone somewhere said this could potentially be a security risk, but I'm not sure how yet.

      Hope this helps. BTW, if anyone knows why the above is bad, please let me know. Thanks.

Re: Variable Variables
by chipmunk (Parson) on Dec 05, 2000 at 22:38 UTC
    I just want to point out, since multiple people have suggested using foreach ($query->param()) { $$_ = $query->param($_) } that the CGI module provides a method for importing names: $query->import_names('Q'); imports all the parameters into the Q namespace. CGI warns against importing into the main package, for good reason: it's a huge security flaw! Someone could replace the value of any scalar variable in your code just by faking the form data.

    Also note that using the above 'roll-your-own' approach to importing fails for multi-valued parameters, assigning them to scalars instead of to arrays. Using CGI's built-in method handles multi-valued parameters correctly.

      Using CGI's built-in method handles multi-valued parameters correctly.

      Actually I tried it ($cg->import_names('Q')) and it still loses values on muti-valued params. - Bob Niederman
        I just tried it too. It turns out that import_names() does this in an interesting fashion; it imports all parameters as both scalars and arrays.
        #!/usr/local/bin/perl use CGI; my $query = new CGI ( { single => 'one', multi => ['two', 'three'], } ); $query->import_names('Q'); 1;
        Now, using the debugger to examine the results:
        Loading DB routines from perl5db.pl version 1.0402 Emacs support available. Enter h or `h h' for help. main::(tmp.cgi:5): my $query = new CGI ( { single => 'one', main::(tmp.cgi:6): multi => ['two', 'thr +ee'], DB<1> n main::(tmp.cgi:10): $query->import_names('Q'); DB<1> n main::(tmp.cgi:12): 1; DB<1> V Q $single = 'one' @single = ( 0 'one' ) $multi = 'two' @multi = ( 0 'two' 1 'three' ) DB<2>
Re: Variable Variables
by Anonymous Monk on Dec 05, 2000 at 21:55 UTC
    I always do something like this when using CGI.pm:
    foreach ($query->param) {
    	$$_ = $query->param($_);
    }
    
    Saves plenty of typing ;) Of course, you could always just select a few variables to use:
    foreach (qw(alpha bravo charlie)) {
    	$$_ = $query->param($_);
    }
    
    Which gives you $alpha, $bravo and $charlie with the values returned from the form. HTH :-)
      When I try this using mod_perl I'm told: Can't use string ("some_variable") as a SCALAR ref while "strict refs" in use. Is there another way to do this in mod_perl? Thanks

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://44922]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-03-19 08:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found