File Coverage

blib/lib/Template/EmbeddedPerl.pm
Criterion Covered Total %
statement 384 413 92.9
branch 64 96 66.6
condition 13 24 54.1
subroutine 93 97 95.8
pod 19 26 73.0
total 573 656 87.3


line stmt bran cond sub pod time code
1             package Template::EmbeddedPerl;
2              
3             our $VERSION = '0.001015';
4             $VERSION = eval $VERSION;
5              
6 7     7   872588 use warnings;
  7         15  
  7         421  
7 7     7   39 use strict;
  7         40  
  7         205  
8 7     7   8554 use utf8;
  7         1658  
  7         36  
9              
10 7     7   5763 use PPI::Document;
  7         1425884  
  7         346  
11 7     7   64 use File::Spec;
  7         20  
  7         198  
12 7     7   38 use Digest::MD5;
  7         15  
  7         305  
13 7     7   41 use Scalar::Util;
  7         15  
  7         256  
14 7     7   3573 use Template::EmbeddedPerl::Compiled;
  7         29  
  7         404  
15 7     7   55 use Template::EmbeddedPerl::Utils qw(normalize_linefeeds generate_error_message);
  7         16  
  7         437  
16 7     7   4351 use Template::EmbeddedPerl::SafeString;
  7         21  
  7         506  
17 7     7   4882 use Regexp::Common qw /balanced/;
  7         20027  
  7         81  
18              
19             # used for the variable interpolation feature
20             my $balanced_parens = $RE{balanced}{-parens => '()'};
21             my $balanced_brackets = $RE{balanced}{-parens => '[]'};
22             my $balanced_curlies = $RE{balanced}{-parens => '{}'};
23              
24             # Custom recursive pattern for ${...}
25             my $balanced_dollar_curly = qr/
26             \$\{
27             (?
28             [^{}]+
29             |
30             \{ (?&brace_content) \}
31             )*
32             \}
33             /x;
34              
35             my $variable_regex = qr/
36             (?
37             (?
38             (?: # Variable can be in one of two formats:
39             \$ \w+ (?: :: \w+ )* # 1. $variable, possibly with package names
40             |
41             $balanced_dollar_curly # 2. ${...}, using balanced delimiters
42             )
43             (?: # Non-capturing group for operators
44             (?:
45             (\s*)->(\s*) # Dereference operator
46             (?:
47             \$? \w+ # Method name, possibly starting with $
48             (?: $balanced_parens )? # Optional method arguments
49             |
50             $balanced_brackets # Array dereference after '->'
51             |
52             $balanced_curlies # Hash dereference after '->'
53             )
54             )
55             |
56             $balanced_brackets # Array dereference
57             |
58             $balanced_curlies # Hash dereference
59             )*
60             )
61             /x;
62              
63              
64             my $variable_regex2 = qr/
65             (?
66             (? # Named capturing group 'variable'
67             \$
68             \w+ (?: :: \w+ )* # Variable name, possibly with package names
69             (?: # Non-capturing group for operators
70             (?:
71             -> # Dereference operator
72             (?:
73             \$? \w+ # Method name
74             (?: $balanced_parens )? # Optional method arguments
75             |
76             $balanced_brackets # Array dereference after '->'
77             |
78             $balanced_curlies # Hash dereference after '->'
79             )
80             )
81             |
82             $balanced_brackets # Array dereference
83             |
84             $balanced_curlies # Hash dereference
85             )*
86             )
87             /x;
88              
89             ## New Instance of the core template methods
90              
91 62     62 1 181 sub raw { my ($self, @args) = @_; return Template::EmbeddedPerl::SafeString::raw(@args) }
  62         159  
92 1     1 1 82 sub safe { my ($self, @args) = @_; return Template::EmbeddedPerl::SafeString::safe(@args) }
  1         8  
93 36     36 1 89 sub safe_concat { my ($self, @args) = @_; return Template::EmbeddedPerl::SafeString::safe_concat(@args) }
  36         203  
94 1     1 1 7 sub html_escape { my ($self, @args) = @_; return Template::EmbeddedPerl::SafeString::html_escape(@args) }
  1         2  
95 1     1 1 5 sub url_encode { my ($self, @args) = @_; return Template::EmbeddedPerl::Utils::uri_escape(@args) }
  1         8  
96 1     1 1 2 sub escape_javascript { my ($self, @args) = @_; return Template::EmbeddedPerl::Utils::escape_javascript(@args) }
  1         36  
97              
98             sub trim {
99 16     16 1 30 my ($self, $string) = @_;
100 16 100 100     55 if ( (Scalar::Util::blessed($string)||'') eq 'Template::EmbeddedPerl::SafeString') {
101 14         96 $string =~s/^[ \t]+|[ \t]+$//g;
102 14         48 return $self->raw($string);
103             } else {
104 3         15 $string =~s/^[ \t]+|[ \t]+$//g;
105             }
106 3         42 return $string;
107             }
108              
109             sub mtrim {
110 1     1 1 6 my ($self, $string) = @_;
111 1 0 0     2 if ( (Scalar::Util::blessed($string)||'') eq 'Template::EmbeddedPerl::SafeString') {
112 1         24 $string =~s/^[ \t]+|[ \t]+$//mg;
113 1         3 return $self->raw($string);
114             } else {
115 1         2 $string =~s/^[ \t]+|[ \t]+$//mg;
116             }
117 1         31 return $string;
118             }
119              
120             sub directory_for_package {
121 1     1 1 4 my $self = shift;
122 1   0     1 my $class = ref($self) || $self;
123 1 0       4 my $package = @_ ? shift(@_) : $class;
124            
125 1         5 $package =~ s/::/\//g;
126 1         2 my $path = $INC{"${package}.pm"};
127 1         18 my ($volume,$directories,$file) = File::Spec->splitpath( $path );
128              
129 1         3 return $directories;
130             }
131              
132             sub new {
133 11     11 1 2029786 my $class = shift;
134 11         219 my (%args) = (
135             open_tag => '<%',
136             close_tag => '%>',
137             expr_marker => '=',
138             line_start => '%',
139             sandbox_ns => 'Template::EmbeddedPerl::Sandbox',
140             directories => [],
141             template_extension => 'epl',
142             auto_escape => 0,
143             auto_flatten_expr => 1,
144             prepend => '',
145             preamble => '',
146             use_cache => 0,
147             vars => 0,
148             comment_mark => '#',
149             interpolation => 0,
150             @_,
151             );
152              
153 11 50       140 %args = (%args, $class->config,) if $class->can('config');
154              
155 11         58 my $self = bless \%args, $class;
156              
157 11         63 $self->inject_helpers;
158 11         100 return $self;
159             }
160              
161             sub inject_helpers {
162 11     11 0 33 my ($self) = @_;
163 11         67 my %helpers = $self->get_helpers;
164 11         58 foreach my $helper(keys %helpers) {
165 93 100       678 if($self->{sandbox_ns}->can($helper)) {
166             warn "Skipping injection of helper '$helper'; already exists in namespace $self->{sandbox_ns}"
167 37 50       127 if $ENV{DEBUG_TEMPLATE_EMBEDDED_PERL};
168 37         134 next;
169             }
170 57     1   106 eval qq[
  1     1   2  
  1     1   19  
  1     49   3  
  49     1   161  
  1     36   28  
  36     35   137  
  35     16   200  
  16     1   49  
  1     2   5  
  2     1   7  
  1         3  
171 57         4371 package @{[ $self->{sandbox_ns} ]};
172             sub $helper { \$self->get_helpers('$helper')->(\$self, \@_) }
173 57 50       417 ]; die $@ if $@;
174             }
175             }
176              
177             sub get_helpers {
178 145     145 1 298 my ($self, $helper) = @_;
179 145 100       301 my %helpers = ($self->default_helpers, %{ $self->{helpers} || +{} });
  145         970  
180              
181 145 50       590 %helpers = (%helpers, $self->helpers) if $self->can('helpers');
182            
183 145 100       1292 return $helpers{$helper} if defined $helper;
184 11         82 return %helpers;
185             }
186              
187             sub default_helpers {
188 145     145 1 218 my $self = shift;
189             return (
190 49     49   114 raw => sub { my ($self, @args) = @_; return $self->raw(@args); },
  49         166  
191 1     1   4 safe => sub { my ($self, @args) = @_; return $self->safe(@args); },
  1         2  
192 36     36   97 safe_concat => sub { my ($self, @args) = @_; return $self->safe_concat(@args); },
  36         74  
193 1     1   2 html_escape => sub { my ($self, @args) = @_; return $self->html_escape(@args); },
  1         3  
194 1     1   5 url_encode => sub { my ($self, @args) = @_; return $self->url_encode(@args); },
  1         2  
195 1     1   18 escape_javascript => sub { my ($self, @args) = @_; return $self->escape_javascript(@args); },
  1         3  
196 16     16   26 trim => sub { my ($self, $arg) = @_; return $self->trim($arg); },
  16         77  
197 1     0   4 mtrim => sub { my ($self, $arg) = @_; return $self->mtrim($arg); },
  1         1  
198             to_safe_string => sub {
199 35     34   68 my ($self, @args) = @_;
200             return map {
201 35 100 100     63 Scalar::Util::blessed($_) && $_->can('to_safe_string')
  37         865  
202             ? $_->to_safe_string($self)
203             : $_;
204             } @args;
205             },
206 145         1860 );
207             }
208              
209             # Create a new template document in various ways
210              
211             sub from_string {
212 64     63 1 64119 my ($proto, $template, %args) = @_;
213 64         160 my $source = delete($args{source});
214 64 50       248 my $self = ref($proto) ? $proto : $proto->new(%args);
215              
216 64         135 my $digest;
217 64 50       223 if($self->{use_cache}) {
218 1         2 $digest = Digest::MD5::md5_hex($template);
219 1 0       4 if(my $cached = $self->{compiled_cache}->{$digest}) {
220 0         0 return $self->{compiled_cache}->{$digest};
221             return bless {
222             template => $cached->{template},
223             parsed => $cached->{parsed},
224             code => $cached->{code},
225 0         0 yat => $self,
226             source => $source,
227             }, 'Template::EmbeddedPerl::Compiled';
228             }
229             }
230              
231 63         317 $template = normalize_linefeeds($template);
232              
233 63         319 my @template = split(/\n/, $template);
234 63         258 my @parsed = $self->parse_template($template);
235 63         259 my $code = $self->compile(\@template, $source, @parsed);
236              
237 61 50       232 if($self->{use_cache}) {
238 0         0 $self->{compiled_cache}->{$digest} = {
239             template => \@template,
240             parsed => \@parsed,
241             code => $code,
242             };
243             }
244              
245 61         705 return bless {
246             template => \@template,
247             parsed => \@parsed,
248             code => $code,
249             yat => $self,
250             source => $source,
251             }, 'Template::EmbeddedPerl::Compiled';
252             }
253              
254             sub from_data {
255 2     2 1 35 my ($proto, $package, @args) = @_;
256              
257 2 50       212 eval "require $package;"; if ($@) {
  2         15  
258 0         0 die "Failed to load package '$package': $@";
259             }
260              
261 7     7   31210 my $data_handle = do { no strict 'refs'; *{"${package}::DATA"}{IO} };
  7         84  
  7         23170  
  2         5  
  2         4  
  2         19  
262 2 50       10 if (defined $data_handle) {
263             #my $position = tell( $data_handle );
264 2         5 my $data_content = do { local $/; <$data_handle> };
  2         11  
  2         97  
265             #seek $data_handle, $position, 0;
266 2         7 my $package_file = $package;
267 2         55 $package_file =~ s/::/\//g;
268 2         9 my $path = $INC{"${package_file}.pm"};
269 2         13 return $proto->from_string($data_content, @args, source => "${path}/DATA");
270             } else {
271 0         0 print "No __DATA__ section found in package $package.\n";
272             }
273             }
274              
275             sub from_fh {
276 0     0 1 0 my ($proto, $fh, @args) = @_;
277 0         0 my $data = do { local $/; <$fh> };
  0         0  
  0         0  
278 0         0 close $fh;
279              
280 0         0 return $proto->from_string($data, @args);
281             }
282              
283             sub from_file {
284 0     0 1 0 my ($proto, $file_proto, @args) = @_;
285 0 0       0 my $self = ref($proto) ? $proto : $proto->new(@args);
286 0         0 my $file = "${file_proto}.@{[ $self->{template_extension} ]}";
  0         0  
287              
288             # find if it exists in the directories
289 0         0 foreach my $dir (@{ $self->{directories} }) {
  0         0  
290 0 0 0     0 $dir = File::Spec->catdir(@$dir) if ((ref($dir)||'') eq 'ARRAY');
291 0         0 my $path = File::Spec->catfile($dir, $file);
292 0 0       0 if (-e $path) {
293 0 0       0 open my $fh, '<', $path or die "Failed to open file $path: $!";
294 0         0 my %args = (@args, source => $path);
295 0         0 return $self->from_fh($fh, %args);
296             }
297             }
298 0         0 die "File $file not found in directories: @{[ join ', ', @{ $proto->{directories} } ]}";
  0         0  
  0         0  
299             }
300              
301             # Methods to parse and compile the template
302              
303             sub parse_template {
304 63     63 1 146 my ($self, $template) = @_;
305 63         233 my $open_tag = $self->{open_tag};
306 63         120 my $close_tag = $self->{close_tag};
307 63         148 my $expr_marker = $self->{expr_marker};
308 63         110 my $line_start = $self->{line_start};
309 63         124 my $comment_mark = $self->{comment_mark};
310              
311             ## support shorthand line start tags ##
312              
313             # Convert all lines starting with %= to start with <%= and then add %> to the end
314 63         862 $template =~ s/^\s*${line_start}${expr_marker}(.*?)(?=\\?$)/${open_tag}${expr_marker}$1${close_tag}/mg;
315             # Convert all lines starting with % to start with <% and then add %> to the end
316             # Use negative lookahead (?!>\s*\\?$) to exclude %> closing tag from conversion
317 63         796 $template =~ s/^\s*${line_start}(?!>\s*\\?$)(.*?)(?=\\?$)/${open_tag}$1${close_tag}/mg;
318              
319             ## Escapes so you can actually have % and %= in the template
320             # Convert all lines starting with \%= to start instead with %=
321 63         433 $template =~ s/^\s*\\${line_start}${expr_marker}(.*)$/${line_start}${expr_marker}$1/mg;
322             # Convert all lines starting with \% to start instead with %
323 63         373 $template =~ s/^\s*\\${line_start}(.*)$/${line_start}$1/mg;
324              
325             # This code parses the template and returns an array of parsed blocks.
326             # Each block is represented as an array reference with two elements: the type and the content.
327             # The type can be 'expr' for expressions enclosed in double square brackets,
328             # 'code' for code blocks enclosed in double square brackets,
329             # or 'text' for plain text blocks.
330             # The content is the actual content of the block, trimmed of leading and trailing whitespace.
331              
332             #my @segments = split /(\Q${open_tag}\E.*?\Q${close_tag}\E)/s, $template;
333 63         784 my @segments = split /((?
334 63         127 my @parsed = ();
335              
336 63         182 foreach my $segment (@segments) {
337              
338 211         2628 my ($open_type, $content, $close_type) = ($segment =~ /^(\Q${open_tag}${expr_marker}\E|\Q$open_tag\E)(.*?)(\Q${expr_marker}${close_tag}\E|\Q$close_tag\E)?$/s);
339 211 100       541 if(!$open_type) {
340             # Remove \ from escaped line_start, open_tag, and close_tag
341 136         470 $segment =~ s/\\${line_start}/${line_start}/g;
342 136         416 $segment =~ s/\\${open_tag}/${open_tag}/g;
343 136         390 $segment =~ s/\\${close_tag}/${close_tag}/g;
344 136         407 $segment =~ s/\\${expr_marker}${close_tag}/${expr_marker}${close_tag}/g;
345              
346             # check the segment for comment lines
347 136         621 $segment =~ s/^[ \t]*?${comment_mark}.*?(\\?)$/$1/mg;
348 136         463 $segment =~ s/^[ \t]*?\\${comment_mark}/${comment_mark}/mg;
349              
350 136 100       386 if($self->{interpolation}) {
351 52         94 my @parts = ();
352 52         76 my $pos = 0;
353              
354 52         975 while ($segment =~ /$variable_regex/g) {
355              
356 41         161 my $match_start = $-[0];
357 41         109 my $match_end = $+[0];
358 41         311 my $matched_var = $+{variable};
359              
360             # Add non-matching part before the match
361 41 100       130 if ($match_start > $pos) {
362 39         153 my $text = substr($segment, $pos, $match_start - $pos);
363 39         102 $text =~ s/\\\$/\$/gm; # Any escaped $ (\$) should be unescaped
364 39         153 push @parts, ['text', $text ];
365             }
366              
367             # Add the matching variable
368 41         97 push @parts, ['expr', $matched_var ];
369              
370 41         373 $pos = $match_end;
371             }
372             # Add any remaining non-matching part
373 52 50       141 if ($pos < length($segment)) {
374 52         106 my $text = substr($segment, $pos);
375 52         131 $text =~ s/\\\$/\$/gm; # Any escaped $ (\$) should be unescaped
376 52         148 push @parts, [ 'text', $text ];
377             }
378 52         176 push @parsed, @parts;
379             } else {
380 84         318 push @parsed, ['text', $segment];
381             }
382             } else {
383             # Support trim with =%>
384 75 100       229 $content = "trim $content" if $close_type eq "${expr_marker}${close_tag}";
385              
386             # ?? ==%> or maybe something else...
387             # $parsed[-1][1] =~s/[ \t]+$//mg if $close_type eq "${expr_marker}${close_tag}";
388            
389             # Remove \ from escaped line_start, open_tag, and close_tag
390 75         341 $content =~ s/\\${line_start}/${line_start}/g;
391 75         357 $content =~ s/\\${open_tag}/${open_tag}/g;
392 75         226 $content =~ s/\\${close_tag}/${close_tag}/g;
393 75         278 $content =~ s/\\${expr_marker}${close_tag}/${expr_marker}${close_tag}/g;
394              
395 75 100       289 if ($open_type eq "${open_tag}${expr_marker}") {
    50          
396 28         111 push @parsed, ['expr', tokenize($content)];
397             } elsif ($open_type eq $open_tag) {
398 47         130 push @parsed, ['code', tokenize($content)];
399             }
400             }
401             }
402              
403 63         353 return @parsed;
404             }
405              
406             sub compile {
407 63     63 1 191 my ($self, $template, $source, @parsed) = @_;
408              
409 63         112 my $compiled = '';
410 63         109 my $safe_or_not = '';
411 63         100 my $flatten_or_not = '';
412              
413 63 100 66     250 if($self->{auto_escape} && $self->{auto_flatten_expr}) {
414 5         13 $safe_or_not = ' safe_concat to_safe_string ';
415             } else {
416 58 50       139 $safe_or_not = $self->{auto_escape} ? ' safe to_safe_string ' : '';
417 58 50       147 $flatten_or_not = $self->{auto_flatten_expr} ? ' join "", ' : '';
418             }
419              
420 63         135 for my $block (@parsed) {
421 291 50       5647 next if $block eq '';
422 291         595 my ($type, $content, $has_unmatched_open, $has_unmatched_closed) = @$block;
423              
424 291 100       679 if ($type eq 'expr') { # [[= ... ]]
    100          
425 69         216 $compiled .= '$_O .= ' . $flatten_or_not . $safe_or_not . $content . ";";
426             } elsif ($type eq 'code') { # [[ ... ]]
427 47         141 $compiled .= $content . ";";
428             } else {
429             # if \\n is present in the content, replace it with ''
430 175         384 my $escaped_newline_start = $content =~ s/^\\\n//mg;
431 175         360 my $escaped_newline_end = $content =~ s/\\\n$//mg;
432              
433 175         283 $content =~ s/^\\\\/\\/mg;
434 175 100       237 $compiled .= "@{[$escaped_newline_start ? qq[\n]:'' ]} \$_O .= \"" . quotemeta($content) . "\";@{[$escaped_newline_end ? qq[\n]:'' ]}";
  175 100       635  
  175         713  
435             }
436             }
437              
438 63         371 $compiled = $self->compiled($compiled);
439              
440 63 50       236 warn "Compiled: $compiled\n" if $ENV{DEBUG_TEMPLATE_EMBEDDED_PERL};
441              
442 7 100   7   72 my $code = eval $compiled; if($@) {
  7     7   17  
  7     7   330  
  7     4   41  
  7     4   67  
  7     4   586  
  7     4   50  
  7     4   14  
  7     4   70  
  4     4   38  
  4     4   8  
  4     4   149  
  4     4   23  
  4     4   7  
  4     4   268  
  4     4   24  
  4     4   10  
  4     4   38  
  4     4   37  
  4     4   13  
  4     4   122  
  4     3   21  
  4     3   10  
  4     3   306  
  4     3   32  
  4     3   22  
  4     3   25  
  4     3   40  
  4     3   10  
  4     3   155  
  4     3   24  
  4     3   12  
  4     3   320  
  4     3   27  
  4     3   9  
  4     3   27  
  4     3   39  
  4     3   11  
  4     3   134  
  4         23  
  4         8  
  4         331  
  4         28  
  4         8  
  4         27  
  4         41  
  4         10  
  4         142  
  4         22  
  4         7  
  4         286  
  4         26  
  4         69  
  4         28  
  4         35  
  4         11  
  4         158  
  4         21  
  4         9  
  4         242  
  4         25  
  4         9  
  4         24  
  3         29  
  3         8  
  3         133  
  3         31  
  3         8  
  3         209  
  3         22  
  3         6  
  3         40  
  3         22  
  3         8  
  3         105  
  3         16  
  3         4  
  3         174  
  3         18  
  3         7  
  3         15  
  3         23  
  3         5  
  3         89  
  3         13  
  3         7  
  3         321  
  3         37  
  3         9  
  3         16  
  3         23  
  3         9  
  3         125  
  3         17  
  3         6  
  3         139  
  3         15  
  3         7  
  3         14  
  3         23  
  3         8  
  3         152  
  3         18  
  3         6  
  3         165  
  3         19  
  3         6  
  3         16  
  3         26  
  3         8  
  3         120  
  3         18  
  3         6  
  3         216  
  3         19  
  3         5  
  3         18  
  63         6714  
  63         511  
443 2         16 die generate_error_message($@, $template, $source);
444             }
445              
446 61         238 return $code;
447             }
448              
449             sub compiled {
450 63     63 0 163 my ($self, $compiled) = @_;
451 63         106 my $wrapper = "package @{[ $self->{sandbox_ns} ]}; ";
  63         212  
452 63         117 $wrapper .= "use strict; use warnings; use utf8; @{[ $self->{preamble} ]}; ";
  63         230  
453 63         114 $wrapper .= "sub { my \$_O = ''; @{[ $self->{prepend} ]}; ${compiled}; return \$_O; };";
  63         206  
454 63         208 return $wrapper;
455             }
456              
457             sub tokenize {
458 75     75 0 136 my $content = shift;
459 75         484 my $document = PPI::Document->new(\$content);
460 75         192889 my ($has_unmatched_open, $has_unmatched_closed) = mark_unclosed_blocks($document);
461 75         422 return ($document, $has_unmatched_open, $has_unmatched_closed);
462             }
463              
464             sub mark_unclosed_blocks {
465 75     75 0 174 my ($element) = @_;
466 75         307 my $blocks = $element->find('PPI::Structure::Block');
467 75         43109 my $has_unmatched_open = mark_unclosed_open_blocks($element);
468 75         308 my $has_unmatched_closed = mark_unmatched_close_blocks($element);
469              
470 75         262 return ($has_unmatched_open, $has_unmatched_closed);
471             }
472              
473             sub is_control_block {
474 23     23 0 52 my ($block) = @_;
475              
476             # Get the parent of the block
477 23         94 my $parent = $block->parent;
478              
479             # Check if the parent is a control statement
480 23 50 66     282 if ($parent && ($parent->isa('PPI::Statement::Compound') || $parent->isa('PPI::Statement'))) {
      66        
481 23         76 my $keyword = $parent->schild(0); # Get the first child of the statement, which should be the control keyword
482 23 100 66     413 if ($keyword && $keyword->isa('PPI::Token::Word')) {
483             # Check if the keyword is a control structure keyword
484 21 100       72 return 1 if $keyword->content =~ /^(if|else|elsif|while|for|foreach|unless|given|when|until)$/;
485             }
486             }
487              
488 9         60 return 0;
489             }
490              
491             sub mark_unclosed_open_blocks {
492 75     75 0 199 my ($element, $level) = @_;
493 75         237 my $blocks = $element->find('PPI::Structure::Block');
494 75 100       38860 return unless $blocks;
495              
496 22         53 my $has_unmatched_open = 0;
497 22         60 foreach my $block (@$blocks) {
498 26 100       102 next if $block->finish; # Skip if closed
499 23 100       125 next if is_control_block($block);
500 9         14 $has_unmatched_open = 1;
501            
502 9 50       13 my @children = @{$block->{children}||[]};
  9         30  
503             $block->{children} = [
504 9         143 bless({ content => " " }, 'PPI::Token::Whitespace'),
505             bless({
506             children => [
507             bless({ content => " " }, 'PPI::Token::Whitespace'),
508             bless({
509             children => [
510             bless({ content => "my" }, 'PPI::Token::Word'),
511             bless({ content => " " }, 'PPI::Token::Whitespace'),
512             bless({ content => "\$_O" }, 'PPI::Token::Symbol'),
513             bless({ content => "=" }, 'PPI::Token::Operator'),
514             bless({ content => "\"\"", separator => "\"" }, 'PPI::Token::Quote::Double'),
515             ],
516             }, 'PPI::Statement::Variable'),
517             @children,
518             ],
519             }, 'PPI::Statement'),
520             ];
521             }
522 22         188 return $has_unmatched_open;
523             }
524              
525             sub mark_unmatched_close_blocks {
526 75     75 0 173 my ($element, $level) = @_;
527 75         220 my $blocks = $element->find('PPI::Statement::UnmatchedBrace');
528 75 100       40462 return unless $blocks;
529              
530 23         99 foreach my $block (@$blocks) {
531 28 100       104 next if $block eq ')'; # we only care about }
532 24 50       662 my @children = @{$block->{children}||[]};
  24         85  
533             $block->{children} = [
534 24         359 bless({ content => 'raw' }, 'PPI::Token::Word'),
535             bless({
536             children => [
537             bless({
538             children => [
539             bless({ content => '$_O' }, 'PPI::Token::Symbol'),
540             ],
541             }, 'PPI::Statement::Expression'),
542             ],
543             start => bless({ content => '(' }, 'PPI::Token::Structure'),
544             finish => bless({ content => ')' }, 'PPI::Token::Structure'),
545             }, 'PPI::Structure::List'),
546             bless({ content => ';' }, 'PPI::Token::Structure'),
547             @children,
548             ],
549             }
550 23         108 return 1;
551             }
552              
553             sub render {
554 0     0 1 0 my ($self, $template, @args) = @_;
555 0         0 my $compiled = $self->from_string($template);
556 0         0 return $compiled->render(@args);
557             }
558              
559             1;
560              
561             =head1 NAME
562              
563             Template::EmbeddedPerl - A template processing engine using embedding Perl code
564              
565             =head1 SYNOPSIS
566              
567             use Template::EmbeddedPerl;
568              
569             Create a new template object:
570              
571             my $template = Template::EmbeddedPerl->new(); # default open and close tags are '<%' and '%>'
572              
573             Compile a template from a string:
574              
575             my $compiled = $template->from_string('Hello, <%= shift %>!');
576              
577             execute the compiled template:
578              
579             my $output = $compiled->render('John');
580              
581             C<$output> is:
582              
583             Hello, John!
584              
585             You can also use class methods to create compiled templates
586             in one step if you don't need the reusable template object
587              
588             my $compiled = Template::EmbeddedPerl->from_string('Hello, <%= shift %>!');
589             my $output = $compiled->render('John');
590              
591             Or you can render templates from strings directly:
592              
593             my $template = Template::EmbeddedPerl->new(use_cache => 1); # cache compiled templates
594             my $output = $template->render('Hello, <%= shift %>!', 'John');
595              
596             Other class methods are available to create compiled templates from files, file handles,
597             and data sections. See the rest of the docs for more information.
598              
599             =head1 DESCRIPTION
600              
601             C is a template engine that allows you to embed Perl code
602             within template files or strings. It provides methods for creating templates
603             from various sources, including strings, file handles, and data sections.
604              
605             The module also supports features like helper functions, automatic escaping,
606             and customizable sandbox environments.
607              
608             Its quite similar to L and other embedded Perl template engines
609             but its got one trick the others can't do (see L below).
610              
611             B: This is a very basic template engine, which doesn't have lots of things
612             you probably need like template includes / partials and so forth. That's by
613             design since I plan to wrap this in a L view which will provide
614             all those features. If you want to use this stand alone you might need to add
615             those features yourself (or ideally put something on CPAN that wraps this to
616             provide those features). Or you can pay me to do it for you ;)
617              
618             =head1 ACKNOWLEDGEMENTS
619              
620             I looked at L and I lifted some code and docs from there. I also
621             copied some of their test cases. I was shooting for something reasonable similar
622             and potentially compatible with L but with some additional features.
623             L is similiar to how template engines in popular frameworks
624             like Ruby on Rails and also similar to EJS in the JavaScript world. So nothing weird
625             here, just something people would understand and be comfortable with. A type of
626             lowest common denominator. If you know Perl, you will be able to use this after
627             a few minutes of reading the docs (or if you've used L or L
628             you might not even need that).
629              
630             =head1 EXCUSE
631              
632             Why create yet another one of these embedded Perl template engines? I wanted one
633             that could properly handle block capture like following:
634              
635             <% my @items = map { %>
636            

<%= $_ %>

637             <% } @items %>
638              
639             Basically none of the existing ones I could find could handle this. If I'm wrong
640             and somehow there's a flag or approach in L or one of the other ones that
641             can handle this please let me know.
642              
643             L is close but you have to use C and C tags to get a similar
644             effect and it's not as flexible as I'd like plus I want to be able to use signatures in
645             code like the following:
646              
647             <%= $f->form_for($person, sub($view, $fb, $person) { %>
648            
649             <%= $fb->label('first_name') %>
650             <%= $fb->input('first_name') %>
651             <%= $fb->label('last_name') %>
652             <%= $fb->input('last_name') %>
653            
654             <% }) %>
655              
656             Again, I couldn't find anything that could do this. Its actually tricky because of the way
657             you need to localize capture of template output when inside a block. I ended up using L
658             to parse the template so I could properly find begin and end blocks and also distinguish between
659             control blocks (like C an C) blocks that have a return like C or C blocks.
660             In L you can do the following (its the same but not as pretty to my eye):
661              
662             <% my $form = $f->form_for($person, begin %>
663             <% my ($view, $fb, $person) = @_; %>
664            
665             <%= $fb->label('first_name') %>
666             <%= $fb->input('first_name') %>
667             <%= $fb->label('last_name') %>
668             <%= $fb->input('last_name') %>
669            
670             <% end; %>
671              
672             On the other hand my system is pretty new and I'm sure there are bugs and issues I haven't
673             thought of yet. So you probably want to use one of the more mature systems like L or
674             L unless you really need the features I've added. Or your being forced to use
675             it because you're working for me ;)
676              
677             =head1 TEMPLATE SYNTAX
678              
679             The template syntax is similar to other embedded Perl template engines. You can embed Perl
680             code within the template using opening and closing tags. The default tags are C<< '<%' >> and
681             C<< '%>' >>, but you can customize them when creating a new template object. You should pick
682             open and closing tags that are not common in your template content.
683              
684             All templates get C, C and C enabled by default. Please note this
685             is different than L which does not seem to have warnings enabled by default.
686             Since I like very strict templates this default makes sense to me but if you tend to play
687             fast and loose with your templates (for example you don't use C to declare variables) you
688             might not like this. Feel free to complain to me, I might change it.
689              
690             Basic Patterns:
691              
692             <% Perl code %>
693             <%= Perl expression, replaced with result %>
694              
695             Examples:
696              
697             <% my @items = qw(foo bar baz) %>
698             <% foreach my $item (@items) { %>
699            

<%= $item %>

700             <% } %>
701              
702             Would output:
703              
704            

foo

705            

bar

706            

baz

707              
708             You can also use the 'line' version of the tags to make it easier to embed Perl code, or at
709             least potentially easier to read. For example:
710              
711             % my @items = qw(foo bar baz)
712             % foreach my $item (@items) {
713            

<%= $item %>

714             % }
715              
716              
717             You can add '=' to the closing tag to indicate that the expression should be trimmed of leading
718             and trailing whitespace. This is useful when you want to include the expression in a block of text.
719             where you don't want the whitespace to affect the output.
720              
721             <% Perl code =%>
722             <%= Perl expression, replaced with result, trimmed =%>
723              
724             If you want to skip the newline after the closing tag you can use a backslash.
725              
726             <% Perl code %>\
727             <%= Perl expression, replaced with result, no newline %>\
728              
729             You probably don't care about this so much with HTML since it collapses whitespace but it can be
730             useful for other types of output like plain text or if you need some embedded Perl inside
731             your JavaScript.
732              
733             If you really need that backslash in your output you can escape it with another backslash.
734              
735             <%= "This is a backslash: " %>\\
736              
737             If you really need to use the actual tags in your output you can escape them with a backslash.
738              
739             \<% => <%
740             \<%= => <%=
741             \%> => %>
742             \%= => %=
743             \% => %
744              
745             Lastly you can add full line comments to your templates that will be removed from the final
746             output
747              
748             # This is a comment
749            

Regular HTML

750              
751             A comment is declared with a single C<#> at the start of the line (or with only whitespace preceeding it).
752             This line will be removed from the output, including its newline. If you really need a '#'you can escape it
753             with C<\#> (this is only needed if the '#' is at the beginning of the line, or there's only preceding whitespace.
754              
755             =head2 Interpolation Syntax
756              
757             If you want to embed Perl variables directly in the template without using the C<%= ... %> syntax,
758             you can enable interpolation. This allows you to embed Perl variables directly in the template
759             without using the C<%= ... %> syntax. For example:
760              
761             my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my $name = shift');
762             my $compiled = $template->from_string('Hello, $name!');
763             my $output = $compiled->render('John');
764              
765             C<$output> is:
766              
767             Hello, John!
768              
769             This works by noticing a '$' followed by a Perl variable name (and method calls, etc). So if you
770             need to put a real '$' in your code you will need to escape it with C<\$>.
771              
772             This only works on a single line and is intended to help reduce template complexity and noise
773             for simple placeholder template sections. Nevertheless I did try top make it work with reasonable
774             complex single variable expressions. Submit a test case if you find something that doesn't work
775             which you think should.
776              
777             See the section on the interpolation configuration switch below for more information. This is
778             disabled by default and I consider it experimental at this time since parsing Perl code with
779             regular expressions is a bit of a hack.
780              
781             =head1 METHODS
782              
783             =head2 new
784              
785             my $template = Template::EmbeddedPerl->new(%args);
786              
787             Creates a new C object. Accepts the following arguments:
788              
789             =over 4
790              
791             =item * C
792              
793             The opening tag for template expressions. Default is C<< '<%' >>. You should use
794             something that's not common in your template content.
795              
796             =item * C
797              
798             The closing tag for template expressions. Default is C<< '%>' >>.
799              
800             =item * C
801              
802             The marker indicating a template expression. Default is C<< '=' >>.
803              
804             =item * C
805              
806             The namespace for the sandbox environment. Default is C<< 'Template::EmbeddedPerl::Sandbox' >>.
807             Basically the template is compiled into an anponymous subroutine and this is the namespace
808             that subroutine is executed in. This is a security feature to prevent the template from
809             accessing the outside environment.
810              
811             =item * C
812              
813             An array reference of directories to search for templates. Default is an empty array.
814             A directory to search can be either a string or an array reference containing each part
815             of the path to the directory. Directories will be searched in order listed.
816              
817             my $template = Template::EmbeddedPerl->new(directories=>['/path/to/templates']);
818             my $template = Template::EmbeddedPerl->new(directories=>[['/path', 'to', 'templates']]);
819              
820             I don't do anything smart to make sure you don't reference templates in dangerous places.
821             So be careful to make sure you don't let application users specify the template path.
822              
823             =item * C
824              
825             The file extension for template files. Default is C<< 'epl' >>. So for example:
826              
827             my $template = Template::EmbeddedPerl->new(directories=>['/path/to/templates', 'path/to/other/templates']);
828             my $compiled = $template->from_file('hello');
829              
830             Would look for a file named C in the directories specified.
831              
832             =item * C
833              
834             Boolean indicating whether to automatically escape content. Default is C<< 0 >>.
835             You probably want this enabled for web content to prevent XSS attacks. If you have this
836             on and want to return actual HTML you can use the C helper function. Example:
837            
838             <%= raw 'Example' %>
839              
840             Obviously you need to be careful with this.
841              
842             The C helper (also available as a method on the template object) takes the string
843             and turns it into an instance of L. The auto escape
844             code when it sees that object knows to pass it thru without trying to escape it. The
845             C helper has a version called C which does any needed encoding first (or passes
846             unchanged any already created safe string objects).
847              
848             If the value is an object that does C then the object will first be converted
849             to a safe string by calling it (with the template object as the first parameter). That will
850             allow you to safely stringify objects without needing to do so manually.
851              
852             B we only check for objects with the C method when using C
853             If you are not using this safety feature and you are manually performing any needed escaping
854             then you can just use ordinary overloading to stringify your object values.
855              
856             =item * C
857              
858             Boolean indicating whether to automatically flatten expressions. Default is C<< 1 >>.
859             What this means is that if you have an expression that returns an array we will join
860             the array into a string before outputting it. Example:
861              
862             <% my @items = qw(foo bar baz); %>
863             <%= map { "$_ " } @items %>
864              
865             Would output:
866              
867             foo bar baz
868              
869             =item * C
870              
871             Add Perl code to the 'preamble' section of the compiled template. This is to top of the generated
872             script prior to the anonymous sub representing your template.Default is an empty string. For example
873             you can enable modern Perl features like signatures by setting this to C<< 'use v5.40;' >>.
874              
875             Use this to setup any pragmas or modules you need to use in your template code.
876              
877             =item * C
878              
879             Perl code to prepend to the compiled template. Default is an empty string. This goes just inside the
880             anonyous subroutine that is called to return your document string. For example you can use this to
881             pull passed arguments off C<@_>.
882              
883             =item * C
884              
885             A hash reference of helper functions available to the templates. Default is an empty hash.
886             You can add your own helper functions to this hash and they will be available to the templates.
887             Example:
888              
889             my $template = Template::EmbeddedPerl->new(helpers => {
890             my_helper => sub { return 'Hello, World!' },
891             });
892              
893             =item * C
894              
895             Boolean indicating whether to cache compiled templates. Default is C<< 0 >>.
896             If you set this to C<< 1 >>, the module will cache compiled templates in memory. This is
897             only useful if you are throwing away the template object after compiling a template.
898             For example:
899              
900             my $ep = Template::EmbeddedPerl->new(use_cache => 1);
901             my $output = $ep->render('Hello, <%= shift %>!', 'John');
902              
903             In the case above since you are not capturing the compiled template object each time
904             you call C you are recompiling the template. which could get expensive.
905              
906             On the other hand if you are keeping the template object around and reusing it you don't
907             need to enable this. Example:
908              
909             my $ep = Template::EmbeddedPerl->new(use_cache => 1);
910             my $compiled = $ep->from_string('Hello, <%= shift %>!');
911             my $output = $compiled->render('John');
912              
913             In the valid above the compiled template is cached and reused each time you call C.
914              
915             Obviously this only works usefully in a persistent environment like mod_perl or a PSGI server.
916              
917             =item * C
918              
919             Defaults to '#'. Indicates the beginning of a comment in the template which is to be removed
920             from the output.
921              
922             =item * C
923              
924             Boolean indicating whether to enable interpolation in the template. Default is C<< 0 >> (disabled).
925              
926             Interpolation allows you to embed Perl variables directly in the template without using the
927             C<%= ... %> syntax. For example:
928              
929             my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($name) = @_');
930             my $output = $template->render('Hello, $name!', 'John');
931              
932             This will output:
933              
934             Hello, John!
935              
936             Interpolation is reasonable sophisticated and will handle many cases including have more
937             then one variable in a line. For example:
938              
939             my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($first, $last) = @_');
940             my $output = $template->render('Hello, $first $last!', 'John', 'Doe');
941              
942             This will output:
943              
944             Hello, John Doe!
945              
946             It can also handle variables that are objects and call methods on them. For example:
947              
948             my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($person_obj) = @_');
949             my $output = $template->render('Hello, $person_obj->first_name $person_obj->last_name!', Person->new('John', 'Doe'));
950              
951             This will output:
952              
953             Hello, John Doe!
954              
955             If you need to disambiguate a variable from following text you enclose the variable in curly braces.
956              
957             my $template = Template::EmbeddedPerl->new(interpolation => 1);
958             my $output = $template->render('Hello, ${arg}XXX', 'John');
959              
960             This will output:
961              
962             Hello, JohnXXX
963              
964             You can nest method calls and the methods can contain arguments of varying complexity, including
965             anonymous subroutines. There is a limited ability to span lines; you make break lines across the
966             deference operator and in many cases across balanced parenthesis, square and curly brackets. If you
967             do so you cannot mix 'template text' with the Perl code. For example:
968              
969            
970             $person->profile
971             ->first_name
972            
973              
974             and
975              
976             $obj->compute(
977             sub {
978             my $arg = shift;
979             return $arg * 2;
980             }
981             )
982              
983             are both valid. But this will fail:
984              
985            

986             $arg->compute(sub {
987             my $value = shift;
988             $value = $value * 5
989            
$value
990             })
991            

992              
993             For this case you need to use the C<%= ... %> syntax or %= and %:
994              
995            

996             %= $arg->compute(sub {
997             % my $value = shift;
998             % $value = $value * 5
999            
$value
1000             % })
1001            

1002              
1003             You can review the existing test case at C for examples.
1004              
1005             This works by noticing a '$' followed by a Perl variable name (and method calls, etc). So if you
1006             need to put a real '$' in your code you will need to escape it with C<\$>. It does not work
1007             for other perl sigils at this time (for example '@' or '%').
1008              
1009             This feature is experimental so if you have trouble with it submit a trouble ticket with test
1010             case (review the C test cases for examples of the type of test cases I need).
1011             I intend interpolation to be a 'sweet spot' feature that tries to reduce amount of typing
1012             and overall template 'noise', not something that fully parses Perl code. Anything super crazy
1013             should probably be encapsulated in a helper function anyway.
1014              
1015             =back
1016              
1017             =head2 from_string
1018              
1019             my $compiled = $template->from_string($template_string, %args);
1020              
1021             Creates a compiled template from a string. Accepts the template content as a
1022             string and optional arguments to modify behavior. Returns a
1023             C object.
1024              
1025             pass 'source => $path' to the arguments to specify the source of the template if you
1026             want neater error messages.
1027              
1028             This can be called as a class method as well::
1029              
1030             my $compiled = Template::EmbeddedPerl->from_string($template_string, %args);
1031              
1032             Useful if you don't need to keep the template object around. This works
1033             for all the other methods as well (C, C, C).
1034              
1035             =head2 from_file
1036              
1037             my $compiled = $template->from_file($file_name, %args);
1038              
1039             Creates a compiled template from a file. Accepts the filename (without extension)
1040             and optional arguments. Searches for the file in the directories specified during
1041             object creation.
1042              
1043             =head2 from_fh
1044              
1045             my $compiled = $template->from_fh($filehandle, %args);
1046              
1047             Creates a compiled template from a file handle. Reads the content from the
1048             provided file handle and processes it as a template.
1049              
1050             pass 'source => $path' to the arguments to specify the source of the template if you
1051             want neater error messages.
1052              
1053             =head2 from_data
1054              
1055             my $compiled = $template->from_data($package, %args);
1056              
1057             Creates a compiled template from the __DATA__ section of a specified package.
1058             Returns a compiled template object or dies if the package cannot be loaded or
1059             no __DATA__ section is found.
1060              
1061             =head2 trim
1062              
1063             my $trimmed = $template->trim($string);
1064              
1065             Trims leading and trailing whitespace from the provided string. Returns the
1066             trimmed string.
1067              
1068             =head2 mtrim
1069              
1070             Same as C but trims leading and trailing whitespace for a multiline string.
1071              
1072             =head2 default_helpers
1073              
1074             my %helpers = $template->default_helpers;
1075              
1076             Returns a hash of default helper functions available to the templates.
1077              
1078             =head2 get_helpers
1079              
1080             my %helpers = $template->get_helpers($helper_name);
1081              
1082             Returns a specific helper function or all helper functions if no name is provided.
1083              
1084             =head2 parse_template
1085              
1086             my @parsed = $template->parse_template($template);
1087              
1088             Parses the provided template content and returns an array of parsed blocks.
1089              
1090             =head2 compile
1091              
1092             my $code = $template->compile($template, @parsed);
1093              
1094             Compiles the provided template content into executable Perl code. Returns a
1095             code reference.
1096              
1097             =head2 directory_for_package
1098              
1099             my $directory = $template->directory_for_package($package);
1100              
1101             Returns the directory containing the package file.
1102             If you don't provide a package name it will use the current package for C<$template>.
1103              
1104             Useful if you want to load templates from the same directory as your package.
1105              
1106             =head2 render
1107              
1108             my $output = $template->render($template, @args);
1109              
1110             Compiles and executes the provided template content with the given arguments. You might
1111             want to enable the cache if you are doing this.
1112              
1113             =head1 HELPER FUNCTIONS
1114              
1115             The module provides a set of default helper functions that can be used in templates.
1116              
1117             =over 4
1118              
1119             =item * C
1120              
1121             Returns a string as a safe string object without escaping. Useful if you
1122             want to return actual HTML to your template but you better be
1123             sure that HTML is safe.
1124              
1125             <%= raw 'Example' %>
1126              
1127             =item * C
1128              
1129             Returns a string as a safe html escaped string object that will not be
1130             escaped again.
1131              
1132             =item * C
1133              
1134             Like C but for multiple strings. This will concatenate the strings into
1135             a single string object that will not be escaped again.
1136              
1137             =item * C
1138              
1139             Escapes HTML entities in a string. This differs for C in that it will
1140             just do the escaping and not wrap the string in a safe string object.
1141              
1142             =item * C
1143              
1144             Encodes a string for use in a URL.
1145              
1146             =item * C
1147              
1148             Escapes JavaScript entities in a string. Useful for making strings safe to use
1149             in JavaScript.
1150              
1151             =item * C
1152              
1153             Trims leading and trailing whitespace from a string.
1154              
1155             =back
1156              
1157             =head1 ERROR HANDLING
1158              
1159             If an error occurs during template compilation or rendering, the module will
1160             throw an exception with a detailed error message. The error message includes
1161             the source of the template, the line number, and the surrounding lines of the
1162             template to help with debugging. Example:
1163              
1164             Can't locate object method "input" at /path/to/templates/hello.yat line 4.
1165              
1166             3: <%= label('first_name') %>
1167             4: <%= input('first_name') %>
1168             5: <%= errors('last_name') %>
1169              
1170             =head1 ENVIRONMENT VARIABLES
1171              
1172             The module respects the following environment variables:
1173              
1174             =over 4
1175              
1176             =item * C
1177              
1178             Set this to a true value to print the compiled template code to the console. Useful
1179             when trying to debug difficult compilation issues, especially given this is early
1180             access code and you might run into bugs.
1181              
1182             =back
1183              
1184             =head1 REPORTING BUGS & GETTING HELP
1185              
1186             If you find a bug, please report it on the GitHub issue tracker at
1187             L. The bug tracker is
1188             the easiest way to get help with this module from me but I'm also on irc.perl.org
1189             under C.
1190              
1191             =head1 DEDICATION
1192              
1193             This module is dedicated to the memory of my dog Bear who passed away on 17 August 2024.
1194             He was a good companion and I miss him.
1195              
1196             If this module is useful to you please consider donating to your local animal shelter
1197             or rescue organization.
1198              
1199             =head1 AUTHOR
1200              
1201             John Napiorkowski, C<< >>
1202              
1203             =head1 LICENSE AND COPYRIGHT
1204              
1205             This library is free software; you can redistribute it and/or modify it under the
1206             same terms as Perl itself.
1207              
1208             =cut
1209              
1210              
1211             __END__