in reply to Re: Variable from a variable: the dark side?
in thread Variable from a variable: the dark side?

Thanks davido, great response.

Okay, "lengthy" reasons are this (I'm sorry I didn't do this before--after all, you ARE Perlmonks). I am using HTML::Template and validating user input from the form with is a .tmpl. If there are user errors, I'm using the associate feature to reprint the form, with values that were entered previously, and a list of errors. As far as I have been able to work it, the associate feature doesn't check previously checked buttons and boxes. So, I have to do it "manually." I'm using <tmpl_if type1>checked</tmpl_if>, etc. for all those 12 radio buttons. There is probably a more efficient way to do this and probably need to rethink this Rube Goldberg I have going.

Again, thanks for your answer.

Added moments later: I'm not use how the suggested hash method (davido, duff, bart) would work in this case, but I'm going to entertain it seriously.

—Brad
"A little yeast leavens the whole dough."

Replies are listed 'Best First'.
Re: Re: Re: Variable from a variable: the dark side?
by davido (Cardinal) on Apr 19, 2004 at 06:01 UTC
    The thing to remember is that the package global symbol table is pretty much a fancy special hash itself. In many cases, what can be done in global namespace, can be done in lexical namespace with a lexically scoped hash. Consider the following code:

    $t = 1; ${ "type" . $t } = "Hello world!\n"; print $type1; print ${$main::{type1}} __OUTPUT__ Hello world! Hello world!

    Ok, so what you've done here is use a "soft reference" to create an entry in the package global symbol hash. The first 'print' shows that the new entry functions just like a regular variable. The second 'print' shows that it is also just the scalar associated with the typeglob held in %main:: You have created, in other words, a hash element whos key is 'type1' in the global hash.

    Next, consider the following code:

    $t = 1; $hash{"type" . $t } = "Hello world!\n"; print $hash{type1}; print ${$main::{hash}}{type1}; __OUTPUT__ Hello world! Hello world!

    Now you've created a hash that has a key named 'type1' in a hash named %hash. There's nothing new about the first 'print'... and the second 'print' just dereferences the global symbol table to get at the element named 'hash', and further treats that element as a reference to a hash with a key named 'type1'. In so many cases, if you think you need something like my first example, you can probably get along just fine with something like my second example, without giving your code access to the symbol table.

    So using a hash instead of a symbolic ref, in many cases can be used instead of the symbolic ref in the first place. All you've really done anyway is create a hash entry either way.

    Now for the lexical part... you can further modify the second example as follows:

    my $t = 1; my %hash; $hash{"type" . $t } = "Hello world!\n"; print $hash{type1}; print ${$main::{hash}}{type1}; __OUTPUT__ Hello world!
    Now you've made your hash lexical, so it doesn't get an entry in the global symbol table, so the second 'print' doesn't print anything. What does this accomplish? Among other things, it reduces global namespace polution. It also reigns in the potential for all sorts of difficult to extinguish headaches, by allowing your code to run properly under use strict; (Without strictures, it's hard to detect misspelled variable names in code, for example.)

    Now for another reason of why this is desirable...

    In your case you're working with CGI. Though your particular code doesn't appear to be at risk of namespace contamination through malicious injections, once you start down the symbolic ref road, you really have to be careful. How easy it would be for you to inadvertantly create an entry in the global symbol table via a symbolic ref that happened to at some point originate via user input, or external file input. And when that happens, you have the potential for trouble.

    So again, feel free to play with matches if your intention and need is to have a flame. But if all you need is a little light, consider a lightbulb -- it's safer.

    Update: Thanks ysth for helping me with a couple edit suggestions that improved this node's readability and verbal accuracy.


    Dave

      ++ davido for that great explanation which was very understandable and informative (should be a Q&A). And thanks again, for the cautions. A big selling point on the last method you mention is to be able to run under use strict, which I honestly hadn't gotten far enough to realize. If I can figure out a way to link it up with my HTML::Template code then I'll be good shape. If I do, I'll update by orig. question.

      —Brad
      "A little yeast leavens the whole dough."
Re^3: Variable from a variable: the dark side?
by Anonymous Monk on Apr 19, 2004 at 05:13 UTC

    Here's one way of doing it. I can't help but think that Template would make this so much easier. In your case, you may just want to use HTML::FillInForm. Check it out :)

    use strict; use CGI::Simple qw(-debug2); use HTML::Template; my $q = CGI::Simple->new(); my $t = HTML::Template->new( filehandle => \*DATA ); my $type = $q->param('type') || 1; $t->param( type_values => [ map { { value => $_, checked => ($_ eq $type ? 'CHECKED' : '') } } 1..12 ] ); print $q->header(), $t->output(); __DATA__ <TMPL_LOOP NAME="type_values"> <input type="radio" name="type" value="<TMPL_VAR NAME="value">" <TMPL_VAR NAME="checked"> /> </TMPL_LOOP>
Re: Re: Re: Variable from a variable: the dark side?
by ishnid (Monk) on Apr 19, 2004 at 08:13 UTC
    Perhaps you could use CGI.pm's "sticky forms" feature. If you get CGI.pm to create the entire radio button code, it will by default remember the previous value. You can still pub your radio buttons wherever you please in the HTML page. Here's an example to illustrate:
    #!/usr/bin/perl -w use strict; use CGI qw(:standard); use HTML::Template; my $template = new HTML::Template (filehandle => \*DATA); my @radio_btns = radio_group(-name=>'type', -value=>[ 1 .. 12 ]); $template->param(type.($_ + 1) => $radio_btns[$_]) for (0 .. $#radio_b +tns); print $template->output; __DATA__ <TMPL_VAR NAME="type1"> <TMPL_VAR NAME="type2"> <TMPL_VAR NAME="type3"> <TMPL_VAR NAME="type4"> <TMPL_VAR NAME="type5"> <TMPL_VAR NAME="type6"> <TMPL_VAR NAME="type7"> <TMPL_VAR NAME="type8"> <TMPL_VAR NAME="type9"> <TMPL_VAR NAME="type10"> <TMPL_VAR NAME="type11"> <TMPL_VAR NAME="type12">
      Thanks ishnid. Great practical solution that I have adapted:
      #!/usr/bin/perl use warnings; use CGI::Carp qw(fatalsToBrowser); use HTML::Template; use strict; use CGI; my $query = new CGI; my $t = $query->param('type'); my @option; $option[1] = $query->param('option1'); $option[2] = $query->param('option2'); $option[3] = $query->param('option3'); $option[4] = $query->param('option4'); my $template = HTML::Template->new(filename => '../htmltemplatetests.t +mpl', associate => $query,die_on_bad_params => 0); { no strict 'subs'; $template->param( type.$t => "1", option.$_ => $option[$_]) for (1 .. 7); } print "Content-type: text/html\n\n"; print $template->output(); exit();
      Of course, the bare 'type' template param returns a strict error, hence the no strict 'subs'. However, all the warnings notwithstanding, I think I'm relatively safe. Thanks all.

      —Brad
      "A little yeast leavens the whole dough."