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.60';
35 33     33   246 use strict;
  33         71  
  33         1056  
36 33     33   174 use warnings;
  33         459  
  33         850  
37              
38 32     32   198 use File::Spec;
  32         76  
  32         597  
39 32     32   13816 use HTML::Mason::Cache::BaseCache;
  32         84  
  32         952  
40 32     32   13105 use HTML::Mason::Plugin::Context;
  32         80  
  32         1007  
41 32     32   14082 use HTML::Mason::Tools qw(can_weaken read_file compress_path load_pkg pkg_loaded absolute_comp_path);
  32         122  
  32         2676  
42 32     32   14121 use HTML::Mason::Utils;
  32         78  
  32         1523  
43 32     32   14375 use Log::Any qw($log);
  32         259930  
  32         186  
44 32     32   82312 use Class::Container;
  32         132790  
  32         1045  
45 32     32   271 use base qw(Class::Container);
  32         77  
  32         3668  
46              
47             # Stack frame constants
48 32     32   226 use constant STACK_COMP => 0;
  32         75  
  32         1852  
49 32     32   191 use constant STACK_ARGS => 1;
  32         84  
  32         1400  
50 32     32   201 use constant STACK_BUFFER => 2;
  32         70  
  32         1405  
51 32     32   184 use constant STACK_MODS => 3;
  32         89  
  32         1334  
52 32     32   190 use constant STACK_PATH => 4;
  32         74  
  32         1424  
53 32     32   174 use constant STACK_BASE_COMP => 5;
  32         59  
  32         1455  
54 32     32   198 use constant STACK_IN_CALL_SELF => 6;
  32         69  
  32         1472  
55 32     32   223 use constant STACK_BUFFER_IS_FLUSHABLE => 7;
  32         75  
  32         1544  
56 32     32   205 use constant STACK_HIDDEN_BUFFER => 8;
  32         71  
  32         1865  
57              
58             # HTML::Mason::Exceptions always exports rethrow_exception() and isa_mason_exception()
59 32         307 use HTML::Mason::Exceptions( abbr => [qw(error param_error syntax_error
60 32     32   218 top_level_not_found_error error)] );
  32         64  
61              
62 32     32   222 use Params::Validate qw(:all);
  32         69  
  32         17778  
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         12930 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         133 default => sub { print STDOUT $_[0] },
128 32     32   1942 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   4894 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         130 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  288         4998  
177             @read_write_params ]
178 32     32   242 );
  32         105  
179              
180 82     82   328 sub _properties { @read_write_params }
181              
182             sub new
183             {
184 479     479 1 20674 my $class = shift;
185 479         1889 my $self = $class->SUPER::new(@_);
186              
187             # These are mandatory values for all requests.
188             #
189 479         53255 %$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         1831 $self->{request_comp} = delete($self->{comp});
201 479         1332 $self->{request_args} = delete($self->{args});
202 479 50       2254 if (UNIVERSAL::isa($self->{request_args}, 'HASH')) {
203 0         0 $self->{request_args} = [%{$self->{request_args}}];
  0         0  
204             }
205 479         1582 $self->{count} = ++$self->{interp}{request_count};
206 479 100       1421 if (ref($self->{out_method}) eq 'SCALAR') {
207 16         39 my $bufref = $self->{out_method};
208 16     13   157 $self->{out_method} = sub { $$bufref .= $_[0] };
  13         43  
209             }
210             $self->{use_internal_component_caches} =
211 479         1488 $self->{interp}->use_internal_component_caches;
212 479         1611 $self->_initialize;
213              
214 444         2868 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 187 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   1021 my ($self) = @_;
228              
229 479 100       1284 local $SIG{'__DIE__'} = $self->component_error_handler
230             if $self->component_error_handler;
231              
232 479         1082 eval {
233             # Check the static_source touch file, if it exists, before the
234             # first component is loaded.
235             #
236 479         1254 $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         1007 my $request_comp = $self->{request_comp};
241 479         754 my ($path);
242 479 100       1225 if (!ref($request_comp)) {
    50          
243 472         3518 $request_comp =~ s{/+}{/}g;
244 472         1431 $self->{top_path} = $path = $request_comp;
245             $log->debugf("top path is '%s'", $self->{top_path})
246 472 100       2689 if $log->is_debug;
247              
248 472         6233 my $retry_count = 0;
249             search: {
250 472         725 $request_comp = $self->interp->load($path);
  483         1118  
251              
252 455 100       1513 last search unless $self->use_dhandlers;
253              
254             # If path was not found, check for dhandler.
255 453 100       1159 unless ($request_comp) {
256 25 100       103 if ( $request_comp = $self->interp->find_comp_upwards($path, $self->dhandler_name) ) {
257 20         54 my $parent_path = $request_comp->dir_path;
258 20         396 ($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     2313 if ($request_comp and $self->{declined_comps}->{$request_comp->comp_id}) {
268 12         37 $path = $request_comp->dir_path;
269 12 100       29 if ($request_comp->name eq $self->dhandler_name) {
270 6 100       17 if ($path eq '/') {
271 1         3 undef $request_comp;
272 1         3 last search; # End search if /dhandler declined
273             } else {
274 5         32 $path =~ s:/[^\/]+$::;
275 5   100     20 $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         32 redo search;
282             }
283             }
284              
285 444 100       1352 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         64 join(", ", map { "'" . $_->[1] . "'" } $self->{interp}->comp_root_array) .
  9         128  
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         918 $self->{has_plugins} = 0;
299 444         1343 $self->{plugin_instances} = [];
300 444         737 foreach my $plugin (@{ delete $self->{plugins} }) {
  444         1260  
301 25         41 $self->{has_plugins} = 1;
302 25         47 my $plugin_instance = $plugin;
303 25 100       55 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       62 unless ($plugin_loaded{$plugin}) {
310             # Load plugin package if it isn't already loaded.
311             #
312             {
313 32     32   269 no strict 'refs';
  32         67  
  32         10799  
  13         18  
314 13 50       17 unless (keys %{$plugin . "::"}) {
  13         144  
315 0         0 eval "use $plugin;";
316 0 0       0 die $@ if $@;
317             }
318             }
319 13         57 $plugin_loaded{$plugin} = 1;
320             }
321 23         206 $plugin_instance = $plugin->new();
322             }
323 25         36 push @{$self->{plugin_instances}}, $plugin_instance;
  25         70  
324             }
325 444         746 $self->{plugin_instances_reverse} = [reverse(@{$self->{plugin_instances}})];
  444         1070  
326              
327             # Check for autoflush and !enable_autoflush
328             #
329 444 100 100     1518 if ($self->{autoflush} && !$self->interp->compiler->enable_autoflush) {
330 1         16 die "Cannot use autoflush unless enable_autoflush is set";
331             }
332              
333             };
334              
335 479         2209 my $err = $@;
336 479 100 66     1310 if ($err and !$self->_aborted_or_declined($err)) {
337 36         212 $self->_handle_error($err);
338             } else {
339 443         1717 $self->{initialized} = 1;
340             }
341             }
342              
343             sub use_dhandlers
344             {
345 456     456 0 775 my $self = shift;
346 456   66     2237 return (defined $self->{dhandler_name} and length $self->{dhandler_name});
347             }
348              
349             sub alter_superclass
350             {
351 2     2 0 184 my $self = shift;
352 2         5 my $new_super = shift;
353              
354 2         3 my $class = caller;
355              
356 2         5 my $isa_ref;
357             {
358 32     32   270 no strict 'refs';
  32         78  
  32         3882  
  2         143  
359 2         91 my @isa = @{ $class . '::ISA' };
  2         18  
360 2         11 $isa_ref = \@isa;
361             }
362              
363             # handles multiple inheritance properly and preserve
364             # inheritance order
365 2         7 for ( my $x = 0; $x <= $#{$isa_ref} ; $x++ )
  2         7  
366             {
367 2 50       10 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   222 no strict 'refs';
  32         96  
  32         211704  
  2         3  
382 2         3 @{ $class . '::ISA' } = @{ $isa_ref };
  2         85  
  2         4  
383             }
384              
385 2         14 $class->valid_params( %{ $class->valid_params } );
  2         46  
386             }
387              
388             sub exec {
389 444     444 1 1057 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       1164 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       1369 if ($self->{execd}++) {
401 1         20 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       1176 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         852 local $HTML::Mason::Commands::m = $self;
415              
416             # Dynamically scoped global pointing at the top of the request stack.
417             #
418 441         792 $self->{top_stack} = undef;
419              
420             # Save context of subroutine for use inside eval.
421 441         791 my $wantarray = wantarray;
422 441         734 my @result;
423              
424             # Initialize output buffer to interpreter's preallocated buffer
425             # before clearing, to reduce memory reallocations.
426             #
427 441         1038 $self->{request_buffer} = $self->interp->preallocated_output_buffer;
428 441         914 $self->{request_buffer} = '';
429              
430 441 100       1640 $log->debugf("starting request for '%s'", $self->request_comp->title)
431             if $log->is_debug;
432              
433 441         4192 eval {
434             # Build wrapper chain and index.
435 441         1126 my $request_comp = $self->request_comp;
436 441         712 my $first_comp;
437             {
438 441         636 my @wrapper_chain = ($request_comp);
  441         928  
439              
440 441         1499 for (my $parent = $request_comp->parent; $parent; $parent = $parent->parent) {
441 26         63 unshift(@wrapper_chain,$parent);
442 26 50       74 error "inheritance chain length > " . $self->max_recurse . " (infinite inheritance loop?)"
443             if (@wrapper_chain > $self->max_recurse);
444             }
445              
446 441         812 $first_comp = $wrapper_chain[0];
447 441         1304 $self->{wrapper_chain} = [@wrapper_chain];
448             $self->{wrapper_index} = { map
449 441         1265 { $wrapper_chain[$_]->comp_id => $_ }
  467         1354  
450             (0..$#wrapper_chain)
451             };
452             }
453              
454             # Get original request_args array reference to avoid copying.
455 441         1048 my $request_args = $self->{request_args};
456             {
457 441         675 local *SELECTED;
  441         1184  
458 441         3474 tie *SELECTED, 'Tie::Handle::Mason';
459              
460 441         1507 my $old = select SELECTED;
461 441         2032 my $mods = {base_comp => $request_comp, store => \($self->{request_buffer}), flushable => 1};
462              
463 441 100       1177 if ($self->{has_plugins}) {
464 20         108 my $context = bless
465             [$self, $request_args],
466             'HTML::Mason::Plugin::Context::StartRequest';
467 20         43 eval {
468 20         27 foreach my $plugin_instance (@{$self->plugin_instances}) {
  20         49  
469 25         135 $plugin_instance->start_request_hook( $context );
470             }
471             };
472 20 100       97 if ($@) {
473 1         9 select $old;
474 1         4 rethrow_exception $@;
475             }
476             }
477              
478 440 100       1323 if ($wantarray) {
    100          
479 2         13 @result = eval {$self->comp($mods, $first_comp, @$request_args)};
  2         26  
480             } elsif (defined($wantarray)) {
481 16         29 $result[0] = eval {$self->comp($mods, $first_comp, @$request_args)};
  16         76  
482             } else {
483 422         654 eval {$self->comp($mods, $first_comp, @$request_args)};
  422         1733  
484             }
485            
486 440         1568 my $error = $@;
487              
488 440 100       1101 if ($self->{has_plugins}) {
489             # plugins called in reverse order when exiting.
490             my $context = bless
491 19         107 [$self, $request_args, \$self->{request_buffer}, $wantarray, \@result, \$error],
492             'HTML::Mason::Plugin::Context::EndRequest';
493 19         40 eval {
494 19         31 foreach my $plugin_instance (@{$self->{plugin_instances_reverse}}) {
  19         38  
495 24         104 $plugin_instance->end_request_hook( $context );
496             }
497             };
498 19 100       120 if ($@) {
499             # plugin errors take precedence over component errors
500 1         8 $error = $@;
501             }
502             }
503            
504 440         1932 select $old;
505 440         1466 rethrow_exception $error;
506             }
507             };
508              
509 441 100       2191 $log->debugf("finishing request for '%s'", $self->request_comp->title)
510             if $log->is_debug;
511              
512             # Purge code cache if necessary.
513 441         4355 $self->interp->purge_code_cache;
514              
515             # Handle errors.
516 441         811 my $err = $@;
517 441 100 100     1205 if ($err and !$self->_aborted_or_declined($err)) {
518 82         329 $self->_handle_error($err);
519 6         101 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       1007 if (length($self->{request_buffer}) > 0) {
526 336         933 $self->out_method->($self->{request_buffer});
527             }
528              
529             # Return aborted value or result.
530 359 100       1122 @result = ($err->aborted_value) if $self->aborted($err);
531 359 100       994 @result = ($err->declined_value) if $self->declined($err);
532 359 100       2924 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   288 my ($self, $err) = @_;
541              
542 118         333 $self->interp->purge_code_cache;
543              
544 118 100       429 rethrow_exception $err if $self->is_subrequest;
545              
546             # Set error format for when error is stringified.
547 78 100       350 if (UNIVERSAL::can($err, 'format')) {
548 77         263 $err->format($self->error_format);
549             }
550              
551             # In fatal mode, die with error. In display mode, output stringified error.
552 78 100       319 if ($self->error_mode eq 'fatal') {
553 71         232 rethrow_exception $err;
554             } else {
555 7 50       27 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     42 local $HTML::Mason::Commands::m ||= $self;
567 7         21 $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 151 my $self = shift;
577 66         104 my $comp = shift;
578              
579 66         199 $self->make_subrequest(comp=>$comp, args=>\@_)->exec;
580             }
581              
582             sub make_subrequest
583             {
584 82     82 1 297 my ($self, %params) = @_;
585 82         238 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     523 if exists $params{comp} && !ref($params{comp});
592              
593             # Give subrequest the same values as parent request for read/write params
594 82         258 my %defaults = map { ($_, $self->$_()) } $self->_properties;
  738         1705  
595              
596 82 100       300 unless ( $params{out_method} )
597             {
598             $defaults{out_method} = sub {
599 35     35   123 $self->print($_[0]);
600 80         417 };
601             }
602              
603             # Make subrequest, and set parent_request and request_depth appropriately.
604 82         381 my $subreq =
605             $interp->make_request(%defaults, %params,
606             parent_request => $self,
607             request_depth => $self->request_depth + 1);
608              
609 77         688 return $subreq;
610             }
611              
612             sub is_subrequest
613             {
614 127     127 0 287 my ($self) = @_;
615              
616 127 100       423 return $self->parent_request ? 1 : 0;
617             }
618              
619             sub clear_and_abort
620             {
621 1     1 1 3 my $self = shift;
622              
623 1         5 $self->clear_buffer;
624 1         4 $self->abort(@_);
625             }
626              
627             sub abort
628             {
629 12     12 1 38 my ($self, $aborted_value) = @_;
630 12         219 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 1148 my ($self, $err) = @_;
638 512 100       1147 $err = $@ if !defined($err);
639 512         1491 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 1014 my ($self, $err) = @_;
647 501 100       1064 $err = $@ if !defined($err);
648 501         1144 return isa_mason_exception( $err, 'Decline' );
649             }
650              
651             sub _aborted_or_declined {
652 148     148   1387 my ($self, $err) = @_;
653 148   100     434 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 289 my ($self, %options) = @_;
662              
663             # If using 1.0x cache API, save off options for end of routine.
664 82         153 my %old_cache_options;
665 82 100       246 if ($self->data_cache_api eq '1.0') {
666 30         93 %old_cache_options = %options;
667 30         79 %options = ();
668             }
669              
670             # Combine defaults with options passed in here.
671 82 50       228 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       194 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     424 $options{cache_root} ||= $self->interp->cache_dir;
691 82   33     438 $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       208 my $cache_class = $self->interp->cache_dir ? 'Cache::FileCache' : 'Cache::MemoryCache';
696 82 100       246 if ($options{cache_class}) {
697 2         3 $cache_class = $options{cache_class};
698 2 100       15 $cache_class = "Cache::$cache_class" unless $cache_class =~ /::/;
699 2         5 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         202 my $mason_cache_class = "HTML::Mason::$cache_class";
706 82 100       226 unless (pkg_loaded($mason_cache_class)) {
707 4         20 load_pkg('Cache::Cache', '$m->cache requires the Cache::Cache module, available from CPAN.');
708 4         24 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   359 eval sprintf('package %s; use base qw(HTML::Mason::Cache::BaseCache %s); use vars qw($' . 'VERSION); $' . 'VERSION = 1.0;',
  3     3   27  
  3         7  
  3         997  
  3         22  
  3         10  
  3         121  
711             $mason_cache_class, $cache_class);
712 4 50       19 error "Error constructing mason cache class $mason_cache_class: $@" if $@;
713             }
714              
715 82 50       552 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       17661 if ($self->data_cache_api eq '1.0') {
720 30         141 return $self->_cache_1_x($cache, %old_cache_options);
721             } else {
722 52         252 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   98 my ($self, $cache, %options) = @_;
734              
735 30   100     117 my $action = $options{action} || 'retrieve';
736 30   100     112 my $key = $options{key} || 'main';
737            
738 30 100       135 if ($action eq 'retrieve') {
    100          
    100          
    50          
739            
740             # Validate parameters.
741 20 50       201 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       89 if (my $sub = $options{expire_if}) {
747 2 50       10 if (my $obj = $cache->get_object($key)) {
748 2 100       1083 if ($sub->($obj->get_created_at)) {
749 1         7 $cache->expire($key);
750             }
751             }
752             }
753              
754             # Return the value or undef, handling busy_lock.
755 20 100       965 if (my $result = $cache->get($key, ($options{busy_lock} ? (busy_lock=>$options{busy_lock}) : ()))) {
    100          
756 11         150 return $result;
757             } else {
758 9         690 return undef;
759             }
760              
761             } elsif ($action eq 'store') {
762              
763             # Validate parameters
764 8 50       97 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       29 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         14 my $expires_in;
774 8         14 my $time = time;
775 8 100       32 if (exists($options{expire_at})) {
    50          
    50          
776 2 50       15 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         32 my $value = $options{value};
794 8         44 $cache->set($key, $value, $expires_in);
795 8         12701 return $value;
796              
797             } elsif ($action eq 'expire') {
798 1 50       7 my @keys = (ref($key) eq 'ARRAY') ? @$key : ($key);
799 1         10 foreach my $key (@keys) {
800 2         933 $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 159 my ($self, %options) = @_;
810              
811 51 100       190 return if $self->{top_stack}->[STACK_IN_CALL_SELF]->{'CACHE_SELF'};
812              
813 33         127 my (%store_options, %retrieve_options);
814 33         0 my ($expires_in, $key, $cache);
815 33 100       94 if ($self->data_cache_api eq '1.0') {
816 3         7 foreach (qw(key expire_if busy_lock)) {
817 9 100       22 $retrieve_options{$_} = $options{$_} if (exists($options{$_}));
818             }
819 3         7 foreach (qw(key expire_at expire_next expire_in)) {
820 12 100       24 $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         61 foreach (qw(expire_if busy_lock)) {
829 60 100       149 $retrieve_options{$_} = delete($options{$_}) if (exists($options{$_}));
830             }
831 30   100     155 $expires_in = delete $options{expires_in} || delete $options{expire_in} || 'never';
832 30   100     101 $key = delete $options{key} || '__mason_cache_self__';
833 30         83 $cache = $self->cache(%options);
834             }
835              
836 33         71 my ($output, @retval, $error);
837              
838 33 100       76 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       2164 if ($cached) {
845 15         38 ($output, my $retval) = @$cached;
846 15         36 @retval = @$retval;
847             } else {
848 18         100 $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       61 rethrow_exception $error
854             unless ($self->_aborted_or_declined($error));
855              
856 16         63 my $value = [$output, \@retval];
857 16 100       57 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         57 $cache->set($key, $value, $expires_in);
861             }
862             }
863              
864             #
865             # Print the component output.
866             #
867 31         22634 $self->print($output);
868              
869             #
870             # Rethrow abort/decline exception if any.
871             #
872 31         119 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         269 return (@retval, 1);
879             }
880              
881             sub call_self
882             {
883 26     26 1 72 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     90 $tag ||= 'DEFAULT';
890 26         56 my $top_stack = $self->{top_stack};
891 26   100     86 $top_stack->[STACK_IN_CALL_SELF] ||= {};
892 26 100       82 return if $top_stack->[STACK_IN_CALL_SELF]->{$tag};
893 22         90 local $top_stack->[STACK_IN_CALL_SELF]->{$tag} = 1;
894              
895             # Determine wantarray based on retval reference
896 22 50       130 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         41 my $dummy;
906 22   100     53 $output ||= \$dummy;
907 22   100     55 $retval ||= \$dummy;
908              
909             # Temporarily put $output in place of the current top buffer.
910 22         47 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         37 my $comp = $top_stack->[STACK_COMP];
916 22         37 my $args = $top_stack->[STACK_ARGS];
917 22         39 my @result;
918 22         36 eval {
919 22 100       59 if ($wantarray) {
    50          
920 20         103 @$retval = $comp->run(@$args);
921             } elsif (defined $wantarray) {
922 0         0 $$retval = $comp->run(@$args);
923             } else {
924 2         7 $comp->run(@$args);
925             }
926             };
927 22 100       105 if ($@) {
928 4 50       30 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         86 return 1;
938             }
939              
940             sub call_dynamic {
941 22     22 0 66 my ($m, $key, @args) = @_;
942 22 100       79 my $comp = ($m->current_comp->is_subcomp) ? $m->current_comp->owner : $m->current_comp;
943 22 100 66     87 if (!defined($comp->dynamic_subs_request) or $comp->dynamic_subs_request ne $m) {
944 13         81 $comp->dynamic_subs_init;
945 8         28 $comp->dynamic_subs_request($m);
946             }
947              
948 17         116 return $comp->run_dynamic_sub($key, @args);
949             }
950              
951             sub call_next {
952 23     23 1 75 my ($self,@extra_args) = @_;
953 23 50       70 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         57  
956             }
957              
958             sub caller
959             {
960 3     3 1 12 my ($self) = @_;
961 3         10 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 99 my ($self, $levels_back) = @_;
970 48 100       93 if (defined($levels_back)) {
971 39         80 my $frame = $self->_stack_frame($levels_back);
972 39 100       118 return unless defined $frame;
973 31         124 return $frame->[STACK_COMP];
974             } else {
975 9         24 my $depth = $self->depth;
976 9         40 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         14 my $frame = $self->_stack_frame($levels_back);
989 3 50       9 return unless $frame;
990 3         6 my $args = $frame->[STACK_ARGS];
991 3 50       21 return wantarray ? @$args : { @$args };
992             }
993              
994             sub comp_exists
995             {
996 17     17 1 34 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       37 return $self->fetch_comp($path) ? 1 : 0;
1002             }
1003              
1004             sub decline
1005             {
1006 9     9 1 19 my ($self) = @_;
1007              
1008 9         41 $self->clear_buffer;
1009             my $subreq = $self->make_subrequest
1010             (comp => $self->{top_path},
1011             args => $self->{request_args},
1012 9         40 declined_comps => {$self->request_comp->comp_id, 1, %{$self->{declined_comps}}});
  9         46  
1013 8         51 my $retval = $subreq->exec;
1014 7         74 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 1761 return scalar @{ $_[0]->{stack} };
  1292         2919  
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 1815 my ($self, $path, $current_comp, $error, $exists_only) = @_;
1037              
1038 800 100       1725 return undef unless defined($path);
1039 798   66     3362 $current_comp ||= $self->{top_stack}->[STACK_COMP];
1040              
1041             return $self->_fetch_comp($path, $current_comp, $error)
1042 798 100       2568 unless $self->{use_internal_component_caches};
1043              
1044 35         73 my $fetch_comp_cache = $current_comp->{fetch_comp_cache};
1045 35 100       95 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       86 if ($path =~ /^(?:SELF|REQUEST)/) {
1058 0         0 return $self->_fetch_comp($path, $current_comp, $error);
1059             } else {
1060 11         63 $fetch_comp_cache->{$path} =
1061             $self->_fetch_comp($path, $current_comp, $error);
1062 11 50       40 Scalar::Util::weaken($fetch_comp_cache->{$path}) if can_weaken;
1063             }
1064             }
1065              
1066 35         145 return $fetch_comp_cache->{$path};
1067             }
1068              
1069             sub _fetch_comp
1070             {
1071 774     774   1625 my ($self, $path, $current_comp, $error) = @_;
1072              
1073             #
1074             # Handle paths SELF, PARENT, and REQUEST
1075             #
1076 774 100       1563 if ($path eq 'SELF') {
1077 63         171 return $self->base_comp;
1078             }
1079 711 100       1387 if ($path eq 'PARENT') {
1080 8         27 my $c = $current_comp->parent;
1081 8 100 100     101 $$error = "PARENT designator used from component with no parent" if !$c && defined($error);
1082 8         34 return $c;
1083             }
1084 703 100       1392 if ($path eq 'REQUEST') {
1085 4         12 return $self->request_comp;
1086             }
1087              
1088             #
1089             # Handle paths of the form comp_path:method_name
1090             #
1091 699 100       1825 if (index($path,':') != -1) {
1092 75         101 my $method_comp;
1093 75         256 my ($owner_path,$method_name) = split(':',$path,2);
1094 75 100       245 if (my $owner_comp = $self->fetch_comp($owner_path, $current_comp, $error)) {
1095 73 100       294 if ($owner_comp->_locate_inherited('methods',$method_name,\$method_comp)) {
1096 68         289 return $method_comp;
1097             } else {
1098 5 100       17 $$error = "no such method '$method_name' for component " . $owner_comp->title if defined($error);
1099             }
1100             } else {
1101 2 100 33     8 $$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       1916 if ($path !~ /\//) {
1112             # Check my subcomponents.
1113 460 100       1520 if (my $subcomp = $current_comp->subcomps($path)) {
1114 56         205 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     1115 if ($current_comp->is_subcomp and my $subcomp = $current_comp->owner->subcomps($path)) {
1119 5         22 return $subcomp;
1120             }
1121             }
1122              
1123             #
1124             # Otherwise pass the canonicalized absolute path to interp->load.
1125             #
1126 563         1690 $path = absolute_comp_path($path, $current_comp->dir_path);
1127 563         1551 my $comp = $self->interp->load($path);
1128              
1129 563         2427 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   54 my ($self) = @_;
1139 29         77 my $index = $self->{wrapper_index}->{$self->current_comp->comp_id};
1140 29 100       85 unless (defined($index)) {
1141 2         12 my @callers = $self->callers;
1142 2         5 shift(@callers);
1143 2   100     21 while (my $comp = shift(@callers) and !defined($index)) {
1144 2         8 $index = $self->{wrapper_index}->{$comp->comp_id};
1145             }
1146             }
1147 29         63 return $index;
1148             }
1149              
1150             #
1151             # Fetch next component in wrapper chain.
1152             #
1153             sub fetch_next {
1154 26     26 1 45 my ($self) = @_;
1155 26         63 my $index = $self->_fetch_next_helper;
1156 26 50       61 error "fetch_next: cannot find next component in chain"
1157             unless defined($index);
1158 26         90 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 6 my ($self) = @_;
1166 3         9 my $index = $self->_fetch_next_helper;
1167 3 50       7 error "fetch_next_all: cannot find next component in chain"
1168             unless defined($index);
1169 3         4 my @wc = @{$self->{wrapper_chain}};
  3         7  
1170 3         18 return @wc[($index+1)..$#wc];
1171             }
1172              
1173             sub file
1174             {
1175 3     3 1 34 my ($self,$file) = @_;
1176 3         11 my $interp = $self->interp;
1177 3 50       26 unless ( File::Spec->file_name_is_absolute($file) ) {
1178             # use owner if current comp is a subcomp
1179 3 100       12 my $context_comp =
1180             ( $self->current_comp->is_subcomp ?
1181             $self->current_comp->owner :
1182             $self->current_comp );
1183              
1184 3 50       11 if ($context_comp->is_file_based) {
1185 3         10 my $source_dir = $context_comp->source_dir;
1186 3         27 $file = File::Spec->catfile( $source_dir, $file );
1187             } else {
1188 0         0 $file = File::Spec->catfile( File::Spec->rootdir, $file );
1189             }
1190             }
1191 3         16 my $content = read_file($file,1);
1192 3         12 return $content;
1193             }
1194              
1195             sub print
1196             {
1197 3417     3417 1 5170 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       6527 );
1206              
1207             # use 'if defined' for maximum efficiency; grep creates a list.
1208 3417         5690 for ( @_ ) {
1209 3421 100       8138 $$bufref .= $_ if defined;
1210             }
1211              
1212 3417 100       8117 $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 1996 my $self = shift;
1222 1184         3613 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         9373 my %mods;
1228 1184         3262 %mods = (%{shift()}, %mods) while ref($_[0]) eq 'HASH';
  532         3132  
1229              
1230             # Get component path or object. If a path, load into object.
1231             #
1232 1184         1727 my $path;
1233 1184         1839 my $comp = shift;
1234 1184 100       2435 if (!ref($comp)) {
1235 686 50       1660 die "comp called without component - must pass a path or component object"
1236             unless defined($comp);
1237 686         976 $path = $comp;
1238 686         924 my $error;
1239 686 100 66     1791 $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         3117 my $depth = $self->depth;
1246             error "$depth levels deep in component stack (infinite recursive call?)\n"
1247 1175 100       2805 if $depth >= $self->{max_recurse};
1248              
1249             # Log start of component call.
1250             #
1251 1174 100       2312 $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         2280 my $filter_buffer = '';
1258 1174 100       2857 my $top_buffer = defined($mods{store}) ? $mods{store} : $self->{top_stack}->[STACK_BUFFER];
1259 1174 100       2345 my $stack_buffer = $comp->{has_filter} ? \$filter_buffer : $top_buffer;
1260 1174 100 100     3890 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         1645 push @{ $self->{stack} },
  1174         4291  
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         3102 local $self->{top_stack} = $self->{stack}->[-1];
1274              
1275             # Run start_component hooks for each plugin.
1276             #
1277 1174 100       2555 if ($self->{has_plugins}) {
1278 54         189 my $context = bless
1279             [$self, $comp, \@_],
1280             'HTML::Mason::Plugin::Context::StartComponent';
1281              
1282 54         81 foreach my $plugin_instance (@{$self->{plugin_instances}}) {
  54         114  
1283 74         259 $plugin_instance->start_component_hook( $context );
1284             }
1285             }
1286              
1287             # Finally, call the component.
1288             #
1289 1173         1867 my $wantarray = wantarray;
1290 1173         1656 my @result;
1291            
1292 1173         1783 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       1568 if ($wantarray) {
  1173 100       2740  
1298 14         66 @result = $comp->run(@_);
1299             } elsif (defined $wantarray) {
1300 22         102 $result[0] = $comp->run(@_);
1301             } else {
1302 1137         4088 $comp->run(@_);
1303             }
1304             }
1305             };
1306 1173         3694 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       2599 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       103 if (defined($comp->filter)) {
1317 31         86 $$top_buffer .= $comp->filter->($filter_buffer);
1318             }
1319 31         231 $self->{top_stack}->[STACK_BUFFER] = $top_buffer;
1320             }
1321              
1322             # Run end_component hooks for each plugin, in reverse order.
1323             #
1324 1171 100       2351 if ($self->{has_plugins}) {
1325 53         189 my $context = bless
1326             [$self, $comp, \@_, $wantarray, \@result, \$error],
1327             'HTML::Mason::Plugin::Context::EndComponent';
1328            
1329 53         81 foreach my $plugin_instance (@{$self->{plugin_instances_reverse}}) {
  53         98  
1330 73         202 $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         1587 pop @{ $self->{stack} };
  1168         2185  
1338              
1339             # Log end of component call.
1340             #
1341 1168 100       2327 $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       2386 rethrow_exception $error if $error;
1346 1019 100       4553 return $wantarray ? @result : $result[0];
1347             }
1348              
1349             #
1350             # Like comp, but return component output.
1351             #
1352             sub scomp {
1353 21     21 1 52 my $self = shift;
1354 21         38 my $buf;
1355 21         92 $self->comp({store => \$buf},@_);
1356 21         131 return $buf;
1357             }
1358              
1359             sub has_content {
1360 2     2 1 5 my $self = shift;
1361 2         20 return defined($self->{top_stack}->[STACK_MODS]->{content});
1362             }
1363              
1364             sub content {
1365 45     45 1 76 my $self = shift;
1366 45         85 my $content = $self->{top_stack}->[STACK_MODS]->{content};
1367 45 100       115 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         70 my $err;
1373             my $buffer;
1374 43         54 my $save_frame = pop @{ $self->{stack} };
  43         76  
1375             {
1376 43         70 local $self->{top_stack} = $self->{stack}[-1];
  43         78  
1377 43         78 local $self->{top_stack}->[STACK_BUFFER] = \$buffer;
1378 43         66 local $self->{top_stack}->[STACK_BUFFER_IS_FLUSHABLE] = 0;
1379 43         84 local $self->{top_stack}->[STACK_HIDDEN_BUFFER] = $save_frame->[STACK_BUFFER];
1380 43         58 eval { $content->(); };
  43         96  
1381 43         121 $err = $@;
1382             }
1383              
1384 43         60 push @{ $self->{stack} }, $save_frame;
  43         91  
1385              
1386 43         141 rethrow_exception $err;
1387              
1388             # Return the output from the content routine.
1389             #
1390 43         135 return $buffer;
1391             }
1392              
1393             sub notes {
1394 3     3 1 6 my $self = shift;
1395 3 100       10 return $self->{notes} unless @_;
1396            
1397 2         4 my $key = shift;
1398 2 100       8 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 72 my $self = shift;
1406              
1407 28         44 foreach my $frame (@{$self->{stack}}) {
  28         125  
1408 40         98 my $bufref = $frame->[STACK_BUFFER];
1409 40         76 $$bufref = '';
1410 40         68 $bufref = $frame->[STACK_HIDDEN_BUFFER];
1411 40 100       127 $$bufref = '' if $bufref;
1412             }
1413             }
1414              
1415             sub flush_buffer
1416             {
1417 147     147 1 222 my $self = shift;
1418              
1419             $self->out_method->($self->{request_buffer})
1420 147 100       454 if length $self->{request_buffer};
1421 147         251 $self->{request_buffer} = '';
1422              
1423 147 50 66     894 if ( $self->{top_stack}->[STACK_BUFFER_IS_FLUSHABLE]
1424             && $self->{top_stack}->[STACK_BUFFER] )
1425             {
1426 140         193 my $comp = $self->{top_stack}->[STACK_COMP];
1427 140 100 66     360 if ( $comp->has_filter()
1428             && defined $comp->filter() )
1429             {
1430             $self->out_method->
1431 2         5 ( $comp->filter->( ${ $self->{top_stack}->[STACK_BUFFER] } ) );
  2         9  
1432             }
1433             else
1434             {
1435 138         179 $self->out_method->( ${ $self->{top_stack}->[STACK_BUFFER] } );
  138         384  
1436             }
1437 140         213 ${$self->{top_stack}->[STACK_BUFFER]} = '';
  140         317  
1438             }
1439             }
1440              
1441             sub request_args
1442             {
1443 3     3 1 10 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         17  
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         92 my $depth = $self->depth();
1475 42         55 my $index;
1476 42 100       84 if ($levels < 0) {
1477 9         20 $index = (-1 * $levels) - 1;
1478             } else {
1479 33         49 $index = $depth-1 - $levels;
1480             }
1481 42 100 100     166 return if $index < 0 or $index >= $depth;
1482 34         79 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   78 my ($self) = @_;
1489              
1490 9         22 my $depth = $self->depth;
1491 9         29 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 1457 sub current_comp { return $_[0]->{top_stack}->[STACK_COMP] }
1498 23     23 1 106 sub current_args { return $_[0]->{top_stack}->[STACK_ARGS] }
1499              
1500             sub base_comp {
1501 89     89 1 176 my ($self) = @_;
1502              
1503 89 50       210 return unless $self->{top_stack};
1504              
1505 89 100       207 unless ( defined $self->{top_stack}->[STACK_BASE_COMP] ) {
1506 40         102 $self->_compute_base_comp_for_frame( $self->depth - 1 );
1507             }
1508 89         331 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   102 my ($self, $frame_num) = @_;
1517 48 50       110 die "Invalid frame number: $frame_num" if $frame_num < 0;
1518              
1519 48         93 my $frame = $self->{stack}->[$frame_num];
1520              
1521 48 100       111 unless (defined($frame->[STACK_BASE_COMP])) {
1522 44         87 my $mods = $frame->[STACK_MODS];
1523 44         71 my $path = $frame->[STACK_PATH];
1524 44         80 my $comp = $frame->[STACK_COMP];
1525            
1526 44         68 my $base_comp;
1527 44 100 100     202 if (exists($mods->{base_comp})) {
    100 100        
    100 66        
1528 32         62 $base_comp = $mods->{base_comp};
1529             } elsif (!$path ||
1530             $path =~ m/^(?:SELF|PARENT|REQUEST)(?:\:..*)?$/ ||
1531             ($comp->is_subcomp && !$comp->is_method)) {
1532 8         33 $base_comp = $self->_compute_base_comp_for_frame($frame_num-1);
1533             } elsif ($path =~ m/(.*):/) {
1534 1         6 my $calling_comp = $self->{stack}->[$frame_num-1]->[STACK_COMP];
1535 1         4 $base_comp = $self->fetch_comp($1, $calling_comp);
1536             } else {
1537 3         7 $base_comp = $comp;
1538             }
1539 44         99 $frame->[STACK_BASE_COMP] = $base_comp;
1540             }
1541 48         85 return $frame->[STACK_BASE_COMP];
1542             }
1543              
1544             sub log
1545             {
1546 2     2 1 6 my ($self) = @_;
1547 2         7 return $self->current_comp->logger();
1548             }
1549              
1550             package Tie::Handle::Mason;
1551             $Tie::Handle::Mason::VERSION = '1.60';
1552             sub TIEHANDLE
1553             {
1554 441     441   1092 my $class = shift;
1555              
1556              
1557 441         1397 return bless {}, $class;
1558             }
1559              
1560             sub PRINT
1561             {
1562 116     116   303 my $self = shift;
1563              
1564 116         265 my $old = select STDOUT;
1565             # Use direct $m access instead of Request->instance() to optimize common case
1566 116         316 $HTML::Mason::Commands::m->print(@_);
1567              
1568 116         527 select $old;
1569             }
1570              
1571             sub PRINTF
1572             {
1573 2     2   4 my $self = shift;
1574              
1575             # apparently sprintf(@_) won't work, it needs to be a scalar
1576             # followed by a list
1577 2         9 $self->PRINT(sprintf(shift, @_));
1578             }
1579              
1580             1;
1581              
1582             __END__