Filename | /var/www/foswikidev/core/lib/Foswiki/Templates.pm |
Statements | Executed 79966 statements in 116ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
47 | 5 | 4 | 70.0ms | 169ms | readTemplate | Foswiki::Templates::
102 | 2 | 1 | 18.5ms | 52.5ms | _readTemplateFile | Foswiki::Templates::
309 | 2 | 1 | 13.1ms | 20.3ms | tmplP (recurses: max depth 9, inclusive time 57.1ms) | Foswiki::Templates::
100 | 1 | 1 | 7.13ms | 16.1ms | _readFile | Foswiki::Templates::
100 | 1 | 1 | 6.84ms | 6.84ms | _decomment | Foswiki::Templates::
308 | 6 | 4 | 2.71ms | 23.6ms | expandTemplate (recurses: max depth 8, inclusive time 48.4ms) | Foswiki::Templates::
1 | 1 | 1 | 988µs | 988µs | finish | Foswiki::Templates::
1 | 1 | 1 | 20µs | 20µs | new | Foswiki::Templates::
1 | 1 | 1 | 15µs | 13.3ms | _expandTrivialTemplate | Foswiki::Templates::
1 | 1 | 1 | 14µs | 27µs | BEGIN@33 | Foswiki::Templates::
1 | 1 | 1 | 12µs | 27µs | BEGIN@136 | Foswiki::Templates::
1 | 1 | 1 | 11µs | 21µs | BEGIN@214 | Foswiki::Templates::
1 | 1 | 1 | 10µs | 14µs | BEGIN@34 | Foswiki::Templates::
1 | 1 | 1 | 10µs | 36µs | BEGIN@35 | Foswiki::Templates::
1 | 1 | 1 | 9µs | 21µs | BEGIN@216 | Foswiki::Templates::
1 | 1 | 1 | 8µs | 41µs | BEGIN@49 | Foswiki::Templates::
1 | 1 | 1 | 7µs | 23µs | BEGIN@138 | Foswiki::Templates::
1 | 1 | 1 | 4µs | 4µs | BEGIN@37 | Foswiki::Templates::
1 | 1 | 1 | 4µs | 4µs | BEGIN@39 | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | getTemplateFromCache | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | haveTemplate | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | saveTemplateToCache | Foswiki::Templates::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Templates | ||||
6 | |||||
7 | Support for Skin Template directives | ||||
8 | |||||
9 | =cut | ||||
10 | |||||
11 | =begin TML | ||||
12 | |||||
13 | The following tokens are supported by this language: | ||||
14 | |||||
15 | | %<nop>TMPL:P% | Instantiates a previously defined template | | ||||
16 | | %<nop>TMPL:DEF% | Opens a template definition | | ||||
17 | | %<nop>TMPL:END% | Closes a template definition | | ||||
18 | | %<nop>TMPL:INCLUDE% | Includes another file of templates | | ||||
19 | |||||
20 | Note; the template cache does not get reset during initialisation, so | ||||
21 | the haveTemplate test will return true if a template was loaded during | ||||
22 | a previous run when used with mod_perl or speedycgi. Frustrating for | ||||
23 | the template author, but they just have to switch off | ||||
24 | the accelerators during development. | ||||
25 | |||||
26 | This is to all intents and purposes a singleton object. It could | ||||
27 | easily be coverted into a true singleton (template manager). | ||||
28 | |||||
29 | =cut | ||||
30 | |||||
31 | package Foswiki::Templates; | ||||
32 | |||||
33 | 2 | 29µs | 2 | 40µs | # spent 27µs (14+13) within Foswiki::Templates::BEGIN@33 which was called:
# once (14µs+13µs) by Foswiki::templates at line 33 # spent 27µs making 1 call to Foswiki::Templates::BEGIN@33
# spent 13µs making 1 call to strict::import |
34 | 2 | 24µs | 2 | 18µs | # spent 14µs (10+4) within Foswiki::Templates::BEGIN@34 which was called:
# once (10µs+4µs) by Foswiki::templates at line 34 # spent 14µs making 1 call to Foswiki::Templates::BEGIN@34
# spent 4µs making 1 call to warnings::import |
35 | 2 | 26µs | 2 | 62µs | # spent 36µs (10+26) within Foswiki::Templates::BEGIN@35 which was called:
# once (10µs+26µs) by Foswiki::templates at line 35 # spent 36µs making 1 call to Foswiki::Templates::BEGIN@35
# spent 26µs making 1 call to Exporter::import |
36 | |||||
37 | 2 | 43µs | 1 | 4µs | # spent 4µs within Foswiki::Templates::BEGIN@37 which was called:
# once (4µs+0s) by Foswiki::templates at line 37 # spent 4µs making 1 call to Foswiki::Templates::BEGIN@37 |
38 | |||||
39 | # spent 4µs within Foswiki::Templates::BEGIN@39 which was called:
# once (4µs+0s) by Foswiki::templates at line 44 | ||||
40 | 1 | 4µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
41 | require locale; | ||||
42 | import locale(); | ||||
43 | } | ||||
44 | 1 | 21µs | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::Templates::BEGIN@39 |
45 | |||||
46 | # Enable TRACE to get HTML comments in the output showing where templates | ||||
47 | # (both DEFs and files) open and close. Will probably bork the output, so | ||||
48 | # normally you should use it with a bin/view command-line. | ||||
49 | 2 | 245µs | 2 | 74µs | # spent 41µs (8+33) within Foswiki::Templates::BEGIN@49 which was called:
# once (8µs+33µs) by Foswiki::templates at line 49 # spent 41µs making 1 call to Foswiki::Templates::BEGIN@49
# spent 33µs making 1 call to constant::import |
50 | |||||
51 | 1 | 500ns | my $MAX_EXPANSION_RECURSIONS = 999; | ||
52 | |||||
53 | =begin TML | ||||
54 | |||||
55 | ---++ ClassMethod new ( $session ) | ||||
56 | |||||
57 | Constructor. Creates a new template database object. | ||||
58 | * $session - session (Foswiki) object | ||||
59 | |||||
60 | =cut | ||||
61 | |||||
62 | # spent 20µs within Foswiki::Templates::new which was called:
# once (20µs+0s) by Foswiki::templates at line 2323 of /var/www/foswikidev/core/lib/Foswiki.pm | ||||
63 | 1 | 1µs | my ( $class, $session ) = @_; | ||
64 | 1 | 14µs | my $this = bless( { session => $session }, $class ); | ||
65 | |||||
66 | 1 | 900ns | $this->{VARS} = {}; | ||
67 | 1 | 1µs | $this->{VARS}->{sep}->{text} = ' | '; | ||
68 | 1 | 400ns | $this->{expansionRecursions} = {}; | ||
69 | 1 | 7µs | return $this; | ||
70 | } | ||||
71 | |||||
72 | =begin TML | ||||
73 | |||||
74 | ---++ ObjectMethod finish() | ||||
75 | Break circular references. | ||||
76 | |||||
77 | =cut | ||||
78 | |||||
79 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
80 | # whether they are references or not. That way this method is "golden | ||||
81 | # documentation" of the live fields in the object. | ||||
82 | # spent 988µs within Foswiki::Templates::finish which was called:
# once (988µs+0s) by Foswiki::finish at line 2488 of /var/www/foswikidev/core/lib/Foswiki.pm | ||||
83 | 1 | 500ns | my $this = shift; | ||
84 | 1 | 984µs | undef $this->{VARS}; | ||
85 | 1 | 1µs | undef $this->{session}; | ||
86 | 1 | 6µs | undef $this->{expansionRecursions}; | ||
87 | } | ||||
88 | |||||
89 | =begin TML | ||||
90 | |||||
91 | ---++ ObjectMethod haveTemplate( $name ) -> $boolean | ||||
92 | |||||
93 | Return true if the template exists and is loaded into the cache | ||||
94 | |||||
95 | =cut | ||||
96 | |||||
97 | sub haveTemplate { | ||||
98 | my ( $this, $template ) = @_; | ||||
99 | |||||
100 | return exists( $this->{VARS}->{$template} ); | ||||
101 | } | ||||
102 | |||||
103 | # Expand only simple templates that can be expanded statically. | ||||
104 | # Templates with conditions can only be expanded after the | ||||
105 | # context is fully known. | ||||
106 | # spent 13.3ms (15µs+13.3) within Foswiki::Templates::_expandTrivialTemplate which was called:
# once (15µs+13.3ms) by Foswiki::Templates::readTemplate at line 368 | ||||
107 | 1 | 1µs | my ( $this, $text ) = @_; | ||
108 | |||||
109 | # SMELL: unchecked implicit untaint? | ||||
110 | 1 | 2µs | $text =~ m/%TMPL\:P\{(.*)}%/; | ||
111 | 1 | 3µs | 1 | 16µs | my $attrs = new Foswiki::Attrs($1); # spent 16µs making 1 call to Foswiki::Attrs::new |
112 | |||||
113 | # Can't expand context-dependant templates | ||||
114 | 1 | 400ns | return $text if ( $attrs->{context} ); | ||
115 | 1 | 5µs | 1 | 13.3ms | return $this->tmplP($attrs); # spent 13.3ms making 1 call to Foswiki::Templates::tmplP |
116 | } | ||||
117 | |||||
118 | =begin TML | ||||
119 | |||||
120 | ---++ ObjectMethod expandTemplate( $params ) -> $string | ||||
121 | |||||
122 | Expand the template specified in the parameter string using =tmplP=. | ||||
123 | |||||
124 | Examples: | ||||
125 | <verbatim> | ||||
126 | $tmpls->expandTemplate("blah"); | ||||
127 | $tmpls->expandTemplate(context="view" then="sigh" else="humph"); | ||||
128 | </verbatim> | ||||
129 | |||||
130 | =cut | ||||
131 | |||||
132 | # spent 23.6ms (2.71+20.9) within Foswiki::Templates::expandTemplate which was called 308 times, avg 77µs/call:
# 180 times (1.48ms+11.7ms) by Foswiki::Templates::tmplP at line 215, avg 73µs/call
# 40 times (281µs+4.51ms) by Foswiki::Search::loadTemplates at line 511 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 120µs/call
# 40 times (573µs+2.44ms) by Foswiki::Search::loadTemplates at line 510 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 75µs/call
# 40 times (302µs+1.72ms) by Foswiki::Search::loadTemplates at line 522 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 51µs/call
# 4 times (34µs+257µs) by Foswiki::Func::expandTemplate at line 2600 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 73µs/call
# 4 times (46µs+221µs) by Foswiki::inlineAlert at line 2621 of /var/www/foswikidev/core/lib/Foswiki.pm, avg 67µs/call | ||||
133 | 308 | 244µs | my ( $this, $params ) = @_; | ||
134 | |||||
135 | 308 | 625µs | 308 | 5.20ms | my $attrs = new Foswiki::Attrs($params); # spent 5.20ms making 308 calls to Foswiki::Attrs::new, avg 17µs/call |
136 | 2 | 35µs | 2 | 43µs | # spent 27µs (12+16) within Foswiki::Templates::BEGIN@136 which was called:
# once (12µs+16µs) by Foswiki::templates at line 136 # spent 27µs making 1 call to Foswiki::Templates::BEGIN@136
# spent 16µs making 1 call to warnings::unimport |
137 | 308 | 600µs | 308 | 6.97ms | my $value = $this->tmplP($attrs); # spent 64.1ms making 308 calls to Foswiki::Templates::tmplP, avg 208µs/call, recursion: max depth 9, sum of overlapping time 57.1ms |
138 | 2 | 345µs | 2 | 38µs | # spent 23µs (7+15) within Foswiki::Templates::BEGIN@138 which was called:
# once (7µs+15µs) by Foswiki::templates at line 138 # spent 23µs making 1 call to Foswiki::Templates::BEGIN@138
# spent 15µs making 1 call to warnings::import |
139 | 308 | 923µs | return $value; | ||
140 | } | ||||
141 | |||||
142 | =begin TML | ||||
143 | |||||
144 | ---+ ObjectMethod tmplP( $attrs ) -> $string | ||||
145 | |||||
146 | Return value expanded text of the template, as found from looking | ||||
147 | in the register of template definitions. The attrs can contain a template | ||||
148 | name in _DEFAULT, and / or =context=, =then= and =else= values. | ||||
149 | |||||
150 | Recursively expands any contained TMPL:P tags. | ||||
151 | |||||
152 | Note that it would be trivial to add template parameters to this, | ||||
153 | simply by iterating over the other parameters (other than _DEFAULT, context, | ||||
154 | then and else) and doing a s/// in the template for that parameter value. This | ||||
155 | would add considerably to the power of templates. | ||||
156 | |||||
157 | =cut | ||||
158 | |||||
159 | sub tmplP { | ||||
160 | 309 | 164µs | my ( $this, $params ) = @_; | ||
161 | |||||
162 | 309 | 251µs | 309 | 601µs | $params->remove('_RAW'); # don't need to iterate over _RAW # spent 601µs making 309 calls to Foswiki::Attrs::remove, avg 2µs/call |
163 | 309 | 378µs | 309 | 631µs | my $template = $params->remove('_DEFAULT') || ''; # spent 631µs making 309 calls to Foswiki::Attrs::remove, avg 2µs/call |
164 | 309 | 323µs | 309 | 495µs | my $context = $params->remove('context'); # spent 495µs making 309 calls to Foswiki::Attrs::remove, avg 2µs/call |
165 | 309 | 338µs | 309 | 455µs | my $then = $params->remove('then'); # spent 455µs making 309 calls to Foswiki::Attrs::remove, avg 1µs/call |
166 | 309 | 319µs | 309 | 440µs | my $else = $params->remove('else'); # spent 440µs making 309 calls to Foswiki::Attrs::remove, avg 1µs/call |
167 | 309 | 58µs | if ($context) { | ||
168 | 21 | 6µs | $template = $then if defined($then); | ||
169 | 21 | 26µs | foreach my $id ( split( /\,\s*/, $context ) ) { | ||
170 | 21 | 26µs | unless ( $this->{session}->{context}->{$id} ) { | ||
171 | 12 | 2µs | $template = ( $else || '' ); | ||
172 | 12 | 6µs | last; | ||
173 | } | ||||
174 | } | ||||
175 | } | ||||
176 | |||||
177 | 309 | 37µs | return '' unless $template; | ||
178 | |||||
179 | 307 | 295µs | $this->{expansionRecursions}->{$template} += 1; | ||
180 | |||||
181 | 307 | 168µs | if ( $this->{expansionRecursions}->{$template} > $MAX_EXPANSION_RECURSIONS ) | ||
182 | { | ||||
183 | throw Foswiki::OopsException( | ||||
184 | 'attention', | ||||
185 | def => 'template_recursion', | ||||
186 | params => [$template] | ||||
187 | ); | ||||
188 | } | ||||
189 | |||||
190 | 307 | 111µs | my $val = ''; | ||
191 | 307 | 213µs | if ( exists( $this->{VARS}->{$template} ) ) { | ||
192 | 302 | 271µs | $val = $this->{VARS}->{$template}->{text}; | ||
193 | $val = "<!--$template-->\n$val<!--/$template-->\n" if (TRACE); | ||||
194 | |||||
195 | 302 | 372µs | foreach my $p ( keys %$params ) { | ||
196 | 8 | 11µs | if ( $p eq 'then' || $p eq 'else' ) { | ||
197 | $val =~ s/%$p%/$this->expandTemplate($1)/ge; | ||||
198 | } | ||||
199 | elsif ( defined( $params->{$p} ) ) { | ||||
200 | 8 | 70µs | $val =~ s/%$p%/$params->{$p}/ge; | ||
201 | } | ||||
202 | } | ||||
203 | |||||
204 | # process default values; this will clean up orphaned %p% params | ||||
205 | 302 | 537µs | foreach my $p ( keys %{ $this->{VARS}->{$template}->{params} } ) { | ||
206 | |||||
207 | # resolve dynamic lookups such as %TMPL:DEF{"LIBJS" name="%id%"}% | ||||
208 | my $pvalue = $this->{VARS}->{$template}->{params}->{$p}; | ||||
209 | $pvalue =~ s/\%(.*?)\%/$params->{$1}/g; | ||||
210 | $val =~ s/%$p%/$pvalue/ge; | ||||
211 | } | ||||
212 | |||||
213 | 302 | 396µs | $val =~ s/%TMPL:PREV%/%TMPL:P{"$template:_PREV"}%/g; | ||
214 | 2 | 55µs | 2 | 30µs | # spent 21µs (11+10) within Foswiki::Templates::BEGIN@214 which was called:
# once (11µs+10µs) by Foswiki::templates at line 214 # spent 21µs making 1 call to Foswiki::Templates::BEGIN@214
# spent 10µs making 1 call to warnings::unimport |
215 | 482 | 1.26ms | 180 | 13.2ms | $val =~ s/%TMPL:P\{(.*?)\}%/$this->expandTemplate($1)/ge; # spent 61.6ms making 180 calls to Foswiki::Templates::expandTemplate, avg 342µs/call, recursion: max depth 8, sum of overlapping time 48.4ms |
216 | 2 | 1.87ms | 2 | 32µs | # spent 21µs (9+12) within Foswiki::Templates::BEGIN@216 which was called:
# once (9µs+12µs) by Foswiki::templates at line 216 # spent 21µs making 1 call to Foswiki::Templates::BEGIN@216
# spent 12µs making 1 call to warnings::import |
217 | } | ||||
218 | |||||
219 | 307 | 187µs | $this->{expansionRecursions}->{$template} -= 1; | ||
220 | 307 | 824µs | return $val; | ||
221 | } | ||||
222 | |||||
223 | =begin TML | ||||
224 | |||||
225 | ---++ ObjectMethod readTemplate ( $name, %options ) -> $text | ||||
226 | |||||
227 | Reads a template, loading the definitions therein. | ||||
228 | |||||
229 | Return value: expanded template text | ||||
230 | |||||
231 | By default throws an OopsException if the template was not found or the | ||||
232 | access controls denied access. | ||||
233 | |||||
234 | %options include: | ||||
235 | * =skin= - skin name, | ||||
236 | * =web= - web to search | ||||
237 | * =no_oops= - if true, will not throw an exception. Instead, returns undef. | ||||
238 | |||||
239 | If template text is found, extracts include statements and fully expands them. | ||||
240 | Also extracts template definitions and adds them to the | ||||
241 | list of loaded templates, overwriting any previous definition. | ||||
242 | |||||
243 | =cut | ||||
244 | |||||
245 | # spent 169ms (70.0+98.9) within Foswiki::Templates::readTemplate which was called 47 times, avg 3.59ms/call:
# 40 times (48.6ms+52.8ms) by Foswiki::Search::loadTemplates at line 495 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2.54ms/call
# 4 times (8.83ms+21.2ms) by Foswiki::inlineAlert at line 2619 of /var/www/foswikidev/core/lib/Foswiki.pm, avg 7.50ms/call
# once (12.3ms+22.7ms) by Foswiki::UI::View::view at line 292 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (108µs+1.39ms) by Foswiki::Func::readTemplate at line 2535 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
# once (193µs+759µs) by Foswiki::Func::loadTemplate at line 2568 of /var/www/foswikidev/core/lib/Foswiki/Func.pm | ||||
246 | 47 | 83µs | my ( $this, $name, %opts ) = @_; | ||
247 | ASSERT($name) if DEBUG; | ||||
248 | 47 | 287µs | 47 | 6.36ms | my $skins = $opts{skins} || $this->{session}->getSkin(); # spent 6.36ms making 47 calls to Foswiki::getSkin, avg 135µs/call |
249 | 47 | 146µs | my $web = $opts{web} || $this->{session}->{webName}; | ||
250 | |||||
251 | 47 | 120µs | $this->{files} = (); | ||
252 | |||||
253 | # recursively read template file(s) | ||||
254 | 47 | 246µs | 47 | 18.9ms | my $text = _readTemplateFile( $this, $name, $skins, $web ); # spent 18.9ms making 47 calls to Foswiki::Templates::_readTemplateFile, avg 402µs/call |
255 | |||||
256 | # Check file was found | ||||
257 | 47 | 20µs | unless ( defined $text ) { | ||
258 | |||||
259 | # if no_oops is given, return undef silently | ||||
260 | if ( $opts{no_oops} ) { | ||||
261 | return undef; | ||||
262 | } | ||||
263 | else { | ||||
264 | throw Foswiki::OopsException( | ||||
265 | 'attention', | ||||
266 | def => 'no_such_template', | ||||
267 | params => [ | ||||
268 | $name, | ||||
269 | |||||
270 | # More info for overridable templates | ||||
271 | ( $name =~ m/^(view|edit)$/ ) ? $name . '_TEMPLATE' : '' | ||||
272 | ] | ||||
273 | ); | ||||
274 | } | ||||
275 | } | ||||
276 | |||||
277 | # SMELL: unchecked implicit untaint? | ||||
278 | 47 | 442µs | while ( $text =~ m/%TMPL\:INCLUDE\{[\s\"]*(.*?)[\"\s]*\}%/s ) { | ||
279 | 49 | 1.12ms | $text =~ s/%TMPL\:INCLUDE\{[\s\"]*(.*?)[\"\s]*\}%/ | ||
280 | 55 | 98µs | 55 | 33.7ms | _readTemplateFile( $this, $1, $skins, $web ) || ''/ge; # spent 33.7ms making 55 calls to Foswiki::Templates::_readTemplateFile, avg 612µs/call |
281 | } | ||||
282 | |||||
283 | 47 | 130µs | if ( $text !~ /%TMPL\:/ ) { | ||
284 | |||||
285 | # no %TMPL's to process | ||||
286 | |||||
287 | # SMELL: legacy - leading spaces to tabs, should not be required | ||||
288 | $text =~ s|^(( {3})+)|"\t" x (length($1)/3)|gem; | ||||
289 | |||||
290 | return $text; | ||||
291 | } | ||||
292 | |||||
293 | 47 | 93µs | my $result = ''; | ||
294 | 47 | 44µs | my $key = ''; | ||
295 | 47 | 41µs | my $val = ''; | ||
296 | 47 | 23µs | my $delim = ''; | ||
297 | 47 | 1.93ms | foreach ( split( /(%TMPL\:)/, $text ) ) { | ||
298 | 5001 | 14.9ms | if (/^(%TMPL\:)$/) { | ||
299 | $delim = $1; | ||||
300 | } | ||||
301 | elsif ( (/^DEF\{(.*?)\}%(.*)/s) && ($1) ) { | ||||
302 | |||||
303 | # handle %TMPL:DEF{"key"}% and %TMPL:DEF{"key" p="1"}% | ||||
304 | 1075 | 151µs | if ($key) { | ||
305 | |||||
306 | # if the key is already defined, rename the existing | ||||
307 | # template to key:_PREV | ||||
308 | 5 | 5µs | my $new_value = $val; | ||
309 | 5 | 2µs | my $prev_key = $key; | ||
310 | 5 | 7µs | my $prev_value = $this->{VARS}->{$prev_key}->{text}; | ||
311 | 5 | 4µs | $this->{VARS}->{$prev_key}->{text} = $new_value; | ||
312 | 5 | 3µs | while ($prev_value) { | ||
313 | 6 | 1µs | $new_value = $prev_value; | ||
314 | 6 | 4µs | $prev_key = "$prev_key:_PREV"; | ||
315 | 6 | 7µs | $prev_value = $this->{VARS}->{$prev_key}->{text}; | ||
316 | 6 | 5µs | $this->{VARS}->{$prev_key}->{text} = $new_value; | ||
317 | } | ||||
318 | } | ||||
319 | |||||
320 | 1075 | 2.70ms | 1075 | 22.2ms | my $attrs = new Foswiki::Attrs($1); # spent 22.2ms making 1075 calls to Foswiki::Attrs::new, avg 21µs/call |
321 | 1075 | 354µs | $key = $attrs->{_DEFAULT}; | ||
322 | |||||
323 | # store params in TMPL:DEF for later retrieval | ||||
324 | 1075 | 1.27ms | 1075 | 2.51ms | $attrs->remove('_DEFAULT'); # spent 2.51ms making 1075 calls to Foswiki::Attrs::remove, avg 2µs/call |
325 | 1075 | 967µs | 1075 | 1.92ms | $attrs->remove('_RAW'); # spent 1.92ms making 1075 calls to Foswiki::Attrs::remove, avg 2µs/call |
326 | 1075 | 1.15ms | foreach my $p ( keys %$attrs ) { | ||
327 | $this->{VARS}->{$key}->{params}->{$p} = $attrs->{$p}; | ||||
328 | } | ||||
329 | |||||
330 | # SMELL: unchecked implicit untaint? | ||||
331 | 1075 | 1.36ms | $val = $2; | ||
332 | |||||
333 | } | ||||
334 | elsif (/^END%[\s\n\r]*(.*)/s) { | ||||
335 | |||||
336 | # handle %TMPL:END% | ||||
337 | |||||
338 | # if the key is already defined, rename the existing template to | ||||
339 | # key:_PREV | ||||
340 | 1071 | 243µs | my $new_value = $val; | ||
341 | 1071 | 175µs | my $prev_key = $key; | ||
342 | 1071 | 1.88ms | my $prev_value = $this->{VARS}->{$prev_key}->{text}; | ||
343 | 1071 | 845µs | $this->{VARS}->{$prev_key}->{text} = $new_value; | ||
344 | 1071 | 384µs | while ($prev_value) { | ||
345 | 7614 | 796µs | $new_value = $prev_value; | ||
346 | 7614 | 2.72ms | $prev_key = "$prev_key:_PREV"; | ||
347 | 7614 | 14.7ms | $prev_value = $this->{VARS}->{$prev_key}->{text}; | ||
348 | 7614 | 16.3ms | $this->{VARS}->{$prev_key}->{text} = $new_value; | ||
349 | } | ||||
350 | |||||
351 | 1071 | 186µs | $key = ''; | ||
352 | 1071 | 103µs | $val = ''; | ||
353 | |||||
354 | # SMELL: unchecked implicit untaint? | ||||
355 | 1071 | 720µs | $result .= $1; | ||
356 | |||||
357 | } | ||||
358 | elsif ($key) { | ||||
359 | $val .= "$delim$_"; | ||||
360 | |||||
361 | } | ||||
362 | else { | ||||
363 | 48 | 70µs | $result .= "$delim$_"; | ||
364 | } | ||||
365 | } | ||||
366 | |||||
367 | # handle %TMPL:P{"..."}% recursively | ||||
368 | 48 | 48µs | 1 | 13.3ms | $result =~ s/(%TMPL\:P\{.*?\}%)/_expandTrivialTemplate( $this, $1)/ge; # spent 13.3ms making 1 call to Foswiki::Templates::_expandTrivialTemplate |
369 | |||||
370 | # SMELL: legacy - leading spaces to tabs, should not be required | ||||
371 | 47 | 80µs | $result =~ s|^(( {3})+)|"\t" x (length($1)/3)|gem; | ||
372 | |||||
373 | $this->saveTemplateToCache( '_complete', $name, $skins, $web, $result ) | ||||
374 | if (TRACE); | ||||
375 | 47 | 214µs | return $result; | ||
376 | } | ||||
377 | |||||
378 | # STATIC: Return value: raw template text, or undef if read fails | ||||
379 | # spent 52.5ms (18.5+34.0) within Foswiki::Templates::_readTemplateFile which was called 102 times, avg 515µs/call:
# 55 times (9.54ms+24.1ms) by Foswiki::Templates::readTemplate at line 280, avg 612µs/call
# 47 times (8.96ms+9.92ms) by Foswiki::Templates::readTemplate at line 254, avg 402µs/call | ||||
380 | 102 | 154µs | my ( $this, $name, $skins, $web ) = @_; | ||
381 | 102 | 61µs | my $session = $this->{session}; | ||
382 | |||||
383 | # SMELL: not i18n-friendly (can't have accented characters in template name) | ||||
384 | # zap anything suspicious | ||||
385 | 102 | 128µs | $name =~ s/[^A-Za-z0-9_,.\/]//g; | ||
386 | |||||
387 | # if the name ends in .tmpl, then this is an explicit include from | ||||
388 | # the templates directory. No further searching required. | ||||
389 | 102 | 109µs | if ( $name =~ m/\.tmpl$/ ) { | ||
390 | my $text = | ||||
391 | _decomment( | ||||
392 | _readFile( $session, "$Foswiki::cfg{TemplateDir}/$name" ) ); | ||||
393 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
394 | if (TRACE); | ||||
395 | return $text; | ||||
396 | } | ||||
397 | |||||
398 | 102 | 50µs | my $userdirweb = $web; | ||
399 | 102 | 41µs | my $userdirname = $name; | ||
400 | 102 | 160µs | if ( $name =~ m/^(.+)\.(.+?)$/ ) { | ||
401 | |||||
402 | # ucfirst taints if use locale is in force | ||||
403 | $userdirweb = Foswiki::Sandbox::untaintUnchecked( ucfirst($1) ); | ||||
404 | $userdirname = Foswiki::Sandbox::untaintUnchecked( ucfirst($2) ); | ||||
405 | |||||
406 | # if the name can be parsed into $web.$name, then this is an attempt | ||||
407 | # to explicit include that topic. No further searching required. | ||||
408 | if ( $session->topicExists( $userdirweb, $userdirname ) ) { | ||||
409 | my $meta = | ||||
410 | Foswiki::Meta->load( $session, $userdirweb, $userdirname ); | ||||
411 | |||||
412 | # Check we are allowed access | ||||
413 | unless ( $meta->haveAccess( 'VIEW', $session->{user} ) ) { | ||||
414 | return $this->{session}->inlineAlert( 'alerts', 'access_denied', | ||||
415 | "$userdirweb.$userdirname" ); | ||||
416 | } | ||||
417 | my $text = $meta->text(); | ||||
418 | $text = '' unless defined $text; | ||||
419 | |||||
420 | $text = | ||||
421 | "<!--$userdirweb/$userdirname-->\n" | ||||
422 | . $text | ||||
423 | . "<!--/$userdirweb/$userdirname-->\n" | ||||
424 | if (TRACE); | ||||
425 | |||||
426 | $text = _decomment($text); | ||||
427 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
428 | if (TRACE); | ||||
429 | return $text; | ||||
430 | } | ||||
431 | } | ||||
432 | else { | ||||
433 | |||||
434 | # ucfirst taints if use locale is in force | ||||
435 | 102 | 364µs | 102 | 612µs | $userdirweb = # spent 612µs making 102 calls to Foswiki::Sandbox::untaintUnchecked, avg 6µs/call |
436 | Foswiki::Sandbox::untaintUnchecked( ucfirst($userdirweb) ); | ||||
437 | 102 | 234µs | 102 | 243µs | $userdirname = # spent 243µs making 102 calls to Foswiki::Sandbox::untaintUnchecked, avg 2µs/call |
438 | Foswiki::Sandbox::untaintUnchecked( ucfirst($userdirname) ); | ||||
439 | } | ||||
440 | |||||
441 | 102 | 241µs | my @skinList = split( /\,\s*/, $skins ); | ||
442 | 102 | 50µs | my $nrskins = $#skinList; | ||
443 | |||||
444 | 102 | 1.03ms | my @templatePath = split( /\s*,\s*/, $Foswiki::cfg{TemplatePath} ); | ||
445 | 102 | 139µs | if ( | ||
446 | ( $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{Enabled} ) | ||||
447 | && ( lc($name) eq 'foswiki' ) | ||||
448 | && defined( | ||||
449 | $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{TemplatePath} | ||||
450 | ) | ||||
451 | ) | ||||
452 | { | ||||
453 | |||||
454 | # TWikiCompatibility, need to test to see if there is a twiki.skin tmpl | ||||
455 | @templatePath = | ||||
456 | @{ $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{TemplatePath} }; | ||||
457 | } | ||||
458 | |||||
459 | # Search the $Foswiki::cfg{TemplatePath} for the skinned versions | ||||
460 | 102 | 45µs | my @candidates = (); | ||
461 | |||||
462 | 102 | 36µs | $nrskins = 0 if $nrskins < 0; | ||
463 | |||||
464 | 102 | 36µs | my $nrtemplates = $#templatePath; | ||
465 | |||||
466 | 102 | 329µs | for ( my $templateixd = 0 ; $templateixd <= $nrtemplates ; $templateixd++ ) | ||
467 | { | ||||
468 | 816 | 721µs | for ( my $idx = 0 ; $idx <= $nrskins ; $idx++ ) { | ||
469 | 816 | 247µs | my $file = $templatePath[$templateixd]; | ||
470 | 816 | 105µs | my $userdir = 0; | ||
471 | |||||
472 | # also need to do %PUBURL% etc.? | ||||
473 | # push the first time even if not modified | ||||
474 | 816 | 174µs | my $skin = $skinList[$idx] || ''; | ||
475 | |||||
476 | # consider skin templates first | ||||
477 | # this is done by giving the template path with 'skin' in it | ||||
478 | # a higher sort priority (so a lower number: 0) | ||||
479 | 816 | 414µs | my $isSkinned = ( $file =~ m/\$skin/ ? 0 : 1 ); | ||
480 | |||||
481 | 816 | 130µs | my $webName = $web || ''; | ||
482 | 816 | 101µs | my $tmplName = $name || ''; | ||
483 | 816 | 494µs | unless ( $file =~ m/.tmpl$/ ) { | ||
484 | |||||
485 | # Could also use $Skin, $Web, $Name to indicate uppercase | ||||
486 | 408 | 77µs | $userdir = 1; | ||
487 | |||||
488 | # Again untainting when using ucfirst | ||||
489 | 408 | 503µs | 408 | 1.06ms | $skin = Foswiki::Sandbox::untaintUnchecked( ucfirst($skin) ); # spent 1.06ms making 408 calls to Foswiki::Sandbox::untaintUnchecked, avg 3µs/call |
490 | 408 | 56µs | $webName = $userdirweb; | ||
491 | 408 | 109µs | $tmplName = $userdirname; | ||
492 | } | ||||
493 | 816 | 841µs | $file =~ s/\$skin/$skin/g; | ||
494 | 816 | 705µs | $file =~ s/\$web/$webName/g; | ||
495 | 816 | 1.20ms | $file =~ s/\$name/$tmplName/g; | ||
496 | |||||
497 | # sort priority is: | ||||
498 | # primary: if template path has 'skin' in it; so that skin templates are considered first | ||||
499 | # secondary: the skin order number | ||||
500 | # tertiary: the template path order number | ||||
501 | |||||
502 | 816 | 1.59ms | push( | ||
503 | @candidates, | ||||
504 | { | ||||
505 | primary => $isSkinned, | ||||
506 | secondary => $idx, | ||||
507 | tertiary => $templateixd, | ||||
508 | file => $file, | ||||
509 | userdir => $userdir, | ||||
510 | skin => $skin | ||||
511 | } | ||||
512 | ); | ||||
513 | } | ||||
514 | } | ||||
515 | |||||
516 | # sort | ||||
517 | @candidates = sort { | ||||
518 | 1326 | 768µs | foreach my $i (qw/primary secondary tertiary/) | ||
519 | { | ||||
520 | 2856 | 1.95ms | if ( $a->{$i} < $b->{$i} ) { | ||
521 | return -1; | ||||
522 | } | ||||
523 | elsif ( $a->{$i} > $b->{$i} ) { | ||||
524 | return 1; | ||||
525 | } | ||||
526 | } | ||||
527 | return 0; | ||||
528 | } @candidates; | ||||
529 | |||||
530 | 102 | 74µs | foreach my $candidate (@candidates) { | ||
531 | 432 | 235µs | my $file = $candidate->{file}; | ||
532 | |||||
533 | 432 | 1.36ms | if ( $candidate->{userdir} ) { | ||
534 | |||||
535 | 116 | 350µs | 116 | 1.10ms | my ( $web1, $name1 ) = # spent 1.10ms making 116 calls to Foswiki::normalizeWebTopicName, avg 9µs/call |
536 | $session->normalizeWebTopicName( $web, $file ); | ||||
537 | |||||
538 | 116 | 349µs | 116 | 8.07ms | if ( $session->topicExists( $web1, $name1 ) ) { # spent 8.07ms making 116 calls to Foswiki::topicExists, avg 70µs/call |
539 | |||||
540 | # recursion prevention. | ||||
541 | next | ||||
542 | if ( | ||||
543 | defined( | ||||
544 | $this->{files} | ||||
545 | ->{ 'topic' . $session->{user}, $name1, $web1 } | ||||
546 | ) | ||||
547 | ); | ||||
548 | $this->{files}->{ 'topic' . $session->{user}, $name1, $web1 } = | ||||
549 | 1; | ||||
550 | |||||
551 | # access control | ||||
552 | my $meta = Foswiki::Meta->load( $session, $web1, $name1 ); | ||||
553 | next unless $meta->haveAccess( 'VIEW', $session->{user} ); | ||||
554 | |||||
555 | my $text = $meta->text(); | ||||
556 | $text = '' unless defined $text; | ||||
557 | |||||
558 | $text = "<!--$web1.$name1-->\n$text<!--/$web1.$name1-->\n" | ||||
559 | if (TRACE); | ||||
560 | |||||
561 | $text = _decomment($text); | ||||
562 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, | ||||
563 | $text ) | ||||
564 | if (TRACE); | ||||
565 | return $text; | ||||
566 | } | ||||
567 | } | ||||
568 | elsif ( -e $file ) { | ||||
569 | 145 | 222µs | next if ( defined( $this->{files}->{$file} ) ); | ||
570 | |||||
571 | # recursion prevention. | ||||
572 | 100 | 142µs | $this->{files}->{$file} = 1; | ||
573 | |||||
574 | 100 | 456µs | 200 | 23.0ms | my $text = _decomment( _readFile( $session, $file ) ); # spent 16.1ms making 100 calls to Foswiki::Templates::_readFile, avg 161µs/call
# spent 6.84ms making 100 calls to Foswiki::Templates::_decomment, avg 68µs/call |
575 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
576 | if (TRACE); | ||||
577 | 100 | 942µs | return $text; | ||
578 | } | ||||
579 | } | ||||
580 | |||||
581 | # File was not found | ||||
582 | 2 | 12µs | return undef; | ||
583 | } | ||||
584 | |||||
585 | # spent 16.1ms (7.13+8.99) within Foswiki::Templates::_readFile which was called 100 times, avg 161µs/call:
# 100 times (7.13ms+8.99ms) by Foswiki::Templates::_readTemplateFile at line 574, avg 161µs/call | ||||
586 | 100 | 116µs | my ( $session, $fn ) = @_; | ||
587 | 100 | 15µs | my $F; | ||
588 | |||||
589 | 102 | 3.23ms | 301 | 3.96ms | if ( open( $F, '<:encoding(utf-8)', $fn ) ) { # spent 2.01ms making 100 calls to Encode::find_encoding, avg 20µs/call
# spent 1.40ms making 100 calls to Encode::Encoding::renew, avg 14µs/call
# spent 411µs making 1 call to PerlIO::import
# spent 136µs making 100 calls to Encode::Encoding::needs_lines, avg 1µs/call |
590 | 100 | 176µs | local $/; | ||
591 | 100 | 6.46ms | 590 | 5.23ms | my $text = <$F>; # spent 4.85ms making 295 calls to Encode::utf8::decode_xs, avg 16µs/call
# spent 374µs making 295 calls to Encode::Encoding::renewed, avg 1µs/call |
592 | 100 | 709µs | close($F); | ||
593 | |||||
594 | $text = "<!--$fn-->\n$text<!--/$fn-->\n" if (TRACE); | ||||
595 | |||||
596 | 100 | 666µs | return $text; | ||
597 | } | ||||
598 | else { | ||||
599 | $session->logger->log( 'warning', "$fn: $!" ); | ||||
600 | return undef; | ||||
601 | } | ||||
602 | } | ||||
603 | |||||
604 | # spent 6.84ms within Foswiki::Templates::_decomment which was called 100 times, avg 68µs/call:
# 100 times (6.84ms+0s) by Foswiki::Templates::_readTemplateFile at line 574, avg 68µs/call | ||||
605 | 100 | 122µs | my $text = shift; | ||
606 | |||||
607 | 100 | 25µs | return $text unless $text; | ||
608 | |||||
609 | # Kill comments, marked by %{ ... }% | ||||
610 | # (and remove whitespace either side of the comment) | ||||
611 | 100 | 6.44ms | $text =~ s/\s*%\{.*?\}%\s*//sg; | ||
612 | 100 | 330µs | return $text; | ||
613 | } | ||||
614 | |||||
615 | #See http://wikiring.com/Blog/BlogEntry8?cat=WikiRing | ||||
616 | #used for debugging templates, and later maybe for speed. | ||||
617 | sub saveTemplateToCache { | ||||
618 | my ( $this, $cacheName, $name, $skins, $web, $tmplText ) = @_; | ||||
619 | $skins = '' unless ( defined($skins) ); | ||||
620 | $web = '' unless ( defined($web) ); | ||||
621 | |||||
622 | my $tmpl_cachedir = $Foswiki::cfg{TemplateDir} . $cacheName; | ||||
623 | mkdir($tmpl_cachedir) unless ( -e $tmpl_cachedir ); | ||||
624 | my $filename = Foswiki::Sandbox::untaintUnchecked( | ||||
625 | $tmpl_cachedir . '/' . $name . '__' . $skins . '__' . $web . '.tmpl' ); | ||||
626 | |||||
627 | open( my $file, '>:encoding(utf-8)', $filename ) or do { | ||||
628 | die "Can't create file $filename - $!\n" if DEBUG; | ||||
629 | print STDERR "Can't create file $filename - $!\n"; | ||||
630 | |||||
631 | return; | ||||
632 | }; | ||||
633 | print $file $tmplText; | ||||
634 | close($file); | ||||
635 | } | ||||
636 | |||||
637 | #unused, but can be used for a speedup by caching the expanded Template | ||||
638 | sub getTemplateFromCache { | ||||
639 | my ( $this, $name, $skins, $web ) = @_; | ||||
640 | $skins = '' unless ( defined($skins) ); | ||||
641 | $web = '' unless ( defined($web) ); | ||||
642 | |||||
643 | my $tmpl_cachedir = $Foswiki::cfg{TemplateDir} . '_cache'; | ||||
644 | mkdir($tmpl_cachedir) unless ( -e $tmpl_cachedir ); | ||||
645 | my $filename = Foswiki::Sandbox::untaintUnchecked( | ||||
646 | $tmpl_cachedir . '/' . $name . '__' . $skins . '__' . $web . '.tmpl' ); | ||||
647 | |||||
648 | if ( -e $filename ) { | ||||
649 | open( my $in_file, '<:encoding(utf-8)', $filename ) or return; | ||||
650 | local $/ = undef; # set to read to EOF | ||||
651 | my $data = <$in_file>; | ||||
652 | close($in_file); | ||||
653 | return $data; | ||||
654 | } | ||||
655 | } | ||||
656 | |||||
657 | 1 | 2µs | 1; | ||
658 | __END__ |