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.003'; # VERSION
3 36     36   484942 use Data::Dumper;
  36         192583  
  36         1999  
4 36     36   204 use Scalar::Util;
  36         40  
  36         1944  
5 0         0 BEGIN {
6 36   50 36   140 use constant D => $ENV{HTC_DEBUG} || 0;
  36     0   48  
  36         2207  
7             }
8 36     36   141 use strict;
  36         42  
  36         699  
9 36     36   107 use warnings;
  36         47  
  36         940  
10 36     36   117 use Digest::MD5 qw/ md5_hex /;
  36         45  
  36         1456  
11              
12 36     36   138 use Carp;
  36         80  
  36         1586  
13 36     36   139 use Fcntl qw(:seek :flock);
  36         58  
  36         4561  
14 36     36   175 use File::Spec;
  36         55  
  36         887  
15 36     36   302 use File::Basename qw(dirname basename);
  36         43  
  36         2278  
16 36     36   14575 use HTML::Template::Compiled::Utils qw(:walkpath :log :escape &md5);
  36         62  
  36         6183  
17 36     36   13157 use HTML::Template::Compiled::Expression qw(:expressions);
  36         62  
  36         4640  
18 36     36   17235 use HTML::Template::Compiled::Compiler;
  36         76  
  36         1479  
19             # TODO
20             eval {
21             require URI::Escape;
22             };
23             #eval {
24             # require Encode;
25             #};
26             #my $Encode = $@ ? 0 : 1;
27              
28 36     36   188 use base 'Exporter';
  36         33  
  36         3219  
29             our @EXPORT_OK = qw(&HTC);
30 36         5814 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   16926 );
  36         83  
36 36     36   183 use vars qw($__ix__);
  36         41  
  36         1235  
37              
38 36     36   120 use constant MTIME => 0;
  36         39  
  36         1614  
39 36     36   125 use constant CHECKED => 1;
  36         39  
  36         1325  
40 36     36   112 use constant LMTIME => 2;
  36         35  
  36         1344  
41 36     36   150 use constant LCHECKED => 3;
  36         42  
  36         1276  
42              
43 36     36   116 use constant DEBUG_COMPILED => 0b001;
  36         39  
  36         1605  
44              
45 36     36   121 use constant DEBUG_CACHE_FILE_MISS => 0b0001;
  36         40  
  36         1264  
46 36     36   118 use constant DEBUG_CACHE_FILE_HIT => 0b0010;
  36         31  
  36         1504  
47 36     36   122 use constant DEBUG_CACHE_MEM_MISS => 0b0100;
  36         32  
  36         1234  
48 36     36   111 use constant DEBUG_CACHE_MEM_HIT => 0b1000;
  36         31  
  36         2077  
49              
50             our $DEBUG = 0;
51             our $LAST_EXCEPTION;
52              
53             # options / object attributes
54 36     36   126 use constant PARAM => 0;
  36         31  
  36         4713  
55              
56             BEGIN {
57 36     36   230 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         132 for my $i ( 1 .. $#map ) {
73 1332         1240 my $method = "_$map[$i]";
74 1332     9698   2160 my $get = sub { return $_[0]->[$i] };
  9698         26798  
75 1332         814 my $set;
76 1332     4583   1821 $set = sub { $_[0]->[$i] = $_[1] };
  4583         5237  
77 36     36   140 no strict 'refs';
  36         39  
  36         2105  
78 1332         812 *{"get$method"} = $get;
  1332         3084  
79 1332         788 *{"set$method"} = $set;
  1332         247743  
80             }
81             }
82              
83             # tired of typing?
84 1     1 0 296 sub HTC { __PACKAGE__->new(@_) }
85              
86             sub new {
87 140     140 1 9056327 my ( $class, %args ) = @_;
88 140         184 D && $class->log("new()");
89             # handle the "type", "source" parameter format (does anyone use it?)
90 140 100       434 if ( exists $args{type} ) {
91 3 100       8 exists $args{source} or $class->_error_no_source();
92 2 100       14 $args{type} =~ m/^(?:filename|scalarref|arrayref|filehandle)$/
93             or $class->_error_wrong_source();
94 1         3 $args{ $args{type} } = $args{source};
95 1         2 delete $args{type};
96 1         2 delete $args{source};
97             }
98 138 100       426 if (exists $args{filename}) {
    100          
    100          
    50          
99 50         254 return $class->new_file($args{filename}, %args);
100             }
101             elsif (exists $args{scalarref}) {
102 84         361 return $class->new_scalar_ref($args{scalarref}, %args);
103             }
104             elsif (exists $args{filehandle}) {
105 3         26 return $class->new_filehandle($args{filehandle}, %args);
106             }
107             elsif (exists $args{arrayref}) {
108 1         5 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   2 my ($self) = @_;
115 1   33     3 my $class = ref $self || $self;
116 1         99 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   3 my ($self) = @_;
128 1   33     7 my $class = ref $self || $self;
129 1         139 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   1 my ($self) = @_;
135 1   33     7 my $class = ref $self || $self;
136 1         180 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     3 my $class = ref $self || $self;
143 1         127 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         97 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 1037 my ($class, $filename, %args) = @_;
182 54         126 my $self = bless [], $class;
183 54         181 $self->init_args(\%args);
184 54         172 $args{path} = $self->build_path($args{path});
185 54 100 33     216 $self->_error_empty_filename()
186             if (!defined $filename or !length $filename);
187 53         72 $args{filename} = $filename;
188 53 50 66     287 if (exists $args{scalarref}
      33        
189             || exists $args{arrayref} || exists $args{filehandle}) {
190 1         3 $self->_error_template_sources;
191             }
192 52         123 $self->set_filename( $filename );
193 52         129 $self->init_cache(\%args);
194 52 50       59 my $md5path = md5_hex(@{ $args{path} || [] });
  52         328  
195 52         128 $self->set_path( $args{path} );
196 52         103 $self->set_md5_path( $md5path );
197 52 100       132 if (my $t = $self->from_cache(\%args)) {
198 11         25 $t->init_includes();
199 11         64 return $t;
200             }
201 41         217 $self->init(%args);
202 41         126 $self->from_scratch;
203 37         124 $self->init_includes;
204 37         206 return $self;
205             }
206              
207             sub new_filehandle {
208 4     4 0 100 my ($class, $filehandle, %args) = @_;
209 4         13 my $self = bless [], $class;
210 4         23 $self->init_args(\%args);
211 4 50 33     39 if (exists $args{scalarref}
      33        
212             || exists $args{arrayref} || exists $args{filename}) {
213 0         0 $self->_error_template_sources;
214             }
215 4         10 $args{filehandle} = $filehandle;
216 4         22 $args{path} = $self->build_path($args{path});
217 4         19 $self->set_filehandle( $args{filehandle} );
218 4         5 $args{cache} = 0;
219 4         17 $self->init_cache(\%args);
220 4 50       5 my $md5path = md5_hex(@{ $args{path} || [] });
  4         42  
221 4         13 $self->set_path( $args{path} );
222 4         13 $self->set_md5_path( $md5path );
223 4 50       15 if (my $t = $self->from_cache(\%args)) {
224 0         0 return $t;
225             }
226 4         26 $self->init(%args);
227 4         15 $self->from_scratch;
228 4         10 $self->init_includes;
229 4         19 return $self;
230             }
231              
232             sub new_array_ref {
233 2     2 0 279 my ($class, $arrayref, %args) = @_;
234 2 50 33     16 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         3 delete $args{arrayref};
240 2         8 return $class->new_scalar_ref($scalarref, %args);
241             }
242              
243             sub new_scalar_ref {
244 87     87 0 464 my ($class, $scalarref, %args) = @_;
245 87         165 my $self = bless [], $class;
246 87         273 $self->init_args(\%args);
247 87 50 33     477 if (exists $args{arrayref}
      33        
248             || exists $args{filehandle} || exists $args{filename}) {
249 0         0 $self->_error_template_sources;
250             }
251 87         104 $args{scalarref} = $scalarref;
252 87         280 $args{path} = $self->build_path($args{path});
253 87         246 $self->init_cache(\%args);
254 87         194 $self->set_scalar( $args{scalarref} );
255 87         203 my $text = $self->get_scalar;
256 87         330 my $md5 = md5($$text);
257             # if ($args{cache} and !$md5) {
258             # croak "For caching scalarrefs you need Digest::MD5";
259             # }
260 87         213 $self->set_filename($md5);
261 87         71 D && $self->log("md5: $md5");
262 87 50       88 my $md5path = md5_hex(@{ $args{path} || [] });
  87         371  
263 87         202 $self->set_path( $args{path} );
264 87         156 $self->set_md5_path( $md5path );
265 87 100       221 if (my $t = $self->from_cache(\%args)) {
266 1         5 return $t;
267             }
268 86         385 $self->init(%args);
269 86         240 $self->from_scratch;
270 76         181 $self->init_includes;
271 76         352 return $self;
272             }
273              
274             sub init_includes {
275 186     186 0 217 my ($self) = @_;
276 186         349 my $includes = $self->get_includes;
277 186   100     305 my $cache = $self->get_cache_dir||'';
278 186         511 for my $fullpath (keys %$includes) {
279 40         40 my ($path, $filename, $htc) = @{ $includes->{$fullpath} };
  40         81  
280 40         38 D && $self->log("checking $fullpath ($filename) $htc?");
281             # TODO check $cache
282 40         76 $cache .= '-' . $self->get_md5_path;
283             #warn __PACKAGE__.':'.__LINE__.": init_includes() $filename\n";
284 40 100 50     169 if (not $htc or HTML::Template::Compiled::needs_new_check($cache||'',$filename, $self->get_expire_time)
      100        
285             ) {
286 18         77 $htc = $self->new_from_object($path,$filename,$fullpath,$cache);
287             }
288 40         65 $includes->{$fullpath}->[2] = $htc;
289 40         152 $includes->{$fullpath}->[2]->set_plugins($self->get_plugins);
290             }
291             }
292              
293             sub build_path {
294 145     145 0 253 my ($self, $path) = @_;
295 145 100       411 unless (defined $path) {
    100          
296 83         122 $path = [];
297             }
298             elsif (!ref $path) {
299 60         113 $path = [$path];
300             }
301             defined $ENV{'HTML_TEMPLATE_ROOT'}
302 145 50       364 and push @$path, $ENV{'HTML_TEMPLATE_ROOT'};
303 145         242 return $path;
304             }
305              
306             sub from_scratch {
307 163     163 0 198 my ($self) = @_;
308 163         139 D && $self->log("from_scratch filename=".$self->get_filename);
309 163         262 my $fname = $self->get_filename;
310 163 100 100     565 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         145 my $file = $self->createFilename( $self->get_path, \$fname );
314 70         67 D && $self->log("set_file $file ($fname)");
315 70         158 $self->set_file($file);
316             }
317             elsif ( defined $fname ) {
318 87         166 $self->set_file($fname);
319             }
320 161         128 D && $self->log( "compiling... " . $self->get_filename );
321 161         399 $self->compile();
322 149         243 return $self;
323             }
324              
325             sub from_cache {
326 201     201 0 207 my ($self, $args) = @_;
327 201         212 my $t;
328 201         169 D && $self->log( "from_cache() filename=" . $self->get_filename );
329              
330 201   50     413 $args ||= {};
331 201   100     741 my $plug = $args->{plugin} || [];
332 201   66     393 my $debug = $self->get_debug || $args->{debug};
333             # try to get memory cache
334 201 100       344 if ( $self->get_cache ) {
335 151         267 my $dir = $self->get_cache_dir;
336 151 100       341 $dir = '' unless defined $dir;
337 151         259 $dir .= '-' . $self->get_md5_path;
338 151         272 my $fname = $self->get_filename;
339 151         327 $t = $self->from_mem_cache($dir,$fname, $args);
340 151 100       265 if ($t) {
341 37         69 $t->set_args($args);
342 37 50       70 if (@$plug) {
343 0         0 $t->set_plugins($plug);
344 0         0 $t->load_plugins($plug);
345             }
346 37 100       87 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       347 if ($debug->{cache} & DEBUG_CACHE_MEM_MISS) {
353 2         2 warn "### HTML::Template::Compiled Cache Debug ### MEM CACHE MISS: @{[ $self->get_filename ]}\n";
  2         3  
354             }
355             }
356 164         167 D && $self->log( "from_cache() 2 filename=" . $self->get_filename );
357              
358             # not in memory cache, try file cache
359 164 100       289 if ( $self->get_cache_dir ) {
360 33 100 66     72 my $file = $self->get_scalar || $self->get_filehandle
361             ? $self->get_filename
362             : $self->createFilename( $self->get_path, \$self->get_filename );
363 33         76 my $dir = $self->get_cache_dir;
364 33 50 33     510 if (defined $dir and not -d $dir) {
365 0         0 croak "Cachedir '$dir' does not exist";
366             }
367 33         107 $t = $self->from_file_cache($dir, $file);
368 33 100       108 if ($t) {
369 2         4 $t->set_args($args);
370 2 50       5 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         1 warn "### HTML::Template::Compiled Cache Debug ### FILE CACHE HIT: @{[ $self->get_filename ]}\n";
  1         2  
376             }
377 2         9 return $t;
378             }
379 31 100       104 if ($debug->{cache} & DEBUG_CACHE_FILE_MISS) {
380 2         2 warn "### HTML::Template::Compiled Cache Debug ### FILE CACHE MISS: @{[ $self->get_filename ]}\n";
  2         4  
381             }
382             }
383 162         158 D && $self->log( "from_cache() 3 filename=" . $self->get_filename );
384 162         434 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 40 my ($dir, $fname, $expire_time) = @_;
396 33 100       106 my $times = $times->{$dir}->{$fname} or return 1;
397 25         25 my $now = time;
398 25 100       100 return 0 if $now - $times->{checked} < $expire_time;
399 3         17 return 1;
400             }
401              
402             sub from_mem_cache {
403 151     151 0 215 my ($self, $dir, $fname, $args) = @_;
404 151         314 my $cached = $cache->{$dir}->{$fname};
405 151         233 my $times = $times->{$dir}->{$fname};
406 151         117 D && $self->log("\$cached=$cached \$times=$times \$fname=$fname\n");
407 151 100 100     405 if ( $cached && $self->uptodate($times, $args) ) {
408 37         72 return $cached->clone;
409             }
410 114         86 D && $self->log("no or old memcache");
411 114         202 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 116 my ( $self, %times ) = @_;
425 38         35 D && $self->stack(1);
426 38         70 my $dir = $self->get_cache_dir;
427 38 100       94 $dir = '' unless defined $dir;
428 38         116 my @c = caller();
429 38         166 $dir .= '-' . $self->get_md5_path;
430 38         67 my $fname = $self->get_filename;
431 38         35 D && $self->log( "add_mem_cache $fname" );
432 38         83 my $clone = $self->clone;
433 38         132 $clone->clear_params();
434 38 100       38 my @plugs = @{ $self->get_plugins || [] };
  38         67  
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         80 $clone->set_plugins(\@plugs);
443 38         85 $cache->{$dir}->{$fname} = $clone;
444 38         289 $times->{$dir}->{$fname} = \%times;
445             }
446              
447             sub clear_cache {
448 11     11 1 2875 my $dir = $_[0]->get_cache_dir;
449              
450             # clear the whole cache
451 11 100       32 $cache = {}, $times = {}, return unless defined $dir;
452              
453             # only specific directory
454 9         18 $cache->{$dir} = {};
455 9         63 $times->{$dir} = {};
456             }
457              
458             sub clear_filecache {
459 24     24 1 3012767 my ( $self, $dir ) = @_;
460 24 50       129 defined $dir
461             or $dir = $self->get_cache_dir;
462 24 100       414 return unless -d $dir;
463 22 50       50 ref $self and $self->lock;
464 22 50       592 opendir my $dh, $dir or die "Could not open '$dir': $!";
465 22         527 my @files = grep { m/(\.pl|\.storable)$/ } readdir $dh;
  87         221  
466 22         51 for my $file (@files) {
467 24         259 my $file = File::Spec->catfile( $dir, $file );
468 24 50       1386 unlink $file or die "Could not delete '$file': $!";
469             }
470 22 50       66 ref $self and $self->unlock;
471 22         268 return 1;
472             }
473              
474             sub uptodate {
475 54     54 0 69 my ( $self, $cached_times, $args ) = @_;
476 54 100       109 return 1 if $self->get_scalar;
477 53         100 my $expire_time = $self->get_expire_time;
478 53 100       108 $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         70 my $now = time;
488 53 100       159 if ( $now - $cached_times->{checked} < $expire_time ) {
489 35         122 return 1;
490             }
491             else {
492 18         46 my $file = $self->createFilename( $self->get_path, \$self->get_filename );
493 18         46 $self->set_file($file);
494             #print STDERR "uptodate($file)\n";
495 18         48 my @times = $self->_checktimes($file);
496 18 100       67 if ( $times[MTIME] <= $cached_times->{mtime} ) {
497 4         6 D && $self->log("uptodate template old");
498             # set last check time to new value
499 4         8 $cached_times->{checked} = $now;
500 4         22 return 1;
501             }
502             }
503             # template is not up to date, re-compile it
504 14         71 return 0;
505             }
506              
507              
508              
509             }
510              
511             sub compile {
512 161     161 0 170 my ($self) = @_;
513 161         129 my ( $source, $compiled );
514 161         274 my $compiler = $self->get_compiler;
515 161 100 100     264 if ( my $file = $self->get_file and !$self->get_scalar ) {
    100          
    50          
516              
517 70         68 D && $self->log( "compile from file " . $file );
518 70 50       861 die "Could not open '$file': $!" unless -f $file;
519 70         161 my @times = $self->_checktimes($file);
520 70         215 my $text = $self->_readfile($file);
521 70         610 my ( $source, $compiled ) = $compiler->compile( $self, $text, $file );
522 68         206 $self->set_perl($compiled);
523 68 100       115 $self->get_cache and $self->add_mem_cache(
524             checked => time,
525             mtime => $times[MTIME],
526             );
527 68         59 D && $self->log("compiled $file");
528              
529 68 100       127 if ( $self->get_cache_dir ) {
530 27         27 D && $self->log("add_file_cache($file)");
531 27         89 $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         146 my $md5 = $self->get_filename; # yeah, weird
540 87         87 D && $self->log("compiled $md5");
541 87         275 my ( $source, $compiled ) = $compiler->compile( $self, $$text, $md5 );
542 77         235 $self->set_perl($compiled);
543 77 100       146 if ( $self->get_cache_dir ) {
544 4         5 D && $self->log("add_file_cache($file)");
545 4         17 $self->add_file_cache(
546             $source,
547             checked => time,
548             mtime => time,
549             );
550             }
551             }
552             elsif ( my $fh = $self->get_filehandle ) {
553 4         18 local $/;
554 4         95 my $data = <$fh>;
555 4         24 my ( $source, $compiled ) = $compiler->compile( $self, $data, '' );
556 4         18 $self->set_perl($compiled);
557              
558             }
559             }
560              
561             sub add_file_cache {
562 31     31 0 92 my ( $self, $source, %times ) = @_;
563 31         84 $self->lock;
564 31         75 my $cache = $self->get_cache_dir;
565 31 50 33     349 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         65 my $filename = $self->get_filename;
570 31         632 my $lmtime = localtime $times{mtime};
571 31         294 my $lchecked = localtime $times{checked};
572 31         60 my $cachefile = "$cache/$plfile";
573 31         28 D && $self->log("add_file_cache() $cachefile");
574             {
575 31         31 require Storable;
  31         6351  
576 31         25898 require B::Deparse;
577 31         53 local $Storable::Deparse = 1;
578 31         74 my $clone = $self->clone;
579 31         85 $clone->prepare_for_cache;
580 31   100     277 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         174 };
589 31         114 Storable::store($to_cache, "$cachefile.storable");
590             }
591 31         313098 $self->unlock;
592             }
593              
594             sub get_plugin {
595 3     3 1 2 my ($self, $class) = @_;
596 3 50       4 for my $plug (@{ $self->get_plugins || [] }) {
  3         3  
597 3 50 33     14 return $plug if (ref $plug || $plug) eq $class;
598             }
599 0         0 return;
600             }
601              
602             sub from_file_cache {
603 33     33 0 59 my ($self, $dir, $file) = @_;
604 33         27 D && $self->stack;
605 33         35 D && $self->log("include file: $file");
606              
607 33         98 my $escaped = $self->escape_filename($file);
608 33         387 my $req = File::Spec->catfile( $dir, "$escaped.storable" );
609 33 100       579 return unless -f $req;
610 9         40 return $self->include_file($req);
611             }
612              
613             sub include_file {
614 10     10 0 20 my ( $self, $req ) = @_;
615 10         12 D && $self->log("do $req");
616 10         12 my $r;
617             my $t;
618             {
619 10         14 require Storable;
  10         81  
620 10         29 require B::Deparse;
621 10         20 local $Storable::Eval = 1;
622 10         13 my $cache;
623 10         16 eval {
624 10         40 $cache = Storable::retrieve($req);
625             };
626             #warn __PACKAGE__.':'.__LINE__.": error? $@\n";
627 10 50       5604 return if $@;
628 10         26 my $cached_version = $cache->{version};
629 10         13 $t = $cache->{htc};
630 10 100 100     153 if (($t->VERSION || '0.01') ne $cached_version || !$t->uptodate( $cache->{times} )) {
      66        
631             # is not uptodate
632 7         193 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       5 );
639             }
640 3         8 return $t;
641             }
642              
643             sub createFilename {
644 147     147 0 187 my ( $self, $path, $filename_ref, $cwd ) = @_;
645 147         150 my $filename = $$filename_ref;
646 147         108 D && $self->log("createFilename($path,$filename)");
647 147         120 D && $self->stack(1);
648             #warn __PACKAGE__.':'.__LINE__.": ---- createFilename($path, $$filename_ref, $cwd)\n";
649 147 50       254 if ($path) {
650 147         198 local $" = "\0";
651 147         392 my $cached = $PATHS{"@$path"}->{$filename};
652 147 100       420 return $cached if defined $cached;
653             }
654 40 50 33     481 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         45 D && $self->log( "file: " . File::Spec->catfile( $path, $filename ) );
661 40 100 66     212 if ($path && @$path) {
    50          
662 39         78 my @search = @$path;
663 39         75 for ( @search ) {
664 41         429 my $fp = File::Spec->catfile( $_, $filename );
665 41 100       870 if (-f $fp) {
666 35         48 local $" = "\0";
667 35         125 $PATHS{"@$path"}->{$filename} = $fp;
668 35         177 return $fp;
669             }
670             }
671             # not found in $path, try current template dir
672 4 100       11 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       16 if ($fp =~ m{^\Q$p\E(.*)}) {
677 1         2 my $rest = $1;
678 1         15 my (undef, @p) = File::Spec->splitdir($rest);
679 1         7 $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         3 return $filename;
691             }
692              
693             # TODO - bug with scalarref
694 3         407 croak "'$filename' not found";
695             }
696             }
697              
698             sub dump {
699 3     3 0 6 my ( $self, $var ) = @_;
700 3         22 require Data::Dumper;
701 3         5 local $Data::Dumper::Indent = 1;
702 3         5 local $Data::Dumper::Sortkeys = 1;
703 3         26 return Data::Dumper->Dump( [$var], ['DUMP'] );
704             }
705              
706             sub dump_var {
707 324     324 0 373 my ($class, $var, $varname) = @_;
708 324         302 local $Data::Dumper::Terse = 0;
709 324         320 local $Data::Dumper::Indent = 2;
710 324         278 local $Data::Dumper::Purity = 0;
711 324         288 local $Data::Dumper::Pad = "";
712 324         288 local $Data::Dumper::Useqq = 0;
713 324         278 local $Data::Dumper::Deepcopy = 0;
714 324         253 local $Data::Dumper::Quotekeys = 1;
715 324         298 local $Data::Dumper::Bless = 'bless';
716 324         362 local $Data::Dumper::Pair = ' => ';
717 324         237 local $Data::Dumper::Maxdep = 0;
718 324         256 local $Data::Dumper::Useperl = 0;
719 324         271 local $Data::Dumper::Sortkeys = 1;
720 324         1294 return Data::Dumper->Dump( [$var], [$varname] );
721             }
722              
723             sub init_cache {
724 143     143 0 166 my ($self, $args) = @_;
725 143         208 my $cachedir = $args->{file_cache_dir};
726 143 100       309 if ($args->{file_cache}) {
727 30 50       123 $self->set_cache_dir($cachedir) if $args->{file_cache};
728             }
729 143 100       444 $self->set_cache( exists $args->{cache} ? $args->{cache} : 1 );
730             }
731              
732             sub init_args {
733 145     145 0 181 my ($self, $args) = @_;
734              
735 145 50       359 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     449 if ($args->{plugin} and (ref $args->{plugin}) ne 'ARRAY') {
745 0         0 $args->{plugin} = [$args->{plugin}];
746             }
747 145   100     673 my $debug_cache_args = delete $args->{cache_debug} || 0;
748 145         177 my $debug_cache = 0;
749 145 100       283 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         6 for my $opt (@$debug_cache_args) {
756 24 100       48 if ($opt eq 'file_miss') {
    100          
    100          
    50          
757 6         7 $debug_cache |= DEBUG_CACHE_FILE_MISS;
758             }
759             elsif ($opt eq 'file_hit') {
760 6         5 $debug_cache |= DEBUG_CACHE_FILE_HIT;
761             }
762             elsif ($opt eq 'mem_miss') {
763 6         5 $debug_cache |= DEBUG_CACHE_MEM_MISS;
764             }
765             elsif ($opt eq 'mem_hit') {
766 6         7 $debug_cache |= DEBUG_CACHE_MEM_HIT;
767             }
768             }
769             }
770             }
771             # check deprecated
772 145         305 for (qw(method_call deref formatter_path default_path formatter)) {
773 725 50       1118 if (exists $args->{$_}) {
774 0         0 croak "Option $_ is deprecated, see documentation";
775             }
776             }
777 145 50       316 if (exists $args->{dumper}) {
778 0         0 croak "Option dumper is deprecated, use a plugin instead";
779             }
780              
781 145   100     522 my $debug_file = delete $args->{debug_file} || 0;
782 145 100       320 my $debug_compiled = delete $args->{debug} ? 1 : 0;
783 145         163 my $debug = 0;
784 145 100       275 $debug |= DEBUG_COMPILED if $debug_compiled;
785              
786             $args->{debug} = {
787 145         459 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       190 %{ $args->{optimize} || {} },
  145         802  
796             );
797              
798 145         2073 %$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         525 $self->set_args($args);
821             # return %defaults;
822             }
823              
824             sub init {
825 131     131 0 647 my ( $self, %args ) = @_;
826 131         310 $self->set_expire_time($args{expire_time});
827 131 100       310 $self->set_loop_context(1) if $args{loop_context_vars};
828 131         271 $self->set_case_sensitive( $args{case_sensitive} );
829 131         256 $self->set_default_escape( $args{default_escape} );
830 131         250 $self->set_default_path( $args{default_path} );
831 131         246 $self->set_use_query( $args{use_query} );
832 131         335 $self->set_chomp([$args{pre_chomp}, $args{post_chomp}]);
833 131         267 $self->set_strict( $args{strict} );
834 131         241 $self->set_optimize($args{optimize});
835 131   100     450 my $warnings = $args{warnings} || 0;
836 131 100 100     591 unless ($warnings eq 1 or $warnings eq 'fatal') {
837 127         147 $warnings = 0;
838             }
839 131         265 $self->set_warnings($warnings);
840 131         116 my $line_info = 0;
841 131 100       260 if ($args{line_info}) {
842 3         4 $line_info = 1;
843             }
844 131         248 $self->set_line_info($line_info);
845             #$self->set_use_expressions( $args{use_expressions} );
846 131 100       257 if ($args{use_expressions}) {
847 26         131 require HTML::Template::Compiled::Expr;
848             }
849 131 100       332 $args{open_mode} = '' unless length $args{open_mode};
850 131 100       242 if ($args{open_mode}) {
851 1         5 $args{open_mode} =~ s/^[<>]//; # <:utf8
852             }
853 131         288 $self->set_open_mode( $args{open_mode} );
854 131         244 $self->set_search_path( $args{search_path_on_include} );
855 131         352 $self->set_includes({});
856 131 100       276 if ( $args{filter} ) {
857 3         941 require HTML::Template::Compiled::Filter;
858             $self->set_filter(
859 3         18 HTML::Template::Compiled::Filter->new( $args{filter} ) );
860             }
861 131         269 $self->set_debug( $args{debug} );
862 131         356 $self->set_debug_file( $args{debug_file} );
863 131         306 $self->set_objects( $args{objects} );
864 131         238 $self->set_out_fh( $args{out_fh} );
865 131         249 $self->set_global_vars( $args{global_vars} );
866 131 100       266 if (my $plugins = $args{plugin}) {
867 5         11 $self->set_plugins($plugins);
868             }
869 131         290 my $compiler = $self->compiler_class->new;
870 131         282 $self->set_compiler($compiler);
871 131         144 my $tagstyle = $args{tagstyle};
872 131         115 my $parser;
873 131 100       301 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         62 );
880 33         93 $parser->set_perl($args{use_perl});
881             }
882 131 50       311 $args{parser} = ${$args{parser}} if ref $args{parser} eq 'REF';
  0         0  
883 131 50       515 if (UNIVERSAL::isa($args{parser}, 'HTML::Template::Compiled::Parser')) {
884 0         0 $parser = $args{parser};
885             }
886 131 100       321 unless ($parser) {
887 98   33     377 $parser ||= $self->parser_class->default();
888 98         309 $parser->set_perl($args{use_perl});
889 98         307 $parser->set_expressions($args{use_expressions});
890 98         280 $parser->set_strict($args{strict});
891             }
892 131         466 $parser->set_chomp([$args{pre_chomp}, $args{post_chomp}]);
893 131 100       263 if ($args{use_perl}) {
894             $parser->add_tagnames({
895             HTML::Template::Compiled::Token::OPENING_TAG() => {
896 1     0   9 PERL => [sub { 1 }],
  0         0  
897             }
898             });
899             }
900 131 100       288 if ($args{no_includes}) {
901 1         5 $parser->remove_tags(qw/ INCLUDE INCLUDE_VAR INCLUDE_STRING /);
902             }
903 131         253 $self->set_parser($parser);
904 131 100       219 if (my $plugins = $self->get_plugins) {
905 5         12 $self->init_plugins($plugins);
906 5         10 $self->set_plugins($plugins);
907             }
908             }
909              
910             {
911             my %_plugins;
912             sub load_plugins {
913 5     5 0 5 my ($self, $plugins) = @_;
914 5         8 for my $plug (@$plugins) {
915 6 100       15 next if ref $plug;
916 3 50       11 next if $_plugins{$plug};
917 3 100       9 if ($plug =~ m/^::/) {
918 1         2 $plug = "HTML::Template::Compiled::Plugin$plug";
919             }
920 3 50       6 next if $_plugins{$plug};
921 3 50       31 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         11 $self->load_plugins($plugins);
935 5         12 my $parser = $self->get_parser;
936 5         12 my $compiler = $self->get_compiler;
937 5         8 for my $plug (@$plugins) {
938 6         11 my $actions = $self->get_plugin_actions($plug);
939 6 100       14 if (my $tagnames = $actions->{tagnames}) {
940 3         9 $parser->add_tagnames($tagnames);
941             }
942 6 100       13 if (my $escape = $actions->{escape}) {
943 3   66     21 $compiler->add_escapes((ref $plug) || $plug, $escape);
944             }
945 6 100       16 if (my $tags = $actions->{compile}) {
946 3         8 $compiler->add_tags($tags);
947             }
948             }
949             }
950              
951             {
952             my $classes = {};
953              
954             sub register {
955 6     6 0 2123 my ($class, $plugins) = @_;
956 6 50       22 $plugins = [$plugins] unless ref $plugins eq 'ARRAY';
957 6         10 for my $plug (@$plugins) {
958 6         16 my $actions = $plug->register;
959 6   66     71 my $plug_class = (ref $plug) || $plug;
960 6         9 $classes->{ $plug_class} = $actions;
961 6   100     41 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     20 return $classes->{ref $pclass || $pclass};
968             }
969             }
970            
971              
972             sub _readfile {
973 70     70   97 my ( $self, $file ) = @_;
974 70         148 my $open_mode = $self->get_open_mode;
975 70 50       1938 open my $fh, "<$open_mode", $file or die "Cannot open '$file': $!";
976 70         235 local $/;
977 70         1595 <$fh>;
978             }
979              
980             sub get_code {
981 67     67 0 99 return $_[0]->get_perl;
982             }
983              
984 23     23 1 94 sub compile_early { 1 }
985              
986 149     149 1 252 sub method_call { '.' }
987 149     149 1 211 sub deref { '.' }
988 153     153 1 216 sub formatter_path { '/' }
989              
990 131     131 1 761 sub parser_class { 'HTML::Template::Compiled::Parser' }
991              
992 128     128 0 704 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         108 $f =~ s/'/\\'/g;
997 85         251 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 36 my ( $self, $walk, $path ) = @_;
1018 35   100     39 my $stack = $self->get_globalstack || [];
1019             #warn Data::Dumper->Dump([\$stack], ['stack']);
1020 35         44 for my $item ( $walk, reverse @$stack ) {
1021 55 100       79 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       81 next unless exists $item->{$path};
1027 21         406 return $item->{$path};
1028             }
1029             }
1030 13         255 return;
1031             }
1032              
1033             {
1034             sub _walk_formatter {
1035 3     3   5 my ($self, $walk, $key, $global) = @_;
1036 3         5 my $ref = ref $walk;
1037 3         3 my $fm = $HTML::Template::Compiled::Formatter::formatter;
1038 3 50       8 my $sub = exists $fm->{$ref} ? $fm->{$ref}->{$key} : undef;
1039 3         4 my $stack = [];
1040 3         4 my $new_walk;
1041 3 50       9 if ($global) {
1042 0   0     0 $stack = $self->get_globalstack || [];
1043             }
1044 3         5 for my $item ($walk, reverse @$stack) {
1045             #print STDERR "::::::: formatter $walk -> $key (sub=$sub)\n";
1046 3 50       6 if (defined $sub) {
    0          
1047 3         6 $new_walk = $sub->($walk);
1048 3         19 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         88 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 850 return 1;
1080             #return $_[1] !~ tr{a-zA-Z0-9._[]/#-}{}c;
1081             }
1082              
1083             sub escape_filename {
1084 64     64 0 95 my ( $t, $f ) = @_;
1085 64         342 $f =~ s#([/:\\])#'%'.uc sprintf"%02x",ord $1#ge;
  110         562  
1086 64         169 return $f;
1087             }
1088              
1089             sub _checktimes {
1090 88     88   99 my $self = shift;
1091 88         73 D && $self->stack;
1092 88         104 my $filename = shift;
1093 88         824 my $mtime = ( stat $filename )[9];
1094              
1095             #print STDERR "stat $filename = $mtime\n";
1096 88         192 my $checked = time;
1097 88         3336 my $lmtime = localtime $mtime;
1098 88         877 my $lchecked = localtime $checked;
1099 88         310 return ( $mtime, $checked, $lmtime, $lchecked );
1100             }
1101              
1102             sub clone {
1103 165     165 0 171 my ($self) = @_;
1104 165         996 return bless [@$self], ref $self;
1105             }
1106              
1107             sub new_scalar_from_object {
1108 1     1 0 2 my ($self, $scalar) = @_;
1109 1         2 my $new = $self->clone;
1110 1         3 $new->set_includes({});
1111 1         2 $new->set_perl(undef);
1112 1         2 $new->set_filehandle();
1113 1         2 $new->set_cache(0);
1114 1         3 $new->set_cache_dir(undef);
1115 1         3 $new->set_scalar(\$scalar);
1116 1         2 my $md5 = md5($scalar);
1117 1         3 $new->set_filename($md5);
1118 1         2 $new = $new->from_scratch;
1119 1         26 return $new;
1120             }
1121             # create from existing object (TMPL_INCLUDE)
1122             sub new_from_object {
1123 58     58 0 83 my ( $self, $path, $filename, $fullpath, $cache ) = @_;
1124 58 50       113 unless (defined $filename) {
1125 0         0 my ($file) = (caller(1))[3];
1126 0         0 croak "Filename is undef (in template $file)";
1127             }
1128 58         136 my $new = $self->clone;
1129 58         51 D && $self->log("new_from_object($path,$filename,$fullpath,$cache)");
1130 58         104 $new->set_filename($filename);
1131             #if ($fullpath) {
1132             # $self->set_file($fullpath);
1133             #}
1134 58         102 $new->set_includes({});
1135 58         103 $new->set_scalar();
1136 58         95 $new->set_filehandle();
1137 58 50       36 my $md5path = md5_hex(@{ $path || [] });
  58         291  
1138 58         114 $new->set_path($path);
1139 58         81 $new->set_md5_path( $md5path );
1140 58         105 $new->set_perl(undef);
1141 58 100       110 if (my $cached = $new->from_cache($self->get_args)) {
1142 27         45 $cached->set_plugins($self->get_plugins);
1143 27         49 $cached->init_includes;
1144 27         427 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         98 $new = $new->from_scratch;
1151 31         90 $new->init_includes;
1152 31         134 return $new;
1153             }
1154              
1155             sub prepare_for_cache {
1156 31     31 0 40 my ($self) = @_;
1157 31         71 $self->clear_params;
1158 31 100       29 my @plugs = @{ $self->get_plugins || [] };
  31         80  
1159 31         110 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         70 $self->set_plugins(\@plugs);
1167 31         58 my $includes = $self->get_includes;
1168 31         82 for my $fullpath (keys %$includes) {
1169 7         11 my ($path, $filename, $htc) = @{ $includes->{$fullpath} };
  7         16  
1170 7         32 $includes->{$fullpath} = [$path, $filename];
1171             }
1172 31         67 $self->set_parser(undef);
1173 31         52 $self->set_compiler(undef);
1174 31         57 $self->set_args(undef);
1175 31         64 $self->set_globalstack(undef);
1176             }
1177              
1178             sub preload {
1179 1     1 1 7 my ( $class, $dir ) = @_;
1180 1 50       20 opendir my $dh, $dir or die "Could not open '$dir': $!";
1181 1         23 my @files = grep { m/\.pl|\.storable$/ } readdir $dh;
  4         13  
1182 1         9 closedir $dh;
1183 1         1 my $loaded = 0;
1184 1         2 for my $file (@files) {
1185 1         14 my $success = $class->include_file( File::Spec->catfile( $dir, $file ) );
1186 1 50       5 $loaded++ if $success;
1187             }
1188 1         4 return scalar $loaded;
1189             }
1190              
1191             sub precompile {
1192 1     1 1 11 my ($class, %args) = @_;
1193 1         1 my $files = delete $args{filenames};
1194 1 50       3 return unless ref $files eq 'ARRAY';
1195 1         1 my @precompiled;
1196 1         1 for my $file (@$files) {
1197 2 50       13 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         4 push @precompiled, $htc,
1207             }
1208 1         3 return \@precompiled;
1209             }
1210              
1211             sub clear_params {
1212 74     74 1 2271 $_[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 7968 my $self = shift;
1221 114 100       261 if (!@_) {
1222 1         4 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         124 my %p;
1230 113 100       243 if (@_ == 1) {
1231 3 100       6 if ( ref $_[0] ) {
1232             # feed a hashref or object
1233 2 50       5 if (ref $_[0] eq 'HASH') {
1234             # hash, no object
1235 2         2 %p = %{ $_[0] };
  2         7  
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         327 %p = @_;
1249             }
1250              
1251 112 100       204 if ( !$self->get_case_sensitive ) {
1252 5         35 my $lc = $self->lchash( {%p} );
1253 5         38 %p = %$lc;
1254             }
1255 112         685 $self->[PARAM]->{$_} = $p{$_} for keys %p;
1256             }
1257              
1258             sub query {
1259 16     16 1 1280 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       31 return unless defined wantarray();
1267             #print STDERR "query(@_)\n";
1268             my $info = $self->get_parse_tree
1269 16 100       24 or do {
1270 1         4 $self->_error_no_query();
1271 1         60 return;
1272             };
1273 15 50       24 unless (ref $info) {
1274             # not compiled yet!
1275 0         0 $self->_error_not_compiled();
1276 0         0 return;
1277             }
1278 15         24 my $pointer = {children => $info};
1279 15 100       25 $tags = [] unless defined $tags;
1280 15 100       29 $tags = [$tags] unless ref $tags eq 'ARRAY';
1281 15         19 my $includes = $self->get_includes;
1282             my %include_info = map {
1283 11         18 $includes->{$_}->[1] => $includes->{$_}->[2]->get_parse_tree;
1284 15         13 } keys %{ $includes };
  15         25  
1285 15         22 for my $tag (@$tags) {
1286 16         14 my $value;
1287             my %includes = map {
1288 44         35 my $item = $pointer->{children}->{$_};
1289             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1290 44 100 66     91 ? (%{$include_info{$_}})
  11         17  
1291             : ()
1292 16         10 } keys %{ $pointer->{children} };
  16         26  
1293 16 100       36 if (defined ($value = $pointer->{children}->{lc $tag})) {
    50          
1294 14         23 $pointer = $value;
1295             }
1296             elsif (defined ($value = $includes{lc $tag})) {
1297 2         4 $pointer = $value;
1298             }
1299             else {
1300 0         0 return;
1301             }
1302             }
1303 15 100       35 unless ($what) {
    100          
    50          
1304             my @return = map {
1305 5         7 my $item = $pointer->{children}->{$_};
1306             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1307 5 50 33     18 ? (keys %{$include_info{$_}})
  0         0  
1308             : $_;
1309 3         2 } keys %{ $pointer->{children} };
  3         8  
1310 3         15 return @return;
1311             }
1312             elsif ($what eq 'name') {
1313 7         7 my $type = $pointer->{type};
1314 7         17 return $type;
1315             }
1316             elsif ($what eq 'loop') {
1317 5 100       8 if ($pointer->{type} eq 'LOOP') {
1318             my @return = map {
1319 9         9 my $item = $pointer->{children}->{$_};
1320             ($item->{type} eq 'INCLUDE' and $include_info{$_})
1321 9 50 33     23 ? (keys %{$include_info{$_}})
  0         0  
1322             : $_;
1323 3         3 } keys %{ $pointer->{children} };
  3         7  
1324 3         11 return @return;
1325             }
1326 2         307 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 59 my ( $self, $data ) = @_;
1345 67         71 my $lc;
1346 67 100       123 if ( ref $data eq 'HASH' ) {
    100          
1347 20         40 for my $key ( keys %$data ) {
1348 50         51 my $uc_key = lc $key;
1349 50         89 my $val = $self->lchash( $data->{$key} );
1350 50         93 $lc->{$uc_key} = $val;
1351             }
1352             }
1353             elsif ( ref $data eq 'ARRAY' ) {
1354 7         11 for my $item (@$data) {
1355 12         20 my $new = $self->lchash($item);
1356 12         24 push @$lc, $new;
1357             }
1358             }
1359             else {
1360 40         34 $lc = $data;
1361             }
1362 67         81 return $lc;
1363             }
1364              
1365             sub output {
1366 131     131 1 1288 my ( $self, $fh ) = @_;
1367 131   100     356 my $p = $self->[PARAM] || {};
1368             # if we only have an object as parameter
1369             $p = ref $p eq 'HASH'
1370 131 50       311 ? \% { $p }
  131         189  
1371             : $p;
1372 131         240 my $f = $self->get_file;
1373 131 100       327 $fh = \*STDOUT unless $fh;
1374 131 100       257 if ($DEBUG) {
1375 1         1 my $output;
1376 1         1 eval {
1377 1         2 $output = $self->get_perl()->( $self, $p, \$p, $fh );
1378             };
1379 1 50       2 if ($@) {
1380 1         2 $LAST_EXCEPTION = $@;
1381 1         1 my $filename = $self->get_file;
1382 1         3 die "Error while executing '$filename': $@";
1383             }
1384 0         0 return $output;
1385             }
1386             else {
1387 130         284 $self->get_perl()->( $self, $p, \$p, $fh );
1388             }
1389             }
1390              
1391             sub import {
1392 41     41   414 my ( $class, %args ) = @_;
1393 41 50       201 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       985 if (exists $args{short}) {
1407 1         228 carp "Usage of use option 'short' is deprecated";
1408 1         1657 __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 387 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 14 my $stack = $_[0]->get_globalstack;
1449 9         13 push @$stack, $_[1];
1450 9         17 $_[0]->set_globalstack($stack);
1451             }
1452              
1453             sub popGlobalstack {
1454 9     9 0 13 my $stack = $_[0]->get_globalstack;
1455 9         11 pop @$stack;
1456 9         12 $_[0]->set_globalstack($stack);
1457             }
1458              
1459              
1460             {
1461             my $lock_fh;
1462              
1463             sub lock {
1464 31     31 0 65 my $file = File::Spec->catfile( $_[0]->get_cache_dir, "lock" );
1465 31 100       615 unless ( -f $file ) {
1466             # touch
1467 10 50       853 open $lock_fh, '>', $file
1468             or croak "Could not open lockfile '$file' for writing: $!";
1469 10         75 close $lock_fh;
1470             }
1471 31 50       610 open $lock_fh, '+<', $file
1472             or croak "Could not open lockfile '$file' for read/write: $!";
1473 31         179 flock $lock_fh, LOCK_EX;
1474             }
1475              
1476             sub unlock {
1477 31     31 0 366 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 309 my ($self, $html) = @_;
1506 2         9 my $perl = $self->get_perl;
1507 2         10 require B::Deparse;
1508 2         69 my $deparse = B::Deparse->new("-p", "-sC");
1509 2         17181 my $body = $deparse->coderef2text($perl);
1510 2         7 my $filename = $self->get_file;
1511             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$body], ['body']);
1512 2         2 my $message = '';
1513 2 50 33     18 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         2 $message = $LAST_EXCEPTION;
1546             }
1547 2         10 return $message;
1548              
1549             }
1550              
1551             1;
1552              
1553             __END__