1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Templates;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:
50: class LatteMacros extends Nette\Object
51: {
52:
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% ?>',
111: '$' => '<?php echo %:macroEscape%(%:macroVar%) ?>',
112: '?' => '<?php %:macroModifiers% ?>',
113: );
114:
115:
116: public $macros;
117:
118:
119: private $filter;
120:
121:
122: private $current;
123:
124:
125: private $blocks = array();
126:
127:
128: private $namedBlocks = array();
129:
130:
131: private $extends;
132:
133:
134: private $uniq;
135:
136:
137: const BLOCK_NAMED = 1;
138: const BLOCK_CAPTURE = 2;
139: const BLOCK_ANONYMOUS = 3;
140:
141:
142:
143:
144: 145: 146:
147: public function __construct()
148: {
149: $this->macros = self::$defaultMacros;
150: }
151:
152:
153:
154: 155: 156: 157: 158: 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:
172: $s = preg_replace('#\\{\\*.*?\\*\\}[\r\n]*#s', '', $s);
173:
174:
175: $s = preg_replace(
176: '#@(\\{[^}]+?\\})#s',
177: '<?php } ?>$1<?php if (Nette\Templates\SnippetHelper::$outputAllowed) { ?>',
178: $s
179: );
180: }
181:
182:
183:
184: 185: 186: 187: 188:
189: public function finalize(& $s)
190: {
191:
192: if (count($this->blocks) === 1) {
193: $s .= $this->macro('/block', '', '');
194:
195: } elseif ($this->blocks) {
196: throw new \InvalidStateException("There are some unclosed blocks.");
197: }
198:
199:
200: $s = "<?php\nif (" . 'Nette\Templates\SnippetHelper::$outputAllowed' . ") {\n?>$s<?php\n}\n?>";
201:
202:
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:
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:
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: 229: 230: 231: 232: 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: 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: 273: 274: 275: 276: 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: 298: 299: 300: 301: 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"])) {
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:
344:
345:
346:
347: 348: 349:
350: public function macroVar($var, $modifiers)
351: {
352: return LatteFilter::formatModifiers('$' . $var, $modifiers);
353: }
354:
355:
356:
357: 358: 359:
360: public function macroTranslate($var, $modifiers)
361: {
362: return LatteFilter::formatModifiers($var, 'translate|' . $modifiers);
363: }
364:
365:
366:
367: 368: 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: 403:
404: public function macroInclude($content, $modifiers)
405: {
406: $destination = LatteFilter::fetchToken($content);
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] === '#') {
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 {
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: 449:
450: public function macroExtends($content)
451: {
452: $destination = LatteFilter::fetchToken($content);
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: 470:
471: public function macroBlock($content, $modifiers)
472: {
473: if (substr($content, 0, 1) === '$') {
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);
479:
480: if ($name === NULL) {
481: $this->blocks[] = array(self::BLOCK_ANONYMOUS, NULL, $modifiers);
482: return $modifiers === '' ? '' : 'ob_start()';
483:
484: } else {
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: 512:
513: public function macroBlockEnd($content)
514: {
515: list($type, $name, $modifiers) = array_pop($this->blocks);
516:
517: if ($type === self::BLOCK_CAPTURE) {
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) {
526: return "{/block $name}";
527:
528: } else {
529: return $modifiers === '' ? '' : 'echo ' . LatteFilter::formatModifiers('ob_get_clean()', $modifiers);
530: }
531: }
532:
533:
534:
535: 536: 537:
538: public function macroSnippet($content)
539: {
540: $args = array('');
541: if ($snippet = LatteFilter::fetchToken($content)) {
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: 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: 564:
565: public function macroCapture($content, $modifiers)
566: {
567: $name = LatteFilter::fetchToken($content);
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: 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: 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')
605: . "\n?>$content<?php\n}}";
606: return '';
607: }
608:
609:
610:
611: 612: 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: 623:
624: public function macroAttr($content)
625: {
626: return preg_replace('#\)\s+#', ')->', $content . ' ');
627: }
628:
629:
630:
631: 632: 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:
662: return strpos($content, '/') ? 'Nette\Environment::getHttpResponse()->setHeader("Content-Type", "' . $content . '")' : '';
663: }
664:
665:
666:
667: 668: 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: 679:
680: public function macroWidget($content)
681: {
682: $pair = LatteFilter::fetchToken($content);
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);
692: return ($widget[0] === '$' ? "if (is_object($widget)) {$widget}->$method($param); else " : '')
693: . "\$control->getWidget($widget)->$method($param)";
694: }
695:
696:
697:
698: 699: 700:
701: public function macroLink($content, $modifiers)
702: {
703: return LatteFilter::formatModifiers('$control->link(' . $this->formatLink($content) .')', $modifiers);
704: }
705:
706:
707:
708: 709: 710:
711: public function macroPlink($content, $modifiers)
712: {
713: return LatteFilter::formatModifiers('$presenter->link(' . $this->formatLink($content) .')', $modifiers);
714: }
715:
716:
717:
718: 719: 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: 730:
731: private function formatLink($content)
732: {
733: return LatteFilter::formatString(LatteFilter::fetchToken($content)) . LatteFilter::formatArray($content, ', ');
734: }
735:
736:
737:
738: 739: 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) {
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: 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: 769:
770: public function macroEscape($content)
771: {
772: return $this->filter->escape;
773: }
774:
775:
776:
777: 778: 779:
780: public function macroModifiers($content, $modifiers)
781: {
782: return LatteFilter::formatModifiers($content, $modifiers);
783: }
784:
785:
786:
787:
788:
789:
790:
791: 792: 793: 794: 795: 796: 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: 811: 812: 813: 814: 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: 828: 829: 830: 831: 832:
833: public static function includeTemplate($destination, $params, $template)
834: {
835: if ($destination instanceof ITemplate) {
836: $tpl = $destination;
837:
838: } elseif ($destination == NULL) {
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);
852: return $tpl;
853: }
854:
855:
856:
857: 858: 859: 860: 861: 862: 863:
864: public static function initRuntime($template, $extends, $realFile)
865: {
866: $cb = (object) NULL;
867:
868:
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:
878: if (!empty($cb->caches)) {
879: end($cb->caches)->addFile($template->getFile());
880: }
881:
882: return $cb;
883: }
884:
885: }
886: