File Coverage

blib/lib/HTML/Mason/Request.pm
Criterion Covered Total %
statement 638 672 94.9
branch 285 338 84.3
condition 73 98 74.4
subroutine 84 85 98.8
pod 36 41 87.8
total 1116 1234 90.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              
8             #
9             # A note about the internals:
10             #
11             # Because request is the single most intensively used piece of the
12             # Mason architecture, this module is often the best target for
13             # optimization.
14             #
15             # By far, the two methods called most often are comp() and print().
16             # We have attempted to optimize the parts of these methods that handle
17             # the _normal_ path through the code.
18             #
19             # Code paths that are followed less frequently (like the path that
20             # handles the $mods{store} parameter in comp, for example) are
21             # intentionally not optimized because doing so would clutter the code
22             # while providing a minimal benefit.
23             #
24             # Many of the optimizations consist of ignoring defined interfaces for
25             # accessing parts of the request object's internal data structure, and
26             # instead accessing it directly.
27             #
28             # We have attempted to comment these various optimizations
29             # appropriately, so that future hackers understand that we did indeed
30             # mean to not use the relevant interface in that particular spot.
31             #
32              
33             package HTML::Mason::Request;
34             $HTML::Mason::Request::VERSION = '1.59';
35 33     33   219 use strict;
  33         69  
  33         1079  
36 33     33   161 use warnings;
  33         447  
  33         883  
37              
38 32     32   206 use File::Spec;
  32         74  
  32         629  
39 32     32   14080 use HTML::Mason::Cache::BaseCache;
  32         73  
  32         1002  
40 32     32   13239 use HTML::Mason::Plugin::Context;
  32         72  
  32         949  
41 32     32   13940 use HTML::Mason::Tools qw(can_weaken read_file compress_path load_pkg pkg_loaded absolute_comp_path);
  32         94  
  32         2669  
42 32     32   14140 use HTML::Mason::Utils;
  32         78  
  32         1551  
43 32     32   14547 use Log::Any qw($log);
  32         255937  
  32         175  
44 32     32   82391 use Class::Container;
  32         130324  
  32         1174  
45 32     32   243 use base qw(Class::Container);
  32         71  
  32         3639  
46              
47             # Stack frame constants
48 32     32   208 use constant STACK_COMP => 0;
  32         79  
  32         1784  
49 32     32   192 use constant STACK_ARGS => 1;
  32         64  
  32         1419  
50 32     32   180 use constant STACK_BUFFER => 2;
  32         59  
  32         1277  
51 32     32   175 use constant STACK_MODS => 3;
  32         63  
  32         1263  
52 32     32   167 use constant STACK_PATH => 4;
  32         68  
  32         1294  
53 32     32   168 use constant STACK_BASE_COMP => 5;
  32         70  
  32         1369  
54 32     32   184 use constant STACK_IN_CALL_SELF => 6;
  32         69  
  32         1490  
55 32     32   220 use constant STACK_BUFFER_IS_FLUSHABLE => 7;
  32         54  
  32         1449  
56 32     32   181 use constant STACK_HIDDEN_BUFFER => 8;
  32         73  
  32         1847  
57              
58             # HTML::Mason::Exceptions always exports rethrow_exception() and isa_mason_exception()
59 32         286 use HTML::Mason::Exceptions( abbr => [qw(error param_error syntax_error
60 32     32   211 top_level_not_found_error error)] );
  32         69  
61              
62 32     32   248 use Params::Validate qw(:all);
  32         74  
  32         17707  
63             Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
64              
65             BEGIN
66             {
67             __PACKAGE__->valid_params
68             (
69             args =>
70             { type => ARRAYREF, default => [],
71             descr => "Array of arguments to initial component",
72             public => 0 },
73              
74             autoflush =>
75             { parse => 'boolean', default => 0, type => SCALAR,
76             descr => "Whether output should be buffered or sent immediately" },
77              
78             comp =>
79             { type => SCALAR | OBJECT, optional => 0,
80             descr => "Initial component, either an absolute path or a component object",
81             public => 0 },
82              
83             data_cache_api =>
84             { parse => 'string', default => '1.1', type => SCALAR,
85             regex => qr/^(?:1\.0|1\.1|chi)$/,
86             descr => "Data cache API to use: 1.0, 1.1, or chi" },
87              
88             data_cache_defaults =>
89             { parse => 'hash_list', type => HASHREF|UNDEF, optional => 1,
90             descr => "A hash of default parameters for Cache::Cache or CHI" },
91              
92             declined_comps =>
93             { type => HASHREF, optional => 1,
94             descr => "Hash of components that have been declined in previous parent requests",
95             public => 0 },
96              
97             dhandler_name =>
98             { parse => 'string', default => 'dhandler', type => SCALAR,
99             descr => "The filename to use for Mason's 'dhandler' capability" },
100              
101             interp =>
102             { isa => 'HTML::Mason::Interp',
103             descr => "An interpreter for Mason control functions",
104             public => 0 },
105              
106             error_format =>
107             { parse => 'string', type => SCALAR, default => 'text',
108             callbacks => { "HTML::Mason::Exception->can( method )'" =>
109 97         12192 sub { HTML::Mason::Exception->can("as_$_[0]"); } },
110             descr => "How error conditions are returned to the caller (brief, text, line or html)" },
111              
112             error_mode =>
113             { parse => 'string', type => SCALAR, default => 'fatal',
114             regex => qr/^(?:output|fatal)$/,
115             descr => "How error conditions are manifest (output or fatal)" },
116              
117             component_error_handler =>
118             { parse => 'code', type => CODEREF|SCALAR, default => \&rethrow_exception,
119             descr => "A subroutine reference called on component compilation or runtime errors" },
120              
121             max_recurse =>
122             { parse => 'string', default => 32, type => SCALAR,
123             descr => "The maximum recursion depth for component, inheritance, and request stack" },
124              
125             out_method =>
126             { parse => 'code' ,type => CODEREF|SCALARREF,
127 6         144 default => sub { print STDOUT $_[0] },
128 32     32   1824 descr => "A subroutine or scalar reference through which all output will pass" },
129              
130             # Only used when creating subrequests
131             parent_request =>
132             { isa => __PACKAGE__,
133             default => undef,
134             public => 0,
135             },
136              
137             plugins =>
138             { parse => 'list', default => [], type => ARRAYREF,
139             descr => 'List of plugin classes or objects to run hooks around components and requests' },
140              
141             # Only used when creating subrequests
142             request_depth =>
143             { type => SCALAR,
144             default => 1,
145             public => 0,
146             },
147              
148             );
149             }
150              
151             my @read_write_params;
152 32     32   4640 BEGIN { @read_write_params = qw(
153             autoflush
154             component_error_handler
155             data_cache_api
156             data_cache_defaults
157             dhandler_name
158             error_format
159             error_mode
160             max_recurse
161             out_method
162             ); }
163              
164             use HTML::Mason::MethodMaker
165             ( read_only => [ qw(
166             count
167             dhandler_arg
168             initialized
169             interp
170             parent_request
171             plugin_instances
172             request_depth
173             request_comp
174             ) ],
175              
176 32         177 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  288         4698  
177             @read_write_params ]
178 32     32   262 );
  32         71  
179              
180 82     82   308 sub _properties { @read_write_params }
181              
182             sub new
183             {
184 479     479 1 20091 my $class = shift;
185 479         1743 my $self = $class->SUPER::new(@_);
186              
187             # These are mandatory values for all requests.
188             #
189 479         51795 %$self = (%$self,
190             dhandler_arg => undef,
191             execd => 0,
192             initialized => 0,
193             stack => [],
194             top_stack => undef,
195             wrapper_chain => undef,
196             wrapper_index => undef,
197             notes => {},
198             );
199              
200 479         1654 $self->{request_comp} = delete($self->{comp});
201 479         1216 $self->{request_args} = delete($self->{args});
202 479 50       2086 if (UNIVERSAL::isa($self->{request_args}, 'HASH')) {
203 0         0 $self->{request_args} = [%{$self->{request_args}}];
  0         0  
204             }
205 479         1527 $self->{count} = ++$self->{interp}{request_count};
206 479 100       1435 if (ref($self->{out_method}) eq 'SCALAR') {
207 16         31 my $bufref = $self->{out_method};
208 16     13   127 $self->{out_method} = sub { $$bufref .= $_[0] };
  13         38  
209             }
210             $self->{use_internal_component_caches} =
211 479         1559 $self->{interp}->use_internal_component_caches;
212 479         1653 $self->_initialize;
213              
214 444         2772 return $self;
215             }
216              
217             # in the future this method may do something completely different but
218             # for now this works just fine.
219             sub instance {
220 29     29 1 148 return $HTML::Mason::Commands::m; #; this comment fixes a parsing bug in Emacs cperl-mode
221             }
222              
223             # Attempt to load each plugin module once per process
224             my %plugin_loaded;
225              
226             sub _initialize {
227 479     479   953 my ($self) = @_;
228              
229 479 100       1297 local $SIG{'__DIE__'} = $self->component_error_handler
230             if $self->component_error_handler;
231              
232 479         901 eval {
233             # Check the static_source touch file, if it exists, before the
234             # first component is loaded.
235             #
236 479         1122 $self->interp->check_static_source_touch_file();
237              
238             # request_comp can be an absolute path or component object. If a path,
239             # load into object.
240 479         991 my $request_comp = $self->{request_comp};
241 479         653 my ($path);
242 479 100       1109 if (!ref($request_comp)) {
    50          
243 472         3429 $request_comp =~ s{/+}{/}g;
244 472         1394 $self->{top_path} = $path = $request_comp;
245             $log->debugf("top path is '%s'", $self->{top_path})
246 472 100       2690 if $log->is_debug;
247              
248 472         5998 my $retry_count = 0;
249             search: {
250 472         725 $request_comp = $self->interp->load($path);
  483         1047  
251              
252 455 100       1497 last search unless $self->use_dhandlers;
253              
254             # If path was not found, check for dhandler.
255 453 100       1080 unless ($request_comp) {
256 25 100       74 if ( $request_comp = $self->interp->find_comp_upwards($path, $self->dhandler_name) ) {
257 20         49 my $parent_path = $request_comp->dir_path;
258 20         326 ($self->{dhandler_arg} = $self->{top_path}) =~ s{^$parent_path/?}{};
259             $log->debugf("found dhandler '%s', dhandler_arg '%s'", $parent_path, $self->{dhandler_arg})
260 20 50       88 if $log->is_debug;
261             }
262             }
263              
264             # If the component was declined previously in this
265             # request, look for the next dhandler up the
266             # tree.
267 453 100 100     2248 if ($request_comp and $self->{declined_comps}->{$request_comp->comp_id}) {
268 12         27 $path = $request_comp->dir_path;
269 12 100       27 if ($request_comp->name eq $self->dhandler_name) {
270 6 100       17 if ($path eq '/') {
271 1         2 undef $request_comp;
272 1         2 last search; # End search if /dhandler declined
273             } else {
274 5         29 $path =~ s:/[^\/]+$::;
275 5   100     18 $path ||= '/';
276             }
277             }
278 11 50       30 if ($retry_count++ > $self->max_recurse) {
279 0         0 error "could not find dhandler after " . $self->max_recurse . " tries (infinite loop bug?)";
280             }
281 11         20 redo search;
282             }
283             }
284              
285 444 100       1315 unless ($self->{request_comp} = $request_comp) {
286             top_level_not_found_error "could not find component for initial path '$self->{top_path}' " .
287             "(component roots are: " .
288 7         81 join(", ", map { "'" . $_->[1] . "'" } $self->{interp}->comp_root_array) .
  9         135  
289             ")";
290             }
291              
292             } elsif ( ! UNIVERSAL::isa( $request_comp, 'HTML::Mason::Component' ) ) {
293 0         0 param_error "comp ($request_comp) must be a component path or a component object";
294             }
295              
296             # Construct a plugin instance for each plugin class in each request.
297             #
298 444         885 $self->{has_plugins} = 0;
299 444         946 $self->{plugin_instances} = [];
300 444         656 foreach my $plugin (@{ delete $self->{plugins} }) {
  444         1195  
301 25         37 $self->{has_plugins} = 1;
302 25         47 my $plugin_instance = $plugin;
303 25 100       63 unless (ref $plugin) {
304              
305             # Load information about each plugin class once per
306             # process. Right now the only information we need is
307             # whether there is a new() method.
308             #
309 23 100       51 unless ($plugin_loaded{$plugin}) {
310             # Load plugin package if it isn't already loaded.
311             #
312             {
313 32     32   286 no strict 'refs';
  32         72  
  32         11231  
  13         19  
314 13 50       17 unless (keys %{$plugin . "::"}) {
  13         159  
315 0         0 eval "use $plugin;";
316 0 0       0 die $@ if $@;
317             }
318             }
319 13         53 $plugin_loaded{$plugin} = 1;
320             }
321 23         246 $plugin_instance = $plugin->new();
322             }
323 25         39 push @{$self->{plugin_instances}}, $plugin_instance;
  25         78  
324             }
325 444         716 $self->{plugin_instances_reverse} = [reverse(@{$self->{plugin_instances}})];
  444         1034  
326              
327             # Check for autoflush and !enable_autoflush
328             #
329 444 100 100     1530 if ($self->{autoflush} && !$self->interp->compiler->enable_autoflush) {
330 1         15 die "Cannot use autoflush unless enable_autoflush is set";
331             }
332              
333             };
334              
335 479         2079 my $err = $@;
336 479 100 66     1247 if ($err and !$self->_aborted_or_declined($err)) {
337 36         162 $self->_handle_error($err);
338             } else {
339 443         1705 $self->{initialized} = 1;
340             }
341             }
342              
343             sub use_dhandlers
344             {
345 456     456 0 776 my $self = shift;
346 456   66     2157 return (defined $self->{dhandler_name} and length $self->{dhandler_name});
347             }
348              
349             sub alter_superclass
350             {
351 2     2 0 159 my $self = shift;
352 2         9 my $new_super = shift;
353              
354 2         5 my $class = caller;
355              
356 2         3 my $isa_ref;
357             {
358 32     32   251 no strict 'refs';
  32         75  
  32         3887  
  2         96  
359 2         79 my @isa = @{ $class . '::ISA' };
  2         17  
360 2         10 $isa_ref = \@isa;
361             }
362              
363             # handles multiple inheritance properly and preserve
364             # inheritance order
365 2         6 for ( my $x = 0; $x <= $#{$isa_ref} ; $x++ )
  2         13  
366             {
367 2 50       11 if ( $isa_ref->[$x]->isa('HTML::Mason::Request') )
368             {
369 2         4 my $old_super = $isa_ref->[$x];
370              
371 2 50       6 if ( $old_super ne $new_super )
372             {
373 0         0 $isa_ref->[$x] = $new_super;
374             }
375              
376 2         5 last;
377             }
378             }
379              
380             {
381 32     32   230 no strict 'refs';
  32         112  
  32         207959  
  2         4  
382 2         3 @{ $class . '::ISA' } = @{ $isa_ref };
  2         84  
  2         4  
383             }
384              
385 2         11 $class->valid_params( %{ $class->valid_params } );
  2         13  
386             }
387              
388             sub exec {
389 444     444 1 995 my ($self) = @_;
390              
391             # If the request failed to initialize, the error has already been handled
392             # at the bottom of _initialize(); just return.
393 444 100       1169 return unless $self->initialized();
394              
395 443 100       1089 local $SIG{'__DIE__'} = $self->component_error_handler
396             if $self->component_error_handler;
397              
398             # Cheap way to prevent users from executing the same request twice.
399             #
400 443 100       1406 if ($self->{execd}++) {
401 1         5 error "Can only call exec() once for a given request object. Did you want to use a subrequest?";
402             }
403              
404             # Check for infinite subrequest loop.
405             #
406 442 100       1134 error "subrequest depth > " . $self->max_recurse . " (infinite subrequest loop?)"
407             if $self->request_depth > $self->max_recurse;
408              
409             #
410             # $m is a dynamically scoped global containing this
411             # request. This needs to be defined in the HTML::Mason::Commands
412             # package, as well as the component package if that is different.
413             #
414 441         848 local $HTML::Mason::Commands::m = $self;
415              
416             # Dynamically scoped global pointing at the top of the request stack.
417             #
418 441         764 $self->{top_stack} = undef;
419              
420             # Save context of subroutine for use inside eval.
421 441         658 my $wantarray = wantarray;
422 441         711 my @result;
423              
424             # Initialize output buffer to interpreter's preallocated buffer
425             # before clearing, to reduce memory reallocations.
426             #
427 441         1011 $self->{request_buffer} = $self->interp->preallocated_output_buffer;
428 441         891 $self->{request_buffer} = '';
429              
430 441 100       1541 $log->debugf("starting request for '%s'", $self->request_comp->title)
431             if $log->is_debug;
432              
433 441         4063 eval {
434             # Build wrapper chain and index.
435 441         1110 my $request_comp = $self->request_comp;
436 441         669 my $first_comp;
437             {
438 441         697 my @wrapper_chain = ($request_comp);
  441         917  
439              
440 441         1461 for (my $parent = $request_comp->parent; $parent; $parent = $parent->parent) {
441 26         57 unshift(@wrapper_chain,$parent);
442 26 50       72 error "inheritance chain length > " . $self->max_recurse . " (infinite inheritance loop?)"
443             if (@wrapper_chain > $self->max_recurse);
444             }
445              
446 441         831 $first_comp = $wrapper_chain[0];
447 441         1206 $self->{wrapper_chain} = [@wrapper_chain];
448             $self->{wrapper_index} = { map
449 441         1184 { $wrapper_chain[$_]->comp_id => $_ }
  467         1364  
450             (0..$#wrapper_chain)
451             };
452             }
453              
454             # Get original request_args array reference to avoid copying.
455 441         960 my $request_args = $self->{request_args};
456             {
457 441         666 local *SELECTED;
  441         1187  
458 441         3593 tie *SELECTED, 'Tie::Handle::Mason';
459              
460 441         1541 my $old = select SELECTED;
461 441         2027 my $mods = {base_comp => $request_comp, store => \($self->{request_buffer}), flushable => 1};
462              
463 441 100       1221 if ($self->{has_plugins}) {
464 20         99 my $context = bless
465             [$self, $request_args],
466             'HTML::Mason::Plugin::Context::StartRequest';
467 20         28 eval {
468 20         33 foreach my $plugin_instance (@{$self->plugin_instances}) {
  20         49  
469 25         139 $plugin_instance->start_request_hook( $context );
470             }
471             };
472 20 100       94 if ($@) {
473 1         10 select $old;
474 1         4 rethrow_exception $@;
475             }
476             }
477              
478 440 100       1236 if ($wantarray) {
    100          
479 2         4 @result = eval {$self->comp($mods, $first_comp, @$request_args)};
  2         9  
480             } elsif (defined($wantarray)) {
481 16         30 $result[0] = eval {$self->comp($mods, $first_comp, @$request_args)};
  16         93  
482             } else {
483 422         674 eval {$self->comp($mods, $first_comp, @$request_args)};
  422         1629  
484             }
485            
486 440         1408 my $error = $@;
487              
488 440 100       1020 if ($self->{has_plugins}) {
489             # plugins called in reverse order when exiting.
490             my $context = bless
491 19         84 [$self, $request_args, \$self->{request_buffer}, $wantarray, \@result, \$error],
492             'HTML::Mason::Plugin::Context::EndRequest';
493 19         36 eval {
494 19         28 foreach my $plugin_instance (@{$self->{plugin_instances_reverse}}) {
  19         41  
495 24         125 $plugin_instance->end_request_hook( $context );
496             }
497             };
498 19 100       120 if ($@) {
499             # plugin errors take precedence over component errors
500 1         14 $error = $@;
501             }
502             }
503            
504 440         1778 select $old;
505 440         1467 rethrow_exception $error;
506             }
507             };
508              
509 441 100       2096 $log->debugf("finishing request for '%s'", $self->request_comp->title)
510             if $log->is_debug;
511              
512             # Purge code cache if necessary.
513 441         4196 $self->interp->purge_code_cache;
514              
515             # Handle errors.
516 441         759 my $err = $@;
517 441 100 100     1123 if ($err and !$self->_aborted_or_declined($err)) {
518 82         332 $self->_handle_error($err);
519 6         113 return;
520             }
521              
522             # If there's anything in the output buffer, send it to out_method.
523             # Otherwise skip out_method call to avoid triggering side effects
524             # (e.g. HTTP header sending).
525 359 100       937 if (length($self->{request_buffer}) > 0) {
526 336         895 $self->out_method->($self->{request_buffer});
527             }
528              
529             # Return aborted value or result.
530 359 100       1131 @result = ($err->aborted_value) if $self->aborted($err);
531 359 100       936 @result = ($err->declined_value) if $self->declined($err);
532 359 100       3267 return $wantarray ? @result : defined($wantarray) ? $result[0] : undef;
    100          
533             }
534              
535             #
536             # Display or die with error as dictated by error_mode and error_format.
537             #
538             sub _handle_error
539             {
540 118     118   274 my ($self, $err) = @_;
541              
542 118         321 $self->interp->purge_code_cache;
543              
544 118 100       338 rethrow_exception $err if $self->is_subrequest;
545              
546             # Set error format for when error is stringified.
547 78 100       334 if (UNIVERSAL::can($err, 'format')) {
548 77         260 $err->format($self->error_format);
549             }
550              
551             # In fatal mode, die with error. In display mode, output stringified error.
552 78 100       267 if ($self->error_mode eq 'fatal') {
553 71         250 rethrow_exception $err;
554             } else {
555 7 50       38 if ( UNIVERSAL::isa( $self->out_method, 'CODE' ) ) {
556             # This may not be set if an error occurred in
557             # _initialize(), for example with a compilation error.
558             # But the output method may rely on being able to get at
559             # the request object. This is a nasty code smell but
560             # fixing it properly is probably out of scope.
561             #
562             # Previously this method could only be called from exec().
563             #
564             # Without this one of the tests in 16-live_cgi.t was
565             # failing.
566 7   33     44 local $HTML::Mason::Commands::m ||= $self;
567 7         23 $self->out_method->("$err");
568             } else {
569 0         0 ${ $self->out_method } = "$err";
  0         0  
570             }
571             }
572             }
573              
574             sub subexec
575             {
576 66     66 1 118 my $self = shift;
577 66         106 my $comp = shift;
578              
579 66         191 $self->make_subrequest(comp=>$comp, args=>\@_)->exec;
580             }
581              
582             sub make_subrequest
583             {
584 82     82 1 282 my ($self, %params) = @_;
585 82         295 my $interp = $self->interp;
586              
587             # Coerce a string 'comp' parameter into an absolute path. Don't
588             # create it if it's missing, though - it's required, but for
589             # consistency we let exceptions be thrown later.
590             $params{comp} = absolute_comp_path($params{comp}, $self->current_comp->dir_path)
591 82 100 66     527 if exists $params{comp} && !ref($params{comp});
592              
593             # Give subrequest the same values as parent request for read/write params
594 82         257 my %defaults = map { ($_, $self->$_()) } $self->_properties;
  738         1614  
595              
596 82 100       283 unless ( $params{out_method} )
597             {
598             $defaults{out_method} = sub {
599 35     35   98 $self->print($_[0]);
600 80         415 };
601             }
602              
603             # Make subrequest, and set parent_request and request_depth appropriately.
604 82         388 my $subreq =
605             $interp->make_request(%defaults, %params,
606             parent_request => $self,
607             request_depth => $self->request_depth + 1);
608              
609 77         646 return $subreq;
610             }
611              
612             sub is_subrequest
613             {
614 127     127 0 290 my ($self) = @_;
615              
616 127 100       350 return $self->parent_request ? 1 : 0;
617             }
618              
619             sub clear_and_abort
620             {
621 1     1 1 2 my $self = shift;
622              
623 1         4 $self->clear_buffer;
624 1         3 $self->abort(@_);
625             }
626              
627             sub abort
628             {
629 12     12 1 37 my ($self, $aborted_value) = @_;
630 12         252 HTML::Mason::Exception::Abort->throw( error => 'Request->abort was called', aborted_value => $aborted_value );
631             }
632              
633             #
634             # Determine whether $err (or $@ by default) is an Abort exception.
635             #
636             sub aborted {
637 512     512 1 1037 my ($self, $err) = @_;
638 512 100       1145 $err = $@ if !defined($err);
639 512         1481 return isa_mason_exception( $err, 'Abort' );
640             }
641              
642             #
643             # Determine whether $err (or $@ by default) is an Decline exception.
644             #
645             sub declined {
646 501     501 1 979 my ($self, $err) = @_;
647 501 100       1027 $err = $@ if !defined($err);
648 501         1115 return isa_mason_exception( $err, 'Decline' );
649             }
650              
651             sub _aborted_or_declined {
652 148     148   1333 my ($self, $err) = @_;
653 148   100     403 return $self->aborted($err) || $self->declined($err);
654             }
655              
656             #
657             # Return a new cache object specific to this component.
658             #
659             sub cache
660             {
661 82     82 1 268 my ($self, %options) = @_;
662              
663             # If using 1.0x cache API, save off options for end of routine.
664 82         144 my %old_cache_options;
665 82 100       257 if ($self->data_cache_api eq '1.0') {
666 30         95 %old_cache_options = %options;
667 30         77 %options = ();
668             }
669              
670             # Combine defaults with options passed in here.
671 82 50       242 if ($self->data_cache_defaults) {
672 0         0 %options = (%{$self->data_cache_defaults}, %options);
  0         0  
673             }
674              
675             # If using the CHI API, just create and return a CHI handle. Namespace will be escaped by CHI.
676 82 50       207 if ($self->data_cache_api eq 'chi') {
677 0   0     0 my $chi_root_class = delete($options{chi_root_class}) || 'CHI';
678 0         0 load_pkg($chi_root_class);
679 0 0       0 if (!exists($options{namespace})) {
680 0         0 $options{namespace} = $self->current_comp->comp_id;
681             }
682 0 0 0     0 if (!exists($options{driver}) && !exists($options{driver_class})) {
683 0 0       0 $options{driver} = $self->interp->cache_dir ? 'File' : 'Memory';
684 0 0       0 $options{global} = 1 if $options{driver} eq 'Memory';
685             }
686 0   0     0 $options{root_dir} ||= $self->interp->cache_dir;
687 0         0 return $chi_root_class->new(%options);
688             }
689              
690 82   66     430 $options{cache_root} ||= $self->interp->cache_dir;
691 82   33     449 $options{namespace} ||= compress_path($self->current_comp->comp_id);
692              
693             # Determine cache_class, adding 'Cache::' in front of user's
694             # specification if necessary.
695 82 100       238 my $cache_class = $self->interp->cache_dir ? 'Cache::FileCache' : 'Cache::MemoryCache';
696 82 100       243 if ($options{cache_class}) {
697 2         6 $cache_class = $options{cache_class};
698 2 100       17 $cache_class = "Cache::$cache_class" unless $cache_class =~ /::/;
699 2         7 delete($options{cache_class});
700             }
701              
702             # Now prefix cache class with "HTML::Mason::". This will be a
703             # dynamically constructed package that simply inherits from
704             # HTML::Mason::Cache::BaseCache and the chosen cache class.
705 82         207 my $mason_cache_class = "HTML::Mason::$cache_class";
706 82 100       243 unless (pkg_loaded($mason_cache_class)) {
707 4         22 load_pkg('Cache::Cache', '$m->cache requires the Cache::Cache module, available from CPAN.');
708 4         16 load_pkg($cache_class, 'Fix your Cache::Cache installation or choose another cache class.');
709             # need to break up mention of VERSION var or else CPAN/EU::MM can choke when running 'r'
710 4     3   347 eval sprintf('package %s; use base qw(HTML::Mason::Cache::BaseCache %s); use vars qw($' . 'VERSION); $' . 'VERSION = 1.0;',
  3     3   23  
  3         7  
  3         1029  
  3         22  
  3         5  
  3         108  
711             $mason_cache_class, $cache_class);
712 4 50       21 error "Error constructing mason cache class $mason_cache_class: $@" if $@;
713             }
714              
715 82 50       556 my $cache = $mason_cache_class->new (\%options)
716             or error "could not create cache object";
717              
718             # Implement 1.0x cache API or just return cache object.
719 82 100       17469 if ($self->data_cache_api eq '1.0') {
720 30         113 return $self->_cache_1_x($cache, %old_cache_options);
721             } else {
722 52         292 return $cache;
723             }
724             }
725              
726             #
727             # Implement 1.0x cache API in terms of Cache::Cache.
728             # Supported: action, busy_lock, expire_at, expire_if, expire_in, expire_next, key, value
729             # Silently not supported: keep_in_memory, tie_class
730             #
731             sub _cache_1_x
732             {
733 30     30   90 my ($self, $cache, %options) = @_;
734              
735 30   100     125 my $action = $options{action} || 'retrieve';
736 30   100     128 my $key = $options{key} || 'main';
737            
738 30 100       99 if ($action eq 'retrieve') {
    100          
    100          
    50          
739            
740             # Validate parameters.
741 20 50       166 if (my @invalids = grep(!/^(expire_if|action|key|busy_lock|keep_in_memory|tie_class)$/, keys(%options))) {
742 0         0 param_error "cache: invalid parameter '$invalids[0]' for action '$action'\n";
743             }
744              
745             # Handle expire_if.
746 20 100       79 if (my $sub = $options{expire_if}) {
747 2 50       7 if (my $obj = $cache->get_object($key)) {
748 2 100       1077 if ($sub->($obj->get_created_at)) {
749 1         5 $cache->expire($key);
750             }
751             }
752             }
753              
754             # Return the value or undef, handling busy_lock.
755 20 100       1066 if (my $result = $cache->get($key, ($options{busy_lock} ? (busy_lock=>$options{busy_lock}) : ()))) {
    100          
756 11         161 return $result;
757             } else {
758 9         660 return undef;
759             }
760              
761             } elsif ($action eq 'store') {
762              
763             # Validate parameters
764 8 50       96 if (my @invalids = grep(!/^(expire_(at|next|in)|action|key|value|keep_in_memory|tie_class)$/, keys(%options))) {
765 0         0 param_error "cache: invalid parameter '$invalids[0]' for action '$action'\n";
766             }
767 8 50       40 param_error "cache: no store value provided" unless exists($options{value});
768              
769             # Determine $expires_in if expire flag given. For the "next"
770             # options, we're jumping through hoops to find the *top* of
771             # the next hour or day.
772             #
773 8         13 my $expires_in;
774 8         16 my $time = time;
775 8 100       29 if (exists($options{expire_at})) {
    50          
    50          
776 2 50       16 param_error "cache: invalid expire_at value '$options{expire_at}' - must be a numeric time value\n" if $options{expire_at} !~ /^[0-9]+$/;
777 2         7 $expires_in = $options{expire_at} - $time;
778             } elsif (exists($options{expire_next})) {
779 0         0 my $term = $options{expire_next};
780 0         0 my ($sec, $min, $hour) = localtime($time);
781 0 0       0 if ($term eq 'hour') {
    0          
782 0         0 $expires_in = 60*(59-$min)+(60-$sec);
783             } elsif ($term eq 'day') {
784 0         0 $expires_in = 3600*(23-$hour)+60*(59-$min)+(60-$sec);
785             } else {
786 0         0 param_error "cache: invalid expire_next value '$term' - must be 'hour' or 'day'\n";
787             }
788             } elsif (exists($options{expire_in})) {
789 0         0 $expires_in = $options{expire_in};
790             }
791              
792             # Set and return the value.
793 8         22 my $value = $options{value};
794 8         44 $cache->set($key, $value, $expires_in);
795 8         11822 return $value;
796              
797             } elsif ($action eq 'expire') {
798 1 50       7 my @keys = (ref($key) eq 'ARRAY') ? @$key : ($key);
799 1         11 foreach my $key (@keys) {
800 2         883 $cache->expire($key);
801             }
802              
803             } elsif ($action eq 'keys') {
804 1         17 return $cache->get_keys;
805             }
806             }
807              
808             sub cache_self {
809 51     51 1 153 my ($self, %options) = @_;
810              
811 51 100       198 return if $self->{top_stack}->[STACK_IN_CALL_SELF]->{'CACHE_SELF'};
812              
813 33         94 my (%store_options, %retrieve_options);
814 33         0 my ($expires_in, $key, $cache);
815 33 100       91 if ($self->data_cache_api eq '1.0') {
816 3         9 foreach (qw(key expire_if busy_lock)) {
817 9 100       24 $retrieve_options{$_} = $options{$_} if (exists($options{$_}));
818             }
819 3         8 foreach (qw(key expire_at expire_next expire_in)) {
820 12 100       28 $store_options{$_} = $options{$_} if (exists($options{$_}));
821             }
822             } else {
823             #
824             # key, expires_in/expire_in, expire_if and busy_lock go into
825             # the set and get methods as appropriate. All other options
826             # are passed into $self->cache.
827             #
828 30         66 foreach (qw(expire_if busy_lock)) {
829 60 100       133 $retrieve_options{$_} = delete($options{$_}) if (exists($options{$_}));
830             }
831 30   100     159 $expires_in = delete $options{expires_in} || delete $options{expire_in} || 'never';
832 30   100     101 $key = delete $options{key} || '__mason_cache_self__';
833 30         85 $cache = $self->cache(%options);
834             }
835              
836 33         69 my ($output, @retval, $error);
837              
838 33 100       77 my $cached =
839             ( $self->data_cache_api eq '1.0' ?
840             $self->cache(%retrieve_options) :
841             $cache->get($key, %retrieve_options)
842             );
843              
844 33 100       2278 if ($cached) {
845 15         36 ($output, my $retval) = @$cached;
846 15         38 @retval = @$retval;
847             } else {
848 18         96 $self->call_self( \$output, \@retval, \$error, 'CACHE_SELF' );
849              
850             # If user aborted or declined, store in cache and print output
851             # before repropagating.
852             #
853 18 100       65 rethrow_exception $error
854             unless ($self->_aborted_or_declined($error));
855              
856 16         49 my $value = [$output, \@retval];
857 16 100       49 if ($self->data_cache_api eq '1.0') {
858 1         14 $self->cache(action=>'store', key=>$key, value=>$value, %store_options);
859             } else {
860 15         68 $cache->set($key, $value, $expires_in);
861             }
862             }
863              
864             #
865             # Print the component output.
866             #
867 31         21943 $self->print($output);
868              
869             #
870             # Rethrow abort/decline exception if any.
871             #
872 31         162 rethrow_exception $error;
873              
874             #
875             # Return the component return value in case the caller is interested,
876             # followed by 1 indicating the cache retrieval success.
877             #
878 29         238 return (@retval, 1);
879             }
880              
881             sub call_self
882             {
883 26     26 1 82 my ($self, $output, $retval, $error, $tag) = @_;
884              
885             # Keep track of each individual invocation of call_self in the
886             # component, via $tag. $tag is 'CACHE_SELF' or 'FILTER' when used
887             # by $m->cache_self and <%filter> sections respectively.
888             #
889 26   100     144 $tag ||= 'DEFAULT';
890 26         55 my $top_stack = $self->{top_stack};
891 26   100     82 $top_stack->[STACK_IN_CALL_SELF] ||= {};
892 26 100       94 return if $top_stack->[STACK_IN_CALL_SELF]->{$tag};
893 22         85 local $top_stack->[STACK_IN_CALL_SELF]->{$tag} = 1;
894              
895             # Determine wantarray based on retval reference
896 22 50       112 my $wantarray =
    100          
897             ( defined $retval ?
898             ( UNIVERSAL::isa( $retval, 'ARRAY' ) ? 1 : 0 ) :
899             undef
900             );
901              
902             # If output or retval references were left undefined, just point
903             # them to a dummy variable.
904             #
905 22         34 my $dummy;
906 22   100     49 $output ||= \$dummy;
907 22   100     58 $retval ||= \$dummy;
908              
909             # Temporarily put $output in place of the current top buffer.
910 22         45 local $top_stack->[STACK_BUFFER] = $output;
911              
912             # Call the component again, capturing output, return value and
913             # error. Don't catch errors unless the error reference was specified.
914             #
915 22         40 my $comp = $top_stack->[STACK_COMP];
916 22         36 my $args = $top_stack->[STACK_ARGS];
917 22         38 my @result;
918 22         41 eval {
919 22 100       48 if ($wantarray) {
    50          
920 20         89 @$retval = $comp->run(@$args);
921             } elsif (defined $wantarray) {
922 0         0 $$retval = $comp->run(@$args);
923             } else {
924 2         8 $comp->run(@$args);
925             }
926             };
927 22 100       99 if ($@) {
928 4 50       29 if ($error) {
929 4         9 $$error = $@;
930             } else {
931 0         0 die $@;
932             }
933             }
934              
935             # Return 1, indicating that this invocation of call_self is done.
936             #
937 22         95 return 1;
938             }
939              
940             sub call_dynamic {
941 22     22 0 59 my ($m, $key, @args) = @_;
942 22 100       67 my $comp = ($m->current_comp->is_subcomp) ? $m->current_comp->owner : $m->current_comp;
943 22 100 66     80 if (!defined($comp->dynamic_subs_request) or $comp->dynamic_subs_request ne $m) {
944 13         70 $comp->dynamic_subs_init;
945 8         24 $comp->dynamic_subs_request($m);
946             }
947              
948 17         87 return $comp->run_dynamic_sub($key, @args);
949             }
950              
951             sub call_next {
952 23     23 1 52 my ($self,@extra_args) = @_;
953 23 50       101 my $comp = $self->fetch_next
954             or error "call_next: no next component to invoke";
955 23         71 return $self->comp({base_comp=>$self->request_comp}, $comp, @{$self->current_args}, @extra_args);
  23         47  
956             }
957              
958             sub caller
959             {
960 3     3 1 9 my ($self) = @_;
961 3         8 return $self->callers(1);
962             }
963              
964             #
965             # Return a specified component from the stack, or the whole stack as a list.
966             #
967             sub callers
968             {
969 48     48 1 102 my ($self, $levels_back) = @_;
970 48 100       94 if (defined($levels_back)) {
971 39         92 my $frame = $self->_stack_frame($levels_back);
972 39 100       108 return unless defined $frame;
973 31         110 return $frame->[STACK_COMP];
974             } else {
975 9         22 my $depth = $self->depth;
976 9         32 return map($_->[STACK_COMP], $self->_stack_frames);
977             }
978             }
979              
980             #
981             # Return a specified argument list from the stack.
982             #
983             sub caller_args
984             {
985 3     3 1 10 my ($self, $levels_back) = @_;
986 3 50       9 param_error "caller_args expects stack level as argument" unless defined $levels_back;
987              
988 3         13 my $frame = $self->_stack_frame($levels_back);
989 3 50       15 return unless $frame;
990 3         6 my $args = $frame->[STACK_ARGS];
991 3 50       38 return wantarray ? @$args : { @$args };
992             }
993              
994             sub comp_exists
995             {
996 17     17 1 30 my ($self, $path) = @_;
997              
998             # In order to support SELF, PARENT, REQUEST, subcomponents and
999             # methods, it is easiest just to defer to fetch_comp.
1000             #
1001 17 100       33 return $self->fetch_comp($path) ? 1 : 0;
1002             }
1003              
1004             sub decline
1005             {
1006 9     9 1 18 my ($self) = @_;
1007              
1008 9         31 $self->clear_buffer;
1009             my $subreq = $self->make_subrequest
1010             (comp => $self->{top_path},
1011             args => $self->{request_args},
1012 9         32 declined_comps => {$self->request_comp->comp_id, 1, %{$self->{declined_comps}}});
  9         39  
1013 8         33 my $retval = $subreq->exec;
1014 7         75 HTML::Mason::Exception::Decline->throw( error => 'Request->decline was called', declined_value => $retval );
1015             }
1016              
1017             #
1018             # Return the current number of stack levels. 1 means top level, 0
1019             # means that no component has been called yet.
1020             #
1021             sub depth
1022             {
1023 1292     1292 1 1709 return scalar @{ $_[0]->{stack} };
  1292         2937  
1024             }
1025              
1026             #
1027             # Given a component path (absolute or relative), returns a component.
1028             # Handles SELF, PARENT, REQUEST, comp:method, relative->absolute
1029             # conversion, and local subcomponents.
1030             #
1031             # fetch_comp handles caching if use_internal_component_caches is on.
1032             # _fetch_comp does the real work.
1033             #
1034             sub fetch_comp
1035             {
1036 800     800 1 1782 my ($self, $path, $current_comp, $error, $exists_only) = @_;
1037              
1038 800 100       1538 return undef unless defined($path);
1039 798   66     3385 $current_comp ||= $self->{top_stack}->[STACK_COMP];
1040              
1041             return $self->_fetch_comp($path, $current_comp, $error)
1042 798 100       2570 unless $self->{use_internal_component_caches};
1043              
1044 35         66 my $fetch_comp_cache = $current_comp->{fetch_comp_cache};
1045 35 100       88 unless (defined($fetch_comp_cache->{$path})) {
1046              
1047             # Cache the component objects associated with
1048             # uncanonicalized paths like ../foo/bar.html. SELF and
1049             # REQUEST are dynamic and cannot be cached. Weaken the
1050             # references in this cache so that we don't hang on to the
1051             # coponent if it disappears from the main code cache.
1052             #
1053             # See Interp::_initialize for the definition of
1054             # use_internal_component_caches and the conditions under
1055             # which we can create this cache safely.
1056             #
1057 11 50       57 if ($path =~ /^(?:SELF|REQUEST)/) {
1058 0         0 return $self->_fetch_comp($path, $current_comp, $error);
1059             } else {
1060 11         32 $fetch_comp_cache->{$path} =
1061             $self->_fetch_comp($path, $current_comp, $error);
1062 11 50       31 Scalar::Util::weaken($fetch_comp_cache->{$path}) if can_weaken;
1063             }
1064             }
1065              
1066 35         136 return $fetch_comp_cache->{$path};
1067             }
1068              
1069             sub _fetch_comp
1070             {
1071 774     774   1643 my ($self, $path, $current_comp, $error) = @_;
1072              
1073             #
1074             # Handle paths SELF, PARENT, and REQUEST
1075             #
1076 774 100       1621 if ($path eq 'SELF') {
1077 63         168 return $self->base_comp;
1078             }
1079 711 100       1341 if ($path eq 'PARENT') {
1080 8         24 my $c = $current_comp->parent;
1081 8 100 100     34 $$error = "PARENT designator used from component with no parent" if !$c && defined($error);
1082 8         30 return $c;
1083             }
1084 703 100       1366 if ($path eq 'REQUEST') {
1085 4         14 return $self->request_comp;
1086             }
1087              
1088             #
1089             # Handle paths of the form comp_path:method_name
1090             #
1091 699 100       1829 if (index($path,':') != -1) {
1092 75         102 my $method_comp;
1093 75         264 my ($owner_path,$method_name) = split(':',$path,2);
1094 75 100       249 if (my $owner_comp = $self->fetch_comp($owner_path, $current_comp, $error)) {
1095 73 100       279 if ($owner_comp->_locate_inherited('methods',$method_name,\$method_comp)) {
1096 68         342 return $method_comp;
1097             } else {
1098 5 100       18 $$error = "no such method '$method_name' for component " . $owner_comp->title if defined($error);
1099             }
1100             } else {
1101 2 100 33     7 $$error ||= "could not find component for path '$owner_path'\n" if defined($error);
1102             }
1103              
1104 7         39 return $method_comp;
1105             }
1106              
1107             #
1108             # If path does not contain a slash, check for a subcomponent in the
1109             # current component first.
1110             #
1111 624 100       1931 if ($path !~ /\//) {
1112             # Check my subcomponents.
1113 460 100       1424 if (my $subcomp = $current_comp->subcomps($path)) {
1114 56         230 return $subcomp;
1115             }
1116             # If I am a subcomponent, also check my owner's subcomponents.
1117             # This won't work when we go to multiply embedded subcomponents...
1118 404 100 66     1058 if ($current_comp->is_subcomp and my $subcomp = $current_comp->owner->subcomps($path)) {
1119 5         19 return $subcomp;
1120             }
1121             }
1122              
1123             #
1124             # Otherwise pass the canonicalized absolute path to interp->load.
1125             #
1126 563         1676 $path = absolute_comp_path($path, $current_comp->dir_path);
1127 563         1605 my $comp = $self->interp->load($path);
1128              
1129 563         2376 return $comp;
1130             }
1131              
1132             #
1133             # Fetch the index of the next component in wrapper chain. If current
1134             # component is not in chain, search the component stack for the most
1135             # recent one that was.
1136             #
1137             sub _fetch_next_helper {
1138 29     29   48 my ($self) = @_;
1139 29         79 my $index = $self->{wrapper_index}->{$self->current_comp->comp_id};
1140 29 100       76 unless (defined($index)) {
1141 2         11 my @callers = $self->callers;
1142 2         7 shift(@callers);
1143 2   100     19 while (my $comp = shift(@callers) and !defined($index)) {
1144 2         9 $index = $self->{wrapper_index}->{$comp->comp_id};
1145             }
1146             }
1147 29         53 return $index;
1148             }
1149              
1150             #
1151             # Fetch next component in wrapper chain.
1152             #
1153             sub fetch_next {
1154 26     26 1 51 my ($self) = @_;
1155 26         70 my $index = $self->_fetch_next_helper;
1156 26 50       59 error "fetch_next: cannot find next component in chain"
1157             unless defined($index);
1158 26         89 return $self->{wrapper_chain}->[$index+1];
1159             }
1160              
1161             #
1162             # Fetch remaining components in wrapper chain.
1163             #
1164             sub fetch_next_all {
1165 3     3 1 5 my ($self) = @_;
1166 3         8 my $index = $self->_fetch_next_helper;
1167 3 50       6 error "fetch_next_all: cannot find next component in chain"
1168             unless defined($index);
1169 3         5 my @wc = @{$self->{wrapper_chain}};
  3         7  
1170 3         20 return @wc[($index+1)..$#wc];
1171             }
1172              
1173             sub file
1174             {
1175 3     3 1 7 my ($self,$file) = @_;
1176 3         10 my $interp = $self->interp;
1177 3 50       24 unless ( File::Spec->file_name_is_absolute($file) ) {
1178             # use owner if current comp is a subcomp
1179 3 100       11 my $context_comp =
1180             ( $self->current_comp->is_subcomp ?
1181             $self->current_comp->owner :
1182             $self->current_comp );
1183              
1184 3 50       13 if ($context_comp->is_file_based) {
1185 3         11 my $source_dir = $context_comp->source_dir;
1186 3         30 $file = File::Spec->catfile( $source_dir, $file );
1187             } else {
1188 0         0 $file = File::Spec->catfile( File::Spec->rootdir, $file );
1189             }
1190             }
1191 3         13 my $content = read_file($file,1);
1192 3         14 return $content;
1193             }
1194              
1195             sub print
1196             {
1197 3417     3417 1 5068 my $self = shift;
1198              
1199             # $self->{top_stack} is always defined _except_ in the case of a
1200             # call to print inside a start-/end-request plugin.
1201             my $bufref =
1202             ( defined $self->{top_stack}
1203             ? $self->{top_stack}->[STACK_BUFFER]
1204             : \$self->{request_buffer}
1205 3417 100       6329 );
1206              
1207             # use 'if defined' for maximum efficiency; grep creates a list.
1208 3417         5537 for ( @_ ) {
1209 3421 100       7899 $$bufref .= $_ if defined;
1210             }
1211              
1212 3417 100       7799 $self->flush_buffer if $self->{autoflush};
1213             }
1214              
1215             *out = \&print;
1216              
1217             #
1218             # Execute the given component
1219             #
1220             sub comp {
1221 1184     1184 1 1994 my $self = shift;
1222 1184         3723 my $log_is_debug = $log->is_debug;
1223              
1224             # Get modifiers: optional hash reference passed in as first argument.
1225             # Merge multiple hash references to simplify user and internal usage.
1226             #
1227 1184         9232 my %mods;
1228 1184         3209 %mods = (%{shift()}, %mods) while ref($_[0]) eq 'HASH';
  532         2970  
1229              
1230             # Get component path or object. If a path, load into object.
1231             #
1232 1184         1663 my $path;
1233 1184         1746 my $comp = shift;
1234 1184 100       2445 if (!ref($comp)) {
1235 686 50       1456 die "comp called without component - must pass a path or component object"
1236             unless defined($comp);
1237 686         1039 $path = $comp;
1238 686         875 my $error;
1239 686 100 66     1702 $comp = $self->fetch_comp($path, undef, \$error)
1240             or error($error || "could not find component for path '$path'\n");
1241             }
1242              
1243             # Increment depth and check for maximum recursion. Depth starts at 1.
1244             #
1245 1175         3042 my $depth = $self->depth;
1246             error "$depth levels deep in component stack (infinite recursive call?)\n"
1247 1175 100       2789 if $depth >= $self->{max_recurse};
1248              
1249             # Log start of component call.
1250             #
1251 1174 100       2219 $log->debugf("entering component '%s' [depth %d]", $comp->title(), $depth)
1252             if $log_is_debug;
1253              
1254             # Keep the same output buffer unless store modifier was passed. If we have
1255             # a filter, put the filter buffer on the stack instead of the regular buffer.
1256             #
1257 1174         2088 my $filter_buffer = '';
1258 1174 100       2696 my $top_buffer = defined($mods{store}) ? $mods{store} : $self->{top_stack}->[STACK_BUFFER];
1259 1174 100       2362 my $stack_buffer = $comp->{has_filter} ? \$filter_buffer : $top_buffer;
1260 1174 100 100     3855 my $flushable = exists $mods{flushable} ? $mods{flushable} : ($self->{top_stack}->[STACK_BUFFER_IS_FLUSHABLE] && ! defined($mods{store})) ;
1261              
1262             # Add new stack frame and point dynamically scoped $self->{top_stack} at it.
1263 1174         1704 push @{ $self->{stack} },
  1174         4132  
1264             [ $comp, # STACK_COMP
1265             \@_, # STACK_ARGS
1266             $stack_buffer, # STACK_BUFFER
1267             \%mods, # STACK_MODS
1268             $path, # STACK_PATH
1269             undef, # STACK_BASE_COMP
1270             undef, # STACK_IN_CALL_SELF
1271             $flushable, # STACK_BUFFER_IS_FLUSHABLE
1272             ];
1273 1174         3055 local $self->{top_stack} = $self->{stack}->[-1];
1274              
1275             # Run start_component hooks for each plugin.
1276             #
1277 1174 100       2544 if ($self->{has_plugins}) {
1278 54         182 my $context = bless
1279             [$self, $comp, \@_],
1280             'HTML::Mason::Plugin::Context::StartComponent';
1281              
1282 54         81 foreach my $plugin_instance (@{$self->{plugin_instances}}) {
  54         116  
1283 74         257 $plugin_instance->start_component_hook( $context );
1284             }
1285             }
1286              
1287             # Finally, call the component.
1288             #
1289 1173         1905 my $wantarray = wantarray;
1290 1173         1702 my @result;
1291            
1292 1173         1701 eval {
1293             # By putting an empty block here, we protect against stack
1294             # corruption when a component calls next or last outside of a
1295             # loop. See 05-request.t #28 for a test.
1296             {
1297 1173 100       1616 if ($wantarray) {
  1173 100       2528  
1298 14         51 @result = $comp->run(@_);
1299             } elsif (defined $wantarray) {
1300 22         95 $result[0] = $comp->run(@_);
1301             } else {
1302 1137         4172 $comp->run(@_);
1303             }
1304             }
1305             };
1306 1173         3558 my $error = $@;
1307              
1308             # Run component's filter if there is one, and restore true top buffer
1309             # (e.g. in case a plugin prints something).
1310             #
1311 1173 100       2519 if ($comp->{has_filter}) {
1312             # We have to check $comp->filter because abort or error may
1313             # occur before filter gets defined in component. In such cases
1314             # there should be no output, but should look into this more.
1315             #
1316 33 100       99 if (defined($comp->filter)) {
1317 31         82 $$top_buffer .= $comp->filter->($filter_buffer);
1318             }
1319 31         215 $self->{top_stack}->[STACK_BUFFER] = $top_buffer;
1320             }
1321              
1322             # Run end_component hooks for each plugin, in reverse order.
1323             #
1324 1171 100       2265 if ($self->{has_plugins}) {
1325 53         182 my $context = bless
1326             [$self, $comp, \@_, $wantarray, \@result, \$error],
1327             'HTML::Mason::Plugin::Context::EndComponent';
1328            
1329 53         80 foreach my $plugin_instance (@{$self->{plugin_instances_reverse}}) {
  53         98  
1330 73         212 $plugin_instance->end_component_hook( $context );
1331             }
1332             }
1333              
1334             # This is very important in order to avoid memory leaks, since we
1335             # stick the arguments on the stack. If we don't pop the stack,
1336             # they don't get cleaned up until the component exits.
1337 1168         1540 pop @{ $self->{stack} };
  1168         2082  
1338              
1339             # Log end of component call.
1340             #
1341 1168 100       2266 $log->debug(sprintf("exiting component '%s' [depth %d]", $comp->title(), $depth))
1342             if $log_is_debug;
1343              
1344             # Repropagate error if one occurred, otherwise return result.
1345 1168 100       2358 rethrow_exception $error if $error;
1346 1019 100       4445 return $wantarray ? @result : $result[0];
1347             }
1348              
1349             #
1350             # Like comp, but return component output.
1351             #
1352             sub scomp {
1353 21     21 1 45 my $self = shift;
1354 21         34 my $buf;
1355 21         86 $self->comp({store => \$buf},@_);
1356 21         89 return $buf;
1357             }
1358              
1359             sub has_content {
1360 2     2 1 4 my $self = shift;
1361 2         6 return defined($self->{top_stack}->[STACK_MODS]->{content});
1362             }
1363              
1364             sub content {
1365 45     45 1 74 my $self = shift;
1366 45         95 my $content = $self->{top_stack}->[STACK_MODS]->{content};
1367 45 100       94 return undef unless defined($content);
1368              
1369             # Run the content routine with the previous stack frame active and
1370             # with output going to a new buffer.
1371             #
1372 43         68 my $err;
1373             my $buffer;
1374 43         60 my $save_frame = pop @{ $self->{stack} };
  43         74  
1375             {
1376 43         64 local $self->{top_stack} = $self->{stack}[-1];
  43         83  
1377 43         81 local $self->{top_stack}->[STACK_BUFFER] = \$buffer;
1378 43         70 local $self->{top_stack}->[STACK_BUFFER_IS_FLUSHABLE] = 0;
1379 43         86 local $self->{top_stack}->[STACK_HIDDEN_BUFFER] = $save_frame->[STACK_BUFFER];
1380 43         67 eval { $content->(); };
  43         90  
1381 43         119 $err = $@;
1382             }
1383              
1384 43         67 push @{ $self->{stack} }, $save_frame;
  43         78  
1385              
1386 43         141 rethrow_exception $err;
1387              
1388             # Return the output from the content routine.
1389             #
1390 43         138 return $buffer;
1391             }
1392              
1393             sub notes {
1394 3     3 1 5 my $self = shift;
1395 3 100       10 return $self->{notes} unless @_;
1396            
1397 2         4 my $key = shift;
1398 2 100       7 return $self->{notes}{$key} unless @_;
1399            
1400 1         4 return $self->{notes}{$key} = shift;
1401             }
1402              
1403             sub clear_buffer
1404             {
1405 28     28 1 64 my $self = shift;
1406              
1407 28         46 foreach my $frame (@{$self->{stack}}) {
  28         135  
1408 40         120 my $bufref = $frame->[STACK_BUFFER];
1409 40         73 $$bufref = '';
1410 40         75 $bufref = $frame->[STACK_HIDDEN_BUFFER];
1411 40 100       108 $$bufref = '' if $bufref;
1412             }
1413             }
1414              
1415             sub flush_buffer
1416             {
1417 147     147 1 221 my $self = shift;
1418              
1419             $self->out_method->($self->{request_buffer})
1420 147 100       454 if length $self->{request_buffer};
1421 147         252 $self->{request_buffer} = '';
1422              
1423 147 50 66     531 if ( $self->{top_stack}->[STACK_BUFFER_IS_FLUSHABLE]
1424             && $self->{top_stack}->[STACK_BUFFER] )
1425             {
1426 140         210 my $comp = $self->{top_stack}->[STACK_COMP];
1427 140 100 66     342 if ( $comp->has_filter()
1428             && defined $comp->filter() )
1429             {
1430             $self->out_method->
1431 2         4 ( $comp->filter->( ${ $self->{top_stack}->[STACK_BUFFER] } ) );
  2         7  
1432             }
1433             else
1434             {
1435 138         189 $self->out_method->( ${ $self->{top_stack}->[STACK_BUFFER] } );
  138         319  
1436             }
1437 140         210 ${$self->{top_stack}->[STACK_BUFFER]} = '';
  140         320  
1438             }
1439             }
1440              
1441             sub request_args
1442             {
1443 3     3 1 7 my ($self) = @_;
1444 3 100       9 if (wantarray) {
1445 1         2 return @{$self->{request_args}};
  1         5  
1446             } else {
1447 2         5 return { @{$self->{request_args}} };
  2         6  
1448             }
1449             }
1450              
1451             # For backward compatibility:
1452             *top_args = \&request_args;
1453             *top_comp = \&request_comp;
1454              
1455             #
1456             # Subroutine called by every component while in debug mode, convenient
1457             # for breakpointing.
1458             #
1459             sub debug_hook
1460             {
1461 0     0 0 0 1;
1462             }
1463              
1464              
1465             #
1466             # stack handling
1467             #
1468              
1469             # Return the stack frame $levels down from the top of the stack.
1470             # If $levels is negative, count from the bottom of the stack.
1471             #
1472             sub _stack_frame {
1473 42     42   76 my ($self, $levels) = @_;
1474 42         73 my $depth = $self->depth();
1475 42         64 my $index;
1476 42 100       84 if ($levels < 0) {
1477 9         18 $index = (-1 * $levels) - 1;
1478             } else {
1479 33         57 $index = $depth-1 - $levels;
1480             }
1481 42 100 100     180 return if $index < 0 or $index >= $depth;
1482 34         80 return $self->{stack}->[$index];
1483             }
1484              
1485             # Return all stack frames, in order from the top of the stack to the
1486             # initial frame.
1487             sub _stack_frames {
1488 9     9   20 my ($self) = @_;
1489              
1490 9         20 my $depth = $self->depth;
1491 9         28 return reverse map { $self->{stack}->[$_] } (0..$depth-1);
  27         92  
1492             }
1493              
1494             #
1495             # Accessor methods for top of stack elements.
1496             #
1497 320     320 1 1304 sub current_comp { return $_[0]->{top_stack}->[STACK_COMP] }
1498 23     23 1 95 sub current_args { return $_[0]->{top_stack}->[STACK_ARGS] }
1499              
1500             sub base_comp {
1501 89     89 1 158 my ($self) = @_;
1502              
1503 89 50       205 return unless $self->{top_stack};
1504              
1505 89 100       226 unless ( defined $self->{top_stack}->[STACK_BASE_COMP] ) {
1506 40         96 $self->_compute_base_comp_for_frame( $self->depth - 1 );
1507             }
1508 89         321 return $self->{top_stack}->[STACK_BASE_COMP];
1509             }
1510              
1511             #
1512             # Determine the base_comp for a stack frame. See the user
1513             # documentation for base_comp for a description of these rules.
1514             #
1515             sub _compute_base_comp_for_frame {
1516 48     48   103 my ($self, $frame_num) = @_;
1517 48 50       104 die "Invalid frame number: $frame_num" if $frame_num < 0;
1518              
1519 48         101 my $frame = $self->{stack}->[$frame_num];
1520              
1521 48 100       110 unless (defined($frame->[STACK_BASE_COMP])) {
1522 44         79 my $mods = $frame->[STACK_MODS];
1523 44         75 my $path = $frame->[STACK_PATH];
1524 44         60 my $comp = $frame->[STACK_COMP];
1525            
1526 44         64 my $base_comp;
1527 44 100 100     204 if (exists($mods->{base_comp})) {
    100 100        
    100 66        
1528 32         72 $base_comp = $mods->{base_comp};
1529             } elsif (!$path ||
1530             $path =~ m/^(?:SELF|PARENT|REQUEST)(?:\:..*)?$/ ||
1531             ($comp->is_subcomp && !$comp->is_method)) {
1532 8         46 $base_comp = $self->_compute_base_comp_for_frame($frame_num-1);
1533             } elsif ($path =~ m/(.*):/) {
1534 1         4 my $calling_comp = $self->{stack}->[$frame_num-1]->[STACK_COMP];
1535 1         5 $base_comp = $self->fetch_comp($1, $calling_comp);
1536             } else {
1537 3         7 $base_comp = $comp;
1538             }
1539 44         103 $frame->[STACK_BASE_COMP] = $base_comp;
1540             }
1541 48         96 return $frame->[STACK_BASE_COMP];
1542             }
1543              
1544             sub log
1545             {
1546 2     2 1 5 my ($self) = @_;
1547 2         6 return $self->current_comp->logger();
1548             }
1549              
1550             package Tie::Handle::Mason;
1551             $Tie::Handle::Mason::VERSION = '1.59';
1552             sub TIEHANDLE
1553             {
1554 441     441   1041 my $class = shift;
1555              
1556              
1557 441         1368 return bless {}, $class;
1558             }
1559              
1560             sub PRINT
1561             {
1562 116     116   323 my $self = shift;
1563              
1564 116         259 my $old = select STDOUT;
1565             # Use direct $m access instead of Request->instance() to optimize common case
1566 116         297 $HTML::Mason::Commands::m->print(@_);
1567              
1568 116         513 select $old;
1569             }
1570              
1571             sub PRINTF
1572             {
1573 2     2   5 my $self = shift;
1574              
1575             # apparently sprintf(@_) won't work, it needs to be a scalar
1576             # followed by a list
1577 2         10 $self->PRINT(sprintf(shift, @_));
1578             }
1579              
1580             1;
1581              
1582             __END__