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

OK, here's a simple way of setting the values of complex objects via http name-value pairs:
sub string_to_struct { my $string = shift; my @refs = split /__/, $string; $string = "\$$refs[0]"; for (1 .. $#refs) { $string .= "-> {'$refs[$_]'}" }; return eval "\\$string"; # a ref, for reasons to do with not failing +if eval "\$string" would be an array, hash, hashref etc. }

This turns e.g"FOO__bar__baz_bop" into $FOO->{bar}{baz_bop}

Leaving aside the merits of this WTDI, (but feel free to comment if you wish ;-) I am wondering if there is a simple way to do the reverse - a "struct_to_string" function? My initial thought is, not really. If you pass a value to a subroutine, you just get the value. Even if it's a reference, you just get a memory address. So that's no good if you want to specify the string itself. Is there any way to access a variable's name as a string?

David

Replies are listed 'Best First'.
Re: stringification
by chromatic (Archbishop) on Feb 27, 2001 at 22:17 UTC
    I'm not sure I understand what you mean. Here's how to pass a reference to a string to a sub and still access the contents of the string:
    my $data = "you are hearing me talk"; sub show_data { my $d_ref = shift; print $$d_ref; } show_data{\$data);
    Generally speaking, there's no good reason that comes to mind for why you would need to get the name of a variable during runtime. The compiler doesn't care about names at all. They exist only for your benefit. If your program needs to know the *name* of everything that could possibly be created at runtime, you'll have trouble going beyond a hundred lines of code.

    As long as you can get to variables in hashes or arrays or even objects and other complex data structures, you don't need to know the names. You probably do need to understand references, so bone up on perlref and perlreftut.

    Now if you're providing a serialization mechanism or pretty printer, there's a different story. But that doesn't seem like what you're doing here. Did I completely misunderstand your question?

(jeffa) Re: stringification
by jeffa (Bishop) on Feb 27, 2001 at 22:12 UTC
    I really would like to know why you want to do this. I believe that Data::Dumper and FreezeThaw will take care of serializing data for you, in a much more secure way. Take a look at the internals of those modules if you are searching for the "how does it work" knowledge.

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    
Re: stringification
by davorg (Chancellor) on Feb 27, 2001 at 22:12 UTC

    I think that Data::Dumper might do some of what you want.

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

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

Re: stringification
by dash2 (Hermit) on Feb 27, 2001 at 22:33 UTC
    For those that asked: the rationale is that I want an easily generalisable way of setting values in my program. E.g. a form input of foo__bar=whatever should result in $foo->{bar} = "whatever".

    I think it is fairly secure: you can set variables but you can't execute anything.

    Now equally, I don't always want to convert my variables to the appropriate string by hand. I want a quick method of changing $foo->{bar} to "foo__bar", so I can write

    use CGI; print hidden( -name => struct_to_string($foo->{bar}, -value => $foo->{bar} );
    .. and do that for lots of variables.

    Ysee?

    update: Some guys suggested Data::Dumper or FreezeThaw. That does seem obvious, but these things are also mostly for storing the data in a variable, not the name of the variable - e.g. Data::Dumper dumps stuff as $VAR1, and FreezeThaw freezes the value but not its name! but maybe I should put all the values I want to change into a big hash and then reference into it with Data::Dumper.

    dave

      Makes sense, depending on how far you trust the user. If you come up with a technique that can set a named variable, a saavy malicious user could set *any* named variable, if he guesses it correctly. If you have a variable named $datafile that points to a file to read, what if he sends "data__/etc/passwd" or what have you? Even worse, "data__|rm -rf . *"?

      You could solve two problems in one swoop by sticking every settable variable in a hash. You have to know the hash keys to access the values (so they're available), and if you only allow the hidden fields to modify pairs in the hash, it's a lot harder to do nasty tricks like above.

      On the other hand, you could use Apache::Session or a related technique, saving the data on the server temporarily, and passing only a session key or unique identifier on the hidden field, not serializing your data to hidden fields in HTML. I'd take this last approach, because I don't trust the clients to *type* the right thing, let alone send back correct data.

        merlyn: very good point. but, can't you get round this by enclosing the variables in quotes? e.g.

        >perl -e ' sub string_to_struct { my $string = shift; my @refs = split /__/, $string; $string = "\$$refs[0]"; for (1 .. $#refs) { $string .= "-> {q/$refs[$_]/}" }; return eval "\\$string"; } print string_to_struct "foo__`rm xxx`"; ' SCALAR(0x8056ba4) >ls ... xxx ... >perl -e ' sub string_to_struct { my $string = shift; my @refs = split /__/, $string; $string = "\$$refs[0]"; for (1 .. $#refs) { $string .= "-> {$refs[$_]}" }; return eval "\\$string"; } print string_to_struct "foo__`rm xxx`"; ' SCALAR(0x8056bb0) > ls [argh! xxx has been removed]

        Anyway, I'll check the refs and consider making sure all my configs get written in a safe way. It still wouldn't be nice to let them set any variable, I guess....

        update:
        Great reference. That solves it. Once again, chromatic sorts me out.

        update 2:
        Well, not completely. Chromatics solution in the thread referred to above will traverse a hash and show all the keys (and values). I'll have a think how to hack it to go just to the key and value you want.

        update 3:
        And by the way, my claim that the code above would be safe is not true - see replies to this post for why.

        David

Re: stringification
by merlyn (Sage) on Mar 10, 2001 at 23:18 UTC
Re: stringification
by dash2 (Hermit) on Mar 01, 2001 at 16:45 UTC
    Well this whole discussion led off to the thread of why what I was doing originally was unsafe and slow. So I thought I would post this, which I hope is safe, if not fast. It's a way of getting from a string to a reference into a particular key of a hash. You can specify which hashes you want to be referenceable.

    update: demoronified the code in one place

    sub string_to_struct { my $string = shift; my ($var, $keys) = split /-/, $string, 2; $keys = "-$keys"; # you could use a lookahead in the split, but in my + experience they break on older Perl 5s. return 0 unless ( $var eq "safe" or $var eq "alsosafe" ); $change{$var}++; # global no strict refs; my $ref = $$var; # reference to the original. can alter the original use strict refs; return deref($ref, $keys); } sub deref { return \$_[0] unless $_[1] =~ s/^-([\w_]+)//; deref ($_[0]->{$1}, $_[1]); }

    This should take e.g. safe-foo-bar and return a reference to $safe->{foo}{bar}. Comments are welcome - it's the result of a lot of debugging, so probably not the most elegant in the world!

    I also asked about the reverse process. I'm still not sure there's a way to get from a particular hash key to a string representing it, i.e. from $safe->{foo}{bar} to safe-foo-bar. At least not if you use a subroutine. The subroutine just gets the value of $safe->{foo}{bar}. It gets it by reference, but it still doesn't know where the reference "is". I suppose you could pass it like this:

    struct_to_string($safe,$safe->{foo},$safe->{foo}{bar});
    and do something with the list of values, but by that time, you may as well type it yourself...

    Thanks for all the help.
    dave hj~