Reputation: 4699
I was trying to understand how use vars ($var);
works. I realize that if I want to declare a variable globally, I can use use vars ($var);
or Perl 5.6 and greater I can use our $var;
However, I'm still just trying to understand how it works. Looking at the source, it looks like you just declare the variable as a typeglob and set it equal to a referenced version of itself. WHAT!?
# My_Module.pm
use strict (vars, subs);
use CGI::Carp;
*My_Module::My_Global = \$My_Module::My_Global;
sub my_function {
$My_Global = 'Am I declared?';
}
Now this doesn't work, I obviously get a 'undefined symbol' error. So how does use vars
do it?
----------------UPDATE---------------
I literally copied your code exactly, ran it from test.cgi with use X;
instead of shell...
X.pm
use strict;
BEGIN { package X; no strict qw( refs ); *main::x = \${"main::x"}; }
print "Content-type: text/html\n\n"; # I added this line since not printing to shell
$x = 123; say $x;
1;
test.cgi
#!/usr/bin/perl
use strict;
use X;
...and I got the same compile error I did before:
Global symbol "$x" requires explicit package name at X.pm line 4.
Does this only work in a shell and not in a module?
Upvotes: 2
Views: 2277
Reputation: 385914
Simply using a variable will create it as a (global) package variable.
$ perl -wE'$x = 123; say $x;'
123
That's very dangerous, so programmers tell Perl to forbid this by using use strict qw( vars );
.
$ perl -wE'use strict; $x = 123; say $x;'
Global symbol "$x" requires explicit package name at -e line 1.
Global symbol "$x" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.
However, in order to preserve their usefulness, strict vars allows imported variables to be used without error.
$ perl -wE'use strict; say $Config{version};'
Global symbol "%Config" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.
$ perl -wE'use strict; use Config qw( %Config ); say $Config{version};'
5.18.1
use vars
simply creates a new variables and exports it.
While your code comes close to doing that, it suffers from two problems.
It's crucial that the variable gets imported before any reference to the variable is encountered in the code. Two changes need be done to fix this in your code.
The operands of an assignment are necessarily compiled before that assignment is evaluated, so your assignment can't possible import the variable before it's referenced by the code.
*Package::foo = $Package::foo; # XXX Compile-time lookup
*Package::foo = ${"Package::foo"}; # Runtime looup
*Package::foo = \( my $anon ); # Would work too.
You wait for the entire script to be compiled before importing the variable, so the import happens after strict already forbade the references to the variable. You need to perform the import sooner by using BEGIN { }
[1].
For the variable to be considered imported, the code doing the exporting must be compiled in a different package than the one into which the variable is being exported.
If we apply those fixes, we end up with the following:
$ perl -wE'
use strict;
BEGIN { package X; no strict qw( refs ); *main::x = \${"main::x"}; }
$x = 123; say $x;
'
123
Notes:
Keep in mind that
use Module qw( ... );
is basically the same as
BEGIN { require Module; Module->import(qw( ... )); }
In other words, the module is executed and its import
method is called before any further code is parsed.
Upvotes: 1
Reputation: 98398
Assigning a reference to a typeglob is a special kind of assignment; it replaces only the part of the typeglob of the reference's type with the reference. Also, vars is using a symbolic reference for the variable name, where you are not. use vars
also executes in a different package, and at compile time, not run time. The inline equivalent would be:
BEGIN { package foo; *My_Module::My_Global = \${"My_Module::My_Global"} }
That said, use of global variables usually is a bad idea. Use of no longer supported versions of perl is equally a bad idea.
Upvotes: 3