File Coverage

blib/lib/ASP4/PageParser.pm
Criterion Covered Total %
statement 147 149 98.6
branch 18 24 75.0
condition 2 2 100.0
subroutine 15 15 100.0
pod 0 3 0.0
total 182 193 94.3


line stmt bran cond sub pod time code
1              
2             package ASP4::PageParser;
3              
4 9     9   634 use strict;
  9         11  
  9         266  
5 9     9   33 use warnings 'all';
  9         15  
  9         276  
6 9     9   637 use ASP4::ConfigLoader;
  9         12  
  9         195  
7 9     9   3086 use ASP4::Page;
  9         17  
  9         220  
8 9     9   3709 use ASP4::MasterPage;
  9         22  
  9         15743  
9              
10              
11             sub new
12             {
13 19     19 0 935 my ($class, %args) = @_;
14            
15             my $s = bless {
16             script_name => $args{script_name},
17 19         178 filename => undef,
18             package => undef,
19             compiled_as => undef,
20             base_class => undef,
21             source_code => \"",
22             }, $class;
23 19         78 $s->_init();
24            
25 19         166 return $s;
26             }# end new()
27              
28 99     99 0 441 sub source_code { shift->{source_code} }
29              
30              
31             sub _init
32             {
33 19     19   27 my $s = shift;
34            
35 19         89 my $config = ASP4::ConfigLoader->load();
36 19         234 my $filename = $config->web->www_root . $s->{script_name};
37 19         191 (my $package = $s->{script_name}) =~ s/[^a-z0-9]/_/ig;
38 19         105 $package = $config->web->application_name . '::' . $package;
39 19         172 (my $compiled_as = "$package.pm") =~ s/::/\//g;
40            
41             # What we know so far:
42 19         51 $s->{filename} = $filename;
43 19         41 $s->{package} = $package;
44 19         35 $s->{compiled_as} = $compiled_as;
45 19         102 $s->{saved_to} = $config->web->page_cache_root . "/$compiled_as";
46             }# end _init()
47              
48              
49             sub parse
50             {
51 19     19 0 54 my $s = shift;
52            
53             # Open up the file:
54             open my $ifh, '<', $s->{filename}
55 19 50       1407 or die "Cannot open '$s->{filename}' for reading: $!";
56 19         98 local $/;
57 19         363 $s->{source_code} = \scalar(<$ifh>);
58            
59 19         116 my $directives = $s->_get_directives;
60 19 100       98 if( my $master_uri = $directives->{page}->{usemasterpage} )
    100          
61             {
62 5         56 $s->{masterpage} = ASP4::PageLoader->load( script_name => $master_uri );
63 5         20 $s->{base_class} = $s->{masterpage}->{package};
64             }
65             elsif( $directives->{masterpage} )
66             {
67 3         10 $s->{base_class} = 'ASP4::MasterPage';
68             }
69             else
70             {
71 11         34 $s->{base_class} = 'ASP4::Page';
72             }# end if()
73            
74 19         86 $s->_parse_scriptlet_tags;
75 19         75 $s->_parse_include_tags;
76 19         45 my $ref = $s->source_code;
77            
78             # The ... tags:
79 19         28 my $ident = 0;
80 19         36 my @placeholder_tags = ( );
81 19         28 my $depth = 0;
82             PLACEHOLDERS: {
83 19         29 my @stack = ( );
  19         35  
84 19         793 foreach my $tag ( $$ref =~ m{(|)}gis )
85             {
86 26 100       55 if( $tag =~ m{^
87             {
88             # It's an "end" tag:
89 13         13 my $item = pop(@stack);
90 13         8 $depth--;
91            
92 13         15 my $repl = $item->{end_tag};
93 13         60 $$ref =~ s{$tag}{$repl}s;
94 13         22 unshift @placeholder_tags, $item;
95             }
96             else
97             {
98             # It's a "start" tag:
99 13         46 my ($id) = $tag =~ m{}is;
100 13         32 push @stack, {
101             ident => $ident,
102             id => $id,
103             depth => $depth++,
104             line => $s->_tag_line_number( $tag ),
105             start_tag => '______INP_' . sprintf('%03d',$ident) . '______',
106             end_tag => '______OUTP_' . sprintf('%03d',$ident) . '______'
107             };
108 13         15 $ident++;
109 13         16 my $repl = $stack[-1]->{start_tag};
110 13         127 $$ref =~ s{\Q$tag\E}{$repl}s;
111             }# end if()
112             }# end foreach()
113             };
114            
115 19         86 foreach my $tag ( sort {$b->{depth} <=> $a->{depth} } @placeholder_tags )
  20         24  
116             {
117 13         16 my $start = $tag->{start_tag};
118 13         12 my $end = $tag->{end_tag};
119 13         139 my ($contents) = $$ref =~ m{$start(.*?)$end}s;
120              
121 13         28 $tag->{contents} = "\$Response->Write(q~$contents~);";
122 13         139 $$ref =~ s{$start\Q$contents\E$end}{\~); \$__self->$tag->{id}(\$__context); \$Response->Write(q\~}s;
123             }# end foreach()
124            
125             # The ... tags:
126 19         39 my @content_tags = ( );
127             CONTENT: {
128 19         177 my @stack = ( );
  19         37  
129 19         531 foreach my $tag ( $$ref =~ m{(|)}gis )
130             {
131 20 100       62 if( $tag =~ m{^
132             {
133             # It's an "end" tag:
134 10         18 my $item = pop(@stack);
135 10         14 $depth--;
136 10         16 my $repl = $item->{end_tag};
137 10         82 $$ref =~ s{\Q$tag\E}{$repl}s;
138             }
139             else
140             {
141             # It's a "start" tag:
142 10         58 my ($id) = $tag =~ m{}is;
143 10         42 push @stack, {
144             ident => $ident,
145             id => $id,
146             depth => $depth++,
147             line => $s->_tag_line_number( $tag ),
148             start_tag => '______INC_' . sprintf('%03d',$ident) . '______',
149             end_tag => '______OUTC_' . sprintf('%03d',$ident) . '______'
150             };
151 10         20 $ident++;
152 10         17 my $repl = $stack[-1]->{start_tag};
153 10         145 $$ref =~ s{\Q$tag\E}{$repl}s;
154 10         26 unshift @content_tags, $stack[-1];
155             }# end if()
156             }# end foreach()
157             };
158            
159 19         72 foreach my $tag ( sort {$b->{depth} <=> $a->{depth} } @content_tags )
  9         14  
160             {
161 10         14 my $start = $tag->{start_tag};
162 10         11 my $end = $tag->{end_tag};
163 10         135 my ($contents) = $$ref =~ m{$start(.*?)$end}s;
164              
165 10         32 $tag->{contents} = "\$Response->Write(q~$contents~);";
166 10         153 $$ref =~ s{$start\Q$contents\E$end}{\~); \$__self->$tag->{id}(\$__context); \$Response->Write(q\~}s;
167             }# end foreach()
168            
169 19         180 my $code = <<"CODE";
170             package $s->{package};
171              
172             use strict;
173             use warnings 'all';
174             no warnings 'redefine';
175             use base '$s->{base_class}';
176             use vars __PACKAGE__->VARS;
177             use ASP4::PageLoader;
178              
179             sub _init {
180             my (\$s) = \@_;
181             \$s->{script_name} = q<$s->{script_name}>;
182             \$s->{filename} = q<$s->{filename}>;
183             \$s->{base_class} = q<$s->{base_class}>;
184             \$s->{compiled_as} = q<$s->{compiled_as}>;
185             \$s->{package} = q<$s->{package}>;
186             @{[
187             $s->{masterpage} ?
188 19 100       168 " \$s->{masterpage} = ASP4::PageLoader->load( script_name => q<$s->{masterpage}->{script_name}> );"
189             : ""
190             ]}
191             return;
192             }
193              
194             CODE
195              
196 19 100       72 unless( $s->{masterpage} )
197             {
198 14         64 $code .= <<"CODE";
199             sub run {
200             use warnings 'all';
201             my (\$__self, \$__context) = \@_;
202             #line 1
203             $$ref
204             }
205             CODE
206             }# end unless()
207            
208 19         59 foreach( reverse ( @content_tags, @placeholder_tags ) )
209             {
210 23         79 $code .= <<"SUB";
211              
212             sub $_->{id} {
213             my (\$__self, \$__context) = \@_;
214             #line $_->{line}
215             $_->{contents}
216             }# end $_->{id}
217              
218             SUB
219             }# end foreach()
220            
221 19         41 $code .= "\n1;# return true:\n";
222            
223             open my $ofh, '>', $s->{saved_to}
224 19 50       1936 or die "Cannot open '$s->{saved_to}' for writing: $!";
225 19         219 print $ofh $code;
226 19         987 close($ofh);
227 19         565 chmod(0766, $s->{saved_to});
228              
229 19         188 my $config = ASP4::ConfigLoader->load();
230 19         183 $config->load_class( $s->{package} );
231 19         244 return $s->{package}->new();
232             }# end parse()
233              
234              
235             sub _tag_line_number
236             {
237 23     23   32 my ($s, $tag) = @_;
238            
239 23         18 my $number = 1;
240 23         97 for( split /\r?\n/, ${ $s->source_code } )
  23         32  
241             {
242 181 100       532 if( m/\Q$tag\E/s )
243             {
244 23         230 return $number;
245             }# end if()
246 158         118 $number++;
247             }# end for()
248            
249 0         0 return;
250             }# end _tag_line_number()
251              
252              
253             sub _parse_include_tags
254             {
255 19     19   34 my ($s) = @_;
256            
257 19         51 my $ref = $s->source_code;
258            
259 19         99 $$ref =~ s{
260             \<\!\-\-\s*\#include\s+virtual\="(.*?)"\s*\-\-\>
261             }{~); \$Response->Include(\$Server->MapPath("$1")); \$Response->Write(q~}xsg;
262             }# end _parse_include_tags()
263              
264              
265             sub _parse_scriptlet_tags
266             {
267 19     19   45 my ($s) = @_;
268            
269 19         56 my $ref = $s->source_code;
270            
271             # Parse <%= %> items:
272 19         129 $$ref =~ s{
273             <%\=(.*?)%>
274             }{
275 12         88 '~);$Response->Write(' . $1 . ');$Response->Write(q~'
276             }xgse;
277            
278             # TODO: Add <%& HTMLEncode($str) %>
279            
280             # TODO: Add <%% URLEncode($str) %>
281              
282 19         85 $$ref =~ s{
283             <%\s*([^\@\#\=]?.*?)%>
284             }{
285 8         33 my $txt = $1; '~);' . $txt . ';$Response->Write(q~'
  8         47  
286             }gxse;
287            
288 19         59 $$ref =~ s/(\$Response\->End)/return $1/gs;
289            
290 19         89 $$ref = ';$Response->Write(q~' . $$ref . '~);';
291            
292             # Now do the final ~ substitution:
293 19         161 $$ref =~ s{(\(q~)(.*?)(~\);)}{
294 39         100 my $pre = $1;
295 39         79 my $post = $3;
296 39         103 (my $txt = $2) =~ s/~/\\~/g;
297 39         163 "$pre$txt$post"
298             }xsge;
299             }# end _parse_scriptlet_tags()
300              
301              
302             sub _get_directives
303             {
304 19     19   46 my ($s) = @_;
305            
306 19         68 my $ref = $s->source_code;
307 19         53 my %directives = ( );
308 19         215 while( my ($tag, $directive, $attr_str) = $$ref =~ m/(<%@\s*(.*?)\s+(.*?)\s*%>)/ )
309             {
310 12         44 my $attrs = $s->_parse_tag_attrs( $attr_str );
311 12         254 $$ref =~ s/\Q$tag\E//;
312 12         102 $directives{ lc($directive) } = $attrs;
313             }# end while()
314            
315 19   100     170 $directives{page} ||= { };
316            
317 19         50 return \%directives;
318             }# end _get_directives()
319              
320            
321             sub _parse_tag_attrs
322             {
323 12     12   20 my ($s, $str) = @_;
324            
325 12         19 my $attr = { };
326 12         68 while( $str =~ m@([^\s\=\"\']+)(\s*=\s*(?:(")(.*?)"|(')(.*?)'|([^'"\s=]+)['"]*))?@sg ) #@
327             {
328 5         27 my $key = $1;
329 5         21 my $test = $2;
330 5 0       44 my $val = ( $3 ? $4 : ( $5 ? $6 : $7 ));
    50          
331 5 50       14 if( $test )
332             {
333 5         25 $attr->{ lc($key) } = $val;
334             }
335             else
336             {
337 0         0 $attr->{ lc($key) } = $key;
338             }# end if()
339             }# end while()
340            
341 12         25 return $attr;
342             }# end _parse_tag_attrs()
343              
344              
345             sub DESTROY
346             {
347 19     19   1659 my $s = shift;
348 19         283 undef(%$s);
349             }# end DESTROY()
350              
351             1;# return true:
352