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.58';
9 32     32   135065 use strict;
  32         80  
  32         802  
10 32     32   145 use warnings;
  32         56  
  32         747  
11              
12 32     32   150 use File::Basename;
  32         49  
  32         2157  
13 32     32   167 use File::Path;
  32         59  
  32         1264  
14 32     32   157 use File::Spec;
  32         60  
  32         641  
15 32     32   14800 use File::Temp;
  32         533400  
  32         2310  
16 32     32   983 use HTML::Mason;
  32         65  
  32         761  
17 32     32   8842 use HTML::Mason::Escapes;
  32         84  
  32         1058  
18 32     32   15003 use HTML::Mason::Request;
  32         97  
  32         1201  
19 32     32   11549 use HTML::Mason::Resolver::File;
  32         96  
  32         889  
20 32     32   177 use HTML::Mason::Tools qw(read_file taint_is_on load_pkg);
  32         61  
  32         1670  
21              
22 32     32   1129 use HTML::Mason::Exceptions( abbr => [qw(param_error system_error wrong_compiler_error compilation_error error)] );
  32         61  
  32         140  
23              
24 32     32   150 use Params::Validate qw(:all);
  32         61  
  32         5170  
25             Params::Validate::validation_options( on_fail => sub { param_error join '', @_ } );
26              
27 32     32   228 use Class::Container;
  32         62  
  32         668  
28 32     32   150 use base qw(Class::Container);
  32         71  
  32         8521  
29              
30             BEGIN
31             {
32             # Fields that can be set in new method, with defaults
33 32     32   93924 __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         2067 __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         192 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  32         318  
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   6568 );
  32         89  
149              
150             sub new
151             {
152 390     390 1 5713 my $class = shift;
153 390         2241 my $self = $class->SUPER::new(@_);
154              
155 390         70513 $self->_initialize;
156 390         2067 return $self;
157             }
158              
159             sub _initialize
160             {
161 390     390   984 my ($self) = shift;
162 390         975 $self->{code_cache} = {};
163 390         958 $self->{source_cache} = {};
164 390         1271 $self->{files_written} = [];
165 390         883 $self->{static_source_touch_file_lastmod} = 0;
166              
167 390         1781 $self->_assign_comp_root($self->{comp_root});
168 390         1353 $self->_check_data_dir();
169 390         1577 $self->_create_data_subdirs();
170 390         1417 $self->_initialize_escapes();
171              
172             #
173             # Create preallocated buffer for requests.
174             #
175 390         1262 $self->{preallocated_output_buffer} = ' ' x $self->buffer_preallocate_size;
176              
177 390         1452 $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     1340 !$self->{dynamic_comp_root});
202              
203 390         1019 $self->_preload_components();
204             }
205              
206             sub _check_data_dir
207             {
208 390     390   724 my $self = shift;
209              
210 390 100       1096 return unless $self->{data_dir};
211              
212 382         1488 $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       2245 unless File::Spec->file_name_is_absolute( $self->{data_dir} );
215             }
216              
217             sub _create_data_subdirs
218             {
219 390     390   653 my $self = shift;
220              
221 390 100       1485 if ($self->data_dir) {
222 382         1384 $self->_make_object_dir;
223 382         1244 $self->_make_cache_dir;
224             } else {
225 8         67 $self->{use_object_files} = 0;
226             }
227             }
228              
229             sub _initialize_escapes
230             {
231 390     390   670 my $self = shift;
232              
233             #
234             # Add the escape flags (including defaults)
235             #
236 390         1937 foreach ( [ h => \&HTML::Mason::Escapes::html_entities_escape ],
237             [ u => \&HTML::Mason::Escapes::url_escape ],
238             )
239             {
240 780         2264 $self->set_escape(@$_);
241             }
242              
243 390 100       1567 if ( my $e = delete $self->{escape_flags} )
244             {
245 1         9 while ( my ($flag, $code) = each %$e )
246             {
247 1         6 $self->set_escape( $flag => $code );
248             }
249             }
250             }
251              
252             sub _set_code_cache_attributes
253             {
254 390     390   660 my $self = shift;
255              
256 390         1082 $self->{unlimited_code_cache} = ($self->{code_cache_max_size} eq 'unlimited');
257 390 100       1094 unless ($self->{unlimited_code_cache}) {
258 5         26 $self->{code_cache_min_size} = $self->{code_cache_max_size} * 0.75;
259             }
260             }
261              
262             sub _preload_components
263             {
264 390     390   658 my $self = shift;
265              
266 390 100       1104 return unless $self->preloads;
267              
268 2         5 foreach my $pattern (@{$self->preloads}) {
  2         7  
269 3 50       24 error "preload pattern '$pattern' must be an absolute path"
270             unless File::Spec->file_name_is_absolute($pattern);
271 3         6 my %path_hash;
272 3         10 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         20 $path_hash{$path}++;
276             }
277             }
278 3         10 my @paths = keys(%path_hash);
279 3 50       9 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 2817 sub object_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'obj' ) : ''; }
  1775         3623  
293 384 50   384 0 721 sub object_create_marker_file { my $self = shift; return $self->object_dir ? File::Spec->catfile($self->object_dir, '.__obj_create_marker') : ''; }
  384         854  
294 546 100   546 0 986 sub cache_dir { my $self = shift; return $self->data_dir ? File::Spec->catdir( $self->data_dir, 'cache' ) : ''; }
  546         1474  
295              
296             sub _make_data_subdir
297             {
298 765     765   1531 my ($self, $dir) = @_;
299              
300 765 100       10843 unless (-d $dir) {
301 55         123 my @newdirs = eval { mkpath( $dir, 0, 0775 ) };
  55         6573  
302 55 50       268 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         256 $self->push_files_written(@newdirs);
310             }
311             }
312              
313             sub _make_object_dir
314             {
315 383     383   808 my ($self) = @_;
316              
317 383         1160 my $object_dir = $self->object_dir;
318 383         1435 $self->_make_data_subdir($object_dir);
319 383         1208 my $object_create_marker_file = $self->object_create_marker_file;
320 383 100       3948 unless (-f $object_create_marker_file) {
321 28 50       1354 open my $fh, ">$object_create_marker_file"
322             or system_error "Could not create '$object_create_marker_file': $!";
323 28         131 $self->push_files_written($object_create_marker_file);
324             }
325             }
326              
327             sub _make_cache_dir
328             {
329 382     382   826 my ($self) = @_;
330              
331 382         1082 my $cache_dir = $self->cache_dir;
332 382         1105 $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 1013 my $self = shift;
341 394         996 my $comp = shift;
342 394         2095 $self->make_request(comp=>$comp, args=>\@_)->exec;
343             }
344              
345             sub make_request {
346 479     479 1 1256 my $self = shift;
347              
348 479         1762 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 4260 my ($self, $path) = @_;
363 2189         3187 my ($maxfilemod, $objfile, $objfilemod);
364 2189         3626 my $code_cache = $self->{code_cache};
365 2189         3225 my $resolver = $self->{resolver};
366              
367             #
368             # Path must be absolute.
369             #
370 2189 50       5937 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         5090 my $source = $self->resolve_comp_path_to_source($path);
378              
379             # No component matches this path.
380 2189 100       5127 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         3319 my $comp_id = $source->comp_id;
385              
386             #
387             # Get last modified time of source.
388             #
389 1142         2894 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     4128 if ( exists $code_cache->{$comp_id} &&
      100        
397             ( $self->static_source || $code_cache->{$comp_id}->{lastmod} >= $srcmod )
398             ) {
399 507         3718 return $code_cache->{$comp_id}->{comp};
400             }
401              
402 635 100       1632 if ($self->{use_object_files}) {
403 620         1622 $objfile = $self->comp_id_to_objfile($comp_id);
404              
405 620         11170 my @stat = stat $objfile;
406 620 50 66     2476 if ( @stat && ! -f _ ) {
407 0         0 error "The object file '$objfile' exists but it is not a file!";
408             }
409              
410 620 100       1915 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       68 $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       1679 $objfilemod = @stat ? $stat[9] : 0;
420             }
421             }
422              
423 635         1087 my $comp;
424 635 100       1257 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       1512 if ($objfilemod < $srcmod) {
438 504         1262 $self->compiler->compile_to_file( file => $objfile, source => $source);
439             }
440 604         1367 $comp = eval { $self->eval_object_code( object_file => $objfile ) };
  604         2076  
441              
442 604 100       3017 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
443 15 100 66     104 if (!defined($@) || $@ !~ /failed in require/) {
444 13         58 $self->compiler->compile_to_file( file => $objfile, source => $source);
445 13         35 $comp = eval { $self->eval_object_code( object_file => $objfile ) };
  13         51  
446             }
447              
448 15 100       153 if (!UNIVERSAL::isa($comp, 'HTML::Mason::Component')) {
449 10 50       27 my $error = $@ ? $@ : "Could not get HTML::Mason::Component object from object file '$objfile'";
450 10         69 $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         50 my $object_code = $source->object_code( compiler => $self->compiler );
458 15         54 $comp = eval { $self->eval_object_code( object_code => $object_code ) };
  15         64  
459 15 100       77 $self->_compilation_error( $source->friendly_name, $@ ) if $@;
460             }
461 607         3168 $comp->assign_runtime_properties($self, $source);
462              
463             #
464             # Delete any stale cached version of this component, then
465             # cache it.
466             #
467 607         1878 $self->delete_from_code_cache($comp_id);
468 607         2257 $code_cache->{$comp_id} = { lastmod => $srcmod, comp => $comp };
469              
470 607         4722 return $comp;
471             }
472              
473             sub delete_from_code_cache {
474 636     636 0 1489 my ($self, $comp_id) = @_;
475 636 100       2554 return unless defined $self->{code_cache}{$comp_id}{comp};
476              
477 30         95 delete $self->{code_cache}{$comp_id};
478 30         58 return;
479             }
480              
481             sub comp_id_to_objfile {
482 623     623 0 1356 my ($self, $comp_id) = @_;
483              
484 623         1661 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 29 my $self = shift;
496              
497             # Necessary for preventing memory leaks
498 11 100       29 if ($self->use_internal_component_caches) {
499 6         10 foreach my $entry (values %{$self->{code_cache}}) {
  6         32  
500 11         19 my $comp = $entry->{comp};
501 11         47 $comp->flush_internal_caches;
502             }
503             }
504 11         86 $self->{code_cache} = {};
505 11         104 $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 1000 my ($self) = @_;
514              
515 563 100       1690 return if $self->{unlimited_code_cache};
516 16         25 my $current_size = scalar(keys(%{$self->{code_cache}}));
  16         47  
517 16 100       53 if ($current_size > $self->code_cache_max_size) {
518 9         21 my $code_cache = $self->{code_cache};
519 9         31 my $min_size = $self->code_cache_min_size;
520 9         18 my $decay_factor = 0.75;
521              
522 9         19 my @elems;
523 9         16 while (my ($path,$href) = each(%{$code_cache})) {
  47         172  
524 38         150 push(@elems,[$path,$href->{comp}->mfu_count,$href->{comp}]);
525             }
526 9         47 @elems = sort { $a->[1] <=> $b->[1] } @elems;
  67         134  
527 9   66     56 while (($current_size > $min_size) and @elems) {
528 29         104 $self->delete_from_code_cache(shift(@elems)->[0]);
529 29         236 $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         45 $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 3 my $self = shift;
557              
558 1         3 my $object_dir = $self->object_dir;
559 1 50       9 if (-d $object_dir) {
560 1         5 my $temp_dir = File::Temp::tempdir(DIR => $self->data_dir);
561 1 50       564 rename($object_dir, File::Spec->catdir( $temp_dir, 'target' ) )
562             or die "could not rename '$object_dir' to '$temp_dir': $@";
563 1         7 $self->_make_object_dir();
564 1         1037 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 891 my $self = shift;
578              
579 481 100       1246 if (my $touch_file = $self->static_source_touch_file) {
580 3 100       61 return unless -f $touch_file;
581 1         9 my $touch_file_lastmod = (stat($touch_file))[9];
582 1 50       6 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       5 if ($self->use_object_files) {
590 1         8 my $object_create_marker_file = $self->object_create_marker_file;
591 1 50 33     32 if (!-e $object_create_marker_file ||
592             (stat($object_create_marker_file))[9] < $touch_file_lastmod) {
593 1         5 $self->remove_object_files;
594             }
595             }
596              
597             # Next, clear the in-memory component cache.
598             #
599 1         8 $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 436 my $self = shift;
614              
615 14         412 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       104 $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       44 unless defined $p{comp_source};
622              
623 14   100     84 $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   40 source_callback => sub { $p{comp_source} },
631 14         144 );
632              
633 14         51 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         46  
636 12 50       38 $self->_compilation_error( $p{name}, $@ ) if $@;
637              
638 12         44 $comp->assign_runtime_properties($self, $source);
639              
640 12         89 return $comp;
641             }
642              
643             sub set_global
644             {
645 8     8 1 28 my ($self, $decl, @values) = @_;
646 8 50       20 param_error "Interp->set_global: expects a variable name and one or more values"
647             unless @values;
648 8 100       53 my ($prefix, $name) = ($decl =~ s/^([\$@%])//) ? ($1, $decl) : ('$', $decl);
649              
650 8         21 my $varname = sprintf("%s::%s",$self->compiler->in_package,$name);
651 32     32   332 no strict 'refs';
  32         105  
  32         1443  
652 32     32   196 no warnings 'once';
  32         74  
  32         39321  
653 8 50       266 if ($prefix eq '$') {
    0          
654 8         93 $$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 47 my $self = shift;
665            
666 21 100       46 if (my $new_comp_root = shift) {
667 17 100       40 die "cannot assign new comp_root unless dynamic_comp_root parameter is set"
668             unless $self->dynamic_comp_root;
669 16         35 $self->_assign_comp_root($new_comp_root);
670             }
671 18 100 66     42 if (@{$self->{comp_root}} == 1 and $self->{comp_root}[0][0] eq 'MAIN') {
  18         63  
672 4         15 return $self->{comp_root}[0][1];
673             } else {
674 14         31 return $self->{comp_root};
675             }
676             }
677              
678             sub comp_root_array
679             {
680 2126     2126 0 2806 return @{ $_[0]->{comp_root} };
  2126         5692  
681             }
682              
683             sub _assign_comp_root
684             {
685 406     406   994 my ($self, $new_comp_root) = @_;
686              
687             # Force into lol format.
688 406 100       1204 if (!ref($new_comp_root)) {
    50          
689 387         1146 $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     2286 my $comp_root_key_map = $self->{comp_root_key_map} ||= {};
697 406         1012 foreach my $pair (@$new_comp_root) {
698 449 50       1394 param_error "Multiple-path component root must consist of a list of two-element lists"
699             if ref($pair) ne 'ARRAY';
700 449 50       1850 param_error "Component root key '$pair->[0]' cannot contain slash"
701             if $pair->[0] =~ /\//;
702 449         2185 $pair->[1] = File::Spec->canonpath( $pair->[1] );
703 449 50       4149 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         1346 my ($key, $path) = @$pair;
707 449 100       1253 if (my $orig_path = $comp_root_key_map->{$key}) {
708 39 100       94 if ($path ne $orig_path) {
709 2         19 die "comp_root key '$key' was originally associated with '$path', cannot change to '$orig_path'";
710             }
711             } else {
712 410         1397 $comp_root_key_map->{$key} = $path;
713             }
714             }
715 404         992 $self->{comp_root} = $new_comp_root;
716             }
717              
718             sub resolve_comp_path_to_source
719             {
720 2189     2189 0 3865 my ($self, $path) = @_;
721            
722 2189         2904 my $source;
723 2189 100       4370 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         117 my $source_cache = $self->{source_cache};
729 73         93 foreach my $pair (@{$self->{comp_root}}) {
  73         161  
730 151   100     366 my $source_cache_for_root = $source_cache->{$pair->[0]} ||= {};
731 151 100       271 unless (exists($source_cache_for_root->{$path})) {
732             $source_cache_for_root->{$path}
733 71         196 = $self->{resolver}->get_info($path, @$pair);
734             }
735 151 100       345 last if $source = $source_cache_for_root->{$path};
736             }
737             } else {
738 2116         3253 my $resolver = $self->{resolver};
739 2116         4586 foreach my $pair ($self->comp_root_array) {
740 2206 100       6912 last if $source = $resolver->get_info($path, @$pair);
741             }
742             }
743 2189         5044 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 159 my $self = shift;
758 83         157 my $fref = $self->{'files_written'};
759 83         446 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 1255 my ($self, $startpath, $name) = @_;
769 536         2209 $startpath =~ s{/+$}{};
770              
771             # Don't use File::Spec here, this is a URL path.
772 536         898 do {
773 1108         3544 my $comp = $self->load("$startpath/$name");
774 1108 100       5365 return $comp if $comp;
775             } while $startpath =~ s{/+[^/]*$}{};
776              
777 436         1230 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 2430 my ($self, %p) = @_;
798              
799             #
800             # Evaluate object file or text with warnings on, unless
801             # ignore_warnings_expr is '.'.
802             #
803 645         2169 my $ignore_expr = $self->ignore_warnings_expr;
804 645         1274 my ($comp, $err);
805 645         1231 my $warnstr = '';
806              
807             {
808 645 100       1000 local $^W = $ignore_expr eq '.' ? 0 : 1;
  645         3996  
809             local $SIG{__WARN__} =
810             ( $ignore_expr ?
811             ( $ignore_expr eq '.' ?
812       0     sub { } :
813 6 100   6   501 sub { $warnstr .= $_[0] if $_[0] !~ /$ignore_expr/ }
814             ) :
815 645 100   0   6129 sub { $warnstr .= $_[0] } );
  0 50       0  
816            
817 645         2356 $comp = $self->_do_or_eval(\%p);
818             }
819              
820 645         1767 $err = $warnstr . $@;
821              
822             #
823             # Return component or error
824             #
825 645 100       1566 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         105 compilation_error $err;
829             } else {
830 623         2095 return $comp;
831             }
832             }
833              
834             sub _do_or_eval
835             {
836 645     645   1476 my ($self, $p) = @_;
837              
838 645 100       1524 if ($p->{object_file}) {
839 617         162905 return do $p->{object_file};
840             } else {
841             # If in taint mode, untaint the object text
842 28 100       101 (${$p->{object_code}}) = ${$p->{object_code}} =~ /^(.*)/s if taint_is_on;
  1         3  
  1         5  
843              
844 28     9   60 return eval ${$p->{object_code}};
  28     9   2137  
  9     6   66  
  9     6   17  
  9     4   243  
  9     4   41  
  9     2   18  
  9     2   2181  
  6     1   43  
  6     1   12  
  6     1   162  
  6     1   27  
  6     1   8  
  6     1   1070  
  4     1   28  
  4     1   9  
  4     1   119  
  4     1   27  
  4     1   12  
  4     1   1075  
  2         21  
  2         5  
  2         70  
  2         11  
  2         3  
  2         700  
  1         22  
  1         2  
  1         30  
  1         6  
  1         3  
  1         492  
  1         9  
  1         2  
  1         28  
  1         5  
  1         3  
  1         478  
  1         9  
  1         3  
  1         29  
  1         7  
  1         1  
  1         505  
  1         9  
  1         2  
  1         29  
  1         5  
  1         2  
  1         381  
  1         8  
  1         3  
  1         28  
  1         5  
  1         1  
  1         386  
  1         6  
  1         2  
  1         24  
  1         4  
  1         2  
  1         220  
845             }
846             }
847              
848             sub _compilation_error {
849 12     12   30 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       15 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 1177 my $self = shift;
865 719   66     3808 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 1256 my $self = shift;
963 784         1985 my %p = @_;
964              
965 784         2683 while ( my ($name, $sub) = each %p )
966             {
967 784         2199 my $flag_regex = $self->compiler->lexer->escape_flag_regex;
968              
969 784 100 66     8719 param_error "Invalid escape name ($name)"
970             if $name !~ /^$flag_regex$/ || $name =~ /^n$/;
971              
972 783         1367 my $coderef;
973 783 50       1632 if ( ref $sub )
974             {
975 783         1204 $coderef = $sub;
976             }
977             else
978             {
979 0 0       0 if ( $sub =~ /^\w+$/ )
980             {
981 32     32   293 no strict 'refs';
  32         83  
  32         6866  
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         3912 $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 48 my $self = shift;
1010 24         30 my $text = shift;
1011              
1012 24         47 foreach my $flag (@_)
1013             {
1014             param_error "Invalid escape flag: $flag"
1015 24 100       65 unless exists $self->{escapes}{$flag};
1016              
1017 23         76 $self->{escapes}{$flag}->(\$text);
1018             }
1019              
1020 23         510 return $text;
1021             }
1022              
1023             1;
1024              
1025             __END__