NAME
SymbolTable - An easy interface to symbol tables ( no eval() and no typeglobs )
SYNOPSIS
use SymbolTable;
my $st_pkg = SymbolTable->New('PACKAGE', 'main');
foreach my $subpkg (keys(%$st_pkg))
{
print "package main contains package '$subpkg'\n";
}
DESCRIPTION
Disclaimer
This code is an "acedemic exercise" in manipulating perl's symbol table. It wasn't coded to be fast or efficient. It was simply coded to provide the functionality I wanted it to provide. If you look at the code, you'll notice numerous calls to a subroutine called "debugging", which prints out a string if $main::DEBUG is set. If your script uses the -s perl option, then you can turn on debugging by calling your script with a -DEBUG command line option. This is grossly inefficient from a speed perspective, however.
Constuctor
The SymbolTable constructor is a method called New. It takes up to two parameters and returns a SymbolTable object.
my $symbol_table = SymbolTable->New( TYPE, PACKAGENAME );
You can create symbol tables of 5 different TYPES:
PACKAGE
SCALAR
ARRAY
HASH
CODE
The symbol table created will only contain the symbols of the TYPE specified to the constructor. If no TYPE is specified, the default TYPE is PACKAGE.
The PACKAGENAME specifies the name of the package whose symbol table for which you wish to construct a SymbolTable object. The PACKAGENAME format is a standard perl package name. It is NOT the format used for perl symbol table entries. In other words, use 'main::MyPackage' and do NOT use 'main::MyPackage::'.
If no PACKAGENAME is specified, the constructor defaults to the name of the package from which the constructor was called. If you wish to override the default PACKAGENAME, then you must also specify the TYPE when calling the constructor.
package SomePackage
# TYPE = PACKAGE, PACKAGENAME = 'main::SomePackage'
my $my_pkg_st = SymbolTable->New;
# TYPE = SCALAR, PACKAGENAME = 'main::SomePackage'
my $my_scalar_st = SymbolTable->New('SCALAR');
# TYPE = HASH, PACKAGENAME = 'main'
my $main_hash_st = SymbolTable->New('HASH', 'main');
Hash Keys
The constructor returns a reference to a hash. The keys of the hash are the names of the symbols of the TYPE in the symbol table of the PACKAGENAME specified to the constructor.
Hash Keys when TYPE is PACKAGE
A SymbolTable of TYPE PACKAGE, PACKAGENAME 'main::MyPackage' will contain keys that are all the packages under package 'main::MyPackage'. If there is a package called MyPackage::SubPackage, then one of the keys in the hash will be 'SubPackage'.
# print all the packages contained "under" a package namespace
package MyPackage::SubPackageOne;
package MyPackage::SubPackageTwo;
package MyPackage;
use SymbolTable;
my $st = SymbolTable->New;
foreach my $subpkg (keys(%$st))
{
print "MyPackage contains package '$subpkg'\n";
}
Hash Keys when TYPE is not PACKAGE
A SymbolTable of any other TYPE (SCALAR ARRAY HASH CODE) will contain keys that name all the symbols of that TYPE in PACKAGENAME.
# print the names of all scalars in the current package
package MyPackage;
use SymbolTable;
our $our_scalar=0; $our_scalar++;
my $st = SymbolTable->New('SCALAR');
foreach my $scalar (keys(%$st))
{
print "MyPackage contains scalar '$scalar'\n";
}
Hash Values when TYPE is PACKGAGE
A SymbolTable of TYPE PACKAGE contains values that are SymbolTable objects for the package specified by the key. The key is a package name contained under the current namespace. The value is a SymbolTable object of TYPE PACKAGE for that package.
This bit of code prints out all the package names spaces from 'main' down:
# print a representation of all package names current used.
use SymbolTable;
my $st = SymbolTable->New('PACKAGE', 'main');
sub ShowPackages
{
my ($symbol_table, $indent)=@_;
while( my($subpkgname, $subpkgsymtab)= each(%$symbol_table))
{
print $indent.$subpkgname."\n";
ShowPackages($subpkgsymtab, $indent."\t");
}
}
ShowPackages($st, "\t");
When I ran the above example, it printed out the text below. Note that package Data::Dumper translates into a package 'Data' containing a package 'Dumper'. Here's my output:
attributes
DB
Data
Dumper
overload
UNIVERSAL
DynaLoader
Exporter
Heavy
warnings
IO
Handle
strict
Carp
Heavy
XSLoader
mypackage
subpackage
belowpackage
SymbolTable
Tie
Hash Values when TYPE is not PACKGAGE
A SymbolTable of any other TYPE (SCALAR ARRAY HASH CODE) will contain values that are references to the actual symbol in the symbol table.
You can print out the value of a scalar named $our_scalar contained in package 'main::OtherPackage' like this:
package OtherPackage;
our $our_scalar=13;
package MyPackage;
my $st = SymbolTable->New('SCALAR', 'main::OtherPackage');
my $val = $st->{our_scalar};
print "val is $val\n";
Continuing this example, you could then change the value of the scalar:
my $override=42;
$st->{our_scalar}=\$override;
Remember, the hash VALUE is a REFERENCE to the data, not the data itself. That's why there's a '\' in front of $override in the above example.
If you want to convert someone's package variable into a package constant, you could do this:
package OtherPackage;
our $our_scalar=13;
package MyPackage;
use SymbolTable;
my $st = SymbolTable->New('SCALAR', 'main::OtherPackage');
# using a reference to a CONSTANT.
$st->{our_scalar}=\42;
# can still be read
print "OtherPackage::our_scalar is ";
print $OtherPackage::our_scalar ."\n";
# assignment causes error:
# "Modification of a read-only value attempted"
$OtherPackage::our_scalar = 3;
Note: this is an example. I'm not recommending you do this in production code.
Using SymbolTable to export subroutines
By creating a TYPE CODE SymbolTable and assigning a code reference to a subroutine name, you can install and even override any currently existing subroutine in your own or someone else's package namespace.
Note: I'm not recommending you do it this way, I'm only showing how SymbolTable would allow you to do it.
package DumpTheDumper;
sub import
{
use SymbolTable;
my $caller=caller;
my $st=SymbolTable->New('CODE', $caller);
$st->{Dumper}= sub
{return "Sorry, Dumper cant come to the phone now.\n";};
}
1;
If you then use DumpTheDumper in another file that also happened to use Data::Dumper then you might see some interesting behaviour.
#!/usr/local/bin/perl
use Data::Dumper;
use DumpTheDumper;
my $test_var = [ qw ( alpha bravo charlie delta ) ];
print Dumper $test_var;
If you run this script, the output will look like this:
sorry, Dumper cant come to the phone now.
The above example shows how to export to other packages, but it would be just as easy to change a subroutine in your own package.
EXPORT
None
AUTHOR
Greg London
COPYRIGHT
Copyright (c) 2002, Greg London. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the terms of the Perl Artistic License (see http://www.perl.com/perl/misc/Artistic.html)
SEE ALSO
perl(1).