← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:14 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Configure/Query.pm
StatementsExecuted 23 statements in 2.21ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
11114µs27µsFoswiki::Configure::Query::::BEGIN@4Foswiki::Configure::Query::BEGIN@4
11112µs37µsFoswiki::Configure::Query::::BEGIN@7Foswiki::Configure::Query::BEGIN@7
1119µs13µsFoswiki::Configure::Query::::BEGIN@5Foswiki::Configure::Query::BEGIN@5
1118µs39µsFoswiki::Configure::Query::::BEGIN@17Foswiki::Configure::Query::BEGIN@17
1118µs42µsFoswiki::Configure::Query::::BEGIN@16Foswiki::Configure::Query::BEGIN@16
1114µs4µsFoswiki::Configure::Query::::BEGIN@9Foswiki::Configure::Query::BEGIN@9
1113µs3µsFoswiki::Configure::Query::::BEGIN@11Foswiki::Configure::Query::BEGIN@11
1113µs3µsFoswiki::Configure::Query::::BEGIN@12Foswiki::Configure::Query::BEGIN@12
1113µs3µsFoswiki::Configure::Query::::BEGIN@13Foswiki::Configure::Query::BEGIN@13
1113µs3µsFoswiki::Configure::Query::::BEGIN@10Foswiki::Configure::Query::BEGIN@10
1113µs3µsFoswiki::Configure::Query::::BEGIN@14Foswiki::Configure::Query::BEGIN@14
0000s0sFoswiki::Configure::Query::::_getSetParamsFoswiki::Configure::Query::_getSetParams
0000s0sFoswiki::Configure::Query::::check_current_valueFoswiki::Configure::Query::check_current_value
0000s0sFoswiki::Configure::Query::::getcfgFoswiki::Configure::Query::getcfg
0000s0sFoswiki::Configure::Query::::getspecFoswiki::Configure::Query::getspec
0000s0sFoswiki::Configure::Query::::searchFoswiki::Configure::Query::search
0000s0sFoswiki::Configure::Query::::wizardFoswiki::Configure::Query::wizard
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for default license and copyright information
2package Foswiki::Configure::Query;
3
4227µs240µs
# spent 27µs (14+13) within Foswiki::Configure::Query::BEGIN@4 which was called: # once (14µs+13µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 4
use strict;
# spent 27µs making 1 call to Foswiki::Configure::Query::BEGIN@4 # spent 13µs making 1 call to strict::import
5224µs218µs
# spent 13µs (9+4) within Foswiki::Configure::Query::BEGIN@5 which was called: # once (9µs+4µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 5
use warnings;
# spent 13µs making 1 call to Foswiki::Configure::Query::BEGIN@5 # spent 4µs making 1 call to warnings::import
6
7226µs262µs
# spent 37µs (12+25) within Foswiki::Configure::Query::BEGIN@7 which was called: # once (12µs+25µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 7
use Assert;
# spent 37µs making 1 call to Foswiki::Configure::Query::BEGIN@7 # spent 25µs making 1 call to Exporter::import
8
9218µs14µs
# spent 4µs within Foswiki::Configure::Query::BEGIN@9 which was called: # once (4µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 9
use Foswiki::Configure::Load ();
# spent 4µs making 1 call to Foswiki::Configure::Query::BEGIN@9
10226µs13µs
# spent 3µs within Foswiki::Configure::Query::BEGIN@10 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 10
use Foswiki::Configure::Root ();
# spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@10
11218µs13µs
# spent 3µs within Foswiki::Configure::Query::BEGIN@11 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 11
use Foswiki::Configure::LoadSpec ();
# spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@11
12217µs13µs
# spent 3µs within Foswiki::Configure::Query::BEGIN@12 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 12
use Foswiki::Configure::Reporter ();
# spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@12
13217µs13µs
# spent 3µs within Foswiki::Configure::Query::BEGIN@13 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 13
use Foswiki::Configure::Checker ();
# spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@13
14222µs13µs
# spent 3µs within Foswiki::Configure::Query::BEGIN@14 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 14
use Foswiki::Configure::Wizard ();
# spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@14
15
16232µs275µs
# spent 42µs (8+34) within Foswiki::Configure::Query::BEGIN@16 which was called: # once (8µs+34µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 16
use constant TRACE_CHECK => 0;
# spent 42µs making 1 call to Foswiki::Configure::Query::BEGIN@16 # spent 34µs making 1 call to constant::import
1721.98ms271µs
# spent 39µs (8+32) within Foswiki::Configure::Query::BEGIN@17 which was called: # once (8µs+32µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 17
use constant TRACE_GETSET => 0;
# spent 39µs making 1 call to Foswiki::Configure::Query::BEGIN@17 # spent 32µs making 1 call to constant::import
18
19=begin TML
20
21---+ package Foswiki::Configure::Query
22
23Methods used to query and manipulate the configuration spec.
24
25*Contract*
26
27All the methods take two parameters; a parameter hash, and a
28reporter. The parameter hash is described for each method,
29as is the return value, which is always a perl reference.
30
31All methods return undef if they fail badly. $reporter->ERROR is used to
32describe fatal errors to the caller.
33
34The $reporter should be clear before calling any of these methods,
35as existing errors in the reporter will be detected as fatal errors
36and cause the method to fail.
37
38=cut
39
40# Get =set= parameters and set the values in %Foswiki::cfg
41sub _getSetParams {
42 my ( $params, $root, $reporter ) = @_;
43 if ( $params->{set} ) {
44 while ( my ( $k, $value ) = each %{ $params->{set} } ) {
45 my $spec = $root->getValueObject($k);
46 unless ($spec) {
47 $reporter->ERROR("$k was not found in any Config.spec");
48 next;
49 }
50 if ( defined $value && !ref($value) ) {
51 $value =~ m/^(.*)$/s; # UNTAINT
52 $value = $1;
53 eval { $value = $spec->decodeValue($value); };
54 if ($@) {
55 $reporter->ERROR(
56 "The value of $k was unreadable: <verbatim>"
57 . Foswiki::Configure::Reporter::stripStacktrace($@)
58 . '</verbatim>' );
59 next;
60 }
61 }
62 if ( defined $value ) {
63 if ( $spec->isFormattedType() || ref($value) ) {
64 print STDERR "GETSET $k="
65 . Data::Dumper->Dump( [$value] )
66 . ", spec "
67 . $spec->stringify() . "\n"
68 if TRACE_GETSET;
69 eval("\$Foswiki::cfg$k=\$value");
70 }
71 else {
72 print STDERR "GETSET $k=$value, spec "
73 . $spec->stringify() . "\n"
74 if TRACE_GETSET;
75
76 # This is needed to prevent expansion of embedded
77 # $Foswiki::cfg variables during the eval.
78 eval("\$Foswiki::cfg$k=join('',\$value)");
79 }
80 }
81 else {
82 print STDERR "GETSET undef $k\n" if TRACE_GETSET;
83 eval("undef \$Foswiki::cfg$k");
84 }
85 if ($@) {
86 $reporter->ERROR( '<verbatim>'
87 . Foswiki::Configure::Reporter::stripStacktrace($@)
88 . '</verbatim>' );
89 }
90 elsif ( $params->{trace} ) {
91 $reporter->NOTE("Set $k");
92 }
93 }
94 }
95}
96
97=begin TML
98
99---++ StaticMethod getcfg(\%params, $reporter) -> \%response
100
101Retrieve for the value of one or more keys. \%params may include
102 * =keys= - array of key names to recover values for.
103If there isn't at least one key parameter, returns the
104entire configuration hash. Values are returned unexpanded
105(with embedded $Foswiki::cfg references intact.)
106
107The result is a hash containing that subsection of %Foswiki::cfg
108that has the keys requested.
109
110=cut
111
112sub getcfg {
113 my ( $params, $reporter ) = @_;
114
115 # Reload Foswiki::cfg without expansions
116 local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} );
117 Foswiki::Configure::Load::readConfig( 1, 1 );
118
119 my $keys = $params->{keys}; # expect a list
120 my $what;
121 my $root;
122 if ( defined $keys && scalar(@$keys) ) {
123 $what = {};
124 foreach my $key (@$keys) {
125 unless ( $key =~ m/^($Foswiki::Configure::Load::ITEMREGEX)$/ ) {
126 $reporter->ERROR("Bad key '$key'");
127 return undef;
128 }
129
130 # Implicit untaint for use in eval
131 $key = $1;
132
133 # Avoid loading specs unless we are being asked for a key that's
134 # not in LocalSite.cfg
135 unless ( eval("exists \$Foswiki::cfg$key") || $root ) {
136 $root = Foswiki::Configure::Root->new();
137 Foswiki::Configure::LoadSpec::readSpec( $root, $reporter );
138 if ( $reporter->has_level('errors') ) {
139 return undef;
140 }
141 Foswiki::Configure::LoadSpec::addSpecDefaultsToCfg( $root,
142 \%Foswiki::cfg );
143 }
144 unless ( eval("exists \$Foswiki::cfg$key") ) {
145 $reporter->ERROR("$key not defined");
146 return undef;
147 }
148 eval("\$what->$key=\$Foswiki::cfg$key");
149 if ($@) {
150 $reporter->ERROR(
151 Foswiki::Configure::Reporter::stripStacktrace($@) );
152 return undef;
153 }
154 }
155 }
156 else {
157 $what = \%Foswiki::cfg;
158 }
159 return $what;
160}
161
162=begin TML
163
164---++ StaticMethod search(\%params, $reporter) -> \@response
165
166 * =search= - text fragment to search for
167
168Search headlines and keys for a fragment of text. The response
169gives the path(s) to the item(s) matched in an array of arrays,
170where each entry is a single path.
171
172Searches are case-sensitive.
173
174=cut
175
176sub search {
177 my ( $params, $reporter ) = @_;
178 my $root = Foswiki::Configure::Root->new();
179 Foswiki::Configure::LoadSpec::readSpec( $root, $reporter );
180 if ( $reporter->has_level('errors') ) {
181 return undef;
182 }
183
184 # An empty search isn't fatal, just uninteresting
185 return []
186 unless defined $params->{search}
187 && $params->{search} =~ m/\S/;
188
189 my $re =
190 join( ".*", map { quotemeta($_) } split( /\s+/, $params->{search} ) );
191
192 my %found;
193 foreach my $find ( $root->search($re) ) {
194 my @path = $find->getPath();
195 $found{ join( '>', @path ) } = \@path;
196 }
197 my $finds = [ map { $found{$_} } sort keys %found ];
198
199 return $finds;
200}
201
202=begin TML
203
204---++ StaticMethod getspec(\%params, $reporter) -> \%response
205
206Use a search to find a configuration item spec. \%params may include:
207 * =get= - specifies the search. The following fields can be
208 used in searches:
209 * =headline= - title of a section,
210 * =typename= - type of a leaf spec entry,
211 * =parent= - a structure that will be used to match a parent,
212 * =keys= - keys of a spec entry,
213 * =desc= - descriptive text of a section or entry.
214 * =depth= - matches the depth of a node under the root
215 (which is depth 0)
216 * =depth= - specifies the depth of the subtree below matched items
217 to return.
218Only exact matches are supported.
219
220For example, ={ 'get': {'headline':'Store'}}= will retrieve the entire
221spec subtree for the section called 'Store'.
222
223={ 'get' : {'keys' : '{Store}{Implementation}'}}= will retrieve the spec
224for that one entry. You cannot pass a list; if you require the spec for a
225subsection, retrieve the section title.
226
227={ 'get' : { 'parent' : {'headline' : 'Something'}, 'depth' : 0}= will
228return all specs within the section named =Something=.
229
230The response is a reference to the spec subtree. Note that this will
231contained blessed hashes.
232
233=cut
234
235sub getspec {
236 my ( $params, $reporter ) = @_;
237
238 # Reload Foswiki::cfg without expansions so we get the unexpanded
239 # values in the spec structure
240 my $upper_cfg = \%Foswiki::cfg;
241 local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} );
242 if ( $upper_cfg->{isBOOTSTRAPPING} ) {
243
244 # If we're bootstrapping, retain the values calculated in
245 # the bootstrap process. They are almost certainly wrong,
246 # but are a better starting point that the .spec defaults.
247 %Foswiki::cfg = %$upper_cfg;
248 }
249 Foswiki::Configure::Load::readConfig( 1, 1 );
250
251 my $root = Foswiki::Configure::Root->new();
252 Foswiki::Configure::LoadSpec::readSpec( $root, $reporter );
253 if ( $reporter->has_level('errors') ) {
254 return undef;
255 }
256 Foswiki::Configure::LoadSpec::addCfgValuesToSpec( \%Foswiki::cfg, $root );
257
258 my $depth = $params->{depth};
259 my $search = $params->{get};
260
261 my @matches = ();
262 if ($search) {
263 @matches = $root->find(%$search);
264 }
265 else {
266 @matches = ($root);
267 }
268
269 foreach my $m (@matches) {
270 $m->unparent();
271 $m->prune($depth) if defined $depth;
272 }
273
274 return \@matches;
275}
276
277=begin TML
278
279---++ StaticMethod check_current_value(\%params, $reporter) -> \@response
280
281Runs the server-side =check-current_value= checkers on a set of keys.
282The keys to be checked are passed in as key-value pairs. You can also
283pass in candidate values that will be set before any keys are checked.
284 * =set= - hash of key-value pairs that maps the names of keys
285 to the value to be set. Strings in the values are assumed to be
286 unexpanded (i.e. with =$Foswiki::cfg= references intact).
287 * =keys= - array of keys to be checked (or the headline(s) of the
288 sections(s) to be recursively checked. '' checks the root. All
289 keys under the headlined section(s) will be checked). The default
290 is to check everything under the root.
291 * =check_dependencies= - if true, check everything that depends
292 on any of the keys being checked. This include dependencies
293 explicitly expressed through CHECK and implicit dependencies found
294 from the value of the checked item.
295
296The results of the check are reported in an array where each entry is a
297hash with fields =keys= and =reports=. =reports= is an array of reports,
298each being a hash with keys =level= (e.g. =warnings=, =errors=), and
299=message=.
300
301*NOTE* check_dependencies will look into the values of other keys for
302$Foswiki::cfg references, for example into the entries in a PERL hash.
303If a dependency is found, the closest checkable entity (i.e. the PERL
304key) will be checked, and *not* the subkey.
305
306=cut
307
308sub check_current_value {
309 my ( $params, $frep ) = @_;
310
311 local %Foswiki::cfg = %Foswiki::cfg;
312
313 # Load the spec files
314 my $root = Foswiki::Configure::Root->new();
315 Foswiki::Configure::LoadSpec::readSpec( $root, $frep );
316 if ( $frep->has_level('errors') ) {
317 return undef;
318 }
319
320 my @report;
321
322 my $reporter = Foswiki::Configure::Reporter->new();
323
324 # Apply "set" values to $Foswiki::cfg
325 eval { _getSetParams( $params, $root, $frep ); };
326 if ( $frep->has_level('errors') ) {
327 return [ { reports => $frep->messages() } ];
328 }
329
330 # Because we're running in a plugin, we already have LocalSite.cfg
331 # loaded. It's in $Foswiki::cfg! Of course if we're bootstrapping,
332 # that config is wishful thinking, but hey, can't have everything.
333
334 # Determine the set of value keys being checked. We start with
335 # the keys passed in as parameters.
336
337 my @keys;
338 foreach my $k ( @{ $params->{keys} } ) {
339 if ( $root->getValueObject($k) || $root->getSectionObject($k) ) {
340 push( @keys, $k );
341 }
342 else {
343 $k = "'$k'" unless $k =~ m/^\{.*\}$/;
344 push(
345 @report,
346 {
347 keys => $k,
348 path => [],
349 reports => [
350 {
351 text => "$k was not found in any Config.spec",
352 level => 'errors'
353 }
354 ]
355 }
356 );
357 }
358 }
359
360 if ( scalar(@keys) == 0 ) {
361 push( @keys, '' );
362 }
363
364 my $deps; # forward and reverse dependencies computed from values
365 if ( $params->{check_dependencies} ) {
366
367 # Get reverse dependencies expressed in CHECK_ON_CHANGE
368 # and add them as CHECK="also: forward dependencies to the
369 # item they depend on. We only do this if check_dependencies
370 # is set, as it is quite demanding.
371 $root->find_also_dependencies($root);
372
373 # Reload Foswiki::cfg without expansions so we can find
374 # string-embedded dependencies
375 local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} );
376 Foswiki::Configure::Load::readConfig( 1, 0, 1 );
377 if ( $params->{with} ) {
378 while ( my ( $k, $v ) = each %{ $params->{with} } ) {
379 eval("\$Foswiki::cfg$k=$v");
380 }
381 }
382 $deps = Foswiki::Configure::Load::findDependencies();
383 }
384
385 #print STDERR Data::Dumper->Dump([$deps]);
386
387 my %check; # set of keys to be checked
388 my @checko; # list of Value objects for keys to be checked
389 while ( defined( my $k = shift(@keys) ) ) {
390 print STDERR "Find dependencies for $k\n" if TRACE_CHECK;
391 next if $check{$k}; # already done?
392 $check{$k} = 1;
393 my $v = $root->getValueObject($k);
394 if ($v) {
395 print STDERR "\t'$k' is a key\n" if TRACE_CHECK;
396 push( @checko, $v );
397 if ( $params->{check_dependencies}
398 && defined $v->{CHECK}->{also} )
399 {
400
401 # Look at the CHECK="also:" explicit dependencies
402 foreach my $dep ( @{ $v->{CHECK}->{also} } ) {
403 next if $check{$dep};
404 print STDERR "\t... has a check:also for $dep\n"
405 if TRACE_CHECK;
406 push( @keys, $dep ) unless $check{$dep};
407 }
408 }
409 }
410 else {
411 $v = $root->getSectionObject($k);
412 if ($v) {
413 print STDERR "\n'$k' is a section\n" if TRACE_CHECK;
414 foreach my $kk ( $v->getAllValueKeys() ) {
415 unless ( $check{$kk} ) {
416 print STDERR "\tcontains key '$kk'\n" if TRACE_CHECK;
417 push( @keys, $kk );
418 }
419 }
420 }
421 else {
422 print STDERR "\t'$k' is not a key or a section\n"
423 if TRACE_CHECK;
424 if ( $k =~ s/{[^{}]+}$// && !$check{$k} ) {
425 print STDERR "\tcheck parent '$k' instead\n" if TRACE_CHECK;
426 push( @keys, $k );
427 }
428 }
429 }
430
431 # Look at forward dependencies i.e. the keys that depend
432 # on the value of this key
433 if ( $deps && $deps->{forward}->{$k} ) {
434 my @more = grep { !$check{$_} } @{ $deps->{forward}->{$k} };
435 map { print STDERR "\t$_ depends on $k\n"; $_ } @more
436 if TRACE_CHECK;
437 push( @keys, @more );
438 }
439 }
440
441 SPEC:
442 foreach my $spec (@checko) {
443 my $e = $spec->{CHECK}->{iff};
444 if ( defined $e ) {
445 $e = $e->[0];
446
447 # Expand {x} as $Foswiki::cfg{x}
448 $e =~ s/(({[^}]+})+)/\$Foswiki::cfg$1/g;
449 if ( $e =~ m/\S/ ) {
450 my $only_if;
451 eval("\$only_if=$e");
452 die "Syntax error in $spec->{keys} CHECK='iff:$e' - "
453 . Foswiki::Configure::Reporter::stripStacktrace($@)
454 if $@;
455 next SPEC unless $only_if;
456 }
457 }
458 my $checker = Foswiki::Configure::Checker::loadChecker($spec);
459 next unless $checker;
460 ASSERT( $spec->{keys} ) if DEBUG;
461 $reporter->clear();
462 $reporter->NOTE("Checking $spec->{keys}") if $params->{trace};
463 $checker->check_current_value($reporter);
464 my @path = $spec->getPath();
465 pop(@path); # remove keys
466 push(
467 @report,
468 {
469 keys => $spec->{keys},
470 path => [@path],
471 reports => $reporter->messages()
472 }
473 );
474 }
475 return \@report;
476}
477
478=begin TML
479
480---++ StaticMethod wizard(\%params, $reporter) -> \%response
481
482Call a configuration wizard.
483
484Configuration wizards are modules that support complex operations on
485configuration data; for example, auto-configuration of email and complex
486and time-consuming integrity checks.
487
488 * =wizard= - name of a wizard class to load
489 * =keys= - name of a checker to use if =wizard= is not given
490 * =method= - name of the method in the wizard or checker to call
491
492If the wizard method returns an object, that will be passed back
493as the result of the call. If the wizard method returns undef, the
494return result is a hash containing the following keys:
495 * =report= - Error/Warning etc messages, formatted as HTML. Each
496 entry in this array is a hash with keys 'level' (e.g. error, warning)
497 and 'message'.
498 * =changes= - This is a hash mapping changed keys to their new values
499
500=cut
501
502sub wizard {
503 my ( $params, $reporter ) = @_;
504
505 my $root = Foswiki::Configure::Root->new();
506 Foswiki::Configure::LoadSpec::readSpec( $root, $reporter );
507 if ( $reporter->has_level('errors') ) {
508 return undef;
509 }
510
511 my $target;
512 if ( defined $params->{wizard} ) {
513 unless ( $params->{wizard} =~ m/^(\w+)$/ ) { # untaint
514 $reporter->ERROR("Bad wizard");
515 return undef;
516 }
517 $target = Foswiki::Configure::Wizard::loadWizard( $1, $params );
518 }
519 else {
520 unless ( $params->{keys} ) {
521 $reporter->ERROR("No wizard and no keys");
522 return undef;
523 }
524 my $vob = $root->getValueObject( $params->{keys} );
525 $target = Foswiki::Configure::Checker::loadChecker($vob);
526 }
527 unless ($target) {
528 $reporter->ERROR("Bad thing");
529 return undef;
530 }
531 my $method = $params->{method};
532 unless ( $method =~ m/^(\w+)$/ ) {
533 $reporter->ERROR("Bad method");
534 return undef;
535 }
536 $method = $1; # untaint
537
538 _getSetParams( $params, $root, $reporter );
539 return { messages => $reporter->messages() }
540 if $reporter->has_level('errors');
541
542 # Most wizards won't need the $root, only those that actually
543 # modify it e.g. installers.
544 my $response = $target->$method( $reporter, $root );
545 return $response if $response;
546
547 # Note: we can't used the value recorded at CHANGED time because that
548 # is encoded using Reporter::uneval, which knows nothing about the
549 # real type of the value. For the real type we have to use the
550 # Value's encoder.
551 my %new_values;
552 foreach my $k ( keys %{ $reporter->{changes} } ) {
553 my $v = $root->getValueObject($k);
554 ASSERT( $v, "$k missing from Config.spec $method" ) if DEBUG;
555 $new_values{$k} = $v->encodeValue( eval("\$Foswiki::cfg$k") );
556 }
557
558 return {
559 changes => \%new_values,
560 messages => $reporter->messages()
561 };
562}
563
56412µs1;
565__END__