Jonathan
Jonathan

Reputation: 4699

declaring global variables in perl

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

Answers (2)

ikegami
ikegami

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.

  1. 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.

    1. 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.
      
    2. 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].

  2. 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:

  1. 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

ysth
ysth

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

Related Questions