File Coverage

blib/lib/HTML/Mason/Interp.pm
Criterion Covered Total %
statement 398 431 92.3
branch 120 156 76.9
condition 27 36 75.0
subroutine 79 85 92.9
pod 13 29 44.8
total 637 737 86.4


line stmt bran cond sub pod time code
1             # -*- cperl-indent-level: 4; cperl-continued-brace-offset: -4; cperl-continued-statement-offset: 4 -*-
2              
3             # Copyright (c) 1998-2005 by Jonathan Swartz. All rights reserved.
4             # This program is free software; you can redistribute it and/or modify it
5             # under the same terms as Perl itself.
6              
7             package HTML::Mason::Interp;
8             $HTML::Mason::Interp::VERSION = '1.60';
9 32     32   198178 use strict;
  32         81  
  32         870  
10 32     32   155 use warnings;
  32         57  
  32         783  
11              
12 32     32   169 use File::Basename;
  32         63  
  32         3738  
13 32     32   208 use File::Path;
  32         66  
  32         1590  
14 32     32   190 use File::Spec;
  32         73  
  32         781  
15 32     32   22703 use File::Temp;
  32         656299  
  32         2275  
16 32     32   1596 use HTML::Mason;
  32         80  
  32         752  
17 32     32   13572 use HTML::Mason::Escapes;
  32         80  
  32         989  
18 32     32   22617 use HTML::Mason::Request;
  32         104  
  32         1294  
19 32     32   15858 use HTML::Mason::Resolver::File;
  32         98  
  32         998  
20 32     32   207 use HTML::Mason::Tools qw(read_file taint_is_on load_pkg);
  32         68  
  32         1915  
21              
22 32     32   198 use HTML::Mason::Exceptions( abbr => [qw(param_error system_error wrong_compiler_error compilation_error error)] );
  32         72  
  32         199  
23              
24 32     32   202 use Params::Validate qw(:all);
  32         65  
  32         6075  
25             Params::Validate::validation_options( on_fail => sub { param_error join '', @_ } );
26              
27 32     32   256 use Class::Container;
  32         81  
  32         807  
28 32     32   179 use base qw(Class::Container);
  32         71  
  32         11152  
29              
30             BEGIN
31             {
32             # Fields that can be set in new method, with defaults
33 32     32   122460 __PACKAGE__->valid_params
34             (
35             autohandler_name =>
36             { parse => 'string', default => 'autohandler', type => SCALAR,
37             descr => "The filename to use for Mason's 'autohandler' capability" },
38              
39             buffer_preallocate_size =>
40             { parse => 'string', default => 0, type => SCALAR,
41             descr => "Number of bytes to preallocate in request buffer" },
42            
43             code_cache_max_size =>
44             { parse => 'string', default => 'unlimited', type => SCALAR,
45             descr => "The maximum number of components in the code cache" },
46              
47             comp_root =>
48             { parse => 'list',
49             type => SCALAR|ARRAYREF,
50             default => File::Spec->rel2abs( Cwd::cwd ),
51             descr => "A string or array of arrays indicating the search path for component calls" },
52              
53             compiler =>
54             { isa => 'HTML::Mason::Compiler',
55             descr => "A Compiler object for compiling components" },
56              
57             data_dir =>
58             { parse => 'string', optional => 1, type => SCALAR,
59             descr => "A directory for storing cache files and other state information" },
60              
61             dynamic_comp_root =>
62             { parse => 'boolean', default => 0, type => BOOLEAN,
63             descr => "Indicates whether the comp_root may be changed between requests" },
64              
65             escape_flags =>
66             { parse => 'hash_list', optional => 1, type => HASHREF,
67             descr => "A list of escape flags to set (as if calling the set_escape() method" },
68              
69             object_file_extension =>
70             { parse => 'string', type => SCALAR, default => '.obj',
71             descr => "Extension to add to the end of object files" },
72              
73             # OBJECT cause qr// returns an object
74             ignore_warnings_expr =>
75             { parse => 'string', type => SCALAR|OBJECT, default => qr/Subroutine .* redefined/i,
76             descr => "A regular expression describing Perl warning messages to ignore" },
77              
78             preloads =>
79             { parse => 'list', optional => 1, type => ARRAYREF,
80             descr => "A list of components to load immediately when creating the Interpreter" },
81              
82             resolver =>
83             { isa => 'HTML::Mason::Resolver',
84             descr => "A Resolver object for fetching components from storage" },
85              
86             static_source =>
87             { parse => 'boolean', default => 0, type => BOOLEAN,
88             descr => "When true, we only compile source files once" },
89              
90             static_source_touch_file =>
91             { parse => 'string', optional => 1, type => SCALAR,
92             descr => "A file that, when touched, causes Mason to clear its component caches" },
93              
94             use_object_files =>
95             { parse => 'boolean', default => 1, type => BOOLEAN,
96             descr => "Whether to cache component objects on disk" },
97             );
98              
99 32         5155 __PACKAGE__->contained_objects
100             (
101             resolver => { class => 'HTML::Mason::Resolver::File',
102             descr => "This class is expected to return component information based on a component path" },
103             compiler => { class => 'HTML::Mason::Compiler::ToObject',
104             descr => "This class is used to translate component source into code" },
105             request => { class => 'HTML::Mason::Request',
106             delayed => 1,
107             descr => "Objects returned by make_request are members of this class" },
108             );
109             }
110              
111             use HTML::Mason::MethodMaker
112             ( read_only => [ qw( autohandler_name
113             buffer_preallocate_size
114             code_cache
115             code_cache_min_size
116             code_cache_max_size
117             compiler
118             data_dir
119             dynamic_comp_root
120             object_file_extension
121             preallocated_output_buffer
122             preloads
123             resolver
124             source_cache
125             static_source
126             static_source_touch_file
127             use_internal_component_caches
128             use_object_files
129             ) ],
130              
131 32         490 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  32         657  
132             qw( ignore_warnings_expr
133             )
134             ],
135              
136             read_write_contained => { request =>
137             [ [ autoflush => { type => BOOLEAN } ],
138             [ data_cache_api => { type => SCALAR } ],
139             [ data_cache_defaults => { type => HASHREF } ],
140             [ dhandler_name => { type => SCALAR } ],
141             [ error_format => { type => SCALAR } ],
142             [ error_mode => { type => SCALAR } ],
143             [ max_recurse => { type => SCALAR } ],
144             [ out_method => { type => SCALARREF | CODEREF } ],
145             [ plugins => { type => ARRAYREF } ],
146             ]
147             },
148 32     32   10909 );
  32         247  
149              
150             sub new
151             {
152 390     390 1 7767 my $class = shift;
153 390         2768 my $self = $class->SUPER::new(@_);
154              
155 390         64736 $self->_initialize;
156 390         1721 return $self;
157             }
158              
159             sub _initialize
160             {
161 390     390   997 my ($self) = shift;
162 390         975 $self->{code_cache} = {};
163 390         840 $self->{source_cache} = {};
164 390         1407 $self->{files_written} = [];
165 390         860 $self->{static_source_touch_file_lastmod} = 0;
166              
167 390         1567 $self->_assign_comp_root($self->{comp_root});
168 390         1236 $self->_check_data_dir();
169 390         1351 $self->_create_data_subdirs();
170 390         1776 $self->_initialize_escapes();
171              
172             #
173             # Create preallocated buffer for requests.
174             #
175 390         1148 $self->{preallocated_output_buffer} = ' ' x $self->buffer_preallocate_size;
176              
177 390         1232 $self->_set_code_cache_attributes();
178              
179             #
180             # If static_source=1, unlimited_code_cache=1, and
181             # dynamic_comp_root=0, we can safely cache component objects keyed
182             # on path throughout the framework (e.g. within other component
183             # objects). These internal caches can be cleared in
184             # $interp->flush_code_cache (the only legimiate place for a
185             # component to be eliminated from the cache), eliminating any
186             # chance for leaked objects.
187             #
188             # static_source has to be on or else we might keep around
189             # old versions of components that have changed.
190             #
191             # unlimited_code_cache has to be on or else we might leak
192             # components when we discard.
193             #
194             # dynamic_comp_root has to be 0 because the cache would not be
195             # valid for different combinations of component root across
196             # different requests.
197             #
198             $self->{use_internal_component_caches} =
199             ($self->{static_source} &&
200             $self->{unlimited_code_cache} &&
201 390   66     1311 !$self->{dynamic_comp_root});
202              
203 390         927 $self->_preload_components();
204             }
205              
206             sub _check_data_dir
207             {
208 390     390   699 my $self = shift;
209              
210 390 100       1057 return unless $self->{data_dir};
211              
212 382         1255 $self->{data_dir} = File::Spec->canonpath( $self->{data_dir} );
213             param_error "data_dir '$self->{data_dir}' must be an absolute directory"
214 382 50       1975 unless File::Spec->file_name_is_absolute( $self->{data_dir} );
215             }
216              
217             sub _create_data_subdirs
218             {
219 390     390   631 my $self = shift;
220              
221 390 100       1306 if ($self->data_dir) {
222 382         1283 $self->_make_object_dir;
223 382         1472 $self->_make_cache_dir;
224             } else {
225 8         60 $self->{use_object_files} = 0;
226             }
227             }
228              
229             sub _initialize_escapes
230             {
231 390     390   745 my $self = shift;
232              
233             #
234             # Add the escape flags (including defaults)
235             #
236 390         2195 foreach ( [ h => \&HTML::Mason::Escapes::html_entities_escape ],
237             [ u => \&HTML::Mason::Escapes::url_escape ],
238             )
239             {
240 780         3780 $self->set_escape(@$_);
241             }
242              
243 390 100       1642 if ( my $e = delete $self->{escape_flags} )
244             {
245 1         9 while ( my ($flag, $code) = each %$e )
246             {
247 1         5 $self->set_escape( $flag => $code );
248             }
249             }
250             }
251              
252             sub _set_code_cache_attributes
253             {
254 390     390   651 my $self = shift;
255              
256 390         1083 $self->{unlimited_code_cache} = ($self->{code_cache_max_size} eq 'unlimited');
257 390 100       966 unless ($self->{unlimited_code_cache}) {
258 5         24 $self->{code_cache_min_size} = $self->{code_cache_max_size} * 0.75;
259             }
260             }
261              
262             sub _preload_components
263             {
264 390     390   645 my $self = shift;
265              
266 390 100       1000 return unless $self->preloads;
267              
268 2         6 foreach my $pattern (@{$self->preloads}) {
  2         6  
269 3 50       25 error "preload pattern '$pattern' must be an absolute path"
270             unless File::Spec->file_name_is_absolute($pattern);
271 3         7 my %path_hash;
272 3         21 foreach my $pair ($self->comp_root_array) {
273 3         7 my $root = $pair->[1];
274 3         10 foreach my $path ($self->resolver->glob_path($pattern, $root)) {
275 6         27 $path_hash{$path}++;
276             }
277             }
278 3         10 my @paths = keys(%path_hash);
279 3 50       8 warn "Didn't find any components for preload pattern '$pattern'"
280             unless @paths;
281 3         8 foreach (@paths)
282             {
283 6 50       34 $self->load($_)
284             or error "Cannot load component $_, found via pattern $pattern";
285             }
286             }
287             }
288              
289             #
290             # Functions for retrieving and creating data subdirectories.
291             #
292 1775 50   1775 0 2886 sub object_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'obj' ) : ''; }
  1775         3865  
293 384 50   384 0 1024 sub object_create_marker_file { my $self = shift; return $self->object_dir ? File::Spec->catfile($self->object_dir, '.__obj_create_marker') : ''; }
  384         795  
294 546 100   546 0 1010 sub cache_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'cache' ) : ''; }
  546         1365  
295              
296             sub _make_data_subdir
297             {
298 765     765   1478 my ($self, $dir) = @_;
299              
300 765 100       13368 unless (-d $dir) {
301 55         218 my @newdirs = eval { mkpath( $dir, 0, 0775 ) };
  55         8253  
302 55 50       324 if ($@) {
303 0         0 my $user = getpwuid($<);
304 0         0 my $group = getgrgid($();
305 0         0 my $data_dir = $self->data_dir;
306 0         0 error "Cannot create directory '$dir' ($@) for user '$user', group '$group'. " .
307             "Perhaps you need to create or set permissions on your data_dir ('$data_dir'). ";
308             }
309 55         356 $self->push_files_written(@newdirs);
310             }
311             }
312              
313             sub _make_object_dir
314             {
315 383     383   767 my ($self) = @_;
316              
317 383         1144 my $object_dir = $self->object_dir;
318 383         1351 $self->_make_data_subdir($object_dir);
319 383         1448 my $object_create_marker_file = $self->object_create_marker_file;
320 383 100       6159 unless (-f $object_create_marker_file) {
321 28 50       2211 open my $fh, ">$object_create_marker_file"
322             or system_error "Could not create '$object_create_marker_file': $!";
323 28         259 $self->push_files_written($object_create_marker_file);
324             }
325             }
326              
327             sub _make_cache_dir
328             {
329 382     382   879 my ($self) = @_;
330              
331 382         917 my $cache_dir = $self->cache_dir;
332 382         1071 $self->_make_data_subdir($cache_dir);
333             }
334              
335             #
336             # exec is the initial entry point for executing a component
337             # in a new request.
338             #
339             sub exec {
340 394     394 1 1295 my $self = shift;
341 394         1321 my $comp = shift;
342 394         2404 $self->make_request(comp=>$comp, args=>\@_)->exec;
343             }
344              
345             sub make_request {
346 479     479 1 1440 my $self = shift;
347              
348 479         1717 return $self->create_delayed_object( 'request', interp => $self, @_ );
349             }
350              
351             sub comp_exists {
352 0     0 1 0 my ($self, $path) = @_;
353 0         0 return $self->resolve_comp_path_to_source($path);
354             }
355              
356             #
357             # Load <$path> into a component, possibly parsing the source and/or
358             # caching the code. Returns a component object or undef if the
359             # component was not found.
360             #
361             sub load {
362 2189     2189 1 4257 my ($self, $path) = @_;
363 2189         3211 my ($maxfilemod, $objfile, $objfilemod);
364 2189         3651 my $code_cache = $self->{code_cache};
365 2189         3252 my $resolver = $self->{resolver};
366              
367             #
368             # Path must be absolute.
369             #
370 2189 50       5748 unless (substr($path, 0, 1) eq '/') {
371 0         0 error "Component path given to Interp->load must be absolute (was given $path)";
372             }
373              
374             #
375             # Get source info from resolver.
376             #
377 2189         4927 my $source = $self->resolve_comp_path_to_source($path);
378              
379             # No component matches this path.
380 2189 100       5227 return unless defined $source;
381              
382             # comp_id is the unique name for the component, used for cache key
383             # and object file name.
384 1142         3107 my $comp_id = $source->comp_id;
385              
386             #
387             # Get last modified time of source.
388             #
389 1142         2451 my $srcmod = $source->last_modified;
390              
391             #
392             # If code cache contains an up to date entry for this path, use
393             # the cached comp. Always use the cached comp in static_source
394             # mode.
395             #
396 1142 100 100     3820 if ( exists $code_cache->{$comp_id} &&
      100        
397             ( $self->static_source || $code_cache->{$comp_id}->{lastmod} >= $srcmod )
398             ) {
399 507         3058 return $code_cache->{$comp_id}->{comp};
400             }
401              
402 635 100       1498 if ($self->{use_object_files}) {
403 620         1483 $objfile = $self->comp_id_to_objfile($comp_id);
404              
405 620         15541 my @stat = stat $objfile;
406 620 50 66     2904 if ( @stat && ! -f _ ) {
407 0         0 error "The object file '$objfile' exists but it is not a file!";
408             }
409              
410 620 100       2252 if ($self->static_source) {
411             # No entry in the code cache so if the object file exists,
412             # we will use it, otherwise we must create it. These
413             # values make that happen.
414 21 100       81 $objfilemod = @stat ? $srcmod : 0;
415             } else {
416             # If the object file exists, get its modification time.
417             # Otherwise (it doesn't exist or it is a directory) we
418             # must create it.
419 599 100       1621 $objfilemod = @stat ? $stat[9] : 0;
420             }
421             }
422              
423 635         1017 my $comp;
424 635 100       1276 if ($objfile) {
425             #
426             # We are using object files. Update object file if necessary
427             # and load component from there.
428             #
429             # If loading the object file generates an error, or results in
430             # a non-component object, try regenerating the object file
431             # once before giving up and reporting an error. This can be
432             # handy in the rare case of an empty or corrupted object file.
433             # (But add an exception for "Compilation failed in require" errors, since
434             # the bad module will be added to %INC and the error will not occur
435             # the second time - RT #39803).
436             #
437 620 100       1478 if ($objfilemod < $srcmod) {
438 504         1138 $self->compiler->compile_to_file( file => $objfile, source => $source);
439             }
440 604         1444 $comp = eval { $self->eval_object_code( object_file => $objfile ) };
  604         2056  
441              
442 604 100       2789 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
443 15 100 66     124 if (!defined($@) || $@ !~ /failed in require/) {
444 13         56 $self->compiler->compile_to_file( file => $objfile, source => $source);
445 13         40 $comp = eval { $self->eval_object_code( object_file => $objfile ) };
  13         53  
446             }
447              
448 15 100       185 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
449 10 50       33 my $error = $@ ? $@ : "Could not get HTML::Mason::Component object from object file '$objfile'";
450 10         89 $self->_compilation_error( $source->friendly_name, $error );
451             }
452             }
453             } else {
454             #
455             # Not using object files. Load component directly into memory.
456             #
457 15         44 my $object_code = $source->object_code( compiler => $self->compiler );
458 15         72 $comp = eval { $self->eval_object_code( object_code => $object_code ) };
  15         56  
459 15 100       67 $self->_compilation_error( $source->friendly_name, $@ ) if $@;
460             }
461 607         2550 $comp->assign_runtime_properties($self, $source);
462              
463             #
464             # Delete any stale cached version of this component, then
465             # cache it.
466             #
467 607         1778 $self->delete_from_code_cache($comp_id);
468 607         2284 $code_cache->{$comp_id} = { lastmod => $srcmod, comp => $comp };
469              
470 607         4217 return $comp;
471             }
472              
473             sub delete_from_code_cache {
474 636     636 0 1398 my ($self, $comp_id) = @_;
475 636 100       2646 return unless defined $self->{code_cache}{$comp_id}{comp};
476              
477 30         78 delete $self->{code_cache}{$comp_id};
478 30         53 return;
479             }
480              
481             sub comp_id_to_objfile {
482 623     623 0 1344 my ($self, $comp_id) = @_;
483              
484 623         1405 return File::Spec->catfile
485             ( $self->object_dir,
486             $self->compiler->object_id,
487             ( split /\//, $comp_id ),
488             ) . $self->object_file_extension;
489             }
490              
491             #
492             # Empty in-memory code cache.
493             #
494             sub flush_code_cache {
495 11     11 1 35 my $self = shift;
496              
497             # Necessary for preventing memory leaks
498 11 100       31 if ($self->use_internal_component_caches) {
499 6         13 foreach my $entry (values %{$self->{code_cache}}) {
  6         35  
500 11         23 my $comp = $entry->{comp};
501 11         52 $comp->flush_internal_caches;
502             }
503             }
504 11         112 $self->{code_cache} = {};
505 11         106 $self->{source_cache} = {};
506             }
507              
508             #
509             # If code cache has exceeded maximum, remove least frequently used
510             # elements from cache until size falls below minimum.
511             #
512             sub purge_code_cache {
513 563     563 1 1052 my ($self) = @_;
514              
515 563 100       1669 return if $self->{unlimited_code_cache};
516 16         30 my $current_size = scalar(keys(%{$self->{code_cache}}));
  16         52  
517 16 100       47 if ($current_size > $self->code_cache_max_size) {
518 9         18 my $code_cache = $self->{code_cache};
519 9         24 my $min_size = $self->code_cache_min_size;
520 9         17 my $decay_factor = 0.75;
521              
522 9         22 my @elems;
523 9         23 while (my ($path,$href) = each(%{$code_cache})) {
  47         137  
524 38         108 push(@elems,[$path,$href->{comp}->mfu_count,$href->{comp}]);
525             }
526 9         68 @elems = sort { $a->[1] <=> $b->[1] } @elems;
  66         104  
527 9   66     61 while (($current_size > $min_size) and @elems) {
528 29         81 $self->delete_from_code_cache(shift(@elems)->[0]);
529 29         150 $current_size--;
530             }
531              
532             #
533             # Multiply each remaining cache item's count by a decay factor,
534             # to gradually reduce impact of old information.
535             #
536 9         37 foreach my $elem (@elems) {
537 9         23 $elem->[2]->mfu_count( $elem->[2]->mfu_count * $decay_factor );
538             }
539             }
540             }
541              
542             #
543             # Clear the object directory of all current files and subdirectories.
544             # Do this by renaming the object directory to a temporary name,
545             # immediately recreating an empty object directory, then removing
546             # the empty object directory. If another process tries to write
547             # the object file in between these steps, it'll create the top
548             # object directory instead.
549             #
550             # Would be nice to fork off a separate process to do the removing so
551             # that it doesn't affect a request's response time, but difficult to
552             # do this in an environment-generic way.
553             #
554             sub remove_object_files
555             {
556 1     1 0 11 my $self = shift;
557              
558 1         7 my $object_dir = $self->object_dir;
559 1 50       31 if (-d $object_dir) {
560 1         12 my $temp_dir = File::Temp::tempdir(DIR => $self->data_dir);
561 1 50       1373 rename($object_dir, File::Spec->catdir( $temp_dir, 'target' ) )
562             or die "could not rename '$object_dir' to '$temp_dir': $@";
563 1         15 $self->_make_object_dir();
564 1         2573 rmtree($temp_dir);
565             } else {
566 0         0 $self->_make_object_dir();
567             }
568             }
569              
570             #
571             # Check the static_source_touch_file, if one exists, to see if it has
572             # changed since we last checked. If it has, clear the code cache and
573             # object files if appropriate.
574             #
575             sub check_static_source_touch_file
576             {
577 481     481 0 882 my $self = shift;
578              
579 481 100       1157 if (my $touch_file = $self->static_source_touch_file) {
580 3 100       98 return unless -f $touch_file;
581 1         27 my $touch_file_lastmod = (stat($touch_file))[9];
582 1 50       13 if ($touch_file_lastmod > $self->{static_source_touch_file_lastmod}) {
583              
584             # File has been touched since we last checked. First,
585             # clear the object file directory if the last mod of
586             # its ._object_create_marker is earlier than the touch file,
587             # or if the marker doesn't exist.
588             #
589 1 50       10 if ($self->use_object_files) {
590 1         9 my $object_create_marker_file = $self->object_create_marker_file;
591 1 50 33     91 if (!-e $object_create_marker_file ||
592             (stat($object_create_marker_file))[9] < $touch_file_lastmod) {
593 1         10 $self->remove_object_files;
594             }
595             }
596              
597             # Next, clear the in-memory component cache.
598             #
599 1         9 $self->flush_code_cache;
600              
601             # Reset lastmod value.
602             #
603 1         4 $self->{static_source_touch_file_lastmod} = $touch_file_lastmod;
604             }
605             }
606             }
607              
608             #
609             # Construct a component on the fly. Virtual if 'path' parameter is
610             # given, otherwise anonymous.
611             #
612             sub make_component {
613 14     14 1 238 my $self = shift;
614              
615 14         482 my %p = validate(@_, { comp_source => { type => SCALAR, optional => 1 },
616             comp_file => { type => SCALAR, optional => 1 },
617             name => { type => SCALAR, optional => 1 } });
618              
619 14 50       122 $p{comp_source} = read_file(delete $p{comp_file}) if exists $p{comp_file};
620             param_error "Must specify either 'comp_source' or 'comp_file' parameter to 'make_component()'"
621 14 50       49 unless defined $p{comp_source};
622              
623 14   100     100 $p{name} ||= '';
624              
625             my $source = HTML::Mason::ComponentSource->new( friendly_name => $p{name},
626             comp_path => $p{name},
627             comp_id => undef,
628             last_modified => time,
629             comp_class => 'HTML::Mason::Component',
630 14     14   42 source_callback => sub { $p{comp_source} },
631 14         153 );
632              
633 14         58 my $object_code = $source->object_code( compiler => $self->compiler);
634              
635 12         31 my $comp = eval { $self->eval_object_code( object_code => $object_code ) };
  12         52  
636 12 50       39 $self->_compilation_error( $p{name}, $@ ) if $@;
637              
638 12         60 $comp->assign_runtime_properties($self, $source);
639              
640 12         110 return $comp;
641             }
642              
643             sub set_global
644             {
645 8     8 1 26 my ($self, $decl, @values) = @_;
646 8 50       24 param_error "Interp->set_global: expects a variable name and one or more values"
647             unless @values;
648 8 100       65 my ($prefix, $name) = ($decl =~ s/^([\$@%])//) ? ($1, $decl) : ('$', $decl);
649              
650 8         32 my $varname = sprintf("%s::%s",$self->compiler->in_package,$name);
651 32     32   379 no strict 'refs';
  32         149  
  32         2413  
652 32     32   287 no warnings 'once';
  32         499  
  32         55893  
653 8 50       28 if ($prefix eq '$') {
    0          
654 8         140 $$varname = $values[0];
655             } elsif ($prefix eq '@') {
656 0         0 @$varname = @values;
657             } else {
658 0         0 %$varname = @values;
659             }
660             }
661              
662             sub comp_root
663             {
664 21     21 1 48 my $self = shift;
665            
666 21 100       46 if (my $new_comp_root = shift) {
667 17 100       47 die "cannot assign new comp_root unless dynamic_comp_root parameter is set"
668             unless $self->dynamic_comp_root;
669 16         38 $self->_assign_comp_root($new_comp_root);
670             }
671 18 100 66     46 if (@{$self->{comp_root}} == 1 and $self->{comp_root}[0][0] eq 'MAIN') {
  18         80  
672 4         21 return $self->{comp_root}[0][1];
673             } else {
674 14         30 return $self->{comp_root};
675             }
676             }
677              
678             sub comp_root_array
679             {
680 2126     2126 0 2912 return @{ $_[0]->{comp_root} };
  2126         5689  
681             }
682              
683             sub _assign_comp_root
684             {
685 406     406   927 my ($self, $new_comp_root) = @_;
686              
687             # Force into lol format.
688 406 100       1109 if (!ref($new_comp_root)) {
    50          
689 387         1170 $new_comp_root = [[ MAIN => $new_comp_root ]];
690             } elsif (ref($new_comp_root) ne 'ARRAY') {
691 0         0 die "Component root $new_comp_root must be a scalar or array reference";
692             }
693              
694             # Validate key/path pairs, and check to see if any of them
695             # conflict with old pairs.
696 406   100     2141 my $comp_root_key_map = $self->{comp_root_key_map} ||= {};
697 406         1077 foreach my $pair (@$new_comp_root) {
698 449 50       1350 param_error "Multiple-path component root must consist of a list of two-element lists"
699             if ref($pair) ne 'ARRAY';
700 449 50       1722 param_error "Component root key '$pair->[0]' cannot contain slash"
701             if $pair->[0] =~ /\//;
702 449         2053 $pair->[1] = File::Spec->canonpath( $pair->[1] );
703 449 50       4000 param_error "comp_root path '$pair->[1]' is not an absolute directory"
704             unless File::Spec->file_name_is_absolute( $pair->[1] );
705            
706 449         1237 my ($key, $path) = @$pair;
707 449 100       1159 if (my $orig_path = $comp_root_key_map->{$key}) {
708 39 100       92 if ($path ne $orig_path) {
709 2         21 die "comp_root key '$key' was originally associated with '$path', cannot change to '$orig_path'";
710             }
711             } else {
712 410         1279 $comp_root_key_map->{$key} = $path;
713             }
714             }
715 404         853 $self->{comp_root} = $new_comp_root;
716             }
717              
718             sub resolve_comp_path_to_source
719             {
720 2189     2189 0 3608 my ($self, $path) = @_;
721            
722 2189         2827 my $source;
723 2189 100       4219 if ($self->{static_source}) {
724             # Maintain a separate source_cache for each component root,
725             # because the set of active component roots can change
726             # from request to request.
727             #
728 73         120 my $source_cache = $self->{source_cache};
729 73         111 foreach my $pair (@{$self->{comp_root}}) {
  73         190  
730 151   100     421 my $source_cache_for_root = $source_cache->{$pair->[0]} ||= {};
731 151 100       313 unless (exists($source_cache_for_root->{$path})) {
732             $source_cache_for_root->{$path}
733 71         227 = $self->{resolver}->get_info($path, @$pair);
734             }
735 151 100       492 last if $source = $source_cache_for_root->{$path};
736             }
737             } else {
738 2116         3098 my $resolver = $self->{resolver};
739 2116         4096 foreach my $pair ($self->comp_root_array) {
740 2206 100       6679 last if $source = $resolver->get_info($path, @$pair);
741             }
742             }
743 2189         5559 return $source;
744             }
745              
746             sub files_written
747             {
748 0     0 0 0 my $self = shift;
749 0         0 return @{$self->{files_written}};
  0         0  
750             }
751              
752             #
753             # Push onto list of written files.
754             #
755             sub push_files_written
756             {
757 83     83 0 187 my $self = shift;
758 83         202 my $fref = $self->{'files_written'};
759 83         693 push(@$fref,@_);
760             }
761              
762             #
763             # Look for component <$name> starting in <$startpath> and moving upwards
764             # to the root. Return component object or undef.
765             #
766             sub find_comp_upwards
767             {
768 536     536 0 1243 my ($self, $startpath, $name) = @_;
769 536         2290 $startpath =~ s{/+$}{};
770              
771             # Don't use File::Spec here, this is a URL path.
772 536         890 do {
773 1108         3923 my $comp = $self->load("$startpath/$name");
774 1108 100       6418 return $comp if $comp;
775             } while $startpath =~ s{/+[^/]*$}{};
776              
777 436         1214 return; # Nothing found
778             }
779              
780             ###################################################################
781             # The eval_object_code & write_object_file methods used to be in
782             # Parser.pm. This is a temporary home only. They need to be moved
783             # again at some point in the future (during some sort of interp
784             # re-architecting).
785             ###################################################################
786              
787             #
788             # eval_object_code
789             # (object_code, object_file, error)
790             # Evaluate an object file or object text. Return a component object
791             # or undef if error.
792             #
793             # I think this belongs in the resolver (or comp loader) - Dave
794             #
795             sub eval_object_code
796             {
797 645     645 0 2485 my ($self, %p) = @_;
798              
799             #
800             # Evaluate object file or text with warnings on, unless
801             # ignore_warnings_expr is '.'.
802             #
803 645         2051 my $ignore_expr = $self->ignore_warnings_expr;
804 645         1215 my ($comp, $err);
805 645         1155 my $warnstr = '';
806              
807             {
808 645 100       1020 local $^W = $ignore_expr eq '.' ? 0 : 1;
  645         4096  
809             local $SIG{__WARN__} =
810             ( $ignore_expr ?
811             ( $ignore_expr eq '.' ?
812       0     sub { } :
813 6 100   6   554 sub { $warnstr .= $_[0] if $_[0] !~ /$ignore_expr/ }
814             ) :
815 645 100   0   5511 sub { $warnstr .= $_[0] } );
  0 50       0  
816            
817 645         2016 $comp = $self->_do_or_eval(\%p);
818             }
819              
820 645         1746 $err = $warnstr . $@;
821              
822             #
823             # Return component or error
824             #
825 645 100       1476 if ($err) {
826             # attempt to stem very long eval errors
827 22         88 $err =~ s/has too many errors\..+/has too many errors./s;
828 22         146 compilation_error $err;
829             } else {
830 623         2026 return $comp;
831             }
832             }
833              
834             sub _do_or_eval
835             {
836 645     645   1348 my ($self, $p) = @_;
837              
838 645 100       1439 if ($p->{object_file}) {
839 617         211749 return do $p->{object_file};
840             } else {
841             # If in taint mode, untaint the object text
842 28 100       95 (${$p->{object_code}}) = ${$p->{object_code}} =~ /^(.*)/s if taint_is_on;
  1         3  
  1         8  
843              
844 28     9   54 return eval ${$p->{object_code}};
  28     9   2309  
  9     6   98  
  9     6   21  
  9     4   343  
  9     4   53  
  9     2   23  
  9     2   3032  
  6     1   45  
  6     1   18  
  6     1   180  
  6     1   33  
  6     1   33  
  6     1   1334  
  4     1   28  
  4     1   9  
  4     1   114  
  4     1   21  
  4     1   12  
  4     1   1393  
  2         18  
  2         6  
  2         59  
  2         11  
  2         3  
  2         964  
  1         6  
  1         3  
  1         26  
  1         5  
  1         2  
  1         469  
  1         7  
  1         2  
  1         49  
  1         7  
  1         2  
  1         490  
  1         8  
  1         2  
  1         27  
  1         5  
  1         2  
  1         488  
  1         6  
  1         3  
  1         27  
  1         5  
  1         3  
  1         431  
  1         7  
  1         2  
  1         28  
  1         7  
  1         2  
  1         430  
  1         7  
  1         2  
  1         34  
  1         5  
  1         16  
  1         347  
845             }
846             }
847              
848             sub _compilation_error {
849 12     12   36 my ($self, $filename, $err) = @_;
850              
851 12         46 HTML::Mason::Exception::Compilation->throw(error=>$err, filename=>$filename);
852             }
853              
854              
855             sub object_file {
856 3     3 0 11 my ($self, $comp) = @_;
857 3 50       23 return $comp->persistent ?
858             $self->comp_id_to_objfile($comp->comp_id) :
859             undef;
860             }
861              
862             sub use_autohandlers
863             {
864 719     719 0 1158 my $self = shift;
865 719   66     3822 return (defined $self->{autohandler_name} and length $self->{autohandler_name});
866             }
867              
868             # Generate HTML that describes Interp's current status.
869             # This is used in things like Apache::Status reports. Currently shows:
870             # -- Interp properties
871             # -- loaded (cached) components
872             sub status_as_html {
873 0     0 0 0 my ($self, %p) = @_;
874              
875             # Should I be scared about this? =)
876              
877 0         0 my $comp_source = <<'EOF';
878            

Interpreter properties:

879            
880            

Startup options:

881            
882            
883             <%perl>
884             foreach my $property (sort keys %$interp) {
885             my $val = $interp->{$property};
886              
887             my $default = ( defined $val && defined $valid{$property}{default} && $val eq $valid{$property}{default} ) || ( ! defined $val && exists $valid{$property}{default} && ! defined $valid{$property}{default} );
888              
889             my $display = $val;
890             if (ref $val) {
891             $display = '';
892             # only object can ->can, others die
893             my $is_object = eval { $val->can('anything'); 1 };
894             if ($is_object) {
895             $display .= ref $val . ' object';
896             } else {
897             if (UNIVERSAL::isa($val, 'ARRAY')) {
898             $display .= 'ARRAY reference - [ ';
899             $display .= join ', ', @$val;
900             $display .= '] ';
901             } elsif (UNIVERSAL::isa($val, 'HASH')) {
902             $display .= 'HASH reference - { ';
903             my @pairs;
904             while (my ($k, $v) = each %$val) {
905             push @pairs, "$k => $v";
906             }
907             $display .= join ', ', @pairs;
908             $display .= ' }';
909             } else {
910             $display = ref $val . ' reference';
911             }
912             }
913             $display .= '';
914             }
915              
916             defined $display && $display =~ s,([\x00-\x1F]),'control-' . chr( ord('A') + ord($1) - 1 ) . '',eg; # does this work for non-ASCII?
917            
918            
919            
920             <% $property | h %>
921            
922            
923             <% defined $display ? $display : 'undef' %>
924             <% $default ? '(default)' : '' %>
925            
926            
927             % }
928            
929            
930              
931            

Components in memory cache:

932            
933             % my $cache;
934             % if ($cache = $interp->code_cache and %$cache) {
935             % foreach my $key (sort keys %$cache) {
936             <% $key |h%> (modified <% scalar localtime $cache->{$key}->{lastmod} %>)
937            
938             % }
939             % } else {
940             None
941             % }
942            
943            
944              
945             <%args>
946             $interp # The interpreter we'll elucidate
947             %valid # Default values for interp member data
948            
949             EOF
950              
951 0         0 my $comp = $self->make_component(comp_source => $comp_source);
952 0         0 my $out;
953              
954 0         0 my $args = [interp => $self, valid => $self->validation_spec];
955 0         0 $self->make_request(comp=>$comp, args=>$args, out_method=>\$out, %p)->exec;
956              
957 0         0 return $out;
958             }
959              
960             sub set_escape
961             {
962 784     784 1 1220 my $self = shift;
963 784         2155 my %p = @_;
964              
965 784         2655 while ( my ($name, $sub) = each %p )
966             {
967 784         2224 my $flag_regex = $self->compiler->lexer->escape_flag_regex;
968              
969 784 100 66     9671 param_error "Invalid escape name ($name)"
970             if $name !~ /^$flag_regex$/ || $name =~ /^n$/;
971              
972 783         1377 my $coderef;
973 783 50       1655 if ( ref $sub )
974             {
975 783         1116 $coderef = $sub;
976             }
977             else
978             {
979 0 0       0 if ( $sub =~ /^\w+$/ )
980             {
981 32     32   338 no strict 'refs';
  32         113  
  32         9087  
982 0 0       0 unless ( defined &{"HTML::Mason::Escapes::$sub"} )
  0         0  
983             {
984 0         0 param_error "Invalid escape: $sub (no matching subroutine in HTML::Mason::Escapes";
985             }
986              
987 0         0 $coderef = \&{"HTML::Mason::Escapes::$sub"};
  0         0  
988             }
989             else
990             {
991 0         0 $coderef = eval $sub;
992 0 0       0 param_error "Invalid escape: $sub ($@)" if $@;
993             }
994             }
995              
996 783         4082 $self->{escapes}{$name} = $coderef;
997             }
998             }
999              
1000             sub remove_escape
1001             {
1002 0     0 1 0 my $self = shift;
1003              
1004 0         0 delete $self->{escapes}{ shift() };
1005             }
1006              
1007             sub apply_escapes
1008             {
1009 24     24 1 43 my $self = shift;
1010 24         39 my $text = shift;
1011              
1012 24         49 foreach my $flag (@_)
1013             {
1014             param_error "Invalid escape flag: $flag"
1015 24 100       65 unless exists $self->{escapes}{$flag};
1016              
1017 23         81 $self->{escapes}{$flag}->(\$text);
1018             }
1019              
1020 23         581 return $text;
1021             }
1022              
1023             1;
1024              
1025             __END__