Perl has a number of features that permit introspection, chief among them the ability to get information about the contents of the symbol table. This property is sometimes called reflection or introspection.
Reflection makes it easy to write system-level tools such as debuggers and profilers. We will also use this property in Chapter 11, Implementing Object Persistence, to develop a module that can transparently dump an object's data to a file or a database (and subsequently restore it) without having to write any application-specific code.
We saw earlier in this chapter that each package gets its own symbol table (also called stash, short for "symbol table hash"). Perl makes these stashes available as regular associative arrays. The stash for a package named Foo can be accessed by using the hash called %Foo::. The main package is available as %main::, or simply as %::. In fact, all other packages' hash tables are available from the main stash (%main:: hence points to itself), as illustrated in Figure 6.1.
Iterating through the all the symbolic names inside a package is simple:
foreach $name (keys %main::) { print "$name, \n"; }
As we saw earlier, each of these symbolic names maps to a typeglob, which itself points to one or more values (one or more of each type: scalar, array, hash, subroutine, filehandle, format name, or directory handle). Unfortunately, there's no direct way to find out which values actually exist. Example 6.2 shows a way to dump all the variables in a given package and also demonstrates a way to find out which values exist for a given typeglob.
package DUMPVAR; sub dumpvar { my ($packageName) = @_; local (*alias); # a local typeglob # We want to get access to the stash corresponding to the package # name *stash = *{"${packageName}::"}; # Now %stash is the symbol table $, = " "; # Output separator for print # Iterate through the symbol table, which contains glob values # indexed by symbol names. while (($varName, $globValue) = each %stash) { print "$varName ============================= \n"; *alias = $globValue; if (defined ($alias)) { print "\t \$$varName $alias \n"; } if (defined (@alias)) { print "\t \@$varName @alias \n"; } if (defined (%alias)) { print "\t \%$varName ",%alias," \n"; } } }
This snippet of a code illustrates how to use DUMPVAR:
package XX; $x = 10; @y = (1,3,4); %z = (1,2,3,4, 5, 6); $z = 300; DUMPVAR::dumpvar("XX");
This prints:
x ============================= $x 10 y ============================= @y 1 3 4 z ============================= $z 300 %z 1 2 3 4 5 6
dumpvar() works by creating an alias to each typeglob successively and then enumerating each type to see whether that value is defined. It is important to realize that it merely dumps the global data at the topmost level, because anonymous data structures hanging off various references are not dumped at all.