File Coverage

blib/lib/HTML/KTemplate.pm
Criterion Covered Total %
statement 279 293 95.2
branch 184 226 81.4
condition 42 69 60.8
subroutine 20 22 90.9
pod 10 10 100.0
total 535 620 86.2


line stmt bran cond sub pod time code
1              
2             #=======================================================================
3             #
4             # Copyright (c) 2002-2003 Kasper Dziurdz. All rights reserved.
5             #
6             # This module is free software; you can redistribute it and/or
7             # modify it under the same terms as Perl itself.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # Artistic License for more details.
13             #
14             # Please email me any comments, questions, suggestions or bug
15             # reports to:
16             #
17             #=======================================================================
18              
19             package HTML::KTemplate;
20 1     1   13570 use strict;
  1         3  
  1         39  
21 1     1   5 use Carp;
  1         4  
  1         67  
22 1     1   5 use File::Spec;
  1         6  
  1         35  
23              
24 1         5844 use vars qw(
25             $VAR_START_TAG $VAR_END_TAG
26             $BLOCK_START_TAG $BLOCK_END_TAG
27             $INCLUDE_START_TAG $INCLUDE_END_TAG
28             $ROOT $CHOMP $VERSION $CACHE
29             $FIRST $INNER $LAST
30 1     1   4 );
  1         1  
31              
32             $VERSION = '1.33';
33              
34             $VAR_START_TAG = '[%';
35             $VAR_END_TAG = '%]';
36              
37             $BLOCK_START_TAG = '';
39              
40             $INCLUDE_START_TAG = '';
42              
43             $ROOT = undef;
44             $CHOMP = 1;
45             $CACHE = {};
46              
47             $FIRST = { 'FIRST' => 1, 'first' => 1 };
48             $INNER = { 'INNER' => 1, 'inner' => 1 };
49             $LAST = { 'LAST' => 1, 'last' => 1 };
50              
51              
52             sub TEXT () { 0 }
53             sub VAR () { 1 }
54             sub BLOCK () { 2 }
55             sub FILE () { 3 }
56             sub IF () { 4 }
57             sub ELSE () { 5 }
58             sub UNLESS () { 6 }
59             sub LOOP () { 7 }
60              
61             sub TYPE () { 0 }
62             sub IDENT () { 1 }
63             sub STACK () { 2 }
64              
65             sub NAME () { 0 }
66             sub PATH () { 1 }
67              
68              
69             sub new {
70              
71 36     36 1 2629 my $class = shift;
72 36         355 my $self = {
73             'vars' => [{}], # values for template vars
74             'loop' => [], # loop context variables
75             'block' => undef, # current block reference
76             'files' => [], # file paths for include
77             'output' => '', # template output
78             'config' => { # configuration
79             'cache' => 0,
80             'strict' => 0,
81             'no_includes' => 0,
82             'max_includes' => 15,
83             'loop_vars' => 0,
84             'blind_cache' => 0,
85             'include_vars' => 0,
86             'parse_vars' => 0,
87             },
88             };
89              
90 36 100       100 $self->{'config'}->{'root'} = shift if @_ == 1;
91 36 50       76 croak('Odd number of option parameters') if @_ % 2 != 0;
92              
93             # load in all option parameters
94 36         122 $self->{'config'}->{$_} = shift while $_ = lc shift;
95              
96 36 100       116 $self->{'config'}->{'root'} = $ROOT
97             unless exists $self->{'config'}->{'root'};
98              
99 36 50       71 $self->{'config'}->{'cache'} = 1
100             if $self->{'config'}->{'blind_cache'};
101              
102 36         70 bless ($self, $class);
103 36         76 return $self;
104              
105             }
106              
107              
108             sub assign {
109              
110 59     59 1 372 my $self = shift;
111 59         55 my ($target, $block);
112              
113             # odd number of arguments: block
114 59 100 100     139 if (@_ % 2 != 0 && @_ >= 3) {
115 1         3 $self->block(shift);
116 1         1 ++$block;
117             }
118            
119             # if a block reference is defined,
120             # assign the variables to the block
121 25         63 $target = defined $self->{'block'}
122 59 100       130 ? $self->{'block'}->[ $#{ $self->{'block'} } ]
123             : $self->{'vars'}->[0];
124              
125 59 100       103 if (ref $_[0] eq 'HASH') {
126             # copy data for faster variable lookup
127 3         5 @{ $target }{ keys %{$_[0]} } = values %{$_[0]};
  3         6  
  3         7  
  3         9  
128             } else {
129 56         150 my %assign = @_;
130 56         108 @{ $target }{ keys %assign } = values %assign;
  56         153  
131             }
132            
133             # remove block reference
134 59 100       116 $self->block() if $block;
135              
136 59         106 return 1;
137              
138             }
139              
140              
141             sub block {
142             # - creates a new loop in the defined block
143             # - sets a reference so all future variable values will
144             # be assigned there (until this method is called again)
145              
146 46     46 1 173 my $self = shift;
147 46         40 my (@ident, $root, $key, $last_key);
148            
149             # no argument: undefine block reference
150 46 100 66     140 if (!defined $_[0] || !length $_[0]) {
151 5         7 $self->{'block'} = undef;
152 5         10 return 1;
153             }
154            
155 41         170 push @ident, split /\./, shift while @_;
156 41         50 $last_key = pop @ident;
157            
158 41         59 $root = $self->{'vars'}->[0];
159            
160 41         52 foreach $key (@ident) {
161            
162             # hash reference: perfect!
163 40 100 66     132 if (ref $root->{$key} eq 'HASH') {
  22 100       70  
164 9         15 $root = $root->{$key};
165             }
166            
167             # array reference: block continues in hash
168             # reference at the end of the array
169             elsif (ref $root->{$key} eq 'ARRAY'
170             && ref $root->{$key}->[ $#{ $root->{$key} } ] eq 'HASH' ) {
171 22         27 $root = $root->{$key}->[ $#{ $root->{$key} } ];
  22         52  
172             }
173            
174             else { # create new hash reference
175 9         25 $root = $root->{$key} = {};
176             }
177            
178             }
179            
180 41 100       82 if (ref $root->{$last_key} eq 'ARRAY') {
181             # block exists: add new loop
182 26         23 push @{ $root->{$last_key} }, {};
  26         52  
183             } else {
184             # create new block
185 15         33 $root->{$last_key} = [{}];
186             }
187            
188 41         95 $self->{'block'} = $root->{$last_key};
189            
190 41         69 return 1;
191            
192             }
193              
194              
195             sub process {
196              
197 39     39 1 222 my $self = shift;
198              
199 39         65 foreach (@_) {
200 41 50       69 next unless defined;
201 41         77 $self->_include($_);
202             }
203              
204 36         80 return 1;
205            
206             }
207              
208              
209             sub _include {
210              
211 69     69   81 my $self = shift;
212 69         83 my $filename = shift;
213 69         66 my ($stack, $filepath);
214            
215             # check whether includes are disabled
216 69 100 100     180 if ($self->{'config'}->{'no_includes'} && scalar @{ $self->{'files'} } != 0) {
  2         10  
217 1 50       200 croak('Include blocks are disabled at ' . $self->{'files'}->[0]->[NAME])
218             if $self->{'config'}->{'strict'};
219 0         0 return; # no strict
220             }
221            
222             # check for recursive includes
223 68         537 croak('Recursive includes: maximum recursion depth of ' . $self->{'config'}->{'max_includes'} . ' files exceeded')
224 68 100       66 if scalar @{ $self->{'files'} } > $self->{'config'}->{'max_includes'};
225              
226 67         127 ($stack, $filepath) = $self->_load($filename);
227            
228             # add file path to use as include path
229 67 100       145 unshift @{ $self->{'files'} }, [ $filename, $filepath ]
  62         151  
230             if defined $filepath;
231            
232             # create output
233 67         166 $self->_output($stack);
234            
235             # delete file info if it was added
236 49 100       115 shift @{ $self->{'files'} } if defined $filepath;
  44         251  
237              
238             }
239              
240              
241             sub _load {
242             # - loads the template file from cache or hard drive
243             # - returns the parsed stack and the full template path
244              
245 67     67   69 my $self = shift;
246 67         67 my $filename = shift;
247 67         64 my ($filepath, $mtime, $filedata);
248            
249             # slurp the file
250 67         176 local $/ = undef;
251            
252             # when the passed argument is a reference to a scalar,
253             # array or file handle, load and use it as template
254              
255 67 100       125 if (ref $filename eq 'SCALAR') {
256             # skip undef and do not change passed scalar
257 2 50       7 $filedata = defined $$filename ? $$filename : '';
258 2         6 return $self->_parse(\$filedata, '[scalar_ref]');
259             }
260              
261 65 100       109 if (ref $filename eq 'ARRAY') {
262 1         3 $filedata = join("", @$filename);
263 1         6 return $self->_parse(\$filedata, '[array_ref]');
264             }
265              
266 64 100       98 if (ref $filename eq 'GLOB') {
267 1         18 $filedata = readline($$filename);
268 1 50       3 $filedata = '' unless defined $filedata; # skip undef
269 1         4 return $self->_parse(\$filedata, '[file_handle]');
270             }
271              
272             # file handle (no reference)
273 63 100       140 if (ref \$filename eq 'GLOB') {
274 1         14 $filedata = readline($filename);
275 1 50       4 $filedata = '' unless defined $filedata; # skip undef
276 1         3 return $self->_parse(\$filedata, '[file_handle]');
277             }
278              
279 62         117 ($filepath, $mtime) = $self->_find($filename);
280            
281 62 50       145 croak("Can't open file $filename: file not found")
282             unless defined $filepath;
283            
284 62 50       132 if ($self->{'config'}->{'cache'}) {
285             # load parsed template from cache
286 0         0 $filedata = $CACHE->{$filepath};
287            
288 0 0 0     0 return ($filedata->[0], $filepath)
289             if $self->{'config'}->{'blind_cache'} && defined $filedata;
290 0 0 0     0 return ($filedata->[0], $filepath)
291             if defined $filedata && $filedata->[1] == $mtime;
292             }
293            
294 62 50       1666 open (TEMPLATE, '<' . $filepath) ||
295             croak("Can't open file $filename: $!");
296 62         1243 $filedata =