cLive ;-) has asked for the wisdom of the Perl Monks concerning the following question:

Hey peeps,

I have the joyful task of customizing a bunch of scripts. In trying to make them strict, I encountered the following problem. This file, config.cgi (abbreviated)

$config{'username'} = "zxcvb"; $config{'password'} = "zzzzz";
etc, is pulled in through a do:
do 'config.cgi';
So I tried this:
use strict; use warnings; my %config = {}; do 'config.cgi';
This parses OK, but when I dump %config, there's nothing there.

And if I remove the my %config, the script dies saying %config is not defined.

So, why does strict require the "my %config", but not populate it on "doing the do".

very perplexed, thoughts welcomed...

cLive ;-)

Replies are listed 'Best First'.
Re: "do" what?
by runrig (Abbot) on Oct 21, 2002 at 22:15 UTC
    Try use vars qw(%config); instead of my.
    Update: Also consider turning the file into a package so you could say something like:
    use Myconfig qw(%config); print $config{this}, "\n"; # etc. ## And in your 'Myconfig' package: package Myconfig; use strict; use warnings; require Exporter; use vars qw(%config @ISA @EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(%config); $config{this} = "that"; $config{here} = "there"; #etc. 1;
    I like this solution better because the place where 'config' is created is self-documenting.
    Update: The do block populates a package variable %config in a non-strict environment (your 'do' file), whereas the my creates a lexical variable which never gets populated (I may be wrong, but I think that's it).

    Or consider using AppConfig

      wow - that worked.

      Care to explain why 'my' failed though? I'm puzzled why 'my' kept strict happy, yet didn't populate the hash.

      cLive ;-)

      --

        Because my globals: lexicals defined outside any blocks are scoped to their file. The file you do'd was compiled without strictures and created a different, global %config that it populated. Inside your own file, strict is happy, because you cannot see the global %config.

        Makeshifts last the longest.

        my creates variables only visible to the current scope, and the closures or blocks defined within it. A my variable is not available to anyone outside the scope, or anything called from within the scope, which was not declared inside it (and even so, that gets tricky).

        Example:
        my $var1; $_ = ''; FOO: { my $var2; BAR: { my $var3; local $_ = 'bar set it'; &foo(); } GORCH: { my $var4; } } sub foo { my $var5; print "$_\n"; }
        Every scope in the above code can see $var1, because they were all declared under the main scope, in which $var1 was created. However, the main scope, and sub foo cannot see any variables declared within FOO: {}, and FOO: {} cannot see $var5, which was declared in sub foo. Furthermore, BAR: {} AND GORCH: {} can see everything except each other's private variables, and sub foo's $var5. sub foo, even when called from FOO: {}, BAR: {}, or GORCH: {} cannot see their private (my) variables. It can see local versions of global (our, $_ sorts) variables though - sub foo, called from bar will print "bar set it".

        You may also notice, if you use Data::Dumper; print Dumper \%::; that any files you do are also assigned a package, starting with "_<", and then the relative path. You may be able to use those. my experiments have been unsuccessful, but they weren't very thorough either.

        Update: I forgot to mention that my and local both create new copies, and new variables, leaving anything with the same name on an outer scope untouched. Also fixed HTML entity.

        -nuffin
        zz zZ Z Z #!perl
Re: "do" what?
by sauoq (Abbot) on Oct 21, 2002 at 22:19 UTC
    So, why does strict require the "my %config", but not populate it on "doing the do".

    Because your %config and the one in the file you are do'ing are different. Your %config shadows the one in the file. You'll need to declare it as a global.

    use vars qw( %config );

    If you are using 5.6 or greater, you can write that as our %config; instead.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: "do" what?
by Zaxo (Archbishop) on Oct 21, 2002 at 22:21 UTC

    Variables declared with my are file scoped. Globals are namespace scoped. This works:

    #!/usr/bin/perl use strict; use warnings; our %config; do 'config.cgi'; print keys %config, $/;

    Update: Clarification, a file under do cannot see lexicals in the enclosing scope. The %config that config.cgi writes to is therefore global. When do returns, the lexical %config masks the global one which was just populated.

    After Compline,
    Zaxo

Re: "do" what?
by mojotoad (Monsignor) on Oct 21, 2002 at 22:15 UTC
    Two observations. First, check $! after the 'do' statement and make sure that the file is indeed being loaded.

    Second, inspect config.cgi and ensure that there are no package statements that could be defining %config in a different namespace.

    Matt

    Update: runrig has it right in this case; the static my %config is not visible in from the point of view of config.cgi; this static %config is therefore masking the global %config variable visible from the do block.

Re: "do" what?
by grantm (Parson) on Oct 21, 2002 at 22:25 UTC
    This parses OK

    It shouldn't. The line 'my %config = {};' should trigger the error 'Reference found where even-sized list expected'

    Assuming we change it to 'my %config = ();' it still doesn't work, since %config is lexically scoped and the do block doesn't seem to be able to access lexically scoped variables. If you change it to:

    our %config = ();

    Then you should get the effect you're after.

    Having said that, evaluating a config file to set global variables is not a great idea. In fact this is the exact problem XML::Simple was written to solve. If your config file was called scriptname.xml and looked like this:

    <config> <username>zxcvb</username> <password>zzzzz</password> </config>

    Then you could use it like this:

    use XML::Simple; my $config = XMLin(); print $config->{username};

    In real life, you would set explicit values for XMLin's keyattr and forcearray options and you'd probably also want to investigate the caching options.

      Or if you don't want to go that far, you can change it to something like:
      { username => 'zxcvb', password => 'zzzzz', }
      and use it like this: my $config = do "config.pl";

      Makeshifts last the longest.

        From a security perspective, that's just as bad. Someone with write access to the config file could change it to:

        system("insert malicious command here"); { username => 'zxcvb', password => 'zzzzz', }

        If you don't need to trust the keeper of the config file to refrain from malicious or even dumb stuff, why expose yourself to the risk.

        Check out YAML for another way to represent complex data structures in config files without needing eval (or "do" in this case).

Re: "do" what?
by Juerd (Abbot) on Oct 22, 2002 at 07:07 UTC

    You managed to not read or not understand the documentation. The relevant paragraph in perlfunc's do-item is:

    do 'stat.pl';

    is just like

    eval `cat stat.pl`;

    except that it's more efficient and concise, keeps track of the current filename for error messages, searches the @INC libraries, and updates %INC if the file is found. See "Prede- fined Names" in perlvar for these variables. It also differs in that code evaluated with do FILENAME cannot see lexicals in the enclosing scope; eval STRING does. It's the same, however, in that it does reparse the file every time you call it, so you probably don't want to do this inside a loop.

    - Yes, I reinvent wheels.
    - Spam: Visit eurotraQ.
    

*oops*
by cLive ;-) (Prior) on Oct 21, 2002 at 23:06 UTC

    <blush>
    No more mystery - I forgot to remove the loop that printed the values of %config when I removed the 'my' statement. (Note to self - d'oh, d'oh, d'oh). It all makes sense now.
    </blush>

    cLive ;-)