File Coverage

blib/lib/Hook/WrapSub.pm
Criterion Covered Total %
statement 82 87 94.2
branch 17 28 60.7
condition 3 6 50.0
subroutine 12 12 100.0
pod 2 2 100.0
total 116 135 85.9


line stmt bran cond sub pod time code
1             package Hook::WrapSub;
2             $Hook::WrapSub::VERSION = '0.08';
3 1     1   74688 use 5.006;
  1         3  
4 1     1   7 use strict;
  1         6  
  1         21  
5 1     1   5 use warnings;
  1         2  
  1         35  
6              
7 1     1   6 use Exporter;
  1         1  
  1         37  
8 1     1   560 use Symbol;
  1         801  
  1         488  
9              
10             our @ISA = qw/ Exporter /;
11             our @EXPORT_OK = qw/ wrap_subs unwrap_subs /;
12              
13              
14             sub wrap_subs(@) {
15 2     2 1 120 my( $precall_cr, $postcall_cr );
16 2 50       10 ref($_[0]) and $precall_cr = shift;
17 2 50       7 ref($_[-1]) and $postcall_cr = pop;
18 2         5 my @names = @_;
19              
20 2         5 my( $calling_package ) = caller;
21              
22 2         7 for my $name ( @names ) {
23              
24 2         5 my $fullname;
25 2         3 my $sr = *{ qualify_to_ref($name,$calling_package) }{CODE};
  2         7  
26 2 50       59 if ( defined $sr ) {
27 2         6 $fullname = qualify($name,$calling_package);
28             }
29             else {
30 0         0 warn "Can't find subroutine named '$name'\n";
31 0         0 next;
32             }
33              
34              
35             my $cr = sub {
36 6 100   6   23 $Hook::WrapSub::UNWRAP and return $sr;
37              
38             #
39             # this is a bunch of kludg to make a list of values
40             # that look like a "real" caller() result.
41             #
42              
43 4         23 my $up = 0;
44 4         42 my @args = caller($up);
45 4         17 while ( $args[0] =~ /Hook::WrapSub/ ) {
46 1         2 $up++;
47 1         7 @args = caller($up);
48             }
49 4         10 my @vargs = @args; # save temp
50 4   66     27 while ( defined($args[3]) && $args[3] =~ /Hook::WrapSub/ ) {
51 4         6 $up++;
52 4         12 @args = caller($up);
53             }
54 4         7 $vargs[3] = $args[3];
55             # now @vargs looks right.
56              
57 4         8 local $Hook::WrapSub::name = $fullname;
58 4         9 local @Hook::WrapSub::result = ();
59 4         24 local @Hook::WrapSub::caller = @vargs;
60 4         12 my $wantarray = $Hook::WrapSub::caller[5];
61             #
62             # try to supply the same calling context to the nested sub:
63             #
64              
65 4 100       10 unless ( defined $wantarray ) {
66             # void context
67 1 50       4 &$precall_cr if $precall_cr;
68 1         10 &$sr;
69 1 50       6 &$postcall_cr if $postcall_cr;
70 1         9 return();
71             }
72              
73 3 100       7 unless ( $wantarray ) {
74             # scalar context
75 1 50       17 &$precall_cr if $precall_cr;
76 1         22 $Hook::WrapSub::result[0] = &$sr;
77 1 50       13 &$postcall_cr if $postcall_cr;
78 1         13 return $Hook::WrapSub::result[0];
79             }
80              
81             # list context
82 2 50       8 &$precall_cr if $precall_cr;
83 2         24 @Hook::WrapSub::result = &$sr;
84 2 50       11 &$postcall_cr if $postcall_cr;
85 2         23 return( @Hook::WrapSub::result );
86 2         36 };
87              
88 1     1   7 no warnings 'redefine';
  1         15  
  1         44  
89 1     1   6 no strict 'refs';
  1         2  
  1         233  
90 2         4 *{ $fullname } = $cr;
  2         11  
91             }
92             }
93              
94             sub unwrap_subs(@) {
95 2     2 1 9 my @names = @_;
96              
97 2         6 my( $calling_package ) = caller;
98              
99 2         5 for my $name ( @names ) {
100 2         3 my $fullname;
101 2         3 my $sr = *{ qualify_to_ref($name,$calling_package) }{CODE};
  2         17  
102 2 50       45 if ( defined $sr ) {
103 2         7 $fullname = qualify($name,$calling_package);
104             }
105             else {
106 0         0 warn "Can't find subroutine named '$name'\n";
107 0         0 next;
108             }
109 2         31 local $Hook::WrapSub::UNWRAP = 1;
110 2         5 my $cr = $sr->();
111 2 50 33     28 if ( defined $cr and $cr =~ /\bCODE\b/ ) {
112 1     1   7 no strict 'refs';
  1         2  
  1         42  
113 1     1   6 no warnings 'redefine';
  1         2  
  1         155  
114 2         4 *{ $fullname } = $cr;
  2         34  
115             }
116             else {
117 0           warn "Subroutine '$fullname' not wrapped!";
118             }
119             }
120             }
121              
122             1;
123              
124             =head1 NAME
125              
126             Hook::WrapSub - wrap subs with pre- and post-call hooks
127              
128             =head1 SYNOPSIS
129              
130             use Hook::WrapSub qw( wrap_subs unwrap_subs );
131              
132             wrap_subs \&before, 'some_func', 'another_func', \&after;
133              
134             unwrap_subs 'some_func';
135              
136              
137             =head1 DESCRIPTION
138              
139             This module lets you wrap a function,
140             providing one or both of functions that are called just before and just after,
141             whenever the wrapped function is called.
142              
143             There are a number of other modules that provide the same functionality
144             as this module, some of them better. Have a look at the list in SEE ALSO,
145             below, before you decide which to use.
146              
147             =head2 wrap_subs
148              
149             This function enables intercepting a call to any named
150             function; handlers may be added both before and after
151             the call to the intercepted function.
152              
153             For example:
154              
155             wrap_subs \&before, 'some_func', \&after;
156              
157             In this case, whenever the sub named 'some_func' is called,
158             the &before sub is called first, and the &after sub is called
159             afterwards. These are both optional. If you only want
160             to intercept the call beforehand:
161              
162             wrap_subs \&before, 'some_func';
163              
164             You may pass more than one sub name:
165              
166             wrap_subs \&before, 'foo', 'bar', 'baz', \&after;
167              
168             and each one will have the same hooks applied.
169              
170             The sub names may be qualified. Any unqualified names
171             are assumed to reside in the package of the caller.
172              
173             The &before sub and the &after sub are both passed the
174             argument list which is destined for the wrapped sub.
175             This can be inspected, and even altered, in the &before
176             sub:
177              
178             sub before {
179             ref($_[1]) && $_[1] =~ /\bARRAY\b/
180             or croak "2nd arg must be an array-ref!";
181             @_ or @_ = qw( default values );
182             # if no args passed, insert some default values
183             }
184              
185             The &after sub is also passed this list. Modifications
186             to it will (obviously) not be seen by the wrapped sub,
187             but the caller will see the changes, if it happens to
188             be looking.
189              
190             Here's an example that causes a certain method call
191             to be redirected to a specific object. (Note, we
192             use splice to change $_[0], because assigning directly
193             to $_[0] would cause the change to be visible to the caller,
194             due to the magical aliasing nature of @_.)
195              
196             my $handler_object = new MyClass;
197              
198             Hook::WrapSub::wrap_subs
199             sub { splice @_, 0, 1, $handler_object },
200             'MyClass::some_method';
201              
202             my $other_object = new MyClass;
203             $other_object->some_method;
204              
205             # even though the method is invoked on
206             # $other_object, it will actually be executed
207             # with a 0'th argument = $handler_obj,
208             # as arranged by the pre-call hook sub.
209              
210             =head2 Package Variables
211              
212             There are some Hook::WrapSub package variables defined,
213             which the &before and &after subs may inspect.
214              
215             =over 4
216              
217             =item $Hook::WrapSub::name
218              
219             This is the fully qualified name of the wrapped sub.
220              
221             =item @Hook::WrapSub::caller
222              
223             This is a list which strongly resembles the result of a
224             call to the built-in function C; it is provided
225             because calling C will in fact produce confusing
226             results; if your sub is inclined to call C,
227             have it look at this variable instead.
228              
229             =item @Hook::WrapSub::result
230              
231             This contains the result of the call to the wrapped sub.
232             It is empty in the &before sub. In the &after sub, it
233             will be empty if the sub was called in a void context,
234             it will contain one value if the sub was called in a
235             scalar context; otherwise, it may have any number of
236             elements. Note that the &after function is not prevented
237             from modifying the contents of this array; any such
238             modifications will be seen by the caller!
239              
240              
241             =back
242              
243             This simple example shows how Hook::WrapSub can be
244             used to log certain subroutine calls:
245              
246             sub before {
247             print STDERR <<" EOF";
248             About to call $Hook::WrapSub::name( @_ );
249             Wantarray=$Hook::WrapSub::caller[5]
250             EOF
251             }
252              
253             sub after {
254             print STDERR <<" EOF";
255             Called $Hook::WrapSub::name( @_ );
256             Result=( @Hook::WrapSub::result )
257             EOF
258             @Hook::WrapSub::result
259             or @Hook::WrapSub::result = qw( default return );
260             # if the sub failed to return something...
261             }
262              
263             Much more elaborate uses are possible. Here's one
264             one way it could be used with database operations:
265              
266             my $dbh; # initialized elsewhere.
267              
268             wrap_subs
269             sub {
270             $dbh->checkpoint
271             },
272              
273             'MyDb::update',
274             'MyDb::delete',
275              
276             sub {
277             # examine result of sub call:
278             if ( $Hook::WrapSub::result[0] ) {
279             # success
280             $dbh->commit;
281             }
282             else {
283             # failure
284             $dbh->rollback;
285             }
286             };
287              
288             =head2 unwrap_subs
289              
290             This removes the most recent wrapping of the named subs.
291              
292             NOTE: Any given sub may be wrapped an unlimited
293             number of times. A "stack" of the wrappings is
294             maintained internally. wrap_subs "pushes" a wrapping,
295             and unwrap_subs "pops".
296              
297              
298             =head1 SEE ALSO
299              
300             L provides a similar capability to C,
301             but has the benefit that the C function works correctly
302             within the wrapped subroutine.
303              
304             L lets you provide a sub that will be called before
305             a named sub. The C function works correctly in the
306             wrapped sub.
307              
308             L provides a number of related functions.
309             You can provide pre- and post-call hooks,
310             you can temporarily override a function and then restore it later,
311             and more.
312              
313             L lets you add pre- and post-call hooks around any
314             methods called by your code. It doesn't support functions.
315              
316             L lets you register callbacks that will be invoked
317             when execution leaves the scope they were registered in.
318              
319             L provides an OO interface for wrapping
320             a function with pre- and post-call hook functions.
321             Last updated in 1997, and marked as alpha.
322              
323             L provides an OO interface for wrapping pre- and post-call
324             hooks around functions or methods in a package. Not updated sinc 2003,
325             and has a 20% failed rate on CPAN Testers.
326              
327             L describes L's mechanism
328             for hooking a superclass's method.
329             The I and I subs are called immediately before or
330             after the specified methods are called.
331             The I sub wraps the superclass method,
332             and can even decide not to invoke the superclass method.
333              
334             L provides a L-style mechanism
335             for a subclass to have I, I, or I
336             method modifiers.
337              
338             L provides the C function, which takes a coderef
339             and a package name. The coderef is invoked every time a method in
340             the package is called.
341              
342             L lets you stack pre- and post-call hooks.
343             Last updated in 2001.
344              
345             =head1 REPOSITORY
346              
347             L
348              
349             =head1 AUTHOR
350              
351             This module was written by John Porter Ejdporter@min.netE
352              
353             It is now being maintained by Neil Bowers.
354              
355             =head1 COPYRIGHT
356              
357             This is free software. This software may be modified and/or
358             distributed under the same terms as Perl itself.
359              
360             =cut
361