PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
← All changes | assets/lib/Mustache/Compiler.php +300 -87 2.0.24.0.3 View file →
@@ -2,9 +2,9 @@
2 2
3 3 /*
4 4 * This file is part of Mustache.php.
5 5 *
6 - * (c) 2012 Justin Hileman
6 + * (c) 2010-2017 Justin Hileman
7 7 *
8 8 * For the full copyright and license information, please view the LICENSE
9 9 * file that was distributed with this source code.
10 10 */
@@ -15,10 +15,12 @@
15 15 * This class is responsible for turning a Mustache token parse tree into normal PHP source code.
16 16 */
17 17 class Mustache_Compiler
18 18 {
19 -
19 + private $pragmas;
20 + private $defaultPragmas = array();
20 21 private $sections;
22 + private $blocks;
21 23 private $source;
22 24 private $indentNextLine;
23 25 private $customEscape;
24 26 private $entityFlags;
@@ -23,9 +25,8 @@
23 25 private $customEscape;
24 26 private $entityFlags;
25 27 private $charset;
26 28 private $strictCallables;
27 - private $pragmas;
28 29
29 30 /**
30 31 * Compile a Mustache token parse tree into PHP source code.
31 32 *
@@ -32,18 +33,19 @@
32 33 * @param string $source Mustache Template source code
33 34 * @param string $tree Parse tree of Mustache tokens
34 35 * @param string $name Mustache Template class name
35 36 * @param bool $customEscape (default: false)
36 - * @param int $entityFlags (default: ENT_COMPAT)
37 37 * @param string $charset (default: 'UTF-8')
38 38 * @param bool $strictCallables (default: false)
39 + * @param int $entityFlags (default: ENT_COMPAT)
39 40 *
40 41 * @return string Generated PHP source code
41 42 */
42 43 public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT)
43 44 {
44 - $this->pragmas = array();
45 + $this->pragmas = $this->defaultPragmas;
45 46 $this->sections = array();
47 + $this->blocks = array();
46 48 $this->source = $source;
47 49 $this->indentNextLine = true;
48 50 $this->customEscape = $customEscape;
49 51 $this->entityFlags = $entityFlags;
@@ -53,11 +55,28 @@
53 55 return $this->writeCode($tree, $name);
54 56 }
55 57
56 58 /**
59 + * Enable pragmas across all templates, regardless of the presence of pragma
60 + * tags in the individual templates.
61 + *
62 + * @internal Users should set global pragmas in Mustache_Engine, not here :)
63 + *
64 + * @param string[] $pragmas
65 + */
66 + public function setPragmas(array $pragmas)
67 + {
68 + $this->pragmas = array();
69 + foreach ($pragmas as $pragma) {
70 + $this->pragmas[$pragma] = true;
71 + }
72 + $this->defaultPragmas = $this->pragmas;
73 + }
74 +
75 + /**
57 76 * Helper function for walking the Mustache token parse tree.
58 77 *
59 - * @throws Mustache_Exception_SyntaxException upon encountering unknown token types.
78 + * @throws Mustache_Exception_SyntaxException upon encountering unknown token types
60 79 *
61 80 * @param array $tree Parse tree of Mustache tokens
62 81 * @param int $level (default: 0)
63 82 *
@@ -76,8 +95,9 @@
76 95 case Mustache_Tokenizer::T_SECTION:
77 96 $code .= $this->section(
78 97 $node[Mustache_Tokenizer::NODES],
79 98 $node[Mustache_Tokenizer::NAME],
99 + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
80 100 $node[Mustache_Tokenizer::INDEX],
81 101 $node[Mustache_Tokenizer::END],
82 102 $node[Mustache_Tokenizer::OTAG],
83 103 $node[Mustache_Tokenizer::CTAG],
@@ -88,14 +108,14 @@
88 108 case Mustache_Tokenizer::T_INVERTED:
89 109 $code .= $this->invertedSection(
90 110 $node[Mustache_Tokenizer::NODES],
91 111 $node[Mustache_Tokenizer::NAME],
112 + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
92 113 $level
93 114 );
94 115 break;
95 116
96 117 case Mustache_Tokenizer::T_PARTIAL:
97 - case Mustache_Tokenizer::T_PARTIAL_2:
98 118 $code .= $this->partial(
99 119 $node[Mustache_Tokenizer::NAME],
100 120 isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
101 121 $level
@@ -101,18 +121,53 @@
101 121 $level
102 122 );
103 123 break;
104 124
105 - case Mustache_Tokenizer::T_UNESCAPED:
106 - case Mustache_Tokenizer::T_UNESCAPED_2:
107 - $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level);
125 + case Mustache_Tokenizer::T_PARENT:
126 + $code .= $this->parent(
127 + $node[Mustache_Tokenizer::NAME],
128 + isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
129 + $node[Mustache_Tokenizer::NODES],
130 + $level
131 + );
108 132 break;
109 133
134 + case Mustache_Tokenizer::T_BLOCK_ARG:
135 + $code .= $this->blockArg(
136 + $node[Mustache_Tokenizer::NODES],
137 + $node[Mustache_Tokenizer::NAME],
138 + $node[Mustache_Tokenizer::INDEX],
139 + $node[Mustache_Tokenizer::END],
140 + $node[Mustache_Tokenizer::OTAG],
141 + $node[Mustache_Tokenizer::CTAG],
142 + $level
143 + );
144 + break;
145 +
146 + case Mustache_Tokenizer::T_BLOCK_VAR:
147 + $code .= $this->blockVar(
148 + $node[Mustache_Tokenizer::NODES],
149 + $node[Mustache_Tokenizer::NAME],
150 + $node[Mustache_Tokenizer::INDEX],
151 + $node[Mustache_Tokenizer::END],
152 + $node[Mustache_Tokenizer::OTAG],
153 + $node[Mustache_Tokenizer::CTAG],
154 + $level
155 + );
156 + break;
157 +
110 158 case Mustache_Tokenizer::T_COMMENT:
111 159 break;
112 160
113 161 case Mustache_Tokenizer::T_ESCAPED:
114 - $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level);
162 + case Mustache_Tokenizer::T_UNESCAPED:
163 + case Mustache_Tokenizer::T_UNESCAPED_2:
164 + $code .= $this->variable(
165 + $node[Mustache_Tokenizer::NAME],
166 + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
167 + $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_ESCAPED,
168 + $level
169 + );
115 170 break;
116 171
117 172 case Mustache_Tokenizer::T_TEXT:
118 173 $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
@@ -140,8 +195,9 @@
140 195
141 196 return $buffer;
142 197 }
143 198 %s
199 + %s
144 200 }';
145 201
146 202 const KLASS_NO_LAMBDAS = '<?php
147 203
@@ -169,17 +225,104 @@
169 225 private function writeCode($tree, $name)
170 226 {
171 227 $code = $this->walk($tree);
172 228 $sections = implode("\n", $this->sections);
173 - $klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS;
229 + $blocks = implode("\n", $this->blocks);
230 + $klass = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS;
231 +
174 232 $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
175 233
176 - return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections);
234 + return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections, $blocks);
177 235 }
178 236
237 + const BLOCK_VAR = '
238 + $blockFunction = $context->findInBlock(%s);
239 + if (is_callable($blockFunction)) {
240 + $buffer .= call_user_func($blockFunction, $context);
241 + %s}
242 + ';
243 +
244 + const BLOCK_VAR_ELSE = '} else {%s';
245 +
246 + /**
247 + * Generate Mustache Template inheritance block variable PHP source.
248 + *
249 + * @param array $nodes Array of child tokens
250 + * @param string $id Section name
251 + * @param int $start Section start offset
252 + * @param int $end Section end offset
253 + * @param string $otag Current Mustache opening tag
254 + * @param string $ctag Current Mustache closing tag
255 + * @param int $level
256 + *
257 + * @return string Generated PHP source code
258 + */
259 + private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level)
260 + {
261 + $id = var_export($id, true);
262 +
263 + $else = $this->walk($nodes, $level);
264 + if ($else !== '') {
265 + $else = sprintf($this->prepare(self::BLOCK_VAR_ELSE, $level + 1, false, true), $else);
266 + }
267 +
268 + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $else);
269 + }
270 +
271 + const BLOCK_ARG = '%s => array($this, \'block%s\'),';
272 +
273 + /**
274 + * Generate Mustache Template inheritance block argument PHP source.
275 + *
276 + * @param array $nodes Array of child tokens
277 + * @param string $id Section name
278 + * @param int $start Section start offset
279 + * @param int $end Section end offset
280 + * @param string $otag Current Mustache opening tag
281 + * @param string $ctag Current Mustache closing tag
282 + * @param int $level
283 + *
284 + * @return string Generated PHP source code
285 + */
286 + private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
287 + {
288 + $key = $this->block($nodes);
289 + $id = var_export($id, true);
290 +
291 + return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key);
292 + }
293 +
294 + const BLOCK_FUNCTION = '
295 + public function block%s($context)
296 + {
297 + $indent = $buffer = \'\';%s
298 +
299 + return $buffer;
300 + }
301 + ';
302 +
303 + /**
304 + * Generate Mustache Template inheritance block function PHP source.
305 + *
306 + * @param array $nodes Array of child tokens
307 + *
308 + * @return string key of new block function
309 + */
310 + private function block($nodes)
311 + {
312 + $code = $this->walk($nodes, 0);
313 + $key = ucfirst(md5($code));
314 +
315 + if (!isset($this->blocks[$key])) {
316 + $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 0), $key, $code);
317 + }
318 +
319 + return $key;
320 + }
321 +
179 322 const SECTION_CALL = '
180 - // %s section
181 - $buffer .= $this->section%s($context, $indent, $context->%s(%s));
323 + $value = $context->%s(%s);%s
324 + $buffer .= $this->section%s($context, $indent, $value);
182 325 ';
183 326
184 327 const SECTION = '
185 328 private function section%s(Mustache_Context $context, $indent, $value)
@@ -184,86 +327,103 @@
184 327 const SECTION = '
185 328 private function section%s(Mustache_Context $context, $indent, $value)
186 329 {
187 330 $buffer = \'\';
331 +
188 332 if (%s) {
189 333 $source = %s;
190 - $buffer .= $this->mustache
191 - ->loadLambda((string) call_user_func($value, $source, $this->lambdaHelper)%s)
192 - ->renderInternal($context);
334 + $result = (string) call_user_func($value, $source, %s);
335 + if (strpos($result, \'{{\') === false) {
336 + $buffer .= $result;
337 + } else {
338 + $buffer .= $this->mustache
339 + ->loadLambda($result%s)
340 + ->renderInternal($context);
341 + }
193 342 } elseif (!empty($value)) {
194 343 $values = $this->isIterable($value) ? $value : array($value);
195 344 foreach ($values as $value) {
196 - $context->push($value);%s
345 + $context->push($value);
346 + %s
197 347 $context->pop();
198 348 }
199 349 }
200 350
201 351 return $buffer;
202 - }';
352 + }
353 + ';
203 354
204 355 /**
205 356 * Generate Mustache Template section PHP source.
206 357 *
207 - * @param array $nodes Array of child tokens
208 - * @param string $id Section name
209 - * @param int $start Section start offset
210 - * @param int $end Section end offset
211 - * @param string $otag Current Mustache opening tag
212 - * @param string $ctag Current Mustache closing tag
213 - * @param int $level
358 + * @param array $nodes Array of child tokens
359 + * @param string $id Section name
360 + * @param string[] $filters Array of filters
361 + * @param int $start Section start offset
362 + * @param int $end Section end offset
363 + * @param string $otag Current Mustache opening tag
364 + * @param string $ctag Current Mustache closing tag
365 + * @param int $level
214 366 *
215 367 * @return string Generated section PHP source code
216 368 */
217 - private function section($nodes, $id, $start, $end, $otag, $ctag, $level)
369 + private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level)
218 370 {
219 - $method = $this->getFindMethod($id);
220 - $id = var_export($id, true);
221 371 $source = var_export(substr($this->source, $start, $end - $start), true);
222 372 $callable = $this->getCallable();
223 373
224 374 if ($otag !== '{{' || $ctag !== '}}') {
225 - $delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
375 + $delimTag = var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
376 + $helper = sprintf('$this->lambdaHelper->withDelimiters(%s)', $delimTag);
377 + $delims = ', ' . $delimTag;
226 378 } else {
379 + $helper = '$this->lambdaHelper';
227 380 $delims = '';
228 381 }
229 382
230 - $key = ucfirst(md5($delims."\n".$source));
383 + $key = ucfirst(md5($delims . "\n" . $source));
231 384
232 385 if (!isset($this->sections[$key])) {
233 - $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2));
386 + $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $delims, $this->walk($nodes, 2));
234 387 }
235 388
236 - return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id);
389 + $method = $this->getFindMethod($id);
390 + $id = var_export($id, true);
391 + $filters = $this->getFilters($filters, $level);
392 +
393 + return sprintf($this->prepare(self::SECTION_CALL, $level), $method, $id, $filters, $key);
237 394 }
238 395
239 396 const INVERTED_SECTION = '
240 - // %s inverted section
241 - $value = $context->%s(%s);
397 + $value = $context->%s(%s);%s
242 398 if (empty($value)) {
243 399 %s
244 - }';
400 + }
401 + ';
245 402
246 403 /**
247 404 * Generate Mustache Template inverted section PHP source.
248 405 *
249 - * @param array $nodes Array of child tokens
250 - * @param string $id Section name
251 - * @param int $level
406 + * @param array $nodes Array of child tokens
407 + * @param string $id Section name
408 + * @param string[] $filters Array of filters
409 + * @param int $level
252 410 *
253 411 * @return string Generated inverted section PHP source code
254 412 */
255 - private function invertedSection($nodes, $id, $level)
413 + private function invertedSection($nodes, $id, $filters, $level)
256 414 {
257 - $method = $this->getFindMethod($id);
258 - $id = var_export($id, true);
415 + $method = $this->getFindMethod($id);
416 + $id = var_export($id, true);
417 + $filters = $this->getFilters($filters, $level);
259 418
260 - return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level));
419 + return sprintf($this->prepare(self::INVERTED_SECTION, $level), $method, $id, $filters, $this->walk($nodes, $level));
261 420 }
262 421
422 + const PARTIAL_INDENT = ', $indent . %s';
263 423 const PARTIAL = '
264 424 if ($partial = $this->mustache->loadPartial(%s)) {
265 - $buffer .= $partial->renderInternal($context, %s);
425 + $buffer .= $partial->renderInternal($context%s);
266 426 }
267 427 ';
268 428
269 429 /**
@@ -276,58 +436,96 @@
276 436 * @return string Generated partial call PHP source code
277 437 */
278 438 private function partial($id, $indent, $level)
279 439 {
440 + if ($indent !== '') {
441 + $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true));
442 + } else {
443 + $indentParam = '';
444 + }
445 +
280 446 return sprintf(
281 447 $this->prepare(self::PARTIAL, $level),
282 448 var_export($id, true),
283 - var_export($indent, true)
449 + $indentParam
284 450 );
285 451 }
286 452
287 - const VARIABLE = '
288 - $value = $this->resolveValue($context->%s(%s), $context, $indent);%s
289 - $buffer .= %s%s;
453 + const PARENT = '
454 + if ($parent = $this->mustache->loadPartial(%s)) {
455 + $context->pushBlockContext(array(%s
456 + ));
457 + $buffer .= $parent->renderInternal($context, $indent);
458 + $context->popBlockContext();
459 + }
290 460 ';
291 461
462 + const PARENT_NO_CONTEXT = '
463 + if ($parent = $this->mustache->loadPartial(%s)) {
464 + $buffer .= $parent->renderInternal($context, $indent);
465 + }
466 + ';
467 +
292 468 /**
293 - * Generate Mustache Template variable interpolation PHP source.
469 + * Generate Mustache Template inheritance parent call PHP source.
294 470 *
295 - * @param string $id Variable name
296 - * @param boolean $escape Escape the variable value for output?
297 - * @param int $level
471 + * @param string $id Parent tag name
472 + * @param string $indent Whitespace indent to apply to parent
473 + * @param array $children Child nodes
474 + * @param int $level
298 475 *
299 - * @return string Generated variable interpolation PHP source
476 + * @return string Generated PHP source code
300 477 */
301 - private function variable($id, $escape, $level)
478 + private function parent($id, $indent, array $children, $level)
302 479 {
303 - $filters = '';
480 + $realChildren = array_filter($children, array(__CLASS__, 'onlyBlockArgs'));
304 481
305 - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) {
306 - list($id, $filters) = $this->getFilters($id, $level);
482 + if (empty($realChildren)) {
483 + return sprintf($this->prepare(self::PARENT_NO_CONTEXT, $level), var_export($id, true));
307 484 }
308 485
309 - $method = $this->getFindMethod($id);
310 - $id = ($method !== 'last') ? var_export($id, true) : '';
311 - $value = $escape ? $this->getEscape() : '$value';
486 + return sprintf(
487 + $this->prepare(self::PARENT, $level),
488 + var_export($id, true),
489 + $this->walk($realChildren, $level + 1)
490 + );
491 + }
312 492
313 - return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
493 + /**
494 + * Helper method for filtering out non-block-arg tokens.
495 + *
496 + * @param array $node
497 + *
498 + * @return bool True if $node is a block arg token
499 + */
500 + private static function onlyBlockArgs(array $node)
501 + {
502 + return $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_BLOCK_ARG;
314 503 }
315 504
505 + const VARIABLE = '
506 + $value = $this->resolveValue($context->%s(%s), $context);%s
507 + $buffer .= %s($value === null ? \'\' : %s);
508 + ';
509 +
316 510 /**
317 - * Generate Mustache Template variable filtering PHP source.
511 + * Generate Mustache Template variable interpolation PHP source.
318 512 *
319 - * @param string $id Variable name
320 - * @param int $level
513 + * @param string $id Variable name
514 + * @param string[] $filters Array of filters
515 + * @param bool $escape Escape the variable value for output?
516 + * @param int $level
321 517 *
322 - * @return string Generated variable filtering PHP source
518 + * @return string Generated variable interpolation PHP source
323 519 */
324 - private function getFilters($id, $level)
520 + private function variable($id, $filters, $escape, $level)
325 521 {
326 - $filters = array_map('trim', explode('|', $id));
327 - $id = array_shift($filters);
522 + $method = $this->getFindMethod($id);
523 + $id = ($method !== 'last') ? var_export($id, true) : '';
524 + $filters = $this->getFilters($filters, $level);
525 + $value = $escape ? $this->getEscape() : '$value';
328 526
329 - return array($id, $this->getFilter($filters, $level));
527 + return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
330 528 }
331 529
332 530 const FILTER = '
333 531 $filter = $context->%s(%s);
@@ -337,16 +535,16 @@
337 535 $value = call_user_func($filter, $value);%s
338 536 ';
339 537
340 538 /**
341 - * Generate PHP source for a single filter.
539 + * Generate Mustache Template variable filtering PHP source.
342 540 *
343 - * @param array $filters
344 - * @param int $level
541 + * @param string[] $filters Array of filters
542 + * @param int $level
345 543 *
346 544 * @return string Generated filter PHP source
347 545 */
348 - private function getFilter(array $filters, $level)
546 + private function getFilters(array $filters, $level)
349 547 {
350 548 if (empty($filters)) {
351 549 return '';
352 550 }
@@ -356,9 +554,9 @@
356 554 $filter = ($method !== 'last') ? var_export($name, true) : '';
357 555 $callable = $this->getCallable('$filter');
358 556 $msg = var_export($name, true);
359 557
360 - return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level));
558 + return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level));
361 559 }
362 560
363 561 const LINE = '$buffer .= "\n";';
364 562 const TEXT = '$buffer .= %s%s;';
@@ -382,18 +580,18 @@
382 580
383 581 /**
384 582 * Prepare PHP source code snippet for output.
385 583 *
386 - * @param string $text
387 - * @param int $bonus Additional indent level (default: 0)
388 - * @param boolean $prependNewline Prepend a newline to the snippet? (default: true)
389 - * @param boolean $appendNewline Append a newline to the snippet? (default: false)
584 + * @param string $text
585 + * @param int $bonus Additional indent level (default: 0)
586 + * @param bool $prependNewline Prepend a newline to the snippet? (default: true)
587 + * @param bool $appendNewline Append a newline to the snippet? (default: false)
390 588 *
391 589 * @return string PHP source code snippet
392 590 */
393 591 private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
394 592 {
395 - $text = ($prependNewline ? "\n" : '').trim($text);
593 + $text = ($prependNewline ? "\n" : '') . trim($text);
396 594 if ($prependNewline) {
397 595 $bonus++;
398 596 }
399 597 if ($appendNewline) {
@@ -399,9 +597,9 @@
399 597 if ($appendNewline) {
400 598 $text .= "\n";
401 599 }
402 600
403 - return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text);
601 + return preg_replace("/\n( {8})?/", "\n" . str_repeat(' ', $bonus * 4), $text);
404 602 }
405 603
406 604 const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)';
407 605 const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
@@ -416,17 +614,17 @@
416 614 private function getEscape($value = '$value')
417 615 {
418 616 if ($this->customEscape) {
419 617 return sprintf(self::CUSTOM_ESCAPE, $value);
420 - } else {
421 - return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true));
422 618 }
619 +
620 + return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true));
423 621 }
424 622
425 623 /**
426 624 * Select the appropriate Context `find` method for a given $id.
427 625 *
428 - * The return value will be one of `find`, `findDot` or `last`.
626 + * The return value will be one of `find`, `findDot`, `findAnchoredDot` or `last`.
429 627 *
430 628 * @see Mustache_Context::find
431 629 * @see Mustache_Context::findDot
432 630 * @see Mustache_Context::last
@@ -438,18 +636,33 @@
438 636 private function getFindMethod($id)
439 637 {
440 638 if ($id === '.') {
441 639 return 'last';
442 - } elseif (strpos($id, '.') === false) {
640 + }
641 +
642 + if (isset($this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) && $this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) {
643 + if (substr($id, 0, 1) === '.') {
644 + return 'findAnchoredDot';
645 + }
646 + }
647 +
648 + if (strpos($id, '.') === false) {
443 649 return 'find';
444 - } else {
445 - return 'findDot';
446 650 }
651 +
652 + return 'findDot';
447 653 }
448 654
449 655 const IS_CALLABLE = '!is_string(%s) && is_callable(%s)';
450 656 const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';
451 657
658 + /**
659 + * Helper function to compile strict vs lax "is callable" logic.
660 + *
661 + * @param string $variable (default: '$value')
662 + *
663 + * @return string "is callable" logic
664 + */
452 665 private function getCallable($variable = '$value')
453 666 {
454 667 $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE;
455 668