Packages

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • NBaseTemplate
  • NCachingHelper
  • NCurlyBracketsMacros
  • NLatteFilter
  • NLatteMacros
  • NSnippetHelper
  • NTemplate
  • NTemplateCacheStorage
  • NTemplateFilters
  • NTemplateHelpers

Interfaces

  • IFileTemplate
  • ITemplate
  • Overview
  • Package
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  * @package Nette\Templates
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * Default macros for filter LatteFilter.
 17:  *
 18:  * - {$variable} with escaping
 19:  * - {!$variable} without escaping
 20:  * - {*comment*} will be removed
 21:  * - {=expression} echo with escaping
 22:  * - {!=expression} echo without escaping
 23:  * - {?expression} evaluate PHP statement
 24:  * - {_expression} echo translation with escaping
 25:  * - {!_expression} echo translation without escaping
 26:  * - {link destination ...} control link
 27:  * - {plink destination ...} presenter link
 28:  * - {if ?} ... {elseif ?} ... {else} ... {/if}
 29:  * - {ifset ?} ... {elseifset ?} ... {/if}
 30:  * - {for ?} ... {/for}
 31:  * - {foreach ?} ... {/foreach}
 32:  * - {include ?}
 33:  * - {cache ?} ... {/cache} cached block
 34:  * - {snippet ?} ... {/snippet ?} control snippet
 35:  * - {attr ?} HTML element attributes
 36:  * - {block|texy} ... {/block} block
 37:  * - {contentType ...} HTTP Content-Type header
 38:  * - {status ...} HTTP status
 39:  * - {capture ?} ... {/capture} capture block to parameter
 40:  * - {var var => value} set template parameter
 41:  * - {default var => value} set default template parameter
 42:  * - {dump $var}
 43:  * - {debugbreak}
 44:  *
 45:  * @author     David Grudl
 46:  * @package Nette\Templates
 47:  */
 48: class NLatteMacros extends NObject
 49: {
 50:     /** @var array */
 51:     public static $defaultMacros = array(
 52:         'syntax' => '%:macroSyntax%',
 53:         '/syntax' => '%:macroSyntax%',
 54: 
 55:         'block' => '<?php %:macroBlock% ?>',
 56:         '/block' => '<?php %:macroBlockEnd% ?>',
 57: 
 58:         'capture' => '<?php %:macroCapture% ?>',
 59:         '/capture' => '<?php %:macroCaptureEnd% ?>',
 60: 
 61:         'snippet' => '<?php %:macroSnippet% ?>',
 62:         '/snippet' => '<?php %:macroSnippetEnd% ?>',
 63: 
 64:         'cache' => '<?php if ($_cb->foo = NCachingHelper::create($_cb->key = md5(__FILE__) . __LINE__, $template->getFile(), array(%%))) { $_cb->caches[] = $_cb->foo ?>',
 65:         '/cache' => '<?php array_pop($_cb->caches)->save(); } if (!empty($_cb->caches)) end($_cb->caches)->addItem($_cb->key) ?>',
 66: 
 67:         'if' => '<?php if (%%): ?>',
 68:         'elseif' => '<?php elseif (%%): ?>',
 69:         'else' => '<?php else: ?>',
 70:         '/if' => '<?php endif ?>',
 71:         'ifset' => '<?php if (isset(%%)): ?>',
 72:         '/ifset' => '<?php endif ?>',
 73:         'elseifset' => '<?php elseif (isset(%%)): ?>',
 74:         'foreach' => '<?php foreach (%:macroForeach%): ?>',
 75:         '/foreach' => '<?php endforeach; array_pop($_cb->its); $iterator = end($_cb->its) ?>',
 76:         'for' => '<?php for (%%): ?>',
 77:         '/for' => '<?php endfor ?>',
 78:         'while' => '<?php while (%%): ?>',
 79:         '/while' => '<?php endwhile ?>',
 80:         'continueIf' => '<?php if (%%) continue ?>',
 81:         'breakIf' => '<?php if (%%) break ?>',
 82: 
 83:         'include' => '<?php %:macroInclude% ?>',
 84:         'extends' => '<?php %:macroExtends% ?>',
 85:         'layout' => '<?php %:macroExtends% ?>',
 86: 
 87:         'plink' => '<?php echo %:macroEscape%(%:macroPlink%) ?>',
 88:         'link' => '<?php echo %:macroEscape%(%:macroLink%) ?>',
 89:         'ifCurrent' => '<?php %:macroIfCurrent%; if ($presenter->getLastCreatedRequestFlag("current")): ?>',
 90:         '/ifCurrent' => '<?php endif ?>',
 91:         'widget' => '<?php %:macroWidget% ?>',
 92:         'control' => '<?php %:macroWidget% ?>',
 93: 
 94:         'attr' => '<?php echo NHtml::el(NULL)->%:macroAttr%attributes() ?>',
 95:         'contentType' => '<?php %:macroContentType% ?>',
 96:         'status' => '<?php NEnvironment::getHttpResponse()->setCode(%%) ?>',
 97:         'var' => '<?php %:macroAssign% ?>',
 98:         'assign' => '<?php %:macroAssign% ?>',
 99:         'default' => '<?php %:macroDefault% ?>',
100:         'dump' => '<?php NDebug::consoleDump(%:macroDump%, "Template " . str_replace(NEnvironment::getVariable("appDir"), "\xE2\x80\xA6", $template->getFile())) ?>',
101:         'debugbreak' => '<?php if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break() ?>',
102: 
103:         '!_' => '<?php echo %:macroTranslate% ?>',
104:         '!=' => '<?php echo %:macroModifiers% ?>',
105:         '_' => '<?php echo %:macroEscape%(%:macroTranslate%) ?>',
106:         '=' => '<?php echo %:macroEscape%(%:macroModifiers%) ?>',
107:         '!$' => '<?php echo %:macroVar% ?>',
108:         '!' => '<?php echo %:macroVar% ?>', // deprecated
109:         '$' => '<?php echo %:macroEscape%(%:macroVar%) ?>',
110:         '?' => '<?php %:macroModifiers% ?>', // deprecated?
111:     );
112: 
113:     /** @var array */
114:     public $macros;
115: 
116:     /** @var NLatteFilter */
117:     private $filter;
118: 
119:     /** @var array */
120:     private $current;
121: 
122:     /** @var array */
123:     private $blocks = array();
124: 
125:     /** @var array */
126:     private $namedBlocks = array();
127: 
128:     /** @var bool */
129:     private $extends;
130: 
131:     /** @var string */
132:     private $uniq;
133: 
134:     /**#@+ @internal block type */
135:     const BLOCK_NAMED = 1;
136:     const BLOCK_CAPTURE = 2;
137:     const BLOCK_ANONYMOUS = 3;
138:     /**#@-*/
139: 
140: 
141: 
142:     /**
143:      * Constructor.
144:      */
145:     public function __construct()
146:     {
147:         $this->macros = self::$defaultMacros;
148:     }
149: 
150: 
151: 
152:     /**
153:      * Initializes parsing.
154:      * @param  NLatteFilter
155:      * @param  string
156:      * @return void
157:      */
158:     public function initialize($filter, & $s)
159:     {
160:         $this->filter = $filter;
161:         $this->blocks = array();
162:         $this->namedBlocks = array();
163:         $this->extends = NULL;
164:         $this->uniq = substr(md5(uniqid('', TRUE)), 0, 10);
165: 
166:         $filter->context = NLatteFilter::CONTEXT_TEXT;
167:         $filter->escape = 'NTemplateHelpers::escapeHtml';
168: 
169:         // remove comments
170:         $s = preg_replace('#\\{\\*.*?\\*\\}[\r\n]*#s', '', $s);
171: 
172:         // snippets support (temporary solution)
173:         $s = preg_replace(
174:             '#@(\\{[^}]+?\\})#s',
175:             '<?php } ?>$1<?php if (NSnippetHelper::$outputAllowed) { ?>',
176:             $s
177:         );
178:     }
179: 
180: 
181: 
182:     /**
183:      * Finishes parsing.
184:      * @param  string
185:      * @return void
186:      */
187:     public function finalize(& $s)
188:     {
189:         // blocks closing check
190:         if (count($this->blocks) === 1) { // auto-close last block
191:             $s .= $this->macro('/block', '', '');
192: 
193:         } elseif ($this->blocks) {
194:             throw new InvalidStateException("There are some unclosed blocks.");
195:         }
196: 
197:         // snippets support (temporary solution)
198:         $s = "<?php\nif (" . 'NSnippetHelper::$outputAllowed' . ") {\n?>$s<?php\n}\n?>";
199: 
200:         // extends support
201:         if ($this->namedBlocks || $this->extends) {
202:             $s = "<?php\n"
203:                 . 'if ($_cb->extends) { ob_start(); }' . "\n"
204:                 . '?>' . $s . "<?php\n"
205:                 . 'if ($_cb->extends) { ob_end_clean(); NLatteMacros::includeTemplate($_cb->extends, get_defined_vars(), $template)->render(); }' . "\n";
206:         }
207: 
208:         // named blocks
209:         if ($this->namedBlocks) {
210:             foreach (array_reverse($this->namedBlocks, TRUE) as $name => $foo) {
211:                 $name = preg_quote($name, '#');
212:                 $s = preg_replace_callback("#{block ($name)} \?>(.*)<\?php {/block $name}#sU", array($this, 'cbNamedBlocks'), $s);
213:             }
214:             $s = "<?php\n\n" . implode("\n\n\n", $this->namedBlocks) . "\n\n//\n// end of blocks\n//\n?>" . $s;
215:         }
216: 
217:         // internal state holder
218:         $s = "<?php\n"
219:             . '$_cb = NLatteMacros::initRuntime($template, ' . var_export($this->extends, TRUE) . ', ' . var_export($this->uniq, TRUE) . "); unset(\$_extends);\n"
220:             . '?>' . $s;
221:     }
222: 
223: 
224: 
225:     /**
226:      * Process {macro content | modifiers}
227:      * @param  string
228:      * @param  string
229:      * @param  string
230:      * @return string
231:      */
232:     public function macro($macro, $content, $modifiers)
233:     {
234:         if ($macro === '') {
235:             $macro = substr($content, 0, 2);
236:             if (!isset($this->macros[$macro])) {
237:                 $macro = substr($content, 0, 1);
238:                 if (!isset($this->macros[$macro])) {
239:                     return NULL;
240:                 }
241:             }
242:             $content = substr($content, strlen($macro));
243: 
244:         } elseif (!isset($this->macros[$macro])) {
245:             return NULL;
246:         }
247:         $this->current = array($content, $modifiers);
248:         return preg_replace_callback('#%(.*?)%#', array($this, 'cbMacro'), $this->macros[$macro]);
249:     }
250: 
251: 
252: 
253:     /**
254:      * Callback for self::macro().
255:      */
256:     private function cbMacro($m)
257:     {
258:         list($content, $modifiers) = $this->current;
259:         if ($m[1]) {
260:             return callback($m[1][0] === ':' ? array($this, substr($m[1], 1)) : $m[1])
261:                 ->invoke($content, $modifiers);
262:         } else {
263:             return $content;
264:         }
265:     }
266: 
267: 
268: 
269:     /**
270:      * Process <n:tag attr> (experimental).
271:      * @param  string
272:      * @param  array
273:      * @param  bool
274:      * @return string
275:      */
276:     public function tagMacro($name, $attrs, $closing)
277:     {
278:         $knownTags = array(
279:             'include' => 'block',
280:             'for' => 'each',
281:             'block' => 'name',
282:             'if' => 'cond',
283:             'elseif' => 'cond',
284:         );
285:         return $this->macro(
286:             $closing ? "/$name" : $name,
287:             isset($knownTags[$name], $attrs[$knownTags[$name]]) ? $attrs[$knownTags[$name]] : substr(var_export($attrs, TRUE), 8, -1),
288:             isset($attrs['modifiers']) ? $attrs['modifiers'] : ''
289:         );
290:     }
291: 
292: 
293: 
294:     /**
295:      * Process <tag n:attr> (experimental).
296:      * @param  string
297:      * @param  array
298:      * @param  bool
299:      * @return string
300:      */
301:     public function attrsMacro($code, $attrs, $closing)
302:     {
303:         $left = $right = '';
304:         foreach ($this->macros as $name => $foo) {
305:             if (!isset($this->macros["/$name"])) { // must be pair-macro
306:                 continue;
307:             }
308: 
309:             $macro = $closing ? "/$name" : $name;
310:             if (isset($attrs[$name])) {
311:                 if ($closing) {
312:                     $right .= $this->macro($macro, '', '');
313:                 } else {
314:                     $left = $this->macro($macro, $attrs[$name], '') . $left;
315:                 }
316:             }
317: 
318:             $innerName = "inner-$name";
319:             if (isset($attrs[$innerName])) {
320:                 if ($closing) {
321:                     $left .= $this->macro($macro, '', '');
322:                 } else {
323:                     $right = $this->macro($macro, $attrs[$innerName], '') . $right;
324:                 }
325:             }
326: 
327:             $tagName = "tag-$name";
328:             if (isset($attrs[$tagName])) {
329:                 $left = $this->macro($name, $attrs[$tagName], '') . $left;
330:                 $right .= $this->macro("/$name", '', '');
331:             }
332: 
333:             unset($attrs[$name], $attrs[$innerName], $attrs[$tagName]);
334:         }
335: 
336:         return $attrs ? NULL : $left . $code . $right;
337:     }
338: 
339: 
340: 
341:     /********************* macros ****************d*g**/
342: 
343: 
344: 
345:     /**
346:      * {$var |modifiers}
347:      */
348:     public function macroVar($var, $modifiers)
349:     {
350:         return NLatteFilter::formatModifiers('$' . $var, $modifiers);
351:     }
352: 
353: 
354: 
355:     /**
356:      * {_$var |modifiers}
357:      */
358:     public function macroTranslate($var, $modifiers)
359:     {
360:         return NLatteFilter::formatModifiers($var, 'translate|' . $modifiers);
361:     }
362: 
363: 
364: 
365:     /**
366:      * {syntax ...}
367:      */
368:     public function macroSyntax($var)
369:     {
370:         switch ($var) {
371:         case '':
372:         case 'latte':
373:             $this->filter->setDelimiters('\\{(?![\\s\'"{}])', '\\}'); // {...}
374:             break;
375: 
376:         case 'double':
377:             $this->filter->setDelimiters('\\{\\{(?![\\s\'"{}])', '\\}\\}'); // {{...}}
378:             break;
379: 
380:         case 'asp':
381:             $this->filter->setDelimiters('<%\s*', '\s*%>'); /* <%...%> */
382:             break;
383: 
384:         case 'python':
385:             $this->filter->setDelimiters('\\{[{%]\s*', '\s*[%}]\\}'); // {% ... %} | {{ ... }}
386:             break;
387: 
388:         case 'off':
389:             $this->filter->setDelimiters('[^\x00-\xFF]', '');
390:             break;
391: 
392:         default:
393:             throw new InvalidStateException("Unknown macro syntax '$var' on line {$this->filter->line}.");
394:         }
395:     }
396: 
397: 
398: 
399:     /**
400:      * {include ...}
401:      */
402:     public function macroInclude($content, $modifiers)
403:     {
404:         $destination = NLatteFilter::fetchToken($content); // destination [,] [params]
405:         $params = NLatteFilter::formatArray($content) . ($content ? ' + ' : '');
406: 
407:         if ($destination === NULL) {
408:             throw new InvalidStateException("Missing destination in {include} on line {$this->filter->line}.");
409: 
410:         } elseif ($destination[0] === '#') { // include #block
411:             $destination = ltrim($destination, '#');
412:             if (!preg_match('#^' . NLatteFilter::RE_IDENTIFIER . '$#', $destination)) {
413:                 throw new InvalidStateException("Included block name must be alphanumeric string, '$destination' given on line {$this->filter->line}.");
414:             }
415: 
416:             $parent = $destination === 'parent';
417:             if ($destination === 'parent' || $destination === 'this') {
418:                 $item = end($this->blocks);
419:                 while ($item && $item[0] !== self::BLOCK_NAMED) $item = prev($this->blocks);
420:                 if (!$item) {
421:                     throw new InvalidStateException("Cannot include $destination block outside of any block on line {$this->filter->line}.");
422:                 }
423:                 $destination = $item[1];
424:             }
425:             $name = var_export($destination, TRUE);
426:             $params .= 'get_defined_vars()';
427:             $cmd = isset($this->namedBlocks[$destination]) && !$parent
428:                 ? "call_user_func(reset(\$_cb->blocks[$name]), $params)"
429:                 : 'NLatteMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_cb->blocks, $name, $params)";
430:             return $modifiers
431:                 ? "ob_start(); $cmd; echo " . NLatteFilter::formatModifiers('ob_get_clean()', $modifiers)
432:                 : $cmd;
433: 
434:         } else { // include "file"
435:             $destination = NLatteFilter::formatString($destination);
436:             $params .= '$template->getParams()';
437:             return $modifiers
438:                 ? 'echo ' . NLatteFilter::formatModifiers('NLatteMacros::includeTemplate' . "($destination, $params, \$_cb->templates[" . var_export($this->uniq, TRUE) . '])->__toString(TRUE)', $modifiers)
439:                 : 'NLatteMacros::includeTemplate' . "($destination, $params, \$_cb->templates[" . var_export($this->uniq, TRUE) . '])->render()';
440:         }
441:     }
442: 
443: 
444: 
445:     /**
446:      * {extends ...}
447:      */
448:     public function macroExtends($content)
449:     {
450:         $destination = NLatteFilter::fetchToken($content); // destination
451:         if ($destination === NULL) {
452:             throw new InvalidStateException("Missing destination in {extends} on line {$this->filter->line}.");
453:         }
454:         if (!empty($this->blocks)) {
455:             throw new InvalidStateException("{extends} must be placed outside any block; on line {$this->filter->line}.");
456:         }
457:         if ($this->extends !== NULL) {
458:             throw new InvalidStateException("Multiple {extends} declarations are not allowed; on line {$this->filter->line}.");
459:         }
460:         $this->extends = $destination !== 'none';
461:         return $this->extends ? '$_cb->extends = ' . NLatteFilter::formatString($destination) : '';
462:     }
463: 
464: 
465: 
466:     /**
467:      * {block ...}
468:      */
469:     public function macroBlock($content, $modifiers)
470:     {
471:         if (substr($content, 0, 1) === '$') { // capture - back compatibility
472:             trigger_error("Capturing {block $content} is deprecated; use {capture $content} instead on line {$this->filter->line}.", E_USER_WARNING);
473:             return $this->macroCapture($content, $modifiers);
474:         }
475: 
476:         $name = NLatteFilter::fetchToken($content); // block [,] [params]
477: 
478:         if ($name === NULL) { // anonymous block
479:             $this->blocks[] = array(self::BLOCK_ANONYMOUS, NULL, $modifiers);
480:             return $modifiers === '' ? '' : 'ob_start()';
481: 
482:         } else { // #block
483:             $name = ltrim($name, '#');
484:             if (!preg_match('#^' . NLatteFilter::RE_IDENTIFIER . '$#', $name)) {
485:                 throw new InvalidStateException("Block name must be alphanumeric string, '$name' given on line {$this->filter->line}.");
486: 
487:             } elseif (isset($this->namedBlocks[$name])) {
488:                 throw new InvalidStateException("Cannot redeclare block '$name'; on line {$this->filter->line}.");
489:             }
490: 
491:             $top = empty($this->blocks);
492:             $this->namedBlocks[$name] = $name;
493:             $this->blocks[] = array(self::BLOCK_NAMED, $name, '');
494:             if (!$top) {
495:                 return $this->macroInclude('#' . $name, $modifiers) . "{block $name}";
496: 
497:             } elseif ($this->extends) {
498:                 return "{block $name}";
499: 
500:             } else {
501:                 return 'if (!$_cb->extends) { ' . $this->macroInclude('#' . $name, $modifiers) . "; } {block $name}";
502:             }
503:         }
504:     }
505: 
506: 
507: 
508:     /**
509:      * {/block}
510:      */
511:     public function macroBlockEnd($content)
512:     {
513:         list($type, $name, $modifiers) = array_pop($this->blocks);
514: 
515:         if ($type === self::BLOCK_CAPTURE) { // capture - back compatibility
516:             $this->blocks[] = array($type, $name, $modifiers);
517:             return $this->macroCaptureEnd($content);
518:         }
519: 
520:         if (($type !== self::BLOCK_NAMED && $type !== self::BLOCK_ANONYMOUS) || ($content && $content !== $name)) {
521:             throw new InvalidStateException("Tag {/block $content} was not expected here on line {$this->filter->line}.");
522: 
523:         } elseif ($type === self::BLOCK_NAMED) { // block
524:             return "{/block $name}";
525: 
526:         } else { // anonymous block
527:             return $modifiers === '' ? '' : 'echo ' . NLatteFilter::formatModifiers('ob_get_clean()', $modifiers);
528:         }
529:     }
530: 
531: 
532: 
533:     /**
534:      * {snippet ...}
535:      */
536:     public function macroSnippet($content)
537:     {
538:         $args = array('');
539:         if ($snippet = NLatteFilter::fetchToken($content)) {  // [name [,]] [tag]
540:             $args[] = NLatteFilter::formatString($snippet);
541:         }
542:         if ($content) {
543:             $args[] = NLatteFilter::formatString($content);
544:         }
545:         return '} if ($_cb->foo = NSnippetHelper::create($control' . implode(', ', $args) . ')) { $_cb->snippets[] = $_cb->foo';
546:     }
547: 
548: 
549: 
550:     /**
551:      * {snippet ...}
552:      */
553:     public function macroSnippetEnd($content)
554:     {
555:         return 'array_pop($_cb->snippets)->finish(); } if (NSnippetHelper::$outputAllowed) {';
556:     }
557: 
558: 
559: 
560:     /**
561:      * {capture ...}
562:      */
563:     public function macroCapture($content, $modifiers)
564:     {
565:         $name = NLatteFilter::fetchToken($content); // $variable
566: 
567:         if (substr($name, 0, 1) !== '$') {
568:             throw new InvalidStateException("Invalid capture block parameter '$name' on line {$this->filter->line}.");
569:         }
570: 
571:         $this->blocks[] = array(self::BLOCK_CAPTURE, $name, $modifiers);
572:         return 'ob_start()';
573:     }
574: 
575: 
576: 
577:     /**
578:      * {/capture}
579:      */
580:     public function macroCaptureEnd($content)
581:     {
582:         list($type, $name, $modifiers) = array_pop($this->blocks);
583: 
584:         if ($type !== self::BLOCK_CAPTURE || ($content && $content !== $name)) {
585:             throw new InvalidStateException("Tag {/capture $content} was not expected here on line {$this->filter->line}.");
586:         }
587: 
588:         return $name . '=' . NLatteFilter::formatModifiers('ob_get_clean()', $modifiers);
589:     }
590: 
591: 
592: 
593:     /**
594:      * Converts {block named}...{/block} to functions.
595:      */
596:     private function cbNamedBlocks($matches)
597:     {
598:         list(, $name, $content) = $matches;
599:         $func = '_cbb' . substr(md5($this->uniq . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
600:         $this->namedBlocks[$name] = "//\n// block $name\n//\n"
601:             . "if (!function_exists(\$_cb->blocks[" . var_export($name, TRUE) . "][] = '$func')) { function $func(\$_args) { "
602:             . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v') // PHP bug #46873
603:             . "\n?>$content<?php\n}}";
604:         return '';
605:     }
606: 
607: 
608: 
609:     /**
610:      * {foreach ...}
611:      */
612:     public function macroForeach($content)
613:     {
614:         return '$iterator = $_cb->its[] = new NSmartCachingIterator(' . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $content, 1);
615:     }
616: 
617: 
618: 
619:     /**
620:      * {attr ...}
621:      */
622:     public function macroAttr($content)
623:     {
624:         return preg_replace('#\)\s+#', ')->', $content . ' ');
625:     }
626: 
627: 
628: 
629:     /**
630:      * {contentType ...}
631:      */
632:     public function macroContentType($content)
633:     {
634:         if (strpos($content, 'html') !== FALSE) {
635:             $this->filter->escape = 'NTemplateHelpers::escapeHtml';
636:             $this->filter->context = NLatteFilter::CONTEXT_TEXT;
637: 
638:         } elseif (strpos($content, 'xml') !== FALSE) {
639:             $this->filter->escape = 'NTemplateHelpers::escapeXml';
640:             $this->filter->context = NLatteFilter::CONTEXT_NONE;
641: 
642:         } elseif (strpos($content, 'javascript') !== FALSE) {
643:             $this->filter->escape = 'NTemplateHelpers::escapeJs';
644:             $this->filter->context = NLatteFilter::CONTEXT_NONE;
645: 
646:         } elseif (strpos($content, 'css') !== FALSE) {
647:             $this->filter->escape = 'NTemplateHelpers::escapeCss';
648:             $this->filter->context = NLatteFilter::CONTEXT_NONE;
649: 
650:         } elseif (strpos($content, 'plain') !== FALSE) {
651:             $this->filter->escape = '';
652:             $this->filter->context = NLatteFilter::CONTEXT_NONE;
653: 
654:         } else {
655:             $this->filter->escape = '$template->escape';
656:             $this->filter->context = NLatteFilter::CONTEXT_NONE;
657:         }
658: 
659:         // temporary solution
660:         return strpos($content, '/') ? 'NEnvironment::getHttpResponse()->setHeader("Content-Type", "' . $content . '")' : '';
661:     }
662: 
663: 
664: 
665:     /**
666:      * {dump ...}
667:      */
668:     public function macroDump($content)
669:     {
670:         return $content ? "array(" . var_export($content, TRUE) . " => $content)" : 'get_defined_vars()';
671:     }
672: 
673: 
674: 
675:     /**
676:      * {widget ...}
677:      */
678:     public function macroWidget($content)
679:     {
680:         $pair = NLatteFilter::fetchToken($content); // widget[:method]
681:         if ($pair === NULL) {
682:             throw new InvalidStateException("Missing widget name in {widget} on line {$this->filter->line}.");
683:         }
684:         $pair = explode(':', $pair, 2);
685:         $widget = NLatteFilter::formatString($pair[0]);
686:         $method = isset($pair[1]) ? ucfirst($pair[1]) : '';
687:         $method = preg_match('#^(' . NLatteFilter::RE_IDENTIFIER . '|)$#', $method) ? "render$method" : "{\"render$method\"}";
688:         $param = NLatteFilter::formatArray($content);
689:         if (strpos($content, '=>') === FALSE) $param = substr($param, 6, -1); // removes array()
690:         return ($widget[0] === '$' ? "if (is_object($widget)) {$widget}->$method($param); else " : '')
691:             . "\$control->getWidget($widget)->$method($param)";
692:     }
693: 
694: 
695: 
696:     /**
697:      * {link ...}
698:      */
699:     public function macroLink($content, $modifiers)
700:     {
701:         return NLatteFilter::formatModifiers('$control->link(' . $this->formatLink($content) .')', $modifiers);
702:     }
703: 
704: 
705: 
706:     /**
707:      * {plink ...}
708:      */
709:     public function macroPlink($content, $modifiers)
710:     {
711:         return NLatteFilter::formatModifiers('$presenter->link(' . $this->formatLink($content) .')', $modifiers);
712:     }
713: 
714: 
715: 
716:     /**
717:      * {ifCurrent ...}
718:      */
719:     public function macroIfCurrent($content)
720:     {
721:         return $content ? 'try { $presenter->link(' . $this->formatLink($content) . '); } catch (NInvalidLinkException $e) {}' : '';
722:     }
723: 
724: 
725: 
726:     /**
727:      * Formats {*link ...} parameters.
728:      */
729:     private function formatLink($content)
730:     {
731:         return NLatteFilter::formatString(NLatteFilter::fetchToken($content)) . NLatteFilter::formatArray($content, ', '); // destination [,] args
732:     }
733: 
734: 
735: 
736:     /**
737:      * {assign ...}
738:      */
739:     public function macroAssign($content, $modifiers)
740:     {
741:         if (!$content) {
742:             throw new InvalidStateException("Missing arguments in {var} or {assign} on line {$this->filter->line}.");
743:         }
744:         if (strpos($content, '=>') === FALSE) { // back compatibility
745:             return '$' . ltrim(NLatteFilter::fetchToken($content), '$') . ' = ' . NLatteFilter::formatModifiers($content === '' ? 'NULL' : $content, $modifiers);
746:         }
747:         return 'extract(' . NLatteFilter::formatArray($content) . ')';
748:     }
749: 
750: 
751: 
752:     /**
753:      * {default ...}
754:      */
755:     public function macroDefault($content)
756:     {
757:         if (!$content) {
758:             throw new InvalidStateException("Missing arguments in {default} on line {$this->filter->line}.");
759:         }
760:         return 'extract(' . NLatteFilter::formatArray($content) . ', EXTR_SKIP)';
761:     }
762: 
763: 
764: 
765:     /**
766:      * Escaping helper.
767:      */
768:     public function macroEscape($content)
769:     {
770:         return $this->filter->escape;
771:     }
772: 
773: 
774: 
775:     /**
776:      * Just modifiers helper.
777:      */
778:     public function macroModifiers($content, $modifiers)
779:     {
780:         return NLatteFilter::formatModifiers($content, $modifiers);
781:     }
782: 
783: 
784: 
785:     /********************* run-time helpers ****************d*g**/
786: 
787: 
788: 
789:     /**
790:      * Calls block.
791:      * @param  array
792:      * @param  string
793:      * @param  array
794:      * @return void
795:      */
796:     public static function callBlock(& $blocks, $name, $params)
797:     {
798:         if (empty($blocks[$name])) {
799:             throw new InvalidStateException("Call to undefined block '$name'.");
800:         }
801:         $block = reset($blocks[$name]);
802:         $block($params);
803:     }
804: 
805: 
806: 
807:     /**
808:      * Calls parent block.
809:      * @param  array
810:      * @param  string
811:      * @param  array
812:      * @return void
813:      */
814:     public static function callBlockParent(& $blocks, $name, $params)
815:     {
816:         if (empty($blocks[$name]) || ($block = next($blocks[$name])) === FALSE) {
817:             throw new InvalidStateException("Call to undefined parent block '$name'.");
818:         }
819:         $block($params);
820:     }
821: 
822: 
823: 
824:     /**
825:      * Includes subtemplate.
826:      * @param  mixed      included file name or template
827:      * @param  array      parameters
828:      * @param  ITemplate  current template
829:      * @return NTemplate
830:      */
831:     public static function includeTemplate($destination, $params, $template)
832:     {
833:         if ($destination instanceof ITemplate) {
834:             $tpl = $destination;
835: 
836:         } elseif ($destination == NULL) { // intentionally ==
837:             throw new InvalidArgumentException("Template file name was not specified.");
838: 
839:         } else {
840:             $tpl = clone $template;
841:             if ($template instanceof IFileTemplate) {
842:                 if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') {
843:                     $destination = dirname($template->getFile()) . '/' . $destination;
844:                 }
845:                 $tpl->setFile($destination);
846:             }
847:         }
848: 
849:         $tpl->setParams($params); // interface?
850:         return $tpl;
851:     }
852: 
853: 
854: 
855:     /**
856:      * Initializes state holder $_cb in template.
857:      * @param  ITemplate
858:      * @param  bool
859:      * @param  string
860:      * @return stdClass
861:      */
862:     public static function initRuntime($template, $extends, $realFile)
863:     {
864:         $cb = (object) NULL;
865: 
866:         // extends support
867:         if (isset($template->_cb)) {
868:             $cb->blocks = & $template->_cb->blocks;
869:             $cb->templates = & $template->_cb->templates;
870:         }
871:         $cb->templates[$realFile] = $template;
872:         $cb->extends = is_bool($extends) ? $extends : (empty($template->_extends) ? FALSE : $template->_extends);
873:         unset($template->_cb, $template->_extends);
874: 
875:         // cache support
876:         if (!empty($cb->caches)) {
877:             end($cb->caches)->addFile($template->getFile());
878:         }
879: 
880:         return $cb;
881:     }
882: 
883: }
884: 
Nette Framework 0.9.7 (for PHP 5.2) API documentation generated by ApiGen 2.3.0