File Coverage

blib/lib/HTML/Template/Compiled.pm
Criterion Covered Total %
statement 789 919 85.8
branch 262 378 69.3
condition 83 147 56.4
subroutine 98 110 89.0
pod 22 67 32.8
total 1254 1621 77.3


line stmt bran cond sub pod time code
1             package HTML::Template::Compiled;
2             our $VERSION = '1.002_001'; # TRIAL VERSION
3 36     36   455724 use Data::Dumper;
  36         187176  
  36         1798  
4 36     36   206 use Scalar::Util;
  36         51  
  36         1928  
5 0         0 BEGIN {
6 36   50 36   147 use constant D => $ENV{HTC_DEBUG} || 0;
  36     0   49  
  36         2173  
7             }
8 36     36   151 use strict;
  36         41  
  36         614  
9 36     36   110 use warnings;
  36         44  
  36         890  
10 36     36   122 use Digest::MD5 qw/ md5_hex /;
  36         42  
  36         1540  
11              
12 36     36   145 use Carp;
  36         46  
  36         1546  
13 36     36   135 use Fcntl qw(:seek :flock);
  36         46  
  36         4108  
14 36     36   160 use File::Spec;
  36         57  
  36         809  
15 36     36   286 use File::Basename qw(dirname basename);
  36         43  
  36         2249  
16 36     36   14514 use HTML::Template::Compiled::Utils qw(:walkpath :log :escape &md5);
  36         60  
  36         6305  
17 36     36   13098 use HTML::Template::Compiled::Expression qw(:expressions);
  36         60  
  36         4751  
18 36     36   17822 use HTML::Template::Compiled::Compiler;
  36         64  
  36         1410  
19             # TODO
20             eval {
21             require URI::Escape;
22             };
23             #eval {
24             # require Encode;
25             #};
26             #my $Encode = $@ ? 0 : 1;
27              
28 36     36   190 use base 'Exporter';
  36         32  
  36         3045  
29             our @EXPORT_OK = qw(&HTC);
30 36         5454 use HTML::Template::Compiled::Parser qw(
31             $CASE_SENSITIVE_DEFAULT $NEW_CHECK
32             $DEBUG_DEFAULT $SEARCHPATH
33             %FILESTACK %COMPILE_STACK %PATHS $DEFAULT_ESCAPE $DEFAULT_QUERY
34             $UNTAINT $DEFAULT_TAGSTYLE $MAX_RECURSE
35 36     36   21698 );
  36         78  
36 36     36   174 use vars qw($__ix__);
  36         38  
  36         1192  
37              
38 36     36   124 use constant MTIME => 0;
  36         39  
  36         1580  
39 36     36   121 use constant CHECKED => 1;
  36         39  
  36         1279  
40 36     36   125 use constant LMTIME => 2;
  36         38  
  36         1387  
41 36     36   121 use constant LCHECKED => 3;
  36         35  
  36         1261  
42              
43 36     36   113 use constant DEBUG_COMPILED => 0b001;
  36         36  
  36         1621  
44              
45 36     36   118 use constant DEBUG_CACHE_FILE_MISS => 0b0001;
  36         33  
  36         1221  
46 36     36   113 use constant DEBUG_CACHE_FILE_HIT => 0b0010;
  36         36  
  36         1437  
47 36     36   121 use constant DEBUG_CACHE_MEM_MISS => 0b0100;
  36         33  
  36         1248  
48 36     36   137 use constant DEBUG_CACHE_MEM_HIT => 0b1000;
  36         33  
  36         1958  
49              
50             our $DEBUG = 0;
51             our $LAST_EXCEPTION;
52              
53             # options / object attributes
54 36     36   130 use constant PARAM => 0;
  36         53  
  36         4649  
55              
56             BEGIN {
57 36     36   223 my @map = (
58             undef, qw(
59             path md5_path filename file scalar filehandle
60             file_cache cache_dir cache search_path
61             loop_context case_sensitive global_vars
62             default_path
63             debug debug_file objects perl out_fh default_escape
64             filter formatter
65             globalstack use_query parse_tree parser compiler includes
66             plugins open_mode chomp expire_time strict warnings line_info
67             args optimize
68             )
69             #use_expressions
70             );
71              
72 36         128 for my $i ( 1 .. $#map ) {
73 1332         1283 my $method = "_$map[$i]";
74 1332     9700   2175 my $get = sub { return $_[0]->[$i] };
  9700         27075  
75 1332         853 my $set;
76 1332     4584   1710 $set = sub { $_[0]->[$i] = $_[1] };
  4584         5212  
77 36     36   132 no strict 'refs';
  36         48  
  36         2098  
78 1332         839 *{"get$method"} = $get;
  1332         3129  
79 1332         817 *{"set$method"} = $set;
  1332         243352  
80             }
81             }
82              
83             # tired of typing?
84 1     1 0 271 sub HTC { __PACKAGE__->new(@_) }
85              
86             sub new {
87 140     140 1 9053877 my ( $class, %args ) = @_;
88 140         176 D && $class->log("new()");
89             # handle the "type", "source" parameter format (does anyone use it?)
90 140 100       415 if ( exists $args{type} ) {
91 3 100       8 exists $args{source} or $class->_error_no_source();
92 2 100       9 $args{type} =~ m/^(?:filename|scalarref|arrayref|filehandle)$/
93             or $class->_error_wrong_source();
94 1         2 $args{ $args{type} } = $args{source};
95 1         1 delete $args{type};
96 1         2 delete $args{source};
97             }
98 138 100       412 if (exists $args{filename}) {
    100          
    100          
    50          
99 50         261 return $class->new_file($args{filename}, %args);
100             }
101             elsif (exists $args{scalarref}) {
102 84         354 return $class->new_scalar_ref($args{scalarref}, %args);
103             }
104             elsif (exists $args{filehandle}) {
105 3         18 return $class->new_filehandle($args{filehandle}, %args);
106             }
107             elsif (exists $args{arrayref}) {
108 1         4 return $class->new_array_ref($args{arrayref}, %args);
109             }
110 0         0 croak("$class->new called with not enough arguments");
111             }
112              
113             sub _error_no_query {
114 1     1   1 my ($self) = @_;
115 1   33     4 my $class = ref $self || $self;
116 1         176 carp "You are using query() but have not specified that you want to use it"
117             . " (specify with use_query => 1)";
118             }
119              
120             sub _error_not_compiled {
121 0     0   0 my ($self) = @_;
122 0   0     0 my $class = ref $self || $self;
123 0         0 carp "Template was not compiled yet";
124             }
125              
126             sub _error_wrong_source {
127 1     1   1 my ($self) = @_;
128 1   33     6 my $class = ref $self || $self;
129 1         96 croak("$class->new() : type parameter must be set to 'filename', "
130             . "'arrayref', 'scalarref' or 'filehandle'!");
131             }
132              
133             sub _error_no_source {
134 1     1   2 my ($self) = @_;
135 1   33     5 my $class = ref $self || $self;
136 1         134 croak("$class->new() called with 'type' parameter set,"
137             . " but no 'source'!");
138             }
139              
140             sub _error_template_sources {
141 1     1   2 my ($self) = @_;
142 1   33     2 my $class = ref $self || $self;
143 1         121 croak(
144             "$class->new called with multiple (or no) template sources specified!"
145             . "A valid call to new() has exactly ne filename => 'file' OR exactly one"
146             . " scalarref => \\\$scalar OR exactly one arrayref => \\\@array OR"
147             . " exactly one filehandle => \*FH"
148             );
149             }
150              
151             sub _error_empty_filename {
152 1     1   2 my ($self) = @_;
153 1   33     3 my $class = ref $self || $self;
154 1         92 croak("$class->new called with empty filename parameter!");
155             }
156              
157             sub new_from_perl {
158 0     0 0 0 my ($class, %args) = @_;
159 0         0 my $self = bless [], $class;
160 0         0 $self->init_args(\%args);
161 0         0 D && $self->log("new(perl) filename: $args{filename}");
162              
163 0         0 $self->init_cache(\%args);
164 0         0 $self->init(%args);
165 0         0 $self->set_perl( $args{perl} );
166 0         0 $self->set_filename( $args{filename} );
167 0 0       0 my $md5path = md5_hex(@{ $args{path} || [] });
  0         0  
168 0         0 $self->set_path( $args{path} );
169 0         0 $self->set_md5_path( $md5path );
170 0         0 $self->set_scalar( $args{scalarref} );
171              
172 0 0       0 unless ( $self->get_scalar ) {
173 0         0 my $file =
174             $self->createFilename( $self->get_path, \$self->get_filename );
175 0         0 $self->set_file($file);
176             }
177 0         0 return $self;
178             }
179              
180             sub new_file {
181 54     54 0 948 my ($class, $filename, %args) = @_;
182 54         119 my $self = bless [], $class;
183 54         179 $self->init_args(\%args);
184 54         165 $args{path} = $self->build_path($args{path});
185 54 100 33     213 $self->_error_empty_filename()
186             if (!defined $filename or !length $filename);
187 53         68 $args{filename} = $filename;
188 53 50 66     291 if (exists $args{scalarref}
      33        
189             || exists $args{arrayref} || exists $args{filehandle}) {
190 1         3 $self->_error_template_sources;
191             }
192 52         122 $self->set_filename( $filename );
193 52         121 $self->init_cache(\%args);
194 52 50       49 my $md5path = md5_hex(@{ $args{path} || [] });
  52         351  
195 52         132 $self->set_path( $args{path} );
196 52         100 $self->set_md5_path( $md5path );
197 52 100       123 if (my $t = $self->from_cache(\%args)) {
198 11         22 $t->init_includes();
199 11         54 return $t;
200             }
201 41         224 $self->init(%args);
202 41         130 $self->from_scratch;
203 37         112 $self->init_includes;
204 37         212 return $self;
205             }
206              
207             sub new_filehandle {
208 4     4 0 45 my ($class, $filehandle, %args) = @_;
209 4         10 my $self = bless [], $class;
210 4         14 $self->init_args(\%args);
211 4 50 33     30 if (exists $args{scalarref}
      33        
212             || exists $args{arrayref} || exists $args{filename}) {
213 0         0 $self->_error_template_sources;
214             }
215 4         7 $args{filehandle} = $filehandle;
216 4         15 $args{path} = $self->build_path($args{path});
217 4         15 $self->set_filehandle( $args{filehandle} );
218 4         6 $args{cache} = 0;
219 4         9 $self->init_cache(\%args);
220 4 50       4 my $md5path = md5_hex(@{ $args{path} || [] });
  4         27  
221 4         11 $self->set_path( $args{path} );
222 4         8 $self->set_md5_path( $md5path );
223 4 50       13 if (my $t = $self->from_cache(\%args)) {
224 0         0 return $t;
225             }
226 4         20 $self->init(%args);
227 4         12 $self->from_scratch;
228 4         10 $self->init_includes;
229 4         16 return $self;
230             }
231              
232             sub new_array_ref {
233 2     2 0 272 my ($class, $arrayref, %args) = @_;
234 2 50 33     13 if (exists $args{scalarref}
      33        
235             || exists $args{filehandle} || exists $args{filename}) {
236 0         0 $class->_error_template_sources;
237             }
238 2         6 my $scalarref = \( join '', @$arrayref );
239 2         2 delete $args{arrayref};
240 2         6 return $class->new_scalar_ref($scalarref, %args);
241             }
242              
243             sub new_scalar_ref {
244 87     87 0 450 my ($class, $scalarref, %args) = @_;
245 87         158 my $self = bless [], $class;
246 87         257 $self->init_args(\%args);
247 87 50 33     457 if (exists $args{arrayref}
      33        
248             || exists $args{filehandle} || exists $args{filename}) {
249 0         0 $self->_error_template_sources;
250             }
251 87         112 $args{scalarref} = $scalarref;
252 87         260 $args{path} = $self->build_path($args{path});
253 87         246 $self->init_cache(\%args);
254 87         165 $self->set_scalar( $args{scalarref} );
255 87         160 my $text = $self->get_scalar;
256 87         292 my $md5 = md5($$text);
257             # if ($args{cache} and !$md5) {
258             # croak "For caching scalarrefs you need Digest::MD5";
259             # }
260 87         191 $self->set_filename($md5);
261 87         66 D && $self->log("md5: $md5");
262 87 50       102 my $md5path = md5_hex(@{ $args{path} || [] });
  87         351  
263 87         186 $self->set_path( $args{path} );
264 87         160 $self->set_md5_path( $md5path );
265 87 100       193 if (my $t = $self->from_cache(\%args)) {
266 1         6 return $t;
267             }
268 86         358 $self->init(%args);
269 86         223 $self->from_scratch;
270 76         152 $self->init_includes;
271 76         326 return $self;
272             }
273              
274             sub init_includes {
275 186     186 0 216 my ($self) = @_;
276 186         329 my $includes = $self->get_includes;
277 186   100     307 my $cache = $self->get_cache_dir||'';
278 186         499 for my $fullpath (keys %$includes) {
279 40         49 my ($path, $filename, $htc) = @{ $includes->{$fullpath} };
  40         82  
280 40         37 D && $self->log("checking $fullpath ($filename) $htc?");
281             # TODO check $cache
282 40         75 $cache .= '-' . $self->get_md5_path;
283             #warn __PACKAGE__.':'.__LINE__.": init_includes() $filename\n";
284 40 100 50     163 if (not $htc or HTML::Template::Compiled::needs_new_check($cache||'',$filename, $self->get_expire_time)
      100        
285             ) {
286 18         62 $htc = $self->new_from_object($path,$filename,$fullpath,$cache);
287             }
288 40         63 $includes->{$fullpath}->[2] = $htc;
289 40         138 $includes->{$fullpath}->[2]->set_plugins($self->get_plugins);
290             }
291             }
292              
293             sub build_path {
294 145     145 0 238 my ($self, $path) = @_;
295 145 100       394 unless (defined $path) {
    100          
296 83         106 $path = [];
297             }
298             elsif (!ref $path) {
299 60         119 $path = [$path];
300             }
301             defined $ENV{'HTML_TEMPLATE_ROOT'}
302 145 50       383 and push @$path, $ENV{'HTML_TEMPLATE_ROOT'};
303 145         252 return $path;
304             }
305              
306             sub from_scratch {
307 163     163 0 185 my ($self) = @_;
308 163         136 D && $self->log("from_scratch filename=".$self->get_filename);
309 163         249 my $fname = $self->get_filename;
310 163 100 100     483 if ( defined $fname and !$self->get_scalar and !$self->get_filehandle ) {
    100 66        
311              
312             #D && $self->log("tried from_cache() filename=".$fname);
313 72         147 my $file = $self->createFilename( $self->get_path, \$fname );
314 70         77 D && $self->log("set_file $file ($fname)");
315 70         153 $self->set_file($file);
316             }
317             elsif ( defined $fname ) {
318 87         170 $self->set_file($fname);
319             }
320 161         136 D && $self->log( "compiling... " . $self->get_filename );
321 161         385 $self->compile();
322 149         247 return $self;
323             }
324              
325             sub from_cache {
326 201     201 0 220 my ($self, $args) = @_;
327 201         211 my $t;
328 201         174 D && $self->log( "from_cache() filename=" . $self->get_filename );
329              
330 201   50     382 $args ||= {};
331 201   100     718 my $plug = $args->{plugin} || [];
332 201   66     388 my $debug = $self->get_debug || $args->{debug};
333             # try to get memory cache
334 201 100       335 if ( $self->get_cache ) {
335 151         258 my $dir = $self->get_cache_dir;
336 151 100       306 $dir = '' unless defined $dir;
337 151         258 $dir .= '-' . $self->get_md5_path;
338 151         272 my $fname = $self->get_filename;
339 151         303 $t = $self->from_mem_cache($dir,$fname, $args);
340 151 100       291 if ($t) {
341 37         62 $t->set_args($args);
342 37 50       73 if (@$plug) {
343 0         0 $t->set_plugins($plug);
344 0         0 $t->load_plugins($plug);
345             }
346 37 100       86 if ($debug->{cache} & DEBUG_CACHE_MEM_HIT) {
347 2         11 warn "### HTML::Template::Compiled Cache Debug ### MEM CACHE HIT: $fname\n";
348             }
349 37         98 return $t;
350             }
351             # warn __PACKAGE__.':'.__LINE__.": not in mem cache: $fname\n";
352 114 100       293 if ($debug->{cache} & DEBUG_CACHE_MEM_MISS) {
353 2         2 warn "### HTML::Template::Compiled Cache Debug ### MEM CACHE MISS: @{[ $self->get_filename ]}\n";
  2         4  
354             }
355             }
356 164         176 D && $self->log( "from_cache() 2 filename=" . $self->get_filename );
357              
358             # not in memory cache, try file cache
359 164 100       268 if ( $self->get_cache_dir ) {
360 33 100 66     73 my $file = $self->get_scalar || $self->get_filehandle
361             ? $self->get_filename
362             : $self->createFilename( $self->get_path, \$self->get_filename );
363 33         74 my $dir = $self->get_cache_dir;
364 33 50 33     473 if (defined $dir and not -d $dir) {
365 0         0 croak "Cachedir '$dir' does not exist";
366             }
367 33         102 $t = $self->from_file_cache($dir, $file);
368 33 100       127 if ($t) {
369 2         6 $t->set_args($args);
370 2 50       6 if (@$plug) {
371 0         0 $t->set_plugins($plug);
372 0         0 $t->load_plugins($plug);
373             }
374 2 100       6 if ($debug->{cache} & DEBUG_CACHE_FILE_HIT) {
375 1         2 warn "### HTML::Template::Compiled Cache Debug ### FILE CACHE HIT: @{[ $self->get_filename ]}\n";
  1         3  
376             }
377 2         10 return $t;
378             }
379 31 100       97 if ($debug->{cache} & DEBUG_CACHE_FILE_MISS) {
380 2         4 warn "### HTML::Template::Compiled Cache Debug ### FILE CACHE MISS: @{[ $self->get_filename ]}\n";
  2         4  
381             }
382             }
383 162         161 D && $self->log( "from_cache() 3 filename=" . $self->get_filename );
384 162         443 return;
385             }
386              
387             {
388             my $cache;
389             # {
390             # $cachedir => {
391             # $filename => $htc_object,
392             my $times;
393              
394             sub needs_new_check {
395 33     33 0 36 my ($dir, $fname, $expire_time) = @_;
396 33 100       98 my $times = $times->{$dir}->{$fname} or return 1;
397 25         37 my $now = time;
398 25 100       102 return 0 if $now - $times->{checked} < $expire_time;
399 3         13 return 1;
400             }
401              
402             sub from_mem_cache {
403 151     151 0 219 my ($self, $dir, $fname, $args) = @_;
404 151         276 my $cached = $cache->{$dir}->{$fname};
405 151         212 my $times = $times->{$dir}->{$fname};
406 151         127 D && $self->log("\$cached=$cached \$times=$times \$fname=$fname\n");
407 151 100 100     404 if ( $cached && $self->uptodate($times, $args) ) {
408 37         71 return $cached->clone;
409             }
410 114         92 D && $self->log("no or old memcache");
411 114         189 return;
412             }
413              
414             sub _debug_cache {
415 0     0   0 my ($self) = @_;
416 0         0 my $dir = $self->get_cache_dir;
417 0         0 my $objects = $cache->{$dir};
418 0         0 my $times = $times->{$dir};
419 0         0 warn Data::Dumper->Dump([\$times], ['times']);
420 0         0 my @keys = keys %$objects;
421 0         0 warn Data::Dumper->Dump([\@keys], ['keys']);
422             }
423             sub add_mem_cache {
424 38     38 0 105 my ( $self, %times ) = @_;
425 38         33 D && $self->stack(1);
426 38         74 my $dir = $self->get_cache_dir;
427 38 100       88 $dir = '' unless defined $dir;
428 38         111 my @c = caller();
429 38         181 $dir .= '-' . $self->get_md5_path;
430 38         74 my $fname = $self->get_filename;
431 38         36 D && $self->log( "add_mem_cache $fname" );
432 38         82 my $clone = $self->clone;
433 38         99 $clone->clear_params();
434 38 100       34 my @plugs = @{ $self->get_plugins || [] };
  38         66  
435 38         127 for my $i (0 .. $#plugs) {
436 0 0       0 if (ref $plugs[$i]) {
437 0 0       0 if ($plugs[$i]->can('serialize')) {
438 0         0 $plugs[$i] = $plugs[$i]->serialize();
439             }
440             }
441             }
442 38         89 $clone->set_plugins(\@plugs);
443 38         92 $cache->{$dir}->{$fname} = $clone;
444 38         296 $times->{$dir}->{$fname} = \%times;
445             }
446              
447             sub clear_cache {
448 11     11 1 3454 my $dir = $_[0]->get_cache_dir;
449              
450             # clear the whole cache
451 11 100       40 $cache = {}, $times = {}, return unless defined $dir;
452              
453             # only specific directory
454 9         16 $cache->{$dir} = {};
455 9         63 $times->{$dir} = {};
456             }
457              
458             sub clear_filecache {
459 24     24 1 3012636 my ( $self, $dir ) = @_;
460 24 50       109 defined $dir
461             or $dir = $self->get_cache_dir;
462 24 100       405 return unless -d $dir;
463 22 50       58 ref $self and $self->lock;
464 22 50       587 opendir my $dh, $dir or die "Could not open '$dir': $!";
465 22         520 my @files = grep { m/(\.pl|\.storable)$/ } readdir $dh;
  87         248  
466 22         56 for my $file (@files) {
467 24         240 my $file = File::Spec->catfile( $dir, $file );
468 24 50       1353 unlink $file or die "Could not delete '$file': $!";
469             }
470 22 50       59 ref $self and $self->unlock;
471 22         264 return 1;
472             }
473              
474             sub uptodate {
475 54     54 0 72 my ( $self, $cached_times, $args ) = @_;
476 54 100       100 return 1 if $self->get_scalar;
477 53         104 my $expire_time = $self->get_expire_time;
478 53 100       114 $expire_time = $args->{expire_time} unless defined $expire_time;
479             # unless ($cached_times) {
480             # my $dir = $self->get_cache_dir;
481             # $dir = '' unless defined $dir;
482             # my $fname = $self->get_filename;
483             # my $cached = $cache->{$dir}->{$fname};
484             # $cached_times = $times->{$dir}->{$fname};
485             # return unless $cached;
486             # }
487 53         68 my $now = time;
488 53 100       135 if ( $now - $cached_times->{checked} < $expire_time ) {
489 34         112 return 1;
490             }
491             else {
492 19         48 my $file = $self->createFilename( $self->get_path, \$self->get_filename );
493 19         47 $self->set_file($file);
494             #print STDERR "uptodate($file)\n";
495 19         49 my @times = $self->_checktimes($file);
496 19 100       73 if ( $times[MTIME] <= $cached_times->{mtime} ) {
497 5         7 D && $self->log("uptodate template old");
498             # set last check time to new value
499 5         12 $cached_times->{checked} = $now;
500 5         23 return 1;
501             }
502             }
503             # template is not up to date, re-compile it
504 14         57 return 0;
505             }
506              
507              
508              
509             }
510              
511             sub compile {
512 161     161 0 176 my ($self) = @_;
513 161         139 my ( $source, $compiled );
514 161         259 my $compiler = $self->get_compiler;
515 161 100 100     272 if ( my $file = $self->get_file and !$self->get_scalar ) {
    100          
    50          
516              
517 70         66 D && $self->log( "compile from file " . $file );
518 70 50       864 die "Could not open '$file': $!" unless -f $file;
519 70         159 my @times = $self->_checktimes($file);
520 70         215 my $text = $self->_readfile($file);
521 70         607 my ( $source, $compiled ) = $compiler->compile( $self, $text, $file );
522 68         198 $self->set_perl($compiled);
523 68 100       114 $self->get_cache and $self->add_mem_cache(
524             checked => time,
525             mtime => $times[MTIME],
526             );
527 68         68 D && $self->log("compiled $file");
528              
529 68 100       109 if ( $self->get_cache_dir ) {
530 27         25 D && $self->log("add_file_cache($file)");
531 27         90 $self->add_file_cache(
532             $source,
533             checked => time,
534             mtime => $times[MTIME],
535             );
536             }
537             }
538             elsif ( my $text = $self->get_scalar ) {
539 87         124 my $md5 = $self->get_filename; # yeah, weird
540 87         83 D && $self->log("compiled $md5");
541 87         261 my ( $source, $compiled ) = $compiler->compile( $self, $$text, $md5 );
542 77         234 $self->set_perl($compiled);
543 77 100       138 if ( $self->get_cache_dir ) {
544 4         5 D && $self->log("add_file_cache($file)");
545 4         15 $self->add_file_cache(
546             $source,
547             checked => time,
548             mtime => time,
549             );
550             }
551             }
552             elsif ( my $fh = $self->get_filehandle ) {
553 4         11 local $/;
554 4         61 my $data = <$fh>;
555 4         19 my ( $source, $compiled ) = $compiler->compile( $self, $data, '' );
556 4         11 $self->set_perl($compiled);
557              
558             }
559             }
560              
561             sub add_file_cache {
562 31     31 0 79 my ( $self, $source, %times ) = @_;
563 31         89 $self->lock;
564 31         63 my $cache = $self->get_cache_dir;
565 31 50 33     348 if (defined $cache and not -d $cache) {
566 0         0 croak "Cachedir '$cache' does not exist";
567             }
568 31         74 my $plfile = $self->escape_filename( $self->get_file );
569 31         66 my $filename = $self->get_filename;
570 31         627 my $lmtime = localtime $times{mtime};
571 31         296 my $lchecked = localtime $times{checked};
572 31         73 my $cachefile = "$cache/$plfile";
573 31         31 D && $self->log("add_file_cache() $cachefile");
574             {
575 31         32 require Storable;
  31         5886  
576 31         23423 require B::Deparse;
577 31         54 local $Storable::Deparse = 1;
578 31         69 my $clone = $self->clone;
579 31         84 $clone->prepare_for_cache;
580 31   100     287 my $v = $self->VERSION || '0.01';
581             my $to_cache = {
582             htc => $clone,
583             version => $v,
584             times => {
585             mtime => $times{mtime},
586             checked => $times{checked},
587             },
588 31         171 };
589 31         122 Storable::store($to_cache, "$cachefile.storable");
590             }
591 31         926202 $self->unlock;
592             }
593              
594             sub get_plugin {
595 3     3 1 5 my ($self, $class) = @_;
596 3 50       3 for my $plug (@{ $self->get_plugins || [] }) {
  3         5  
597 3 50 33     17 return $plug if (ref $plug || $plug) eq $class;
598             }
599 0         0 return;
600             }
601              
602             sub from_file_cache {
603 33     33 0 51 my ($self, $dir, $file) = @_;
604 33         27 D && $self->stack;
605 33         28 D && $self->log("include file: $file");
606              
607 33         94 my $escaped = $self->escape_filename($file);
608 33         389 my $req = File::Spec->catfile( $dir, "$escaped.storable" );
609 33 100       532 return unless -f $req;
610 9         32 return $self->include_file($req);
611             }
612              
613             sub include_file {
614 10     10 0 17 my ( $self, $req ) = @_;
615 10         15 D && $self->log("do $req");
616 10         13 my $r;
617             my $t;
618             {
619 10         14 require Storable;
  10         77  
620 10         30 require B::Deparse;
621 10         19 local $Storable::Eval = 1;
622 10         14 my $cache;
623 10         20 eval {
624 10         40 $cache = Storable::retrieve($req);
625             };
626             #warn __PACKAGE__.':'.__LINE__.": error? $@\n";
627 10 50       5258 return if $@;
628 10         25 my $cached_version = $cache->{version};
629 10         17 $t = $cache->{htc};
630 10 100 100     160 if (($t->VERSION || '0.01') ne $cached_version || !$t->uptodate( $cache->{times} )) {
      66        
631             # is not uptodate
632 7         178 return;
633             }
634 3   50     12 my $plug = $t->get_plugins || [];
635             $t->get_cache and $t->add_mem_cache(
636             checked => $cache->{times}->{checked},
637             mtime => $cache->{times}->{mtime},
638 3 100       6 );
639             }
640 3         9 return $t;
641             }
642              
643             sub createFilename {
644 148     148 0 195 my ( $self, $path, $filename_ref, $cwd ) = @_;
645 148         150 my $filename = $$filename_ref;
646 148         127 D && $self->log("createFilename($path,$filename)");
647 148         101 D && $self->stack(1);
648             #warn __PACKAGE__.':'.__LINE__.": ---- createFilename($path, $$filename_ref, $cwd)\n";
649 148 50       264 if ($path) {
650 148         190 local $" = "\0";
651 148         388 my $cached = $PATHS{"@$path"}->{$filename};
652 148 100       428 return $cached if defined $cached;
653             }
654 40 50 33     451 if ( !$path or
      33        
655             (File::Spec->file_name_is_absolute($filename) &&
656             -f $filename) ) {
657 0         0 return $filename;
658             }
659             else {
660 40         47 D && $self->log( "file: " . File::Spec->catfile( $path, $filename ) );
661 40 100 66     189 if ($path && @$path) {
    50          
662 39         76 my @search = @$path;
663 39         84 for ( @search ) {
664 41         411 my $fp = File::Spec->catfile( $_, $filename );
665 41 100       804 if (-f $fp) {
666 35         52 local $" = "\0";
667 35         113 $PATHS{"@$path"}->{$filename} = $fp;
668 35         155 return $fp;
669             }
670             }
671             # not found in $path, try current template dir
672 4 100       10 if (defined $cwd) {
673 1         6 my $fp = File::Spec->catfile( $cwd, $filename );
674 1 50       13 if (-f $fp) {
675 1         2 for my $p (@search) {
676 1 50       15 if ($fp =~ m{^\Q$p\E(.*)}) {
677 1         2 my $rest = $1;
678 1         11 my (undef, @p) = File::Spec->splitdir($rest);
679 1         6 $rest = File::Spec->catfile(@p);
680 1         2 $$filename_ref = $rest;
681 1         5 $PATHS{"@$path"}->{$rest} = $fp;
682             }
683             }
684 1         3 return $fp;
685             }
686             }
687             }
688             elsif (-f $filename) {
689 1         2 $PATHS{''}->{$filename} = $filename;
690 1         2 return $filename;
691             }
692              
693             # TODO - bug with scalarref
694 3         386 croak "'$filename' not found";
695             }
696             }
697              
698             sub dump {
699 3     3 0 3 my ( $self, $var ) = @_;
700 3         302 require Data::Dumper;
701 3         6 local $Data::Dumper::Indent = 1;
702 3         4 local $Data::Dumper::Sortkeys = 1;
703 3         19 return Data::Dumper->Dump( [$var], ['DUMP'] );
704             }
705              
706             sub dump_var {
707 324     324 0 341 my ($class, $var, $varname) = @_;
708 324         306 local $Data::Dumper::Terse = 0;
709 324         315 local $Data::Dumper::Indent = 2;
710 324         271 local $Data::Dumper::Purity = 0;
711 324         321 local $Data::Dumper::Pad = "";
712 324         257 local $Data::Dumper::Useqq = 0;
713 324         255 local $Data::Dumper::Deepcopy = 0;
714 324         237 local $Data::Dumper::Quotekeys = 1;
715 324         273 local $Data::Dumper::Bless = 'bless';
716 324         375 local $Data::Dumper::Pair = ' => ';
717 324         231 local $Data::Dumper::Maxdep = 0;
718 324         259 local $Data::Dumper::Useperl = 0;
719 324         237 local $Data::Dumper::Sortkeys = 1;
720 324         1215 return Data::Dumper->Dump( [$var], [$varname] );
721             }
722              
723             sub init_cache {
724 143     143 0 177 my ($self, $args) = @_;
725 143         192 my $cachedir = $args->{file_cache_dir};
726 143 100       265 if ($args->{file_cache}) {
727 30 50       114 $self->set_cache_dir($cachedir) if $args->{file_cache};
728             }
729 143 100       446 $self->set_cache( exists $args->{cache} ? $args->{cache} : 1 );
730             }
731              
732             sub init_args {
733 145     145 0 173 my ($self, $args) = @_;
734              
735 145 50       332 if (exists $args->{cache_dir}) {
736             # will soon be deprecated
737 0         0 $args->{file_cache_dir} = delete $args->{cache_dir};
738 0 0       0 unless (exists $args->{file_cache}) {
739             # warn in future versions
740 0         0 $args->{file_cache} = 1;
741             }
742             }
743              
744 145 50 66     431 if ($args->{plugin} and (ref $args->{plugin}) ne 'ARRAY') {
745 0         0 $args->{plugin} = [$args->{plugin}];
746             }
747 145   100     627 my $debug_cache_args = delete $args->{cache_debug} || 0;
748 145         143 my $debug_cache = 0;
749 145 100       285 if ($debug_cache_args) {
750 6 50       12 unless (ref $debug_cache_args) {
751             # no array ref, just a true value
752 0         0 $debug_cache |= DEBUG_CACHE_FILE_MISS | DEBUG_CACHE_FILE_HIT | DEBUG_CACHE_MEM_MISS | DEBUG_CACHE_MEM_HIT;
753             }
754             else {
755 6         12 for my $opt (@$debug_cache_args) {
756 24 100       47 if ($opt eq 'file_miss') {
    100          
    100          
    50          
757 6         10 $debug_cache |= DEBUG_CACHE_FILE_MISS;
758             }
759             elsif ($opt eq 'file_hit') {
760 6         8 $debug_cache |= DEBUG_CACHE_FILE_HIT;
761             }
762             elsif ($opt eq 'mem_miss') {
763 6         7 $debug_cache |= DEBUG_CACHE_MEM_MISS;
764             }
765             elsif ($opt eq 'mem_hit') {
766 6         10 $debug_cache |= DEBUG_CACHE_MEM_HIT;
767             }
768             }
769             }
770             }
771             # check deprecated
772 145         290 for (qw(method_call deref formatter_path default_path formatter)) {
773 725 50       1117 if (exists $args->{$_}) {
774 0         0 croak "Option $_ is deprecated, see documentation";
775             }
776             }
777 145 50       297 if (exists $args->{dumper}) {
778 0         0 croak "Option dumper is deprecated, use a plugin instead";
779             }
780              
781 145   100     488 my $debug_file = delete $args->{debug_file} || 0;
782 145 100       281 my $debug_compiled = delete $args->{debug} ? 1 : 0;
783 145         153 my $debug = 0;
784 145 100       247 $debug |= DEBUG_COMPILED if $debug_compiled;
785              
786             $args->{debug} = {
787 145         409 options => $debug,
788             file => $debug_file,
789             cache => $debug_cache,
790             };
791             my %optimize = (
792             initial_var => 1,
793             object_check => 0,
794             root_hash => 0,
795 145 50       182 %{ $args->{optimize} || {} },
  145         783  
796             );
797              
798 145         1989 %$args = (
799             search_path_on_include => $SEARCHPATH,
800             loop_context_vars => 0,
801             case_sensitive => $CASE_SENSITIVE_DEFAULT,
802             # debug_file => 0,
803             objects => 'strict',
804             out_fh => 0,
805             global_vars => 0,
806             default_escape => $DEFAULT_ESCAPE,
807             default_path => PATH_DEREF,
808             use_query => $DEFAULT_QUERY,
809             #use_expressions => 0,
810             use_perl => 0,
811             open_mode => '',
812             no_includes => 0,
813             pre_chomp => 0,
814             post_chomp => 0,
815             expire_time => $NEW_CHECK,
816             strict => 1,
817             optimize => \%optimize,
818             %$args,
819             );
820 145         541 $self->set_args($args);
821             # return %defaults;
822             }
823              
824             sub init {
825 131     131 0 651 my ( $self, %args ) = @_;
826 131         323 $self->set_expire_time($args{expire_time});
827 131 100       295 $self->set_loop_context(1) if $args{loop_context_vars};
828 131         279 $self->set_case_sensitive( $args{case_sensitive} );
829 131         243 $self->set_default_escape( $args{default_escape} );
830 131         275 $self->set_default_path( $args{default_path} );
831 131         238 $self->set_use_query( $args{use_query} );
832 131         337 $self->set_chomp([$args{pre_chomp}, $args{post_chomp}]);
833 131         266 $self->set_strict( $args{strict} );
834 131         234 $self->set_optimize($args{optimize});
835 131   100     442 my $warnings = $args{warnings} || 0;
836 131 100 100     604 unless ($warnings eq 1 or $warnings eq 'fatal') {
837 127         159 $warnings = 0;
838             }
839 131         278 $self->set_warnings($warnings);
840 131         122 my $line_info = 0;
841 131 100       260 if ($args{line_info}) {
842 3         3 $line_info = 1;
843             }
844 131         231 $self->set_line_info($line_info);
845             #$self->set_use_expressions( $args{use_expressions} );
846 131 100       322 if ($args{use_expressions}) {
847 26         127 require HTML::Template::Compiled::Expr;
848             }
849 131 100       329 $args{open_mode} = '' unless length $args{open_mode};
850 131 100       259 if ($args{open_mode}) {
851 1         4 $args{open_mode} =~ s/^[<>]//; # <:utf8
852             }
853 131         253 $self->set_open_mode( $args{open_mode} );
854 131         272 $self->set_search_path( $args{search_path_on_include} );
855 131         275 $self->set_includes({});
856 131 100       251 if ( $args{filter} ) {
857 3         860 require HTML::Template::Compiled::Filter;
858             $self->set_filter(
859 3         20 HTML::Template::Compiled::Filter->new( $args{filter} ) );
860             }
861 131         235 $self->set_debug( $args{debug} );
862 131         370 $self->set_debug_file( $args{debug_file} );
863 131         329 $self->set_objects( $args{objects} );
864 131         223 $self->set_out_fh( $args{out_fh} );
865 131         245 $self->set_global_vars( $args{global_vars} );
866 131 100       260 if (my $plugins = $args{plugin}) {
867 5         12 $self->set_plugins($plugins);
868             }
869 131         268 my $compiler = $self->compiler_class->new;
870 131         285 $self->set_compiler($compiler);
871 131         138 my $tagstyle = $args{tagstyle};
872 131         133 my $parser;
873 131 100       291 if (ref $tagstyle eq 'ARRAY') {
874             # user specified named styles or regexes
875             $parser = $self->parser_class->new(
876             tagstyle => $tagstyle,
877             use_expressions => $args{use_expressions},
878             strict => $args{strict},
879 33         55 );
880 33         95 $parser->set_perl($args{use_perl});
881             }
882 131 50       306 $args{parser} = ${$args{parser}} if ref $args{parser} eq 'REF';
  0         0  
883 131 50       508 if (UNIVERSAL::isa($args{parser}, 'HTML::Template::Compiled::Parser')) {
884 0         0 $parser = $args{parser};
885             }
886 131 100       316 unless ($parser) {
887 98   33     353 $parser ||= $self->parser_class->default();
888 98         301 $parser->set_perl($args{use_perl});
889 98         305 $parser->set_expressions($args{use_expressions});
890 98         274 $parser->set_strict($args{strict});
891             }
892 131         432 $parser->set_chomp([$args{pre_chomp}, $args{post_chomp}]);
893 131 100       270 if ($args{use_perl}) {
894             $parser->add_tagnames({
895             HTML::Template::Compiled::Token::OPENING_TAG() => {
896 1     0   6 PERL => [sub { 1 }],
  0         0  
897             }
898             });
899             }
900 131 100       258 if ($args{no_includes}) {
901 1         5 $parser->remove_tags(qw/ INCLUDE INCLUDE_VAR INCLUDE_STRING /);
902             }
903 131         260 $self->set_parser($parser);
904 131 100       220 if (my $plugins = $self->get_plugins) {
905 5         18 $self->init_plugins($plugins);
906 5         8 $self->set_plugins($plugins);
907             }
908             }
909              
910             {
911             my %_plugins;
912             sub load_plugins {
913 5     5 0 8 my ($self, $plugins) = @_;
914 5         11 for my $plug (@$plugins) {
915 6 100       23 next if ref $plug;
916 3 50       28 next if $_plugins{$plug};
917 3 100       21 if ($plug =~ m/^::/) {
918 1         3 $plug = "HTML::Template::Compiled::Plugin$plug";
919             }
920 3 50       7 next if $_plugins{$plug};
921 3 50       32 unless ($plug->can('register')) {
922 0         0 eval "require $plug";
923 0 0       0 if ($@) {
924 0         0 carp "Could not load plugin $plug\n";
925             }
926             }
927 3         6 $_plugins{$plug} = 1;
928             }
929             }
930             }
931              
932             sub init_plugins {
933 5     5 0 7 my ($self, $plugins) = @_;
934 5         53 $self->load_plugins($plugins);
935 5         12 my $parser = $self->get_parser;
936 5         11 my $compiler = $self->get_compiler;
937 5         9 for my $plug (@$plugins) {
938 6         14 my $actions = $self->get_plugin_actions($plug);
939 6 100       16 if (my $tagnames = $actions->{tagnames}) {
940 3         11 $parser->add_tagnames($tagnames);
941             }
942 6 100       15 if (my $escape = $actions->{escape}) {
943 3   66     19 $compiler->add_escapes((ref $plug) || $plug, $escape);
944             }
945 6 100       17 if (my $tags = $actions->{compile}) {
946 3         9 $compiler->add_tags($tags);
947             }
948             }
949             }
950              
951             {
952             my $classes = {};
953              
954             sub register {
955 6     6 0 2522 my ($class, $plugins) = @_;
956 6 50       25 $plugins = [$plugins] unless ref $plugins eq 'ARRAY';
957 6         12 for my $plug (@$plugins) {
958 6         18 my $actions = $plug->register;
959 6   66     70 my $plug_class = (ref $plug) || $plug;
960 6         11 $classes->{ $plug_class} = $actions;
961 6   100     40 HTML::Template::Compiled::Compiler->setup_escapes($plug_class, $actions->{escape}||{});
962             }
963             }
964              
965             sub get_plugin_actions {
966 6     6 0 7 my ($self, $pclass) = @_;
967 6   66     23 return $classes->{ref $pclass || $pclass};
968             }
969             }
970            
971              
972             sub _readfile {
973 70     70   91 my ( $self, $file ) = @_;
974 70         144 my $open_mode = $self->get_open_mode;
975 70 50       1934 open my $fh, "<$open_mode", $file or die "Cannot open '$file': $!";
976 70         243 local $/;
977 70         1578 <$fh>;
978             }
979              
980             sub get_code {
981 67     67 0 104 return $_[0]->get_perl;
982             }
983              
984 23     23 1 97 sub compile_early { 1 }
985              
986 149     149 1 263 sub method_call { '.' }
987 149     149 1 209 sub deref { '.' }
988 153     153 1 230 sub formatter_path { '/' }
989              
990 131     131 1 663 sub parser_class { 'HTML::Template::Compiled::Parser' }
991              
992 128     128 0 656 sub compiler_class { 'HTML::Template::Compiled::Compiler' }
993              
994             sub quote_file {
995 85 50   85 0 166 defined(my $f = $_[1]) or return '';
996 85         129 $f =~ s/'/\\'/g;
997 85         301 return qq/'$f'/;
998             }
999              
1000             # this method gets a varname like 'var' or 'object.method'
1001             # or 'hash.key' and makes valid perl code out of it that will
1002             # be eval()ed later
1003             # so assuming . is the character for dereferencing hashes the string
1004             # hash.key (found inside ) will be converted to
1005             # '$t->get_var($P, $$C, 1, [PATH_DEREF, 'key'])'
1006             # the get_var method walks the paths given through the data structure.
1007             # $P is the parameter hash of the template, $C is a reference to the current
1008             # parameter hash. the third argument to get_var is 'final'.
1009             # is a 'final' path, and is not.
1010             # so final means it's in 'print-context'.
1011              
1012              
1013             # -------- warning, ugly code
1014             # i'm trading maintainability for efficiency here
1015              
1016             sub try_global {
1017 35     35 0 32 my ( $self, $walk, $path ) = @_;
1018 35   100     41 my $stack = $self->get_globalstack || [];
1019             #warn Data::Dumper->Dump([\$stack], ['stack']);
1020 35         43 for my $item ( $walk, reverse @$stack ) {
1021 55 100       83 if (my $code = UNIVERSAL::can($item, $path)) {
1022 1         2 my $r = $code->($item);
1023 1         21 return $r;
1024             }
1025             else {
1026 54 100       85 next unless exists $item->{$path};
1027 21         421 return $item->{$path};
1028             }
1029             }
1030 13         255 return;
1031             }
1032              
1033             {
1034             sub _walk_formatter {
1035 3     3   4 my ($self, $walk, $key, $global) = @_;
1036 3         2 my $ref = ref $walk;
1037 3         2 my $fm = $HTML::Template::Compiled::Formatter::formatter;
1038 3 50       6 my $sub = exists $fm->{$ref} ? $fm->{$ref}->{$key} : undef;
1039 3         3 my $stack = [];
1040 3         4 my $new_walk;
1041 3 50       4 if ($global) {
1042 0   0     0 $stack = $self->get_globalstack || [];
1043             }
1044 3         4 for my $item ($walk, reverse @$stack) {
1045             #print STDERR "::::::: formatter $walk -> $key (sub=$sub)\n";
1046 3 50       4 if (defined $sub) {
    0          
1047 3         6 $new_walk = $sub->($walk);
1048 3         12 last;
1049             }
1050             elsif (exists $item->{$key}) {
1051             #print STDERR "===== \$item->{$key} exists! '$item->{$key}'\n";
1052 0         0 $new_walk = $item->{$key};
1053 0         0 last;
1054             }
1055             # try next item in stack
1056             }
1057             #print STDERR "---- formatter $walk\n";
1058 3         64 return $new_walk;
1059             }
1060              
1061             # ----------- still ugly code
1062             # not needed anymore
1063             # if (my $formatter = $self->get_formatter() and $final and my $ref = ref $walk) {
1064             # if (my $sub = $formatter->{$ref}->{''}) {
1065             # my $return = $sub->($walk,$self,$P);
1066             # return $return unless ref $return;
1067             # }
1068             # }
1069             # return $walk;
1070             }
1071              
1072             # end ugly code, phooey
1073              
1074             # returns if the var is valid
1075             # only allow '.', '/', '+', '-' and '_'
1076             # fix 2007-07-23: HTML::Template allows every character
1077             # although the documentation says it doesn't.
1078             sub validate_var {
1079 341     341 0 818 return 1;
1080             #return $_[1] !~ tr{a-zA-Z0-9._[]/#-}{}c;
1081             }
1082              
1083             sub escape_filename {
1084 64     64 0 82 my ( $t, $f ) = @_;
1085 64         397 $f =~ s#([/:\\])#'%'.uc sprintf"%02x",ord $1#ge;
  110         574  
1086 64         183 return $f;
1087             }
1088              
1089             sub _checktimes {
1090 89     89   105 my $self = shift;
1091 89         69 D && $self->stack;
1092 89         97 my $filename = shift;
1093 89         863 my $mtime = ( stat $filename )[9];
1094              
1095             #print STDERR "stat $filename = $mtime\n";
1096 89         182 my $checked = time;
1097 89         3096 my $lmtime = localtime $mtime;
1098 89         826 my $lchecked = localtime $checked;
1099 89         318 return ( $mtime, $checked, $lmtime, $lchecked );
1100             }
1101              
1102             sub clone {
1103 165     165 0 161 my ($self) = @_;
1104 165         922 return bless [@$self], ref $self;
1105             }
1106              
1107             sub new_scalar_from_object {
1108 1     1 0 1 my ($self, $scalar) = @_;
1109 1         2 my $new = $self->clone;
1110 1         2 $new->set_includes({});
1111 1         2 $new->set_perl(undef);
1112 1         2 $new->set_filehandle();
1113 1         1 $new->set_cache(0);
1114 1         3 $new->set_cache_dir(undef);
1115 1         3 $new->set_scalar(\$scalar);
1116 1         3 my $md5 = md5($scalar);
1117 1         2 $new->set_filename($md5);
1118 1         2 $new = $new->from_scratch;
1119 1         37 return $new;
1120             }
1121             # create from existing object (TMPL_INCLUDE)
1122             sub new_from_object {
1123 58     58 0 88 my ( $self, $path, $filename, $fullpath, $cache ) = @_;
1124 58 50       116 unless (defined $filename) {
1125 0         0 my ($file) = (caller(1))[3];
1126 0         0 croak "Filename is undef (in template $file)";
1127             }
1128 58         137 my $new = $self->clone;
1129 58         51 D && $self->log("new_from_object($path,$filename,$fullpath,$cache)");
1130 58         109 $new->set_filename($filename);
1131             #if ($fullpath) {
1132             # $self->set_file($fullpath);
1133             #}
1134 58         115 $new->set_includes({});
1135 58         98 $new->set_scalar();
1136 58         96 $new->set_filehandle();
1137 58 50       47 my $md5path = md5_hex(@{ $path || [] });
  58         318  
1138 58         105 $new->set_path($path);
1139 58         93 $new->set_md5_path( $md5path );
1140 58         115 $new->set_perl(undef);
1141 58 100       107 if (my $cached = $new->from_cache($self->get_args)) {
1142 27         53 $cached->set_plugins($self->get_plugins);
1143 27         53 $cached->init_includes;
1144 27         425 return $cached
1145             }
1146 31 50       65 unless ($new->get_compiler) {
1147 0 0       0 my %args = %{ $self->get_args || {} };
  0         0  
1148 0         0 $new->init(%args);
1149             }
1150 31         93 $new = $new->from_scratch;
1151 31         81 $new->init_includes;
1152 31         142 return $new;
1153             }
1154              
1155             sub prepare_for_cache {
1156 31     31 0 46 my ($self) = @_;
1157 31         59 $self->clear_params;
1158 31 100       38 my @plugs = @{ $self->get_plugins || [] };
  31         53  
1159 31         100 for my $i (0 .. $#plugs) {
1160 0 0       0 if (ref $plugs[$i]) {
1161 0 0       0 if ($plugs[$i]->can('serialize')) {
1162 0         0 $plugs[$i] = $plugs[$i]->serialize();
1163             }
1164             }
1165             }
1166 31         63 $self->set_plugins(\@plugs);
1167 31         61 my $includes = $self->get_includes;
1168 31         103 for my $fullpath (keys %$includes) {
1169 7         11 my ($path, $filename, $htc) = @{ $includes->{$fullpath} };
  7         16  
1170 7         33 $includes->{$fullpath} = [$path, $filename];
1171             }
1172 31         71 $self->set_parser(undef);
1173 31         54 $self->set_compiler(undef);
1174 31         54 $self->set_args(undef);
1175 31         65 $self->set_globalstack(undef);
1176             }
1177              
1178             sub preload {
1179 1     1 1 7 my ( $class, $dir ) = @_;
1180 1 50       18 opendir my $dh, $dir or die "Could not open '$dir': $!";
1181 1         19 my @files = grep { m/\.pl|\.storable$/ } readdir $dh;
  4         11  
1182 1         8 closedir $dh;
1183 1         1 my $loaded = 0;
1184 1         2 for my $file (@files) {
1185 1         12 my $success = $class->include_file( File::Spec->catfile( $dir, $file ) );
1186 1 50       5 $loaded++ if $success;
1187             }
1188 1         2 return scalar $loaded;
1189             }
1190              
1191             sub precompile {
1192 1     1 1 12 my ($class, %args) = @_;
1193 1         2 my $files = delete $args{filenames};
1194 1 50       4 return unless ref $files eq 'ARRAY';
1195 1         1 my @precompiled;
1196 1         2 for my $file (@$files) {
1197 2 50       16 my $htc = $class->new(%args,
    50          
    100          
1198             (ref $file eq 'SCALAR'
1199             ? 'scalarref'
1200             : ref $file eq 'ARRAY'
1201             ? 'arrayref'
1202             : ref $file eq 'GLOB'
1203             ? 'filehandle'
1204             : 'filename') => $file,
1205             );
1206 2         5 push @precompiled, $htc,
1207             }
1208 1         3 return \@precompiled;
1209             }
1210              
1211             sub clear_params {
1212 74     74 1 2262 $_[0]->[PARAM] = ();
1213             }
1214              
1215             sub get_param {
1216 0     0 0 0 return $_[0]->[PARAM];
1217             }
1218              
1219             sub param {
1220 114     114 1 6353 my $self = shift;
1221 114 100       253 if (!@_) {
1222 1         3 return $self->query();
1223             return UNIVERSAL::can($self->[PARAM],'can')
1224             ? $self->[PARAM]
1225             : $self->[PARAM]
1226 0 0       0 ? keys %{$self->[PARAM]}
  0 0       0  
1227             : ();
1228             }
1229 113         116 my %p;
1230 113 100       230 if (@_ == 1) {
1231 3 100       5 if ( ref $_[0] ) {
1232             # feed a hashref or object
1233 2 50       6 if (ref $_[0] eq 'HASH') {
1234             # hash, no object
1235 2         1 %p = %{ $_[0] };
  2         6  
1236             }
1237             else {
1238 0         0 $self->[PARAM] = $_[0];
1239 0         0 return;
1240             }
1241             }
1242             else {
1243             # query a parameter
1244 1         3 return $self->[PARAM]->{ $_[0] };
1245             }
1246             }
1247             else {
1248 110         313 %p = @_;
1249             }
1250              
1251 112 100       195 if ( !$self->get_case_sensitive ) {
1252 5         28 my $lc = $self->lchash( {%p} );
1253 5         27 %p = %$lc;
1254             }
1255 112         669 $self->[PARAM]->{$_} = $p{$_} for keys %p;
1256             }
1257              
1258             sub query {
1259 16     16 1 1284 my ($self, $what, $tags) = @_;
1260             # param() no arguments should behave like query
1261             # query() is not activated by default, and
1262             # my %param = (); $htc->param(%param); should
1263             # *not* call query(). so we check if the user wants
1264             # a return value; that indicates that they wanted to
1265             # use query-like behaviour.
1266 16 50       40 return unless defined wantarray();
1267             #print STDERR "query(@_)\n";
1268             my $info = $self->get_parse_tree
1269 16 100       33 or do {
1270 1         5 $self->_error_no_query();
1271 1         80 return;
1272             };
1273 15 50       27 unless (ref $info) {
1274             # not compiled yet!
1275 0         0 $self->_error_not_compiled();
1276 0         0 return;
1277             }
1278 15         29 my $pointer = {children => $info};
1279 15 100       27 $tags = [] unless defined $tags;
1280 15 100       34 $tags = [$tags] unless ref $tags eq 'ARRAY';
1281 15         22 my $includes = $self->get_includes;
1282             my %include_info = map {
1283 11         16 $includes->{$_}->[1] => $includes->{$_}->[2]->get_parse_tree;
1284 15         14 } keys %{ $includes };
  15         29  
1285 15         24 for my $tag (@$tags) {
1286 16         11 my $value;
1287             my %includes = map {
1288 44         39 my $item = $pointer->{children}->{$_};
1289             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1290 44 100 66     102 ? (%{$include_info{$_}})
  11         25  
1291             : ()
1292 16         15 } keys %{ $pointer->{children} };
  16         29  
1293 16 100       45 if (defined ($value = $pointer->{children}->{lc $tag})) {
    50          
1294 14         29 $pointer = $value;
1295             }
1296             elsif (defined ($value = $includes{lc $tag})) {
1297 2         6 $pointer = $value;
1298             }
1299             else {
1300 0         0 return;
1301             }
1302             }
1303 15 100       39 unless ($what) {
    100          
    50          
1304             my @return = map {
1305 5         6 my $item = $pointer->{children}->{$_};
1306             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1307 5 50 33     20 ? (keys %{$include_info{$_}})
  0         0  
1308             : $_;
1309 3         4 } keys %{ $pointer->{children} };
  3         8  
1310 3         15 return @return;
1311             }
1312             elsif ($what eq 'name') {
1313 7         11 my $type = $pointer->{type};
1314 7         18 return $type;
1315             }
1316             elsif ($what eq 'loop') {
1317 5 100       11 if ($pointer->{type} eq 'LOOP') {
1318             my @return = map {
1319 9         8 my $item = $pointer->{children}->{$_};
1320             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1321 9 50 33     29 ? (keys %{$include_info{$_}})
  0         0  
1322             : $_;
1323 3         6 } keys %{ $pointer->{children} };
  3         8  
1324 3         14 return @return;
1325             }
1326 2         406 else { croak "error: (@$tags) is not a LOOP" }
1327             }
1328 0         0 return;
1329             }
1330              
1331             # =head2 lchash
1332             #
1333             # my $capped_href = $self->lchash(\%href);
1334             #
1335             # Input:
1336             # - hashref or arrayref of hashrefs
1337             #
1338             # Output: Returns a reference to a cloned data structure where all the keys are
1339             # capped.
1340             #
1341             # =cut
1342              
1343             sub lchash {
1344 67     67 0 50 my ( $self, $data ) = @_;
1345 67         43 my $lc;
1346 67 100       92 if ( ref $data eq 'HASH' ) {
    100          
1347 20         32 for my $key ( keys %$data ) {
1348 50         41 my $uc_key = lc $key;
1349 50         63 my $val = $self->lchash( $data->{$key} );
1350 50         63 $lc->{$uc_key} = $val;
1351             }
1352             }
1353             elsif ( ref $data eq 'ARRAY' ) {
1354 7         10 for my $item (@$data) {
1355 12         17 my $new = $self->lchash($item);
1356 12         16 push @$lc, $new;
1357             }
1358             }
1359             else {
1360 40         33 $lc = $data;
1361             }
1362 67         61 return $lc;
1363             }
1364              
1365             sub output {
1366 131     131 1 1647 my ( $self, $fh ) = @_;
1367 131   100     387 my $p = $self->[PARAM] || {};
1368             # if we only have an object as parameter
1369             $p = ref $p eq 'HASH'
1370 131 50       310 ? \% { $p }
  131         175  
1371             : $p;
1372 131         241 my $f = $self->get_file;
1373 131 100       295 $fh = \*STDOUT unless $fh;
1374 131 100       246 if ($DEBUG) {
1375 1         1 my $output;
1376 1         1 eval {
1377 1         3 $output = $self->get_perl()->( $self, $p, \$p, $fh );
1378             };
1379 1 50       3 if ($@) {
1380 1         5 $LAST_EXCEPTION = $@;
1381 1         3 my $filename = $self->get_file;
1382 1         4 die "Error while executing '$filename': $@";
1383             }
1384 0         0 return $output;
1385             }
1386             else {
1387 130         265 $self->get_perl()->( $self, $p, \$p, $fh );
1388             }
1389             }
1390              
1391             sub import {
1392 41     41   364 my ( $class, %args ) = @_;
1393 41 50       186 if ( $args{compatible} ) {
    50          
1394 0         0 carp "Usage of use option 'compatible' is deprecated";
1395 0         0 $class->CaseSensitive(0);
1396 0         0 $class->SearchPathOnInclude(0);
1397 0         0 $class->UseQuery(1);
1398             }
1399             elsif ( $args{speed} ) {
1400 0         0 carp "Usage of use option 'speed' is deprecated";
1401             # default at the moment
1402 0         0 $class->CaseSensitive(1);
1403 0         0 $class->SearchPathOnInclude(1);
1404 0         0 $class->UseQuery(0);
1405             }
1406 41 100       952 if (exists $args{short}) {
1407 1         173 carp "Usage of use option 'short' is deprecated";
1408 1         1231 __PACKAGE__->export_to_level(1, scalar caller(), 'HTC');
1409             }
1410             }
1411              
1412             sub var2expression {
1413 0     0 1 0 my ($self, $var) = @_;
1414 0         0 $var = $self->get_compiler->parse_var($self,
1415             var => $var,
1416             method_call => $self->method_call,
1417             deref => $self->deref,
1418             formatter_path => $self->formatter_path,
1419             );
1420 0         0 return $var;
1421             }
1422              
1423             sub ExpireTime {
1424 2     2 1 297 my ($class, $seconds) = @_;
1425 2         5 $NEW_CHECK = $seconds;
1426             }
1427              
1428             sub EnableSub {
1429 0     0 1 0 carp "Warning: Subref variables are not supported any more, use HTML::Template::Compiled::Classic instead";
1430             }
1431              
1432             sub CaseSensitive {
1433 0     0 1 0 my ($class, $bool) = @_;
1434 0 0       0 $CASE_SENSITIVE_DEFAULT = $bool ? 1 : 0;
1435             }
1436              
1437             sub SearchPathOnInclude {
1438 0     0 1 0 my ($class, $bool) = @_;
1439 0 0       0 $SEARCHPATH = $bool ? 1 : 0;
1440             }
1441              
1442             sub UseQuery {
1443 0     0 1 0 my ($class, $bool) = @_;
1444 0 0       0 $DEFAULT_QUERY = $bool ? 1 : 0;
1445             }
1446              
1447             sub pushGlobalstack {
1448 9     9 0 15 my $stack = $_[0]->get_globalstack;
1449 9         13 push @$stack, $_[1];
1450 9         15 $_[0]->set_globalstack($stack);
1451             }
1452              
1453             sub popGlobalstack {
1454 9     9 0 15 my $stack = $_[0]->get_globalstack;
1455 9         10 pop @$stack;
1456 9         14 $_[0]->set_globalstack($stack);
1457             }
1458              
1459              
1460             {
1461             my $lock_fh;
1462              
1463             sub lock {
1464 31     31 0 71 my $file = File::Spec->catfile( $_[0]->get_cache_dir, "lock" );
1465 31 100       567 unless ( -f $file ) {
1466             # touch
1467 10 50       684 open $lock_fh, '>', $file
1468             or croak "Could not open lockfile '$file' for writing: $!";
1469 10         67 close $lock_fh;
1470             }
1471 31 50       644 open $lock_fh, '+<', $file
1472             or croak "Could not open lockfile '$file' for read/write: $!";
1473 31         183 flock $lock_fh, LOCK_EX;
1474             }
1475              
1476             sub unlock {
1477 31     31 0 369 close $lock_fh;
1478             }
1479             }
1480              
1481              
1482             {
1483             my $loaded = 0;
1484             my $error = 0;
1485             sub require_storable {
1486 0 0   0 0 0 return 1 if $loaded;
1487 0 0       0 return 0 if $error;
1488 0         0 eval {
1489 0         0 require Storable;
1490             };
1491 0 0       0 if ($@) {
1492 0         0 $error = 1;
1493 0         0 return 0;
1494             }
1495 0         0 eval "use B::Deparse 0.61";
1496 0 0       0 if ($@) {
1497 0         0 $error = 1;
1498 0         0 return 0;
1499             }
1500 0         0 return 1;
1501             }
1502             }
1503              
1504             sub debug_code {
1505 2     2 1 354 my ($self, $html) = @_;
1506 2         12 my $perl = $self->get_perl;
1507 2         13 require B::Deparse;
1508 2         124 my $deparse = B::Deparse->new("-p", "-sC");
1509 2         17758 my $body = $deparse->coderef2text($perl);
1510 2         10 my $filename = $self->get_file;
1511             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$body], ['body']);
1512 2         2 my $message = '';
1513 2 50 33     23 if ($LAST_EXCEPTION and $LAST_EXCEPTION =~ m/at (?:\(eval \d*\)|\S+) line (\d+)\./) {
1514 0         0 my $rline = $1;
1515 0         0 my $line = $rline;
1516 0         0 $line--;
1517 0         0 my @lines = split m#$/#, $body;
1518 0 0       0 if ($line > $#lines) {
1519 0         0 $line = $#lines;
1520             }
1521 0 0       0 my $pre = $line > 0 ? join $/, @lines[0 .. $line - 1] : '';
1522 0 0       0 my $post = $line < $#lines ? join $/, @lines[$line + 1 .. $#lines] : '';
1523 0         0 my $error = "$/$/# ------------------- ERROR line $rline in template $filename -----------------$/";
1524 0         0 my $last = $LAST_EXCEPTION;
1525 0         0 $LAST_EXCEPTION =~ s#$/# #g;
1526 0         0 $error .= "# $last$/$lines[$line]$/";
1527 0 0       0 if ($html) {
1528 0         0 for ($pre, $error, $post) {
1529 0         0 s/
1530 0         0 s/>/>/g;
1531             }
1532 0         0 $message = <<"EOM";
1533            
$pre
1534            
$error
1535            
$post
1536             EOM
1537             }
1538             else {
1539 0         0 $message .= $pre;
1540 0         0 $message .= $error;
1541 0         0 $message .= $post;
1542             }
1543             }
1544             else {
1545 2         3 $message = $LAST_EXCEPTION;
1546             }
1547 2         16 return $message;
1548              
1549             }
1550              
1551             1;
1552              
1553             __END__