/usr/share/perl5/Hook/WrapSub.pm is in libhook-wrapsub-perl 0.03-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | package Hook::WrapSub;
use Exporter;
use Symbol;
use strict;
use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
$VERSION = '0.03';
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw(
wrap_subs
unwrap_subs
);
=head1 NAME
Hook::WrapSub - wrap subs with pre- and post-call hooks
=head1 SYNOPSIS
use Hook::WrapSub qw( wrap_subs unwrap_subs );
wrap_subs \&before, 'some_func', 'another_func', \&after;
unwrap_subs 'some_func';
=head1 DESCRIPTION
=head2 wrap_subs
This function enables intercepting a call to any named
function; handlers may be added both before and after
the call to the intercepted function.
For example:
wrap_subs \&before, 'some_func', \&after;
In this case, whenever the sub named 'some_func' is called,
the &before sub is called first, and the &after sub is called
afterwards. These are both optional. If you only want
to intercept the call beforehand:
wrap_subs \&before, 'some_func';
You may pass more than one sub name:
wrap_subs \&before, 'foo', 'bar', 'baz', \&after;
and each one will have the same hooks applied.
The sub names may be qualified. Any unqualified names
are assumed to reside in the package of the caller.
The &before sub and the &after sub are both passed the
argument list which is destined for the wrapped sub.
This can be inspected, and even altered, in the &before
sub:
sub before {
ref($_[1]) && $_[1] =~ /\bARRAY\b/
or croak "2nd arg must be an array-ref!";
@_ or @_ = qw( default values );
# if no args passed, insert some default values
}
The &after sub is also passed this list. Modifications
to it will (obviously) not be seen by the wrapped sub,
but the caller will see the changes, if it happens to
be looking.
Here's an example that causes a certain method call
to be redirected to a specific object. (Note, we
use splice to change $_[0], because assigning directly
to $_[0] would cause the change to be visible to the caller,
due to the magical aliasing nature of @_.)
my $handler_object = new MyClass;
Hook::WrapSub::wrap_subs
sub { splice @_, 0, 1, $handler_object },
'MyClass::some_method';
my $other_object = new MyClass;
$other_object->some_method;
# even though the method is invoked on
# $other_object, it will actually be executed
# with a 0'th argument = $handler_obj,
# as arranged by the pre-call hook sub.
=head2 Package Variables
There are some Hook::WrapSub package variables defined,
which the &before and &after subs may inspect.
=over 4
=item $Hook::WrapSub::name
This is the fully qualified name of the wrapped sub.
=item @Hook::WrapSub::caller
This is a list which strongly resembles the result of a
call to the built-in function C<caller>; it is provided
because calling C<caller> will in fact produce confusing
results; if your sub is inclined to call C<caller>,
have it look at this variable instead.
=item @Hook::WrapSub::result
This contains the result of the call to the wrapped sub.
It is empty in the &before sub. In the &after sub, it
will be empty if the sub was called in a void context,
it will contain one value if the sub was called in a
scalar context; otherwise, it may have any number of
elements. Note that the &after function is not prevented
from modifying the contents of this array; any such
modifications will be seen by the caller!
=back
This simple example shows how Hook::WrapSub can be
used to log certain subroutine calls:
sub before {
print STDERR <<" EOF";
About to call $Hook::WrapSub::name( @_ );
Wantarray=$Hook::WrapSub::caller[5]
EOF
}
sub after {
print STDERR <<" EOF";
Called $Hook::WrapSub::name( @_ );
Result=( @Hook::WrapSub::result )
EOF
@Hook::WrapSub::result
or @Hook::WrapSub::result = qw( default return );
# if the sub failed to return something...
}
Much more elaborate uses are possible. Here's one
one way it could be used with database operations:
my $dbh; # initialized elsewhere.
wrap_subs
sub {
$dbh->checkpoint
},
'MyDb::update',
'MyDb::delete',
sub {
# examine result of sub call:
if ( $Hook::WrapSub::result[0] ) {
# success
$dbh->commit;
}
else {
# failure
$dbh->rollback;
}
};
=head2 unwrap_subs
This removes the most recent wrapping of the named subs.
NOTE: Any given sub may be wrapped an unlimited
number of times. A "stack" of the wrappings is
maintained internally. wrap_subs "pushes" a wrapping,
and unwrap_subs "pops".
=cut
sub wrap_subs(@) {
my( $precall_cr, $postcall_cr );
ref($_[0]) and $precall_cr = shift;
ref($_[-1]) and $postcall_cr = pop;
my @names = @_;
my( $calling_package ) = caller;
for my $name ( @names ) {
my $fullname;
my $sr = *{ qualify_to_ref($name,$calling_package) }{CODE};
if ( defined $sr ) {
$fullname = qualify($name,$calling_package);
}
else {
warn "Can't find subroutine named '$name'\n";
next;
}
my $cr = sub {
$Hook::WrapSub::UNWRAP and return $sr;
#
# this is a bunch of kludg to make a list of values
# that look like a "real" caller() result.
#
my $up = 0;
my @args = caller($up);
while ( $args[0] =~ /Hook::WrapSub/ ) {
$up++;
@args = caller($up);
}
my @vargs = @args; # save temp
while ( $args[3] =~ /Hook::WrapSub/ ) {
$up++;
@args = caller($up);
}
$vargs[3] = $args[3];
# now @vargs looks right.
local $Hook::WrapSub::name = $fullname;
local @Hook::WrapSub::result = ();
local @Hook::WrapSub::caller = @vargs;
my $wantarray = $Hook::WrapSub::caller[5];
#
# try to supply the same calling context to the nested sub:
#
unless ( defined $wantarray ) {
# void context
&$precall_cr if $precall_cr;
&$sr;
&$postcall_cr if $postcall_cr;
return();
}
unless ( $wantarray ) {
# scalar context
&$precall_cr if $precall_cr;
$Hook::WrapSub::result[0] = &$sr;
&$postcall_cr if $postcall_cr;
return $Hook::WrapSub::result[0];
}
# list context
&$precall_cr if $precall_cr;
@Hook::WrapSub::result = &$sr;
&$postcall_cr if $postcall_cr;
return( @Hook::WrapSub::result );
};
$^W = 0;
no strict 'refs';
*{ $fullname } = $cr;
}
}
sub unwrap_subs(@) {
my @names = @_;
my( $calling_package ) = caller;
for my $name ( @names ) {
my $fullname;
my $sr = *{ qualify_to_ref($name,$calling_package) }{CODE};
if ( defined $sr ) {
$fullname = qualify($name,$calling_package);
}
else {
warn "Can't find subroutine named '$name'\n";
next;
}
local $Hook::WrapSub::UNWRAP = 1;
my $cr = $sr->();
if ( defined $cr and $cr =~ /\bCODE\b/ ) {
$^W = 0;
no strict 'refs';
*{ $fullname } = $cr;
}
else {
warn "Subroutine '$fullname' not wrapped!";
}
}
}
1;
=head1 AUTHOR
jdporter@min.net (John Porter)
=head1 COPYRIGHT
This is free software. This software may be modified and/or
distributed under the same terms as Perl itself.
=cut
|