File Coverage

blib/lib/Hook/Filter/Hooker.pm
Criterion Covered Total %
statement 66 66 100.0
branch 12 12 100.0
condition 7 7 100.0
subroutine 18 18 100.0
pod 7 7 100.0
total 110 110 100.0


line stmt bran cond sub pod time code
1             #################################################################
2             #
3             # Hook::Filter::Hooker - Wrap subroutines in a firewalling closure
4             #
5             # $Id: Hooker.pm,v 1.8 2007/05/24 14:58:09 erwan_lemonnier Exp $
6             #
7             # 060302 erwan Created
8             # 070516 erwan Use the rule pool
9             #
10              
11             package Hook::Filter::Hooker;
12              
13 12     12   231683 use strict;
  12         27  
  12         459  
14 12     12   92 use warnings;
  12         22  
  12         406  
15 12     12   88 use Carp qw(croak);
  12         22  
  12         939  
16 12     12   1429 use Data::Dumper;
  12         11787  
  12         806  
17 12     12   3772 use Symbol;
  12         4549  
  12         927  
18 12     12   72 use base qw(Exporter);
  12         34  
  12         1419  
19 12     12   9470 use Hook::Filter::RulePool qw(get_rule_pool);
  12         33  
  12         1294  
20              
21             our @EXPORT = qw();
22             our @EXPORT_OK = qw( get_caller_package
23             get_caller_file
24             get_caller_line
25             get_caller_subname
26             get_subname
27             get_arguments
28             filter_sub
29             );
30              
31              
32              
33 12         8728 use vars qw( $CALLER_PACKAGE
34             $CALLER_FILE
35             $CALLER_LINE
36             $CALLER_SUBNAME
37             $SUBNAME
38 12     12   83 @ARGUMENTS );
  12         21  
39              
40             # singleton instance of Hook::Filter::RulePool
41             my $pool = get_rule_pool();
42              
43             # a hash whose keys are the fully qualified names of all filtered
44             # subroutines, to avoid filtering one twice
45             my %subs;
46              
47             #----------------------------------------------------------------
48             #
49             # accessors for use in Hook::Filter::Plugins:: modules
50             #
51              
52 2     2 1 14 sub get_caller_package { return $CALLER_PACKAGE; };
53 2     2 1 8 sub get_caller_file { return $CALLER_FILE; };
54 2     2 1 12 sub get_caller_line { return $CALLER_LINE; };
55 14     14 1 125 sub get_caller_subname { return $CALLER_SUBNAME; };
56 42     42 1 402 sub get_subname { return $SUBNAME; };
57 7     7 1 25 sub get_arguments { return @ARGUMENTS; };
58              
59             #----------------------------------------------------------------
60             #
61             # filter_sub - build a filter closure wrapping calls to the provided sub
62             #
63              
64             sub filter_sub {
65 45     45 1 4570 my $subname = shift;
66              
67 45 100 100     375 if (!defined $subname || ref \$subname ne "SCALAR" || scalar @_) {
      100        
68 4         7 shift @_;
69 4         18 croak "invalid parameter: Hook::Filter::Hooker->filter_sub expects a subroutine name, but got [".Dumper($subname,@_)."].";
70             }
71              
72 41 100       265 if ($subname !~ /^(.+)::([^:]+)$/) {
73 1         134 croak "invalid parameter: [$subname] is not a valid subroutine name (must include package name).";
74             }
75              
76 40         132 my ($pkg,$func) = ($1,$2);
77              
78             # check whether subroutine is already filtered, and skip if so
79 40 100       127 return if (exists $subs{$subname});
80              
81 38         101 my $filtered_func = *{ qualify_to_ref($func,$pkg) }{CODE};
  38         136  
82              
83             # create the closure that will replace $func in package $pkg
84             my $filter = sub {
85 52     52   6341 my (@args) = @_;
86              
87             # TODO: looking at source for Hook::WrapSub, it might be a good idea to copy/paste some of its code here, to build a valid caller stack
88             # TODO: look at Hook::LexWrap and fix so that caller() work in subroutines
89              
90             # set global variables
91 52         346 $CALLER_PACKAGE = (caller(0))[0];
92 52         259 $CALLER_FILE = (caller(0))[1];
93 52         248 $CALLER_LINE = (caller(0))[2];
94 52   100     303 $CALLER_SUBNAME = (caller(1))[3] || "";
95 52         92 $SUBNAME = $subname;
96 52         105 @ARGUMENTS = @args;
97              
98             # evaluate all rules. if true is returned, forward the call
99 52 100       179 if ($pool->eval_rules) {
100 31 100       74 if (wantarray) {
101 3         11 my @results = $filtered_func->(@args);
102 3         26 return @results;
103             } else {
104 28         92 my $result = $filtered_func->(@args);
105 28         234 return $result;
106             }
107             }
108              
109             # the call was blocked. fake a return value (ugly.)
110 21 100       59 if (wantarray) {
111 1         5 return ();
112             }
113 20         97 return;
114 38         1527 };
115              
116             # keep track of already hooked subroutines
117 38         111 $subs{$subname} = 1;
118              
119             # replace $package::$func with our closure
120 12     12   78 no strict 'refs';
  12         21  
  12         380  
121 12     12   64 no warnings;
  12         22  
  12         1036  
122              
123 38         53 *{ qualify_to_ref($func,$pkg) } = $filter;
  38         100  
124             }
125              
126             1;
127              
128             __END__