I thought you used to be able to do this in "strict" mode, but I may be remembering incorrectly. Is is possible for something like this to work...
use strict;
use warnings;
package SomePackage;
my $TargetPID="demo:5"; #using "our" also works, but not in strict mode
my $VarName="TargetPID";
print ${$VarName}; #works but not in strict or if the var is declared with "my"
exit;
The reason I'm interested is that I'm trying to select a variable based on a text flag in a text file and I'd like to read in the content of the text file into a hash, then substitute some identifier along the lines of "#TargetPID#" with the corresponding variable. Being also able to specify both a package and a variable (or constant) would be a nice tbonus.
I can't think of any advantage of doing it the way you are trying to over just using a hash:
use strict;
use warnings;
package SomePackage;
my %vars = ();
$vars{'TargetPID'}="demo:5";
my $VarName="TargetPID";
print $vars{$VarName};
exit;
If you really must use $TargetPID as a variable and not a member of a hash, you can use eval:
my $TargetPID = "demo:5";
my $VarName = '$TargetPID';
print eval $VarName;
Or, if for some reason you need the value of $VarName to be 'TargetPID' and not '$TargetPID', you can do print eval '$' . $VarName.
You can use the PadWalker module for this. From the documentation:
PadWalker is a module which allows you to inspect (and even change!)
lexical variables in any subroutine which called you. It will only
show those variables which are in scope at the point of the call.
In your case, you would need to use peek_my, which does what it says: it allows you to peek into variables declared by my in a given scope.
#!/usr/bin/perl
use warnings;
use strict;
package SomePackage;
use PadWalker qw/peek_my/;
my $TargetPID = "demo:5";
my $VarName = "TargetPID";
print ${peek_my(0)->{'$' . $VarName}}
The subroutine peek_my takes one argument, a level, which is the number of subroutine calls to go back on the stack. It then returns a hash map of all the lexical my variables that were in scope at the time of the given call. In your case, the variable you want is defined in the same scope as where it is needed, so you would pass in 0, to go back 0 subroutine calls. Then you pull out the data you need like any other hash ref.
Be careful though, from the documentation:
PadWalker is particularly useful for debugging (emphasis mine.) It's even used by
Perl's built-in debugger. (It can also be used for evil, of course.)
I wouldn't recommend using PadWalker directly in production code, but
it's your call. Some of the modules that use PadWalker internally are
certainly safe for and useful in production.
Related
I am using strict and warning in my Perl scripts to be notified if I am using undeclared variables. Thus, the interpreter will warn that $foo is undeclared in the following scriptlet:
#!/usr/bin/perl
use warnings;
use strict;
$foo = 'bar';
print ($foo);
However, if I use an undeclared variable in another namespace, I am not warned. The following scriptlet runs without warning whatsoever.
#!/usr/bin/perl
use warnings;
use strict;
$BAR::foo = 'bar';
print ($BAR::foo);
Why is this difference?
Since I have lost quite some time figuring out exactly this problem, albeit in a much larger context, I am wondering if it is possible to make Perl me warn about using undeclared variables in other namespaces, too.
When you fully specify the namespace in which a variable belongs, perl assumes you know what you are doing. See perldoc strict:
strict vars
This generates a compile-time error if you access a variable that was neither explicitly declared (using any of my, our, state, or use vars) nor fully qualified.
I don't think there is a way to detect that you have specified a non-existent variable $BAR::foo. However, if the BAR package is under your control, you can avoid using package variables in the first place by mediating access to the state of foo using accessors, and hiding the variable from other modules.
The answer to problems created by using global variables is not to use global variables.
strict vars
This generates a compile-time error if you access a variable that was neither explicitly declared (using any of my, our, state, or use vars) nor fully qualified.
Perl "trusts" users when they use fully-qualified var names. I suspect it's to allow users to sets config variables in modules that don't use use strict;.
For example, let's look at the following snippet using Data::Dumper:
local $Data::Dumper::Useqq = 1;
print(Dumper($s));
Even long after use strict; was introduced, Data::Dumper didn't declare $Useqq. There wouldn't even have been a mechanism to do so before use strict;! So the above snippet would be using an undeclared variable. That means strict code would not have been able to use Data::Dumper in the above fashion if strict vars was enforced covered fully-qualified names.
It doesn't make sense to prevent strict code from using modules that aren't strict-safe, so strict vars doesn't cover fully-qualified names. These are rare enough and easily-identifiable enough to simply have programmers take more care when using them.
I am wondering if it is possible to make Perl me warn about using undeclared variables in other namespaces, too.
I don't know of existing solutions. It might be possible to hook into Perl to do that, but it would be very hard.
Keep in mind that Perl already warns you if you only use a package variable once, so this should help you catch typos.
It seems that our is only needed for exposing a (global) variable in a package. In other contexts, its use only helps readability but is not required.
And if the above observation is right, then by following the practice of encapsulation, it's not even needed in a package, because my would be used, and getter and setter would be provided.
Assuming my application can completely be implemented using OOD, and that within a package, data is strictly passed around using args to subroutines, would I then completely obviate the need for our?
Use our when...
You're required to use a global variable.
You want to use local (which you probably shouldn't).
In general you're correct, anything you might do as a global variable could be done with a class method accessor gaining all the advantages of encapsulation.
For example...
package Foo;
use strict;
use warnings;
our $Thing = 42;
compared to...
package Foo;
use strict;
use warnings;
sub thing { 42 }
What happens if $Foo::Thing is no longer a simple constant? What if it's something that turns out to be expensive to calculate and rarely used? By encapsulating with Foo->thing you can do the calculation only when needed.
It also allows subclasses to override class information.
package Bar;
our #ISA = qw(Foo);
sub thing { 23 }
And that brings us to when to use our: when you have to. There's a lot of Perl features and libraries that read global variables either by convention or implementation. The most common examples are #ISA for subclassing, $VERSION, and the salad of Exporter variables like #EXPORT.
There are better ways to do this, and many modules like Exporter have replacements, but many of these conventions were laid down when Perl 5 wasn't comfortable with OO.
There is one final use of our and that's to take advantage of local. It can be used to pass extra data around without changing the function signatures. The original value is automatically restored when the function exits.
our $foo;
sub something {
...do something involving $foo and set $stuff...
local $foo = $stuff;
something();
}
Yes, this is a poor example.
The circumstances where this is useful and advisable are, again, indicative of bad design. Usually it's used to pass extra data between functions without changing their signature, often as part of recursion. File::Find is littered with this technique. Run perldoc -m File::Find and poke around.
It is needed in certain circumstances, such as accessing the class variables from other locations where you don't need/can't use a getter:
package Package;
our $VERSION = '0.01';
1;
Now:
perl -wMstrict -MPackage -E 'say $Package::VERSION'
0.01
If the $VERSION variable was declared with my:
Use of uninitialized value $Package::VERSION in say at -e line 1.
That is, the variable is not visible outside of the package namespace itself, because when using my, it is lexical to the package itself, ie. it's in package scope only.
You must also use our if you are exporting variables:
package Package;
use Exporter;
our #ISA = 'Exporter';
our #EXPORT = qw($x);
our $x = 10;
1;
This will print 10:
perl -wMstrict -MPackage -E 'say $x'
...but with my, you'll get the same warning as above.
This is usex.pl:
#use strict;
require 'x.pl';
print $x;
Here is x.pl:
#use strict;
our $x = 99;
1;
It runs fine as shown. If I uncomment the line to use strict in usesx.pl, I get
Global symbol "$x" requires explicit package name
The use or not of strict in x.pl seems not to matter (unless I drop the 'our' keyword, but I'm not interested in that.)
I'm fairly new to Perl. Why does strict make $x not visible in the main script, and what is the normal solution to this?
Two reasons.
The error happens at compile-time, before require is ever executed. That's easy to fix using BEGIN.
our is lexically-scoped, and it's in a different lexical scope (file or block) than the print, so it's no longer in effect.
The whole approach is fundamentally bad. Here's one better way:
package MyConfig;
use strict;
use warnings;
use Exporter qw( import );
our #EXPORT = qw( $x );
our $x = 123;
1;
use strict;
use warnings;
use MyConfig;
print "$x\n";
Hehe, our is not easy to grok as it mixes the concepts of global and lexical scope. What it does is exempting a global variable from the strict 'vars' pragma and allowing unqualified access to it within its scope, which is the enclosing block, or the end of the current file, whatever comes first. Read the full (but brief) story in the manual, which is also accessible by saying perldoc -f our on the command line.
As for your script, you can verify the truthfulness of the words in the manual by modifying the variable accessor to use a package-qualified name:
use strict;
require 'x.pl';
print $main::x;
So, I got a PERL script I am trying to run, it start like this:
use strict;
use a;
use CGI 'param';
use URI::Escape;
use HTML::FromText 'text2html';
use XML::Simple;
use LWP::UserAgent;
use Data::Dumper;
use URI::Escape;
use DBI;
use Tie::DBI;
use Digest::MD5 'md5_hex';
use MIME::Base64;
use Encode;
my $r = shift; $r->content_type("text/html; charset=utf-8"); my $tmp = a::tmp();
When it get's to the part where content_type() function is called, it fails with this error message:
Can't call method "content_type" on an undefined value at script.pl line 18.
Any ideas? I am kinda PERL newbie.
If $r is coming from #ARGV, it won't have a content_type method.
You could potentially bless $r into some package, but that's surely not what you're intending to do, here.
I'm guessing that you want to obtain a CGI parameter, probably a POSTed upload file? So you want $r to be a CGI object, not a parameter. You'd start with
my $r = CGI->new;
But, then, I refer you to the very fine manual for CGI, http://perldoc.perl.org/CGI.html or perldoc CGI from the shell.
(To expand a bit:)
In Perl, a $scalar var holds "any one thing." Things coming in from the command-line are generally strings (maybe numbers, on a good day); that's what shift would get at the top level. (The special variable #ARGV contains command-line parameters passed in to your program.)
"One thing" can also be a reference to an object. In Perl's Object Oriented model, the methods of a package ("class") are tied to that reference using bless. That's usually handled for you, though; the special subroutine (aka function, method) CGI::new will create a new CGI object with some state data (things like form fields' values), and bless it into the CGI package.
The -> notation going to a function call will only work if your variable contains a blessed reference. You can "ask" what kind of a reference you have in a variable using ref; you'll get the name of its package (aka class). ($foo = []; bless $foo => 'Some::Package'; print ref $foo; => Some::Package)
— But, again, for your specific case, check out some of the examples in the CGI module's manual :-)
Perldoc says:
If ARRAY is omitted, shifts the #_ array within the lexical scope of
subroutines and formats, and the #ARGV array outside a subroutine (...)
This is second case (#ARGV) which seems to empty. Try dumping #ARGV to check its contents.
I have a question relating to Perl and scoping. I have a common file with lots of various variables. I require the common file in my main script, but I cannot access the variables; they seem to be outside of its scope. I assumed that an our declaration would overcome that problem, but it doesn't seem to work.
Script 1: common.pl
#!/usr/bin/perl
our $var1 = "something";
our $var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
require('common.pl');
print $var1;
I get the error: Global symbol "$var1" requires explicit package name
There's no require statement in your second example, but it wouldn't work anyway. What our does is declare a lexically-scoped package variable. Since you have no package statement, it uses the default package main. So your first script sets up the variable $main::var1, but this will only be available within that file's scope.
A better way to provide common variables for other scripts is to use Exporter. You can define package symbols in one place and Exporter will take care of copying them to the requesting script or class's namespace when needed.
I would put the config in a module instead.
File: MyConfig.pm
package MyConfig;
require Exporter;
use strict;
our #ISA = qw(Exporter);
our #EXPORT = qw( getconfig );
my %confighash = (
thisone => 'one',
thatone => 2,
somthingelse => 'froboz',
);
sub getconfig {
return %confighash;
}
1;
Example usage:
#!/usr/bin/perl
use strict;
use warnings;
use MyConfig;
my %config = getconfig();
print $config{ somthingelse };
This should print froboz
It looks like you need a proper configuration file there. I'd go for a non-code configuration file that you can read when you need to setup things. There are modules on CPAN to handle just about any configuration format you can imagine.
If you want to do it the way you have it, get rid of our and declare them with use vars. Don't let the PBP police scare you off that. :) You only really need our to limit a scope of a package variable, and that's exactly the opposite of what you are trying to do.
our() does something a little different than you think. Its sole purpose is to work with strict in requiring you to declare package variables that you are going to use (unless they are fully-qualified or imported). Like strict, its effect is lexically-scoped. To use it to allow accessing a global $main:var1 from multiple files (which are separate scopes) as just $var1, you need to say our $var1 in each file.
Alternatively, you would change your required file to be a module with its own package that exports the variables to any package that uses it.
Try this. I am new at Perl but this is how I got it to work on a script I made
#!/usr/bin/perl
$var1 = "something";
$var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
our $var1;
our $var2;
require('common.pl');
print $var1;