Almost every time when you get handover of someone else code, before you crawl thorough all its hairy functions and figure out to which database tables they are scratching, your manager comes and ask for some enhancements in it with stringent deadlines. If your functions are working like blackbox, means for particular set of inputs always returns fixed type of outputs then here is way to extend functionality of any function in perl.
I will use this kind of functionality to maintain my own cache on top of someone else code to make code run faster. All this possible because of access to symbol tables in perl. So symbol table is hash like structure which holds all the the typeglobs in that particular namespace, and typeglob is key and value pair structure with limited set of keys like SCALAR, ARRAY, HASH, CODE,GLOB,FORMAT,IO where each key have value as reference of data for particular variable type. Its very short definition of symbol table and typeglob, to understand it better ask for help from your friend called internet.
All subroutines in program are global that means you can always find their reference in symbol table that means you can not define subroutine with ‘my’ ( actually you can attach ‘my’ variable to anonymous subroutine reference , but we are not going to consider that over here ). The trick over here is about pulling out reference of original function from symbol table and wrap it under your customized function. Then update symbol table with reference of your wrapper function which internally calls original function with its magical overheads.
Take a look at following example code, in this exercise we are going to steal reference of subroutine named my_function() which prints one statement with all input arguments. On line 6 we are stealing and replacing my_function() with help of subroutine called Proxy() which is imported from package named PROXY.pm.
example.pl
#!/usr/bin/perl
#
use strict;
use PROXY;
Proxy 'my_function';
sub my_function {
my @input = @_;
print "This is original my_function and inputs are ";
print "@_\n";
}
&my_function ("one two three");
So subroutine Proxy() takes subroutine name as an input argument, it generates full name of that subroutine with namespace part attached on line 11 in following code. Once it gets to that it steals reference of that particular subroutine from symbol table on line 14 . On line 16 with help of selective aliasing it replace that subroutine reference with another reference.
Now new reference is pointing to anonymous subroutine which returns a closure having subroutine named Stub() getting feed with original function’s reference ( i.e. my_function() reference ) and its input arguments. Stub() should be designed to do whatever you want to do prior the execution of original subroutine. On line 24 it prints extra line and then calls my_function() along with its arguments.
PROXY.pm
package PROXY;
use strict;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw( Proxy );
sub Proxy {
my $function_to_steal = shift;
my $who_called = caller;
my $function_name =
$who_called . '::' . $function_to_steal;
no strict;
my $function_referance = *{$function_name}{CODE};
*{$function_name} =
sub { &Stub($function_referance,@_); };
}
sub Stub {
my $original_function = shift;
my @input = @_;
print "This is originating from stub function\n";
$original_function->( @input );
}
In the output when you called &my_function(“one two three”) in example.pl, it will following two lines instead of only one line.
Output
This is originating from stub function
This is original function and inputs are one two three
You can enhance Stub() for your own devious purpose like I mentioned to maintain local cache of some expensive database queries.
reference
Above code is very basic version of Memoize.pm by Mark-Jason Dominus