PluginProbe
GetResponse Forms by Optin Cat / 1.4.1
GetResponse Forms by Optin Cat v1.4.1
1.3.4 1.3.5 1.3.6 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 2.0.0 All 36 releases
getresponse / includes / classes / Mustache / Compiler.php

Compiler.php in GetResponse Forms by Optin Cat 1.4.1, at includes/classes/Mustache/Compiler.php

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