File Coverage

blib/lib/Class/Inspector.pm
Criterion Covered Total %
statement 173 196 88.2
branch 86 108 79.6
condition 8 16 50.0
subroutine 23 25 92.0
pod 10 12 83.3
total 300 357 84.0


line stmt bran cond sub pod time code
1             package Class::Inspector;
2              
3 5     5   218022 use 5.006;
  5         44  
4             # We don't want to use strict refs anywhere in this module, since we do a
5             # lot of things in here that aren't strict refs friendly.
6 5     5   30 use strict qw{vars subs};
  5         11  
  5         188  
7 5     5   75 use warnings;
  5         13  
  5         188  
8 5     5   30 use File::Spec ();
  5         8  
  5         379  
9              
10             # ABSTRACT: Get information about a class and its structure
11             our $VERSION = '1.36'; # VERSION
12              
13              
14             # If Unicode is available, enable it so that the
15             # pattern matches below match unicode method names.
16             # We can safely ignore any failure here.
17             BEGIN {
18 5     5   17 local $@;
19 5         11 eval {
20 5         3160 require utf8;
21 5         97 utf8->import;
22             };
23             }
24              
25             # Predefine some regexs
26             our $RE_IDENTIFIER = qr/\A[^\W\d]\w*\z/s;
27             our $RE_CLASS = qr/\A[^\W\d]\w*(?:(?:\'|::)\w+)*\z/s;
28              
29             # Are we on something Unix-like?
30             our $UNIX = !! ( $File::Spec::ISA[0] eq 'File::Spec::Unix' );
31              
32              
33             #####################################################################
34             # Basic Methods
35              
36              
37             sub _resolved_inc_handler {
38 5     5   11 my $class = shift;
39 5 50       15 my $filename = $class->_inc_filename(shift) or return undef;
40              
41 5         15 foreach my $inc ( @INC ) {
42 31         47 my $ref = ref $inc;
43 31 100 66     123 if($ref eq 'CODE') {
    100 66        
    100          
44 2         6 my @ret = $inc->($inc, $filename);
45 2 50 33     97 if(@ret == 1 && ! defined $ret[0]) {
    100          
46             # do nothing.
47             } elsif(@ret) {
48 1         32 return 1;
49             }
50             }
51             elsif($ref eq 'ARRAY' && ref($inc->[0]) eq 'CODE') {
52 3         10 my @ret = $inc->[0]->($inc, $filename);
53 3 100       73 if(@ret) {
54 1         19 return 1;
55             }
56             }
57 4         38 elsif($ref && eval { $inc->can('INC') }) {
58 4         15 my @ret = $inc->INC($filename);
59 4 100       81 if(@ret) {
60 1         19 return 1;
61             }
62             }
63             }
64              
65 2         17 '';
66             }
67              
68             sub installed {
69 7     7 1 16 my $class = shift;
70 7   100     31 !! ($class->loaded_filename($_[0]) or $class->resolved_filename($_[0]) or $class->_resolved_inc_handler($_[0]));
71             }
72              
73              
74             sub loaded {
75 31     31 1 892 my $class = shift;
76 31 50       76 my $name = $class->_class(shift) or return undef;
77 31         80 $class->_loaded($name);
78             }
79              
80             sub _loaded {
81 999     999   1376 my $class = shift;
82 999         1311 my $name = shift;
83              
84             # Handle by far the two most common cases
85             # This is very fast and handles 99% of cases.
86 999 100       1254 return 1 if defined ${"${name}::VERSION"};
  999         5142  
87 664 100       925 return 1 if @{"${name}::ISA"};
  664         2325  
88              
89             # Are there any symbol table entries other than other namespaces
90 442         595 foreach ( keys %{"${name}::"} ) {
  442         1506  
91 963 100       1815 next if substr($_, -2, 2) eq '::';
92 708 100       837 return 1 if defined &{"${name}::$_"};
  708         2287  
93             }
94              
95             # No functions, and it doesn't have a version, and isn't anything.
96             # As an absolute last resort, check for an entry in %INC
97 302         675 my $filename = $class->_inc_filename($name);
98 302 50       890 return 1 if defined $INC{$filename};
99              
100 302         681 '';
101             }
102              
103              
104             sub filename {
105 2     2 1 8 my $class = shift;
106 2 50       9 my $name = $class->_class(shift) or return undef;
107 2         92 File::Spec->catfile( split /(?:\'|::)/, $name ) . '.pm';
108             }
109              
110              
111             sub resolved_filename {
112 7     7 1 16 my $class = shift;
113 7 50       16 my $filename = $class->_inc_filename(shift) or return undef;
114 7         16 my @try_first = @_;
115              
116             # Look through the @INC path to find the file
117 7         24 foreach ( @try_first, @INC ) {
118 71         203 my $full = "$_/$filename";
119 71 100       928 next unless -e $full;
120 2 50       24 return $UNIX ? $full : $class->_inc_to_local($full);
121             }
122              
123             # File not found
124 5         44 '';
125             }
126              
127              
128             sub loaded_filename {
129 9     9 1 300 my $class = shift;
130 9         29 my $filename = $class->_inc_filename(shift);
131 9 50       69 $UNIX ? $INC{$filename} : $class->_inc_to_local($INC{$filename});
132             }
133              
134              
135              
136              
137              
138             #####################################################################
139             # Sub Related Methods
140              
141              
142             sub functions {
143 3     3 1 629 my $class = shift;
144 3 50       18 my $name = $class->_class(shift) or return undef;
145 3 100       11 return undef unless $class->loaded( $name );
146              
147             # Get all the CODE symbol table entries
148 18         80 my @functions = sort grep { /$RE_IDENTIFIER/o }
149 27         33 grep { defined &{"${name}::$_"} }
  27         78  
150 1         4 keys %{"${name}::"};
  1         15  
151 1         7 \@functions;
152             }
153              
154              
155             sub function_refs {
156 1     1 1 3 my $class = shift;
157 1 50       5 my $name = $class->_class(shift) or return undef;
158 1 50       13 return undef unless $class->loaded( $name );
159              
160             # Get all the CODE symbol table entries, but return
161             # the actual CODE refs this time.
162 18         23 my @functions = map { \&{"${name}::$_"} }
  18         46  
163 18         53 sort grep { /$RE_IDENTIFIER/o }
164 27         33 grep { defined &{"${name}::$_"} }
  27         69  
165 1         3 keys %{"${name}::"};
  1         11  
166 1         8 \@functions;
167             }
168              
169              
170             sub function_exists {
171 4     4 1 9 my $class = shift;
172 4 50       12 my $name = $class->_class( shift ) or return undef;
173 4 100       14 my $function = shift or return undef;
174              
175             # Only works if the class is loaded
176 3 100       8 return undef unless $class->loaded( $name );
177              
178             # Does the GLOB exist and its CODE part exist
179 2         5 defined &{"${name}::$function"};
  2         16  
180             }
181              
182              
183             sub methods {
184 23     23 1 5570 my $class = shift;
185 23 50       59 my $name = $class->_class( shift ) or return undef;
186 23         57 my @arguments = map { lc $_ } @_;
  20         63  
187              
188             # Process the arguments to determine the options
189 23         47 my %options = ();
190 23         45 foreach ( @arguments ) {
191 20 100       62 if ( $_ eq 'public' ) {
    100          
    100          
    50          
192             # Only get public methods
193 6 100       20 return undef if $options{private};
194 5         13 $options{public} = 1;
195              
196             } elsif ( $_ eq 'private' ) {
197             # Only get private methods
198 4 100       16 return undef if $options{public};
199 3         54 $options{private} = 1;
200              
201             } elsif ( $_ eq 'full' ) {
202             # Return the full method name
203 4 100       16 return undef if $options{expanded};
204 3         9 $options{full} = 1;
205              
206             } elsif ( $_ eq 'expanded' ) {
207             # Returns class, method and function ref
208 6 100       17 return undef if $options{full};
209 5         12 $options{expanded} = 1;
210              
211             } else {
212             # Unknown or unsupported options
213 0         0 return undef;
214             }
215             }
216              
217             # Only works if the class is loaded
218 19 100       54 return undef unless $class->loaded( $name );
219              
220             # Get the super path ( not including UNIVERSAL )
221             # Rather than using Class::ISA, we'll use an inlined version
222             # that implements the same basic algorithm.
223 13         26 my @path = ();
224 13         26 my @queue = ( $name );
225 13         30 my %seen = ( $name => 1 );
226 13         37 while ( my $cl = shift @queue ) {
227 21         37 push @path, $cl;
228 8         49 unshift @queue, grep { ! $seen{$_}++ }
229 8         22 map { s/^::/main::/; s/\'/::/g; $_ } ## no critic
  8         29  
  8         18  
230 8         24 map { "$_" }
231 21         32 ( @{"${cl}::ISA"} );
  21         86  
232             }
233              
234             # Find and merge the function names across the entire super path.
235             # Sort alphabetically and return.
236 13         25 my %methods = ();
237 13         24 foreach my $namespace ( @path ) {
238 246         466 my @functions = grep { ! $methods{$_} }
239 246         592 grep { /$RE_IDENTIFIER/o }
240 381         469 grep { defined &{"${namespace}::$_"} }
  381         950  
241 21         55 keys %{"${namespace}::"};
  21         136  
242 21         60 foreach ( @functions ) {
243 240         423 $methods{$_} = $namespace;
244             }
245             }
246              
247             # Filter to public or private methods if needed
248 13         184 my @methodlist = sort keys %methods;
249 13 100       50 @methodlist = grep { ! /^\_/ } @methodlist if $options{public};
  80         155  
250 13 100       36 @methodlist = grep { /^\_/ } @methodlist if $options{private};
  40         96  
251              
252             # Return in the correct format
253 13 100       29 @methodlist = map { "$methods{$_}::$_" } @methodlist if $options{full};
  40         88  
254             @methodlist = map {
255 66         157 [ "$methods{$_}::$_", $methods{$_}, $_, \&{"$methods{$_}::$_"} ]
  66         275  
256 13 100       30 } @methodlist if $options{expanded};
257              
258 13         96 \@methodlist;
259             }
260              
261              
262              
263              
264              
265             #####################################################################
266             # Search Methods
267              
268              
269             sub subclasses {
270 5     5 1 862 my $class = shift;
271 5 100       21 my $name = $class->_class( shift ) or return undef;
272              
273             # Prepare the search queue
274 4         11 my @found = ();
275 4         15 my @queue = grep { $_ ne 'main' } $class->_subnames('');
  280         444  
276 4         73 while ( @queue ) {
277 968         1641 my $c = shift(@queue); # c for class
278 968 100       1733 if ( $class->_loaded($c) ) {
279             # At least one person has managed to misengineer
280             # a situation in which ->isa could die, even if the
281             # class is real. Trap these cases and just skip
282             # over that (bizarre) class. That would at limit
283             # problems with finding subclasses to only the
284             # modules that have broken ->isa implementation.
285 678         930 local $@;
286 678         987 eval {
287 678 100       3948 if ( $c->isa($name) ) {
288             # Add to the found list, but don't add the class itself
289 8 100       40 push @found, $c unless $c eq $name;
290             }
291             };
292             }
293              
294             # Add any child namespaces to the head of the queue.
295             # This keeps the queue length shorted, and allows us
296             # not to have to do another sort at the end.
297 968         1931 unshift @queue, map { "${c}::$_" } $class->_subnames($c);
  692         2150  
298             }
299              
300 4 100       39 @found ? \@found : '';
301             }
302              
303             sub _subnames {
304 972     972   1690 my ($class, $name) = @_;
305             return sort
306             grep { ## no critic
307 19085 100       48590 substr($_, -2, 2, '') eq '::'
308             and
309             /$RE_IDENTIFIER/o
310             }
311 972         1254 keys %{"${name}::"};
  972         6761  
312             }
313              
314              
315              
316              
317              
318             #####################################################################
319             # Children Related Methods
320              
321             # These can go undocumented for now, until I decide if its best to
322             # just search the children in namespace only, or if I should do it via
323             # the file system.
324              
325             # Find all the loaded classes below us
326             sub children {
327 0     0 0 0 my $class = shift;
328 0 0       0 my $name = $class->_class(shift) or return ();
329              
330             # Find all the Foo:: elements in our symbol table
331 5     5   10928 no strict 'refs';
  5         18  
  5         902  
332 0         0 map { "${name}::$_" } sort grep { s/::$// } keys %{"${name}::"}; ## no critic
  0         0  
  0         0  
  0         0  
333             }
334              
335             # As above, but recursively
336             sub recursive_children {
337 0     0 0 0 my $class = shift;
338 0 0       0 my $name = $class->_class(shift) or return ();
339 0         0 my @children = ( $name );
340              
341             # Do the search using a nicer, more memory efficient
342             # variant of actual recursion.
343 0         0 my $i = 0;
344 5     5   59 no strict 'refs';
  5         13  
  5         2125  
345 0         0 while ( my $namespace = $children[$i++] ) {
346 0         0 push @children, map { "${namespace}::$_" }
347 0         0 grep { ! /^::/ } # Ignore things like ::ISA::CACHE::
348 0         0 grep { s/::$// } ## no critic
349 0         0 keys %{"${namespace}::"};
  0         0  
350             }
351              
352 0         0 sort @children;
353             }
354              
355              
356              
357              
358              
359             #####################################################################
360             # Private Methods
361              
362             # Checks and expands ( if needed ) a class name
363             sub _class {
364 402     402   723 my $class = shift;
365 402 100       807 my $name = shift or return '';
366              
367             # Handle main shorthand
368 400 100       748 return 'main' if $name eq '::';
369 399         762 $name =~ s/\A::/main::/;
370              
371             # Check the class name is valid
372 399 100       2547 $name =~ /$RE_CLASS/o ? $name : '';
373             }
374              
375             # Create a INC-specific filename, which always uses '/'
376             # regardless of platform.
377             sub _inc_filename {
378 325     325   796 my $class = shift;
379 325 50       595 my $name = $class->_class(shift) or return undef;
380 325         1832 join( '/', split /(?:\'|::)/, $name ) . '.pm';
381             }
382              
383             # Convert INC-specific file name to local file name
384             sub _inc_to_local {
385             # Shortcut in the Unix case
386 1 50   1   821 return $_[1] if $UNIX;
387              
388             # On other places, we have to deal with an unusual path that might look
389             # like C:/foo/bar.pm which doesn't fit ANY normal pattern.
390             # Putting it through splitpath/dir and back again seems to normalise
391             # it to a reasonable amount.
392 0           my $class = shift;
393 0 0         my $inc_name = shift or return undef;
394 0           my ($vol, $dir, $file) = File::Spec->splitpath( $inc_name );
395 0   0       $dir = File::Spec->catdir( File::Spec->splitdir( $dir || "" ) );
396 0   0       File::Spec->catpath( $vol, $dir, $file || "" );
397             }
398              
399             1;
400              
401             __END__