Namespaces

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

Classes

  • BaseTemplate
  • CachingHelper
  • CurlyBracketsMacros
  • LatteFilter
  • LatteMacros
  • SnippetHelper
  • Template
  • TemplateCacheStorage
  • TemplateFilters
  • TemplateHelpers

Interfaces

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