NAME
Symbol::Table - An easy interface to symbol tables (no eval(), no *typeglobs )
SYNOPSIS
use Symbol::Table;
# constructor takes two arguments,
# which TYPE of symbols (PACKAGE,CODE,SCALAR,ARRAY,HASH)
# and what package namespace do you wish to examine
# the return value is a symbol table object.
my $st_pkg = Symbol::Table->New('PACKAGE', 'main');
# the keys to a PACKAGE type symbol table are all the
# sub packages under the objects namespace.
# For all other types, the keys are the names of the
# symbols (of that TYPE) in the objects namespace.
foreach my $subpkg (keys(%$st_pkg))
{
print "package main contains package '$subpkg'\n";
}
ABSTRACT
Symbol::Table allows the user to manipulate Perl's symbol table while hiding all those nasty eval's and *typeglobs from the user. Symbol::Table gives the user an object oriented interface to perl's actual symbol table. The constructor returns a reference to a tied hash as a Symbol::Table object. The object acts like a reference to a hash: the keys are the name of the symbols in the symbol table, and the values are references to the symbol itself. The tied bit of magic allows changes in the actual symbol table to be reflected as changes in the tied hash. Tieing also allows assignments to the hash to translate into assignments into perl's actual symbol table.
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 Symbol::Table constructor is a method called New. It takes up to two parameters and returns a Symbol::Table object.
my $symbol_table = Symbol::Table->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 Symbol::Table 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 = Symbol::Table->New;
# TYPE = SCALAR, PACKAGENAME = 'main::SomePackage'
my $my_scalar_st = Symbol::Table->New('SCALAR');
# TYPE = HASH, PACKAGENAME = 'main'
my $main_hash_st = Symbol::Table->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 Symbol::Table 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 Symbol::Table;
my $st = Symbol::Table->New;
foreach my $subpkg (keys(%$st))
{
print "MyPackage contains package '$subpkg'\n";
}
Hash Keys when TYPE is not PACKAGE
A Symbol::Table 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 Symbol::Table;
our $our_scalar=0; $our_scalar++;
my $st = Symbol::Table->New('SCALAR');
foreach my $scalar (keys(%$st))
{
print "MyPackage contains scalar '$scalar'\n";
}
Hash Values when TYPE is PACKGAGE
A Symbol::Table of TYPE PACKAGE contains values that are Symbol::Table objects for the package specified by the key. The key is a package name contained under the current namespace. The value is a Symbol::Table 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 Symbol::Table;
my $st = Symbol::Table->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
Symbol
Table
Tie
Hash Values when TYPE is not PACKGAGE
A Symbol::Table 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 = Symbol::Table->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 Symbol::Table;
my $st = Symbol::Table->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 Symbol::Table to export subroutines
By creating a TYPE CODE Symbol::Table 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 Symbol::Table would allow you to do it.
package DumpTheDumper;
sub import
{
use Symbol::Table;
my $caller=caller;
my $st=Symbol::Table->New('CODE', $caller);
$st->{Dumper}= sub
{return "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:
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 by default.
SEE ALSO
AUTHOR
Greg London, http://www.greglondon.com
COPYRIGHT AND LICENSE
Copyright 2003 by Greg London, All Rights Reserved
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.