PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 2.0.6
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v2.0.6
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 2.0.7 All 340 releases
profile-builder / assets / lib / Mustache / Compiler.php

Compiler.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 2.0.6, at assets/lib/Mustache/Compiler.php

477 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of Mustache.php.
5 *
6 * (c) 2012 Justin Hileman
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 /**
13 * Mustache Compiler class.
14 *
15 * This class is responsible for turning a Mustache token parse tree into normal PHP source code.
16 */
17 class Mustache_Compiler
18 {
19
20 private $sections;
21 private $source;
22 private $indentNextLine;
23 private $customEscape;
24 private $entityFlags;
25 private $charset;
26 private $strictCallables;
27 private $pragmas;
28
29 /**
30 * Compile a Mustache token parse tree into PHP source code.
31 *
32 * @param string $source Mustache Template source code
33 * @param string $tree Parse tree of Mustache tokens
34 * @param string $name Mustache Template class name
35 * @param bool $customEscape (default: false)
36 * @param int $entityFlags (default: ENT_COMPAT)
37 * @param string $charset (default: 'UTF-8')
38 * @param bool $strictCallables (default: false)
39 *
40 * @return string Generated PHP source code
41 */
42 public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT)
43 {
44 $this->pragmas = array();
45 $this->sections = array();
46 $this->source = $source;
47 $this->indentNextLine = true;
48 $this->customEscape = $customEscape;
49 $this->entityFlags = $entityFlags;
50 $this->charset = $charset;
51 $this->strictCallables = $strictCallables;
52
53 return $this->writeCode($tree, $name);
54 }
55
56 /**
57 * Helper function for walking the Mustache token parse tree.
58 *
59 * @throws Mustache_Exception_SyntaxException upon encountering unknown token types.
60 *
61 * @param array $tree Parse tree of Mustache tokens
62 * @param int $level (default: 0)
63 *
64 * @return string Generated PHP source code
65 */
66 private function walk(array $tree, $level = 0)
67 {
68 $code = '';
69 $level++;
70 foreach ($tree as $node) {
71 switch ($node[Mustache_Tokenizer::TYPE]) {
72 case Mustache_Tokenizer::T_PRAGMA:
73 $this->pragmas[$node[Mustache_Tokenizer::NAME]] = true;
74 break;
75
76 case Mustache_Tokenizer::T_SECTION:
77 $code .= $this->section(
78 $node[Mustache_Tokenizer::NODES],
79 $node[Mustache_Tokenizer::NAME],
80 $node[Mustache_Tokenizer::INDEX],
81 $node[Mustache_Tokenizer::END],
82 $node[Mustache_Tokenizer::OTAG],
83 $node[Mustache_Tokenizer::CTAG],
84 $level
85 );
86 break;
87
88 case Mustache_Tokenizer::T_INVERTED:
89 $code .= $this->invertedSection(
90 $node[Mustache_Tokenizer::NODES],
91 $node[Mustache_Tokenizer::NAME],
92 $level
93 );
94 break;
95
96 case Mustache_Tokenizer::T_PARTIAL:
97 case Mustache_Tokenizer::T_PARTIAL_2:
98 $code .= $this->partial(
99 $node[Mustache_Tokenizer::NAME],
100 isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
101 $level
102 );
103 break;
104
105 case Mustache_Tokenizer::T_UNESCAPED:
106 case Mustache_Tokenizer::T_UNESCAPED_2:
107 $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level);
108 break;
109
110 case Mustache_Tokenizer::T_COMMENT:
111 break;
112
113 case Mustache_Tokenizer::T_ESCAPED:
114 $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level);
115 break;
116
117 case Mustache_Tokenizer::T_TEXT:
118 $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
119 break;
120
121 default:
122 throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node);
123 }
124 }
125
126 return $code;
127 }
128
129 const KLASS = '<?php
130
131 class %s extends Mustache_Template
132 {
133 private $lambdaHelper;%s
134
135 public function renderInternal(Mustache_Context $context, $indent = \'\')
136 {
137 $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
138 $buffer = \'\';
139 %s
140
141 return $buffer;
142 }
143 %s
144 }';
145
146 const KLASS_NO_LAMBDAS = '<?php
147
148 class %s extends Mustache_Template
149 {%s
150 public function renderInternal(Mustache_Context $context, $indent = \'\')
151 {
152 $buffer = \'\';
153 %s
154
155 return $buffer;
156 }
157 }';
158
159 const STRICT_CALLABLE = 'protected $strictCallables = true;';
160
161 /**
162 * Generate Mustache Template class PHP source.
163 *
164 * @param array $tree Parse tree of Mustache tokens
165 * @param string $name Mustache Template class name
166 *
167 * @return string Generated PHP source code
168 */
169 private function writeCode($tree, $name)
170 {
171 $code = $this->walk($tree);
172 $sections = implode("\n", $this->sections);
173 $klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS;
174 $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
175
176 return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections);
177 }
178
179 const SECTION_CALL = '
180 // %s section
181 $buffer .= $this->section%s($context, $indent, $context->%s(%s));
182 ';
183
184 const SECTION = '
185 private function section%s(Mustache_Context $context, $indent, $value)
186 {
187 $buffer = \'\';
188 if (%s) {
189 $source = %s;
190 $buffer .= $this->mustache
191 ->loadLambda((string) call_user_func($value, $source, $this->lambdaHelper)%s)
192 ->renderInternal($context);
193 } elseif (!empty($value)) {
194 $values = $this->isIterable($value) ? $value : array($value);
195 foreach ($values as $value) {
196 $context->push($value);%s
197 $context->pop();
198 }
199 }
200
201 return $buffer;
202 }';
203
204 /**
205 * Generate Mustache Template section PHP source.
206 *
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
214 *
215 * @return string Generated section PHP source code
216 */
217 private function section($nodes, $id, $start, $end, $otag, $ctag, $level)
218 {
219 $method = $this->getFindMethod($id);
220 $id = var_export($id, true);
221 $source = var_export(substr($this->source, $start, $end - $start), true);
222 $callable = $this->getCallable();
223
224 if ($otag !== '{{' || $ctag !== '}}') {
225 $delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
226 } else {
227 $delims = '';
228 }
229
230 $key = ucfirst(md5($delims."\n".$source));
231
232 if (!isset($this->sections[$key])) {
233 $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2));
234 }
235
236 return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id);
237 }
238
239 const INVERTED_SECTION = '
240 // %s inverted section
241 $value = $context->%s(%s);
242 if (empty($value)) {
243 %s
244 }';
245
246 /**
247 * Generate Mustache Template inverted section PHP source.
248 *
249 * @param array $nodes Array of child tokens
250 * @param string $id Section name
251 * @param int $level
252 *
253 * @return string Generated inverted section PHP source code
254 */
255 private function invertedSection($nodes, $id, $level)
256 {
257 $method = $this->getFindMethod($id);
258 $id = var_export($id, true);
259
260 return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level));
261 }
262
263 const PARTIAL = '
264 if ($partial = $this->mustache->loadPartial(%s)) {
265 $buffer .= $partial->renderInternal($context, %s);
266 }
267 ';
268
269 /**
270 * Generate Mustache Template partial call PHP source.
271 *
272 * @param string $id Partial name
273 * @param string $indent Whitespace indent to apply to partial
274 * @param int $level
275 *
276 * @return string Generated partial call PHP source code
277 */
278 private function partial($id, $indent, $level)
279 {
280 return sprintf(
281 $this->prepare(self::PARTIAL, $level),
282 var_export($id, true),
283 var_export($indent, true)
284 );
285 }
286
287 const VARIABLE = '
288 $value = $this->resolveValue($context->%s(%s), $context, $indent);%s
289 $buffer .= %s%s;
290 ';
291
292 /**
293 * Generate Mustache Template variable interpolation PHP source.
294 *
295 * @param string $id Variable name
296 * @param boolean $escape Escape the variable value for output?
297 * @param int $level
298 *
299 * @return string Generated variable interpolation PHP source
300 */
301 private function variable($id, $escape, $level)
302 {
303 $filters = '';
304
305 if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) {
306 list($id, $filters) = $this->getFilters($id, $level);
307 }
308
309 $method = $this->getFindMethod($id);
310 $id = ($method !== 'last') ? var_export($id, true) : '';
311 $value = $escape ? $this->getEscape() : '$value';
312
313 return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
314 }
315
316 /**
317 * Generate Mustache Template variable filtering PHP source.
318 *
319 * @param string $id Variable name
320 * @param int $level
321 *
322 * @return string Generated variable filtering PHP source
323 */
324 private function getFilters($id, $level)
325 {
326 $filters = array_map('trim', explode('|', $id));
327 $id = array_shift($filters);
328
329 return array($id, $this->getFilter($filters, $level));
330 }
331
332 const FILTER = '
333 $filter = $context->%s(%s);
334 if (!(%s)) {
335 throw new Mustache_Exception_UnknownFilterException(%s);
336 }
337 $value = call_user_func($filter, $value);%s
338 ';
339
340 /**
341 * Generate PHP source for a single filter.
342 *
343 * @param array $filters
344 * @param int $level
345 *
346 * @return string Generated filter PHP source
347 */
348 private function getFilter(array $filters, $level)
349 {
350 if (empty($filters)) {
351 return '';
352 }
353
354 $name = array_shift($filters);
355 $method = $this->getFindMethod($name);
356 $filter = ($method !== 'last') ? var_export($name, true) : '';
357 $callable = $this->getCallable('$filter');
358 $msg = var_export($name, true);
359
360 return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level));
361 }
362
363 const LINE = '$buffer .= "\n";';
364 const TEXT = '$buffer .= %s%s;';
365
366 /**
367 * Generate Mustache Template output Buffer call PHP source.
368 *
369 * @param string $text
370 * @param int $level
371 *
372 * @return string Generated output Buffer call PHP source
373 */
374 private function text($text, $level)
375 {
376 $indentNextLine = (substr($text, -1) === "\n");
377 $code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
378 $this->indentNextLine = $indentNextLine;
379
380 return $code;
381 }
382
383 /**
384 * Prepare PHP source code snippet for output.
385 *
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)
390 *
391 * @return string PHP source code snippet
392 */
393 private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
394 {
395 $text = ($prependNewline ? "\n" : '').trim($text);
396 if ($prependNewline) {
397 $bonus++;
398 }
399 if ($appendNewline) {
400 $text .= "\n";
401 }
402
403 return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text);
404 }
405
406 const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)';
407 const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
408
409 /**
410 * Get the current escaper.
411 *
412 * @param string $value (default: '$value')
413 *
414 * @return string Either a custom callback, or an inline call to `htmlspecialchars`
415 */
416 private function getEscape($value = '$value')
417 {
418 if ($this->customEscape) {
419 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 }
423 }
424
425 /**
426 * Select the appropriate Context `find` method for a given $id.
427 *
428 * The return value will be one of `find`, `findDot` or `last`.
429 *
430 * @see Mustache_Context::find
431 * @see Mustache_Context::findDot
432 * @see Mustache_Context::last
433 *
434 * @param string $id Variable name
435 *
436 * @return string `find` method name
437 */
438 private function getFindMethod($id)
439 {
440 if ($id === '.') {
441 return 'last';
442 } elseif (strpos($id, '.') === false) {
443 return 'find';
444 } else {
445 return 'findDot';
446 }
447 }
448
449 const IS_CALLABLE = '!is_string(%s) && is_callable(%s)';
450 const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';
451
452 private function getCallable($variable = '$value')
453 {
454 $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE;
455
456 return sprintf($tpl, $variable, $variable);
457 }
458
459 const LINE_INDENT = '$indent . ';
460
461 /**
462 * Get the current $indent prefix to write to the buffer.
463 *
464 * @return string "$indent . " or ""
465 */
466 private function flushIndent()
467 {
468 if (!$this->indentNextLine) {
469 return '';
470 }
471
472 $this->indentNextLine = false;
473
474 return self::LINE_INDENT;
475 }
476 }
477