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

Use strict requires me to declare variables using either my or local (as far as I can tell). From Programming Perl 2nd edition, p. 108:

“…dynamic (read: local) variables are also visible to functions called from within the block in which those variables are declared. Lexical (read: my) variables are not. They are totally hidden from the outside world including any called subroutines…”

I added the notes in parenthesis. Two test cases:

#!c:\Apps\xampp\perl\bin\perl.exe -wT use strict; my $user_name; $user_name = $ARGV[0]; print "IN SCRIPT: user_name == $user_name\n"; login(); print "IN SCRIPT: user_name == $user_name\n"; sub login { print "IN SUBROUTINE: user_name == $user_name\n"; print "Can I change it in the subroutine?\n"; $user_name = "Bill"; print "IN SUBROUTINE: user_name == $user_name\n"; }
OUTPUT:
C:\Apps\xampp\htdocs\planning>perl -wT projselect_test2.pl waylon
IN SCRIPT: user_name == waylon
IN SUBROUTINE: user_name == waylon
Can I change it in the subroutine?
IN SUBROUTINE: user_name == Bill
IN SCRIPT: user_name == Bill

This program indicates I can access the variable’s value from a subroutine, and I can change it as well. How does that reconcile with the statements above from Programming Perl? Now the stumper, which is my web login script (greatly simplified here to illustrate only this problem):

use strict; my $user_name=""; $user_name = param('username'); print "Content-type: text/html\n\n"; print "<html><body><pre>\n"; print "IN SCRIPT: user_name == $user_name<br>\n"; login(); print "IN SCRIPT: user_name == $user_name\n"; print "</pre></body></html>\n"; sub login { print "IN SUBROUTINE: user_name == $user_name<br>"; print "Can I change it in the subroutine?<br>"; $user_name = "Bill"; print "IN SUBROUTINE: user_name == $user_name<br>\n"; }
OUTPUT (first run, user name supplied is Sam):
IN SCRIPT: user_name == Sam

IN SUBROUTINE: user_name == Sam
Can I change it in the subroutine?
IN SUBROUTINE: user_name == Bill

IN SCRIPT: user_name == Bill

OUTPUT (second run, user_name supplied is George):
IN SCRIPT: user_name == George

IN SUBROUTINE: user_name == Bill # <--Crazy!!
Can I change it in the subroutine?
IN SUBROUTINE: user_name == Bill

IN SCRIPT: user_name == George

Here’s the stumper part: When I call this the first time, the $user_name within the subroutine matches the $user_name in the main script and the one that was passed from the calling html form (the html form is at the bottom of this post for completeness). If I use the back button and enter a new user name, the master script has the new user name but the subroutine has the name from it’s prior run.

My questions:

Why, if variables declared private using my shouldn’t be available in a subroutine, are they available in a subroutine?
Why does my CGI script maintain information between executions?
Are there any other variable declarations I should know aside from my and local when using strict?

<html> <body><pre> <form method="post" action="projselect_test.pl"> <h2>System login</h2> User Name: <input type="text" name="username" size=15><br> Password: <input type="password" name="password" size=15><br> <input type="submit" value="Login"><p> </pre> </body> </html>
  • Comment on My, subroutines, scope, and CGI – Why can I see a private (my) variable in a subroutine but it doesn’t get updated?
  • Select or Download Code

Replies are listed 'Best First'.
Re: My, subroutines, scope, and CGI – Why can I see a private (my) variable in a subroutine but it doesn’t get updated?
by kyle (Abbot) on Apr 25, 2007 at 18:02 UTC

    Why, if variables declared private using my shouldn’t be available in a subroutine, are they available in a subroutine?

    The subroutine that you're testing with is defined inside the scope where the lexical variable exists. If you do something like this instead:

    { my $user_name=param('username'); print "IN SCRIPT: user_name == $user_name<br>\n"; login(); print "IN SCRIPT: user_name == $user_name\n"; } sub login { print "IN SUBROUTINE: user_name == $user_name<br>"; print "Can I change it in the subroutine?<br>"; $user_name = "Bill"; print "IN SUBROUTINE: user_name == $user_name<br>\n"; }

    ...then it won't even compile under strict because the sub refers to a variable ($user_name) that's not available in its scope. Put the login sub inside the braces that have my $user_name, and it will work again. See?

    Why does my CGI script maintain information between executions?

    This is something that mod_perl does.

    Are there any other variable declarations I should know aside from my and local when using strict?

    No. I general, you should almost always use my instead of local. If you want global variables, look at our or vars.

      Thanks for your response.

      ...then it won't even compile under strict because the sub refers to a variable ($user_name) that's not available in its scope. Put the login sub inside the braces that have my $user_name, and it will work again. See?

      I understand. It didn't sound that way from the book, but I understand and can adjust accordingly.

      This is something that mod_perl does.

      Can I stop it? Should I? Should I just reinitialize the variable in the subroutine the same way I do in the main script? Why does it do that?

      No. In general, you should almost always use my instead of local. If you want global variables, look at our or vars.

      Thank you, and will do.

        Re mod_perl keeping old values in scope:
        Can I stop it? Should I? Should I just reinitialize the variable in the subroutine the same way I do in the main script? Why does it do that?
        Mod_perl (or more precisely, Apache::Registry) converts scripts to subroutines so they can be re-used without recompiling. Basically, if you have a script like this:
        my $var = param('something'); sub print_var { print $var; } print_var();
        It will get converted to something like this:
        sub run_my_script { my $var = param('something'); sub print_var { print $var; } print_var(); }
        And you should get a Variable "$var" will not stay shared warning in your error log.

        AFAIK what happens is that in perl you can't really define named subroutines (like "print_var" here) inside another subroutine. If you try, you can run into scoping issues. In this case, if you call run_my_script() repeatedly, print_var() will always only see the first value of $var.

        One solution is to put most of your code in a module and use that from the script.

        Another is to remove the script entirely and write an request handler module that does whatever the script does.

        Update: yet another solution would be to not access the lexical variables available from the "higher up" scope inside your subroutines -i.e. pass everything needed as subroutine arguments instead. And pay attention to that error log :-)

        See also Exposing Apache::Registry secrets in the CGI to mod_perl porting guide.

Re: My, subroutines, scope, and CGI – Why can I see a private (my) variable in a subroutine but it doesn’t get updated?
by Errto (Vicar) on Apr 25, 2007 at 19:07 UTC
    The above replies explain why the variable's scope includes the subroutine. But the "George" issue is a more subtle problem: the infamous "won't stay shared" issue (which, by the way, you would see if you enable warnings in your code). When running a CGI script with mod_perl using Apache::Registry, you can't use variables declared with my in the main script body inside subroutines, because the subroutine will have a "copy" of the variable from the first time the script is run, which is almost never what you want. Some possible solutions are:
    • declare the variable with our instead of my
    • use an anonymous subroutine instead of a named one
    • pass the value as an argument to the subroutine rather than accessing the variable directly within it
    • use Apache::PerlRun instead of Apache::Registry
      Ahhhh...good explanation and thank you for those solutions. I'm very new to this, so I don't really know anything about mod_perl, Apache::Registry and Apache::PerlRun, but those should be easy search terms to look up. Also, I have the -w flag in my !# line, but I'm not sure where those warnings would be showing themselves. Maybe in this error log people keep mentioning? Looks like I need to research that as well.

      Thanks.

        Should be in the error log, yes :-) Where the log is is dependent on your apache configuration.

        Where your apache configuration is, is dependent on your apache startup scripts/system configuration. On my system (debian) it's in /etc/apache/httpd.conf. Look for the "ErrorLog" line. On my system the error log is in /var/log/apache/error.log

        I have the -w flag in my !# line
        The shebang is not recognized in your system, it's merely treated as comment. (from LP on Win32)

        Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: My, subroutines, scope, and CGI – Why can I see a private (my) variable in a subroutine but it doesn’t get updated?
by MonkE (Hermit) on Apr 25, 2007 at 18:56 UTC
    "my" variables are lexically scoped. That is, they exist only within a certain portion of the text that is your source code. I have annotated your first example below to illustrate the various, overlapping, lexical scopes present.
    1 use strict; 1 1 my $user_name; 1 1 $user_name = $ARGV[0]; 1 1 print "IN SCRIPT: user_name == $user_name\n"; 1 1 login(); 1 1 print "IN SCRIPT: user_name == $user_name\n"; 1 1 2 sub login { 1 2 1 2 print "IN SUBROUTINE: user_name == $user_name\n"; 1 2 print "Can I change it in the subroutine?\n"; 1 2 $user_name = "Bill"; 1 2 print "IN SUBROUTINE: user_name == $user_name\n"; 1 2 1 2} 1
    Since $user_name was declared in the implicit/file-wide lexical scope (which I have labelled as "1"), it is available to all code within the source file (after the point at which it is declared). It comes as no surprise then that you can modify $user_name within a subroutine ... so long as the scope that $user_name was declared in overlaps the code that modifies it.
Re: My, subroutines, scope, and CGI – Why can I see a private (my) variable in a subroutine but it doesn’t get updated?
by Calm (Acolyte) on Apr 25, 2007 at 22:49 UTC
    Alright, thanks to all of you for all the good feedback. I found the error.log file and will keep an eye on it. My current solution is a combination of passing arguments to the subroutine and using variables scoped with our instead of my. I'll keep fooling around with this issue until I understand enough about these different options to make a determination as to which is preferable.

    Thanks again to all of you.