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.59';
9 32     32   211779 use strict;
  32         80  
  32         879  
10 32     32   147 use warnings;
  32         62  
  32         853  
11              
12 32     32   162 use File::Basename;
  32         50  
  32         3692  
13 32     32   208 use File::Path;
  32         53  
  32         1554  
14 32     32   172 use File::Spec;
  32         80  
  32         802  
15 32     32   22775 use File::Temp;
  32         649150  
  32         2297  
16 32     32   1476 use HTML::Mason;
  32         67  
  32         761  
17 32     32   13144 use HTML::Mason::Escapes;
  32         85  
  32         1003  
18 32     32   21675 use HTML::Mason::Request;
  32         114  
  32         1287  
19 32     32   15447 use HTML::Mason::Resolver::File;
  32         88  
  32         990  
20 32     32   205 use HTML::Mason::Tools qw(read_file taint_is_on load_pkg);
  32         63  
  32         3146  
21              
22 32     32   187 use HTML::Mason::Exceptions( abbr => [qw(param_error system_error wrong_compiler_error compilation_error error)] );
  32         63  
  32         173  
23              
24 32     32   169 use Params::Validate qw(:all);
  32         65  
  32         5822  
25             Params::Validate::validation_options( on_fail => sub { param_error join '', @_ } );
26              
27 32     32   256 use Class::Container;
  32         58  
  32         864  
28 32     32   226 use base qw(Class::Container);
  32         69  
  32         10786  
29              
30             BEGIN
31             {
32             # Fields that can be set in new method, with defaults
33 32     32   124449 __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         5433 __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         521 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  32         702  
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   11439 );
  32         229  
149              
150             sub new
151             {
152 390     390 1 7453 my $class = shift;
153 390         2904 my $self = $class->SUPER::new(@_);
154              
155 390         66638 $self->_initialize;
156 390         1672 return $self;
157             }
158              
159             sub _initialize
160             {
161 390     390   961 my ($self) = shift;
162 390         1014 $self->{code_cache} = {};
163 390         886 $self->{source_cache} = {};
164 390         1281 $self->{files_written} = [];
165 390         792 $self->{static_source_touch_file_lastmod} = 0;
166              
167 390         1507 $self->_assign_comp_root($self->{comp_root});
168 390         1152 $self->_check_data_dir();
169 390         1163 $self->_create_data_subdirs();
170 390         1652 $self->_initialize_escapes();
171              
172             #
173             # Create preallocated buffer for requests.
174             #
175 390         1043 $self->{preallocated_output_buffer} = ' ' x $self->buffer_preallocate_size;
176              
177 390         1116 $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     1253 !$self->{dynamic_comp_root});
202              
203 390         951 $self->_preload_components();
204             }
205              
206             sub _check_data_dir
207             {
208 390     390   601 my $self = shift;
209              
210 390 100       969 return unless $self->{data_dir};
211              
212 382         1174 $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       1865 unless File::Spec->file_name_is_absolute( $self->{data_dir} );
215             }
216              
217             sub _create_data_subdirs
218             {
219 390     390   581 my $self = shift;
220              
221 390 100       1311 if ($self->data_dir) {
222 382         1266 $self->_make_object_dir;
223 382         1508 $self->_make_cache_dir;
224             } else {
225 8         35 $self->{use_object_files} = 0;
226             }
227             }
228              
229             sub _initialize_escapes
230             {
231 390     390   739 my $self = shift;
232              
233             #
234             # Add the escape flags (including defaults)
235             #
236 390         2206 foreach ( [ h => \&HTML::Mason::Escapes::html_entities_escape ],
237             [ u => \&HTML::Mason::Escapes::url_escape ],
238             )
239             {
240 780         2323 $self->set_escape(@$_);
241             }
242              
243 390 100       1572 if ( my $e = delete $self->{escape_flags} )
244             {
245 1         16 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   578 my $self = shift;
255              
256 390         1036 $self->{unlimited_code_cache} = ($self->{code_cache_max_size} eq 'unlimited');
257 390 100       978 unless ($self->{unlimited_code_cache}) {
258 5         23 $self->{code_cache_min_size} = $self->{code_cache_max_size} * 0.75;
259             }
260             }
261              
262             sub _preload_components
263             {
264 390     390   576 my $self = shift;
265              
266 390 100       928 return unless $self->preloads;
267              
268 2         7 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         8 foreach my $pair ($self->comp_root_array) {
273 3         7 my $root = $pair->[1];
274 3         9 foreach my $path ($self->resolver->glob_path($pattern, $root)) {
275 6         21 $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       17 $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 2670 sub object_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'obj' ) : ''; }
  1775         3709  
293 384 50   384 0 738 sub object_create_marker_file { my $self = shift; return $self->object_dir ? File::Spec->catfile($self->object_dir, '.__obj_create_marker') : ''; }
  384         778  
294 546 100   546 0 969 sub cache_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'cache' ) : ''; }
  546         1456  
295              
296             sub _make_data_subdir
297             {
298 765     765   1446 my ($self, $dir) = @_;
299              
300 765 100       13968 unless (-d $dir) {
301 55         185 my @newdirs = eval { mkpath( $dir, 0, 0775 ) };
  55         7865  
302 55 50       296 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         310 $self->push_files_written(@newdirs);
310             }
311             }
312              
313             sub _make_object_dir
314             {
315 383     383   741 my ($self) = @_;
316              
317 383         1078 my $object_dir = $self->object_dir;
318 383         1280 $self->_make_data_subdir($object_dir);
319 383         1524 my $object_create_marker_file = $self->object_create_marker_file;
320 383 100       6466 unless (-f $object_create_marker_file) {
321 28 50       2318 open my $fh, ">$object_create_marker_file"
322             or system_error "Could not create '$object_create_marker_file': $!";
323 28         188 $self->push_files_written($object_create_marker_file);
324             }
325             }
326              
327             sub _make_cache_dir
328             {
329 382     382   828 my ($self) = @_;
330              
331 382         912 my $cache_dir = $self->cache_dir;
332 382         1051 $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 1173 my $self = shift;
341 394         1236 my $comp = shift;
342 394         2416 $self->make_request(comp=>$comp, args=>\@_)->exec;
343             }
344              
345             sub make_request {
346 479     479 1 1431 my $self = shift;
347              
348 479         1664 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 4065 my ($self, $path) = @_;
363 2189         3168 my ($maxfilemod, $objfile, $objfilemod);
364 2189         3614 my $code_cache = $self->{code_cache};
365 2189         3389 my $resolver = $self->{resolver};
366              
367             #
368             # Path must be absolute.
369             #
370 2189 50       5557 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         4781 my $source = $self->resolve_comp_path_to_source($path);
378              
379             # No component matches this path.
380 2189 100       5225 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         3101 my $comp_id = $source->comp_id;
385              
386             #
387             # Get last modified time of source.
388             #
389 1142         2402 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     3672 if ( exists $code_cache->{$comp_id} &&
      100        
397             ( $self->static_source || $code_cache->{$comp_id}->{lastmod} >= $srcmod )
398             ) {
399 507         2983 return $code_cache->{$comp_id}->{comp};
400             }
401              
402 635 100       1480 if ($self->{use_object_files}) {
403 620         1772 $objfile = $self->comp_id_to_objfile($comp_id);
404              
405 620         14965 my @stat = stat $objfile;
406 620 50 66     3166 if ( @stat && ! -f _ ) {
407 0         0 error "The object file '$objfile' exists but it is not a file!";
408             }
409              
410 620 100       2319 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       67 $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       1647 $objfilemod = @stat ? $stat[9] : 0;
420             }
421             }
422              
423 635         1033 my $comp;
424 635 100       1226 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       1440 if ($objfilemod < $srcmod) {
438 504         1239 $self->compiler->compile_to_file( file => $objfile, source => $source);
439             }
440 604         1470 $comp = eval { $self->eval_object_code( object_file => $objfile ) };
  604         1978  
441              
442 604 100       2673 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
443 15 100 66     122 if (!defined($@) || $@ !~ /failed in require/) {
444 13         53 $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       159 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
449 10 50       31 my $error = $@ ? $@ : "Could not get HTML::Mason::Component object from object file '$objfile'";
450 10         81 $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         39 my $object_code = $source->object_code( compiler => $self->compiler );
458 15         63 $comp = eval { $self->eval_object_code( object_code => $object_code ) };
  15         54  
459 15 100       62 $self->_compilation_error( $source->friendly_name, $@ ) if $@;
460             }
461 607         2572 $comp->assign_runtime_properties($self, $source);
462              
463             #
464             # Delete any stale cached version of this component, then
465             # cache it.
466             #
467 607         1721 $self->delete_from_code_cache($comp_id);
468 607         2197 $code_cache->{$comp_id} = { lastmod => $srcmod, comp => $comp };
469              
470 607         4055 return $comp;
471             }
472              
473             sub delete_from_code_cache {
474 636     636 0 1302 my ($self, $comp_id) = @_;
475 636 100       2527 return unless defined $self->{code_cache}{$comp_id}{comp};
476              
477 30         79 delete $self->{code_cache}{$comp_id};
478 30         53 return;
479             }
480              
481             sub comp_id_to_objfile {
482 623     623 0 1253 my ($self, $comp_id) = @_;
483              
484 623         1383 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 27 my $self = shift;
496              
497             # Necessary for preventing memory leaks
498 11 100       36 if ($self->use_internal_component_caches) {
499 6         13 foreach my $entry (values %{$self->{code_cache}}) {
  6         42  
500 11         24 my $comp = $entry->{comp};
501 11         47 $comp->flush_internal_caches;
502             }
503             }
504 11         99 $self->{code_cache} = {};
505 11         116 $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 966 my ($self) = @_;
514              
515 563 100       1491 return if $self->{unlimited_code_cache};
516 16         26 my $current_size = scalar(keys(%{$self->{code_cache}}));
  16         41  
517 16 100       49 if ($current_size > $self->code_cache_max_size) {
518 9         18 my $code_cache = $self->{code_cache};
519 9         25 my $min_size = $self->code_cache_min_size;
520 9         18 my $decay_factor = 0.75;
521              
522 9         13 my @elems;
523 9         16 while (my ($path,$href) = each(%{$code_cache})) {
  47         123  
524 38         102 push(@elems,[$path,$href->{comp}->mfu_count,$href->{comp}]);
525             }
526 9         57 @elems = sort { $a->[1] <=> $b->[1] } @elems;
  69         111  
527 9   66     72 while (($current_size > $min_size) and @elems) {
528 29         89 $self->delete_from_code_cache(shift(@elems)->[0]);
529 29         182 $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         63 foreach my $elem (@elems) {
537 9         32 $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 6 my $self = shift;
557              
558 1         5 my $object_dir = $self->object_dir;
559 1 50       20 if (-d $object_dir) {
560 1         7 my $temp_dir = File::Temp::tempdir(DIR => $self->data_dir);
561 1 50       865 rename($object_dir, File::Spec->catdir( $temp_dir, 'target' ) )
562             or die "could not rename '$object_dir' to '$temp_dir': $@";
563 1         9 $self->_make_object_dir();
564 1         1800 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 844 my $self = shift;
578              
579 481 100       1162 if (my $touch_file = $self->static_source_touch_file) {
580 3 100       79 return unless -f $touch_file;
581 1         29 my $touch_file_lastmod = (stat($touch_file))[9];
582 1 50       8 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       8 if ($self->use_object_files) {
590 1         7 my $object_create_marker_file = $self->object_create_marker_file;
591 1 50 33     54 if (!-e $object_create_marker_file ||
592             (stat($object_create_marker_file))[9] < $touch_file_lastmod) {
593 1         12 $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 237 my $self = shift;
614              
615 14         468 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       124 $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       70 unless defined $p{comp_source};
622              
623 14   100     109 $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   48 source_callback => sub { $p{comp_source} },
631 14         183 );
632              
633 14         75 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         47  
636 12 50       39 $self->_compilation_error( $p{name}, $@ ) if $@;
637              
638 12         53 $comp->assign_runtime_properties($self, $source);
639              
640 12         110 return $comp;
641             }
642              
643             sub set_global
644             {
645 8     8 1 40 my ($self, $decl, @values) = @_;
646 8 50       27 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         31 my $varname = sprintf("%s::%s",$self->compiler->in_package,$name);
651 32     32   405 no strict 'refs';
  32         147  
  32         2408  
652 32     32   271 no warnings 'once';
  32         118  
  32         54603  
653 8 50       27 if ($prefix eq '$') {
    0          
654 8         142 $$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 62 my $self = shift;
665            
666 21 100       45 if (my $new_comp_root = shift) {
667 17 100       44 die "cannot assign new comp_root unless dynamic_comp_root parameter is set"
668             unless $self->dynamic_comp_root;
669 16         42 $self->_assign_comp_root($new_comp_root);
670             }
671 18 100 66     50 if (@{$self->{comp_root}} == 1 and $self->{comp_root}[0][0] eq 'MAIN') {
  18         72  
672 4         17 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 2662 return @{ $_[0]->{comp_root} };
  2126         5498  
681             }
682              
683             sub _assign_comp_root
684             {
685 406     406   923 my ($self, $new_comp_root) = @_;
686              
687             # Force into lol format.
688 406 100       1125 if (!ref($new_comp_root)) {
    50          
689 387         1193 $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     2149 my $comp_root_key_map = $self->{comp_root_key_map} ||= {};
697 406         1043 foreach my $pair (@$new_comp_root) {
698 449 50       1232 param_error "Multiple-path component root must consist of a list of two-element lists"
699             if ref($pair) ne 'ARRAY';
700 449 50       1760 param_error "Component root key '$pair->[0]' cannot contain slash"
701             if $pair->[0] =~ /\//;
702 449         2003 $pair->[1] = File::Spec->canonpath( $pair->[1] );
703 449 50       3853 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         1203 my ($key, $path) = @$pair;
707 449 100       1190 if (my $orig_path = $comp_root_key_map->{$key}) {
708 39 100       90 if ($path ne $orig_path) {
709 2         22 die "comp_root key '$key' was originally associated with '$path', cannot change to '$orig_path'";
710             }
711             } else {
712 410         1282 $comp_root_key_map->{$key} = $path;
713             }
714             }
715 404         833 $self->{comp_root} = $new_comp_root;
716             }
717              
718             sub resolve_comp_path_to_source
719             {
720 2189     2189 0 3586 my ($self, $path) = @_;
721            
722 2189         2960 my $source;
723 2189 100       4176 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         108 my $source_cache = $self->{source_cache};
729 73         113 foreach my $pair (@{$self->{comp_root}}) {
  73         184  
730 151   100     442 my $source_cache_for_root = $source_cache->{$pair->[0]} ||= {};
731 151 100       316 unless (exists($source_cache_for_root->{$path})) {
732             $source_cache_for_root->{$path}
733 71         221 = $self->{resolver}->get_info($path, @$pair);
734             }
735 151 100       459 last if $source = $source_cache_for_root->{$path};
736             }
737             } else {
738 2116         2981 my $resolver = $self->{resolver};
739 2116         4067 foreach my $pair ($self->comp_root_array) {
740 2206 100       6963 last if $source = $resolver->get_info($path, @$pair);
741             }
742             }
743 2189         5622 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 184 my $self = shift;
758 83         180 my $fref = $self->{'files_written'};
759 83         597 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 1205 my ($self, $startpath, $name) = @_;
769 536         2367 $startpath =~ s{/+$}{};
770              
771             # Don't use File::Spec here, this is a URL path.
772 536         816 do {
773 1108         3638 my $comp = $self->load("$startpath/$name");
774 1108 100       6026 return $comp if $comp;
775             } while $startpath =~ s{/+[^/]*$}{};
776              
777 436         1195 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 2234 my ($self, %p) = @_;
798              
799             #
800             # Evaluate object file or text with warnings on, unless
801             # ignore_warnings_expr is '.'.
802             #
803 645         1925 my $ignore_expr = $self->ignore_warnings_expr;
804 645         1121 my ($comp, $err);
805 645         1134 my $warnstr = '';
806              
807             {
808 645 100       949 local $^W = $ignore_expr eq '.' ? 0 : 1;
  645         3881  
809             local $SIG{__WARN__} =
810             ( $ignore_expr ?
811             ( $ignore_expr eq '.' ?
812       0     sub { } :
813 6 100   6   510 sub { $warnstr .= $_[0] if $_[0] !~ /$ignore_expr/ }
814             ) :
815 645 100   0   5490 sub { $warnstr .= $_[0] } );
  0 50       0  
816            
817 645         1980 $comp = $self->_do_or_eval(\%p);
818             }
819              
820 645         1687 $err = $warnstr . $@;
821              
822             #
823             # Return component or error
824             #
825 645 100       1448 if ($err) {
826             # attempt to stem very long eval errors
827 22         76 $err =~ s/has too many errors\..+/has too many errors./s;
828 22         126 compilation_error $err;
829             } else {
830 623         1987 return $comp;
831             }
832             }
833              
834             sub _do_or_eval
835             {
836 645     645   1254 my ($self, $p) = @_;
837              
838 645 100       1440 if ($p->{object_file}) {
839 617         212534 return do $p->{object_file};
840             } else {
841             # If in taint mode, untaint the object text
842 28 100       103 (${$p->{object_code}}) = ${$p->{object_code}} =~ /^(.*)/s if taint_is_on;
  1         4  
  1         11  
843              
844 28     9   53 return eval ${$p->{object_code}};
  28     9   2276  
  9     6   81  
  9     6   20  
  9     4   322  
  9     4   70  
  9     2   25  
  9     2   3129  
  6     1   45  
  6     1   14  
  6     1   185  
  6     1   33  
  6     1   13  
  6     1   1348  
  4     1   31  
  4     1   8  
  4     1   110  
  4     1   22  
  4     1   7  
  4     1   1266  
  2         15  
  2         4  
  2         53  
  2         11  
  2         4  
  2         830  
  1         7  
  1         2  
  1         25  
  1         4  
  1         2  
  1         456  
  1         8  
  1         2  
  1         27  
  1         5  
  1         1  
  1         475  
  1         6  
  1         2  
  1         28  
  1         4  
  1         2  
  1         461  
  1         6  
  1         2  
  1         25  
  1         4  
  1         2  
  1         372  
  1         6  
  1         2  
  1         25  
  1         5  
  1         2  
  1         346  
  1         6  
  1         2  
  1         26  
  1         4  
  1         1  
  1         304  
845             }
846             }
847              
848             sub _compilation_error {
849 12     12   38 my ($self, $filename, $err) = @_;
850              
851 12         42 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       24 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 1110 my $self = shift;
865 719   66     3753 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 1177 my $self = shift;
963 784         2018 my %p = @_;
964              
965 784         2617 while ( my ($name, $sub) = each %p )
966             {
967 784         2163 my $flag_regex = $self->compiler->lexer->escape_flag_regex;
968              
969 784 100 66     9514 param_error "Invalid escape name ($name)"
970             if $name !~ /^$flag_regex$/ || $name =~ /^n$/;
971              
972 783         1354 my $coderef;
973 783 50       1621 if ( ref $sub )
974             {
975 783         1085 $coderef = $sub;
976             }
977             else
978             {
979 0 0       0 if ( $sub =~ /^\w+$/ )
980             {
981 32     32   368 no strict 'refs';
  32         126  
  32         8756  
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         3964 $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 37 my $self = shift;
1010 24         36 my $text = shift;
1011              
1012 24         40 foreach my $flag (@_)
1013             {
1014             param_error "Invalid escape flag: $flag"
1015 24 100       68 unless exists $self->{escapes}{$flag};
1016              
1017 23         73 $self->{escapes}{$flag}->(\$text);
1018             }
1019              
1020 23         484 return $text;
1021             }
1022              
1023             1;
1024              
1025             __END__