The real fix for RT#73402 (abaondon tie %^H entirely), FC++
[p5sagit/namespace-clean.git] / lib / namespace / clean.pm
CommitLineData
40aef9d6 1package namespace::clean;
40aef9d6 2
3use warnings;
4use strict;
5
8177960a 6use vars qw( $STORAGE_VAR );
9887772b 7use Package::Stash;
40aef9d6 8
d87b7435 9our $VERSION = '0.21_01';
fa84e425 10
8177960a 11$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
40aef9d6 12
b2e54862 13# FIXME - all of this buggery will migrate to B::H::EOS soon
9887772b 14BEGIN {
1cd61d2e 15 # when changing also change in Makefile.PL
16 my $b_h_eos_req = '0.07';
17
9bb3eaa2 18 if (! $ENV{NAMESPACE_CLEAN_USE_PP} and eval {
9887772b 19 require B::Hooks::EndOfScope;
1cd61d2e 20 B::Hooks::EndOfScope->VERSION($b_h_eos_req);
9887772b 21 1
22 } ) {
23 B::Hooks::EndOfScope->import('on_scope_end');
24 }
b2e54862 25 elsif ($] < 5.009_003_9) {
26 require namespace::clean::_PP_OSE_5_8;
27 *on_scope_end = \&namespace::clean::_PP_OSE_5_8::on_scope_end;
9887772b 28 }
b2e54862 29 else {
30 require namespace::clean::_PP_OSE;
31 *on_scope_end = \&namespace::clean::_PP_OSE::on_scope_end;
9887772b 32 }
33}
34
09e4e594 35=head1 NAME
36
37namespace::clean - Keep imports and functions out of your namespace
38
40aef9d6 39=head1 SYNOPSIS
40
41 package Foo;
42 use warnings;
43 use strict;
44
6c0ece9b 45 use Carp qw(croak); # 'croak' will be removed
40aef9d6 46
6c0ece9b 47 sub bar { 23 } # 'bar' will be removed
40aef9d6 48
6c0ece9b 49 # remove all previously defined functions
40aef9d6 50 use namespace::clean;
51
6c0ece9b 52 sub baz { bar() } # 'baz' still defined, 'bar' still bound
40aef9d6 53
6c0ece9b 54 # begin to collection function names from here again
9b680ffe 55 no namespace::clean;
56
6c0ece9b 57 sub quux { baz() } # 'quux' will be removed
9b680ffe 58
6c0ece9b 59 # remove all functions defined after the 'no' unimport
9b680ffe 60 use namespace::clean;
61
6c0ece9b 62 # Will print: 'No', 'No', 'Yes' and 'No'
40aef9d6 63 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
64 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
65 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
9b680ffe 66 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
40aef9d6 67
68 1;
69
70=head1 DESCRIPTION
71
de673bbf 72=head2 Keeping packages clean
73
40aef9d6 74When you define a function, or import one, into a Perl package, it will
75naturally also be available as a method. This does not per se cause
76problems, but it can complicate subclassing and, for example, plugin
04312494 77classes that are included via multiple inheritance by loading them as
6c0ece9b 78base classes.
40aef9d6 79
80The C<namespace::clean> pragma will remove all previously declared or
81imported symbols at the end of the current package's compile cycle.
6c0ece9b 82Functions called in the package itself will still be bound by their
83name, but they won't show up as methods on your class or instances.
84
85By unimporting via C<no> you can tell C<namespace::clean> to start
86collecting functions for the next C<use namespace::clean;> specification.
40aef9d6 87
53e92ec5 88You can use the C<-except> flag to tell C<namespace::clean> that you
472d4b1e 89don't want it to remove a certain function or method. A common use would
90be a module exporting an C<import> method along with some functions:
53e92ec5 91
92 use ModuleExportingImport;
93 use namespace::clean -except => [qw( import )];
94
472d4b1e 95If you just want to C<-except> a single sub, you can pass it directly.
96For more than one value you have to use an array reference.
97
271df965 98=head2 Explicitly removing functions when your scope is compiled
de673bbf 99
271df965 100It is also possible to explicitly tell C<namespace::clean> what packages
de673bbf 101to remove when the surrounding scope has finished compiling. Here is an
102example:
103
104 package Foo;
105 use strict;
106
107 # blessed NOT available
108
109 sub my_class {
110 use Scalar::Util qw( blessed );
111 use namespace::clean qw( blessed );
112
113 # blessed available
114 return blessed shift;
115 }
116
117 # blessed NOT available
118
1a1be5dc 119=head2 Moose
120
121When using C<namespace::clean> together with L<Moose> you want to keep
122the installed C<meta> method. So your classes should look like:
123
124 package Foo;
125 use Moose;
126 use namespace::clean -except => 'meta';
127 ...
128
129Same goes for L<Moose::Role>.
130
fcfe7810 131=head2 Cleaning other packages
132
133You can tell C<namespace::clean> that you want to clean up another package
134instead of the one importing. To do this you have to pass in the C<-cleanee>
135option like this:
136
137 package My::MooseX::namespace::clean;
138 use strict;
139
140 use namespace::clean (); # no cleanup, just load
141
142 sub import {
143 namespace::clean->import(
144 -cleanee => scalar(caller),
145 -except => 'meta',
146 );
147 }
148
149If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
150just want to remove subroutines, try L</clean_subroutines>.
151
fa84e425 152=head1 METHODS
153
154=head2 clean_subroutines
40aef9d6 155
fcfe7810 156This exposes the actual subroutine-removal logic.
157
158 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
159
160will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
161subroutines B<immediately> and not wait for scope end. If you want to have this
162effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
163it is your responsibility to make sure it runs at that time.
40aef9d6 164
165=cut
166
80e4e267 167# Constant to optimise away the unused code branches
64c1bcfc 168use constant FIXUP_NEEDED => $] < 5.015_005_1;
169use constant FIXUP_RENAME_SUB => $] > 5.008_008_9 && $] < 5.013_006_1;
170{
171 no strict;
172 delete ${__PACKAGE__."::"}{FIXUP_NEEDED};
173 delete ${__PACKAGE__."::"}{FIXUP_RENAME_SUB};
174}
80e4e267 175
64c1bcfc 176# Debugger fixup necessary before perl 5.15.5
177#
80e4e267 178# In perl 5.8.9-5.12, it assumes that sub_fullname($sub) can
179# always be used to find the CV again.
180# In perl 5.8.8 and 5.14, it assumes that the name of the glob
181# passed to entersub can be used to find the CV.
182# since we are deleting the glob where the subroutine was originally
183# defined, those assumptions no longer hold.
184#
185# So in 5.8.9-5.12 we need to move it elsewhere and point the
186# CV's name to the new glob.
187#
188# In 5.8.8 and 5.14 we move it elsewhere and rename the
189# original glob by assigning the new glob back to it.
017bd598 190my $sub_utils_loaded;
80e4e267 191my $DebuggerFixup = sub {
017bd598 192 my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
193
64c1bcfc 194 if (FIXUP_RENAME_SUB) {
80e4e267 195 if (! defined $sub_utils_loaded ) {
196 $sub_utils_loaded = do {
b3279c1c 197
198 # when changing version also change in Makefile.PL
80e4e267 199 my $sn_ver = 0.04;
200 eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
201 or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
017bd598 202
b3279c1c 203 # when changing version also change in Makefile.PL
80e4e267 204 my $si_ver = 0.04;
205 eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
206 or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
fcfe7810 207
80e4e267 208 1;
209 } ? 1 : 0;
210 }
017bd598 211
80e4e267 212 if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
213 my $new_fq = $deleted_stash->name . "::$f";
214 Sub::Name::subname($new_fq, $sub);
215 $deleted_stash->add_symbol("&$f", $sub);
216 }
217 }
218 else {
017bd598 219 $deleted_stash->add_symbol("&$f", $sub);
220 }
221};
222
223my $RemoveSubs = sub {
de673bbf 224 my $cleanee = shift;
225 my $store = shift;
16d8eca5 226 my $cleanee_stash = Package::Stash->new($cleanee);
017bd598 227 my $deleted_stash;
228
de673bbf 229 SYMBOL:
230 for my $f (@_) {
017bd598 231
de673bbf 232 # ignore already removed symbols
233 next SYMBOL if $store->{exclude}{ $f };
de673bbf 234
017bd598 235 my $sub = $cleanee_stash->get_symbol("&$f")
236 or next SYMBOL;
226432f6 237
80e4e267 238 my $need_debugger_fixup =
64c1bcfc 239 FIXUP_NEEDED
240 &&
80e4e267 241 $^P
242 &&
243 ref(my $globref = \$cleanee_stash->namespace->{$f}) eq 'GLOB'
244 ;
245
64c1bcfc 246 if (FIXUP_NEEDED && $need_debugger_fixup) {
80e4e267 247 # convince the Perl debugger to work
248 # see the comment on top of $DebuggerFixup
249 $DebuggerFixup->(
250 $f,
251 $sub,
252 $cleanee_stash,
253 $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
254 );
de673bbf 255 }
d6aecfbf 256
d64ad6f8 257 my @symbols = map {
258 my $name = $_ . $f;
259 my $def = $cleanee_stash->get_symbol($name);
260 defined($def) ? [$name, $def] : ()
ad4b1a60 261 } '$', '@', '%', '';
d64ad6f8 262
c86d6ae2 263 $cleanee_stash->remove_glob($f);
d64ad6f8 264
80e4e267 265 # if this perl needs no renaming trick we need to
266 # rename the original glob after the fact
267 # (see commend of $DebuggerFixup
64c1bcfc 268 if (FIXUP_NEEDED && !FIXUP_RENAME_SUB && $need_debugger_fixup) {
80e4e267 269 *$globref = $deleted_stash->namespace->{$f};
270 }
271
d64ad6f8 272 $cleanee_stash->add_symbol(@$_) for @symbols;
de673bbf 273 }
274};
53e92ec5 275
fcfe7810 276sub clean_subroutines {
277 my ($nc, $cleanee, @subs) = @_;
278 $RemoveSubs->($cleanee, {}, @subs);
279}
280
fa84e425 281=head2 import
fcfe7810 282
283Makes a snapshot of the current defined functions and installs a
284L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
285
286=cut
287
de673bbf 288sub import {
289 my ($pragma, @args) = @_;
53e92ec5 290
de673bbf 291 my (%args, $is_explicit);
fcfe7810 292
293 ARG:
294 while (@args) {
295
296 if ($args[0] =~ /^\-/) {
297 my $key = shift @args;
298 my $value = shift @args;
299 $args{ $key } = $value;
300 }
301 else {
302 $is_explicit++;
303 last ARG;
304 }
9b680ffe 305 }
306
fcfe7810 307 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
de673bbf 308 if ($is_explicit) {
aa2aafae 309 on_scope_end {
de673bbf 310 $RemoveSubs->($cleanee, {}, @args);
aa2aafae 311 };
9b680ffe 312 }
de673bbf 313 else {
314
315 # calling class, all current functions and our storage
316 my $functions = $pragma->get_functions($cleanee);
317 my $store = $pragma->get_class_store($cleanee);
16d8eca5 318 my $stash = Package::Stash->new($cleanee);
de673bbf 319
320 # except parameter can be array ref or single value
321 my %except = map {( $_ => 1 )} (
322 $args{ -except }
323 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
324 : ()
325 );
326
327 # register symbols for removal, if they have a CODE entry
328 for my $f (keys %$functions) {
329 next if $except{ $f };
c86d6ae2 330 next unless $stash->has_symbol("&$f");
de673bbf 331 $store->{remove}{ $f } = 1;
332 }
333
334 # register EOF handler on first call to import
335 unless ($store->{handler_is_installed}) {
aa2aafae 336 on_scope_end {
de673bbf 337 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
aa2aafae 338 };
de673bbf 339 $store->{handler_is_installed} = 1;
340 }
341
342 return 1;
343 }
9b680ffe 344}
345
fa84e425 346=head2 unimport
9b680ffe 347
348This method will be called when you do a
349
350 no namespace::clean;
351
352It will start a new section of code that defines functions to clean up.
353
354=cut
355
356sub unimport {
fcfe7810 357 my ($pragma, %args) = @_;
9b680ffe 358
6c0ece9b 359 # the calling class, the current functions and our storage
fcfe7810 360 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
9b680ffe 361 my $functions = $pragma->get_functions($cleanee);
362 my $store = $pragma->get_class_store($cleanee);
363
6c0ece9b 364 # register all unknown previous functions as excluded
9b680ffe 365 for my $f (keys %$functions) {
366 next if $store->{remove}{ $f }
367 or $store->{exclude}{ $f };
368 $store->{exclude}{ $f } = 1;
369 }
370
371 return 1;
372}
373
fa84e425 374=head2 get_class_store
9b680ffe 375
04312494 376This returns a reference to a hash in a passed package containing
6c0ece9b 377information about function names included and excluded from removal.
9b680ffe 378
379=cut
380
381sub get_class_store {
382 my ($pragma, $class) = @_;
16d8eca5 383 my $stash = Package::Stash->new($class);
5460fcfb 384 my $var = "%$STORAGE_VAR";
c86d6ae2 385 $stash->add_symbol($var, {})
386 unless $stash->has_symbol($var);
387 return $stash->get_symbol($var);
40aef9d6 388}
389
fa84e425 390=head2 get_functions
40aef9d6 391
392Takes a class as argument and returns all currently defined functions
393in it as a hash reference with the function name as key and a typeglob
394reference to the symbol as value.
395
396=cut
397
398sub get_functions {
399 my ($pragma, $class) = @_;
400
16d8eca5 401 my $stash = Package::Stash->new($class);
40aef9d6 402 return {
c86d6ae2 403 map { $_ => $stash->get_symbol("&$_") }
404 $stash->list_all_symbols('CODE')
40aef9d6 405 };
406}
407
6c0ece9b 408=head1 IMPLEMENTATION DETAILS
409
04312494 410This module works through the effect that a
6c0ece9b 411
412 delete $SomePackage::{foo};
413
414will remove the C<foo> symbol from C<$SomePackage> for run time lookups
415(e.g., method calls) but will leave the entry alive to be called by
472d4b1e 416already resolved names in the package itself. C<namespace::clean> will
417restore and therefor in effect keep all glob slots that aren't C<CODE>.
6c0ece9b 418
419A test file has been added to the perl core to ensure that this behaviour
420will be stable in future releases.
421
422Just for completeness sake, if you want to remove the symbol completely,
423use C<undef> instead.
424
40aef9d6 425=head1 SEE ALSO
426
705fe1b1 427L<B::Hooks::EndOfScope>
40aef9d6 428
04312494 429=head1 THANKS
40aef9d6 430
04312494 431Many thanks to Matt S Trout for the inspiration on the whole idea.
40aef9d6 432
fa84e425 433=head1 AUTHORS
434
435=over
436
437=item *
438
439Robert 'phaylon' Sedlacek <rs@474.at>
440
441=item *
442
443Florian Ragwitz <rafl@debian.org>
444
445=item *
446
447Jesse Luehrs <doy@tozt.net>
448
449=item *
450
451Peter Rabbitson <ribasushi@cpan.org>
452
b2e54862 453=item *
454
455Father Chrysostomos <sprout@cpan.org>
456
fa84e425 457=back
458
459=head1 COPYRIGHT AND LICENSE
460
b2e54862 461This software is copyright (c) 2011 by L</AUTHORS>
fa84e425 462
463This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
464
40aef9d6 465=cut
466
de673bbf 467no warnings;
468'Danger! Laws of Thermodynamics may not apply.'