File Coverage

blib/lib/Tie/InSecureHash.pm
Criterion Covered Total %
statement 94 175 53.7
branch 39 76 51.3
condition 4 29 13.7
subroutine 17 22 77.2
pod 0 6 0.0
total 154 308 50.0


line stmt bran cond sub pod time code
1             package Tie::InSecureHash;
2              
3 1     1   428 use strict;
  1         2  
  1         27  
4 1     1   3 use vars qw($VERSION $strict $fast);
  1         1  
  1         45  
5 1     1   3 use Carp;
  1         2  
  1         2809  
6              
7             $VERSION = '1.07';
8              
9             sub import
10             {
11 1     1   7 my $pkg = shift;
12 1   0     3 foreach (@_) { $strict ||= /strict/; $fast ||= /fast/ }
  0   0     0  
  0         0  
13 1 0 33     3981 croak qq{$pkg can't be both "strict" and "fast"} if $strict && $fast;
14             }
15              
16             # TAKE A LIST OF POSSIBLE CLASSES FOR AN IMPLICIT KEY AND REMOVE NON-CONTENDERS
17              
18             sub _winnow
19             {
20 109     109   167 my ($caller, $nonpublic, @classes) = @_;
21              
22             # REMOVE CLASSES NOT IN HIERARCHY FOR NON-PUBLIC KEY
23              
24             # @classes = grep { $caller->isa($_) } @classes if $nonpublic;
25              
26             # COMPARE REMAINING KEYS PAIRWISE, ELIMINATING "SHADOWED" KEYS...
27              
28              
29 109         142 return @classes;
30             };
31              
32             # DETERMINE IF A KEY IS ACCESSIBLE
33              
34             sub _access # ($self,$key,$caller)
35             {
36 464     464   385 my ($self, $key, $caller, $file, $delete) = @_;
37              
38             # EXPLICIT KEYS...
39              
40 464 100       1623 if ($key =~ /\A([\w:]*)::((_{0,2})[^:]+)\Z/)
    100          
    50          
41             {
42 308         445 my ($classname, $shortkey, $mode) = ($1,$2,$3);
43 308 100       377 unless ($classname)
44             {
45 18         12 $classname = 'main';
46 18         21 $key = $classname.$key;
47             }
48 308 100       465 if ($mode eq '__') # PRIVATE
    100          
49             {
50 108 100       143 if (exists $self->{fullkeys}{$key})
51             {
52             }
53             else
54             {
55 9 50       11 if ($delete) { delete $self->{file}{$key} }
  0         0  
56 9         31 else { $self->{file}{$key} = $file }
57             }
58             }
59             elsif ($mode eq '_') # PROTECTED
60             {
61             }
62              
63 308 100       485 if (!exists $self->{fullkeys}{$key})
64             {
65 49 100       49 if ($delete)
66             {
67 2         5 @{$self->{keylist}{$shortkey}} =
  0         0  
68 2         4 grep { $_ !~ /$classname/ }
69 2         3 @{$self->{keylist}{$shortkey}}
70             }
71             else
72             {
73 47         27 push @{$self->{keylist}{$shortkey}}, $classname;
  47         126  
74             }
75             }
76             }
77              
78             # IMPLICIT PRIVATE KEY (MUST BE IN CALLING CLASS)
79             elsif ($key =~ /\A(__[^:]+)\Z/)
80             {
81 21 100       51 if (!exists $self->{fullkeys}{"${caller}::$key"})
82             {
83             }
84 21         25 $key = "${caller}::$key";
85 21 100       34 if (exists $self->{fullkeys}{$key})
86             {
87             }
88             }
89              
90             # IMPLICIT PROTECTED OR PUBLIC KEY
91             # (PROTECTED KEY MUST BE IN ANCESTRAL HIERARCHY OF CALLING CLASS)
92             elsif ($key =~ /\A((_?)[^:]+)\Z/)
93             {
94 135         160 my $fullkey = "${caller}::$key";
95 135 100       185 if (exists $self->{fullkeys}{$fullkey})
96             {
97 26         25 $key = $fullkey;
98             }
99             else
100             {
101 109 100       352 my @classes = _winnow($caller, $2,
102 109         74 @{$self->{keylist}{$key}||[]});
103            
104 109 100       150 if (@classes)
105             {
106             # TOO MANY CHOICES
107 22         33 $key = $classes[0]."::$key";
108             }
109             else # NOT ENOUGH CHOICES
110             {
111             }
112             }
113             }
114             else # INVALID KEY
115             {
116             }
117              
118 464 100       532 if ($delete) { return delete $self->{fullkeys}{$key}; }
  3         13  
119 461         672 return \$self->{fullkeys}{$key};
120              
121             };
122              
123              
124             # NOTE THAT NEW MAY TIE AND BLESS INTO THE SAME CLASS
125             # IF NOTHING MORE APPROPRIATE IS SPECIFIED
126              
127             sub new
128             {
129 1     1 0 37 my %self = ();
130 1   33     4 my $class = ref($_[0])||$_[0];
131 1   33     7 my $blessclass = ref($_[1])||$_[1]||$class;
132 1 50       6 my $impl = tie %self, $class unless $fast;
133 1         2 my $self = bless \%self, $blessclass;
134 1         4 splice(@_,0,2);
135 1 50       25 if (@_) # INITIALIZATION ARGUMENTS PRESENT
136             {
137 0         0 my ($ancestor, $file);
138 0         0 my $i = 0;
139 0         0 while ( ($ancestor,$file) = caller($i++) )
140             {
141 0 0       0 last if $ancestor eq $blessclass;
142             }
143 0         0 my ($key, $value);
144 0         0 while (($key,$value) = splice(@_,0,2))
145             {
146 0 0       0 my $fullkey = $key=~/::/ ? $key : "${blessclass}::$key";
147 0 0       0 if ($fast)
148             {
149 0         0 $self->{$fullkey} = $value;
150             }
151             else
152             {
153 0         0 $impl->{fullkeys}{$fullkey} = $value;
154 0         0 push @{$impl->{keylist}{$key}}, $blessclass;
  0         0  
155 0 0       0 $impl->{file}{$fullkey} = $file
156             if $key =~ /\A__/;
157             }
158             }
159             }
160              
161 1         3 return $self;
162             }
163              
164             # USEFUL METHODS TO DUMP INFORMATION
165              
166             sub debug
167             {
168 0     0 0 0 my $self = tied %{$_[0]};
  0         0  
169 0   0     0 my ($caller, $file, $line, $sub) = (caller,(caller(1))[3]||"(none)");
170 0 0       0 return _simple_debug($_[0],$caller, $file, $line, $sub) unless $self;
171 0         0 my ($key, $val);
172 0         0 my %sorted = ();
173 0         0 while ($key = each %{$self->{fullkeys}})
  0         0  
174             {
175 0         0 $key =~ m/\A(.*?)([^:]*)\Z/;
176 0         0 push @{$sorted{$1}}, $key;
  0         0  
177             }
178              
179 0         0 print STDERR "\nIn subroutine '$sub' called from package '$caller' ($file, line $line):\n";
180 0         0 foreach my $class (keys %sorted)
181             {
182 0         0 print STDERR "\n\t$class\n";
183 0         0 foreach $key ( @{$sorted{$class}} )
  0         0  
184             {
185 0         0 print STDERR "\t\t";
186 0         0 my ($shortkey) = $key =~ /.*::(.*)/;
187 0         0 my $explanation = "";
188 0 0       0 if (eval { _access($self,$shortkey,$caller, $file); 1 })
  0 0       0  
  0         0  
189             {
190 0         0 print STDERR '(+)';
191             }
192             elsif ($@ =~ /\AAmbiguous key/)
193             {
194 0         0 print STDERR '(?)';
195 0         0 ($explanation = $@) =~ s/.*\n//;
196 0         0 $explanation =~ s/.*\n\Z//;
197 0         0 $explanation =~ s/\ACould/Ambiguous unless fully qualified. Could/;
198 0         0 $explanation =~ s/^(?!\Z)/\t\t\t>>> /gm;
199             }
200             else
201             {
202 0         0 print STDERR '(-)';
203 0 0 0     0 if ($shortkey =~ /\A__/ && $@ =~ /file/)
    0          
204             {
205 0         0 $explanation = "\t\t\t>>> Private entry of $class\n\t\t\t>>> declared in file $self->{file}{$key}\n\t\t\t>>> is inaccessable from file $file.\n"
206             }
207             elsif ($shortkey =~ /\A__/)
208             {
209 0         0 $explanation = "\t\t\t>>> Private entry of $class\n\t\t\t>>> is inaccessable from package $caller.\n"
210             }
211             else
212             {
213 0         0 $explanation = "\t\t\t>>> Protected entry of $class\n\t\t\t>>> is inaccessible outside its hierarchy (i.e. from $caller).\n"
214             }
215            
216             }
217 0         0 my $val = $self->{fullkeys}{$key};
218 0 0       0 if (defined $val) { $val = "'$val'" }
  0         0  
219 0         0 else { $val = "undef" }
220 0         0 print STDERR " '$shortkey'\t=> $val";
221 0 0       0 print STDERR "\n$explanation" if $explanation;
222 0         0 print STDERR "\n";
223             }
224             }
225             }
226              
227             sub _simple_debug
228             {
229 0     0   0 my ($self,$caller, $file, $line, $sub) = @_;
230 0         0 my ($key, $val);
231 0         0 my %sorted = ();
232 0         0 while ($key = each %{$self})
  0         0  
233             {
234 0         0 $key =~ m/\A(.*?)([^:]*)\Z/;
235 0         0 push @{$sorted{$1}}, $key;
  0         0  
236             }
237              
238 0         0 print "\nIn subroutine '$sub' called from package '$caller' ($file, line $line):\n";
239 0         0 foreach my $class (keys %sorted)
240             {
241 0         0 print "\n\t$class\n";
242 0         0 foreach $key ( @{$sorted{$class}} )
  0         0  
243             {
244 0         0 print "\t\t";
245 0         0 print " '$key'\t=> '$self->{$key}'\n";
246             }
247             }
248             }
249              
250              
251 17     17 0 354 sub each { each %{$_[0]} }
  17         44  
252 0     0 0 0 sub keys { keys %{$_[0]} }
  0         0  
253 1     1 0 23 sub values { values %{$_[0]} }
  1         4  
254 0     0 0 0 sub exists { exists $_[0]->{$_[1]} }
255              
256             sub TIEHASH # ($class, @args)
257             {
258 1   33 1   4 my $class = ref($_[0]) || $_[0];
259 1 50       4 if ($strict)
    50          
260             {
261 0 0 0     0 carp qq{Tie'ing a securehash directly will be unsafe in 'fast' mode.\n}.
      0        
262             qq{Use Tie::SecureHash::new instead}
263             unless (caller 1)[3] =~ /\A(.*?)::([^:]*)\Z/
264             && $2 eq "new"
265             && $1->isa('Tie::SecureHash');
266             }
267             elsif ($fast)
268             {
269 0         0 carp qq{Tie'ing a securehash directly should never happen in 'fast' mode.\n}.
270             qq{Use Tie::SecureHash::new instead}
271             }
272 1         3 bless {}, $class;
273             }
274              
275             sub FETCH # ($self, $key)
276             {
277 205     205   460 my ($self, $key) = @_;
278 205         357 my $entry = _access($self,$key,(caller)[0..1]);
279 205 50       773 return $$entry if $entry;
280 0         0 return;
281             }
282              
283             sub STORE # ($self, $key, $value)
284             {
285 54     54   156 my ($self, $key, $value) = @_;
286 54         109 my $entry = _access($self,$key,(caller)[0..1]);
287 54 50       189 return $$entry = $value if $entry;
288 0         0 return;
289             }
290              
291             sub DELETE # ($self, $key)
292             {
293 3     3   60 my ($self, $key) = @_;
294 3         11 return _access($self,$key,(caller)[0..1],'DELETE');
295             }
296              
297             sub CLEAR # ($self)
298             {
299 2     2   73 my ($self) = @_;
300 2         4 my ($caller, $file) = caller;
301             my @inaccessibles =
302 56         31 grep { ! eval { _access($self,$_,$caller,$file); 1 } }
  56         52  
  56         68  
  2         12  
303 2         2 keys %{$self->{fullkeys}};
304 0         0 croak "Unable to assign to securehash because the following existing keys\nare inaccessible from package $caller and cannot be deleted:\n" .
305 2 50       5 join("\n", map {"\t$_"} @inaccessibles) . "\n "
306             if @inaccessibles;
307 2         3 %{$self} = ();
  2         20  
308             }
309              
310             sub EXISTS # ($self, $key)
311             {
312 50     50   514 my ($self, $key) = @_;
313 50         93 my @context = (caller)[0..1];
314 50 50       49 eval { _access($self,$key,@context); 1 } ? 1 : '';
  50         52  
  50         154  
315             }
316              
317             sub FIRSTKEY # ($self)
318             {
319 6     6   119 my ($self) = @_;
320 6         4 keys %{$self->{fullkeys}};
  6         10  
321 6         18 goto &NEXTKEY;
322             }
323              
324             sub NEXTKEY # ($self)
325             {
326 102     102   1043 my $self = $_[0];
327 102         62 my $key;
328 102         223 my @context = (caller)[0..1];
329 102         91 while (defined($key = each %{$self->{fullkeys}}))
  102         209  
330             {
331 96 50       86 last if eval { _access($self,$key,@context) };
  96         97  
332             }
333 102         276 return $key;
334             }
335              
336             sub DESTROY # ($self)
337 0     0     {
338             # NOTHING TO DO
339             # (BE CAREFUL SINCE IT DOES DOUBLE DUTY FOR tie AND bless)
340             }
341              
342              
343             1;
344             __END__