1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
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: class NLatteMacros extends NObject
49: {
50:
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% ?>',
109: '$' => '<?php echo %:macroEscape%(%:macroVar%) ?>',
110: '?' => '<?php %:macroModifiers% ?>',
111: );
112:
113:
114: public $macros;
115:
116:
117: private $filter;
118:
119:
120: private $current;
121:
122:
123: private $blocks = array();
124:
125:
126: private $namedBlocks = array();
127:
128:
129: private $extends;
130:
131:
132: private $uniq;
133:
134:
135: const BLOCK_NAMED = 1;
136: const BLOCK_CAPTURE = 2;
137: const BLOCK_ANONYMOUS = 3;
138:
139:
140:
141:
142: 143: 144:
145: public function __construct()
146: {
147: $this->macros = self::$defaultMacros;
148: }
149:
150:
151:
152: 153: 154: 155: 156: 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:
170: $s = preg_replace('#\\{\\*.*?\\*\\}[\r\n]*#s', '', $s);
171:
172:
173: $s = preg_replace(
174: '#@(\\{[^}]+?\\})#s',
175: '<?php } ?>$1<?php if (NSnippetHelper::$outputAllowed) { ?>',
176: $s
177: );
178: }
179:
180:
181:
182: 183: 184: 185: 186:
187: public function finalize(& $s)
188: {
189:
190: if (count($this->blocks) === 1) {
191: $s .= $this->macro('/block', '', '');
192:
193: } elseif ($this->blocks) {
194: throw new InvalidStateException("There are some unclosed blocks.");
195: }
196:
197:
198: $s = "<?php\nif (" . 'NSnippetHelper::$outputAllowed' . ") {\n?>$s<?php\n}\n?>";
199:
200:
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:
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:
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: 227: 228: 229: 230: 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: 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: 271: 272: 273: 274: 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: 296: 297: 298: 299: 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"])) {
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:
342:
343:
344:
345: 346: 347:
348: public function macroVar($var, $modifiers)
349: {
350: return NLatteFilter::formatModifiers('$' . $var, $modifiers);
351: }
352:
353:
354:
355: 356: 357:
358: public function macroTranslate($var, $modifiers)
359: {
360: return NLatteFilter::formatModifiers($var, 'translate|' . $modifiers);
361: }
362:
363:
364:
365: 366: 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: 401:
402: public function macroInclude($content, $modifiers)
403: {
404: $destination = NLatteFilter::fetchToken($content);
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] === '#') {
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 {
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: 447:
448: public function macroExtends($content)
449: {
450: $destination = NLatteFilter::fetchToken($content);
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: 468:
469: public function macroBlock($content, $modifiers)
470: {
471: if (substr($content, 0, 1) === '$') {
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);
477:
478: if ($name === NULL) {
479: $this->blocks[] = array(self::BLOCK_ANONYMOUS, NULL, $modifiers);
480: return $modifiers === '' ? '' : 'ob_start()';
481:
482: } else {
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: 510:
511: public function macroBlockEnd($content)
512: {
513: list($type, $name, $modifiers) = array_pop($this->blocks);
514:
515: if ($type === self::BLOCK_CAPTURE) {
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) {
524: return "{/block $name}";
525:
526: } else {
527: return $modifiers === '' ? '' : 'echo ' . NLatteFilter::formatModifiers('ob_get_clean()', $modifiers);
528: }
529: }
530:
531:
532:
533: 534: 535:
536: public function macroSnippet($content)
537: {
538: $args = array('');
539: if ($snippet = NLatteFilter::fetchToken($content)) {
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: 552:
553: public function macroSnippetEnd($content)
554: {
555: return 'array_pop($_cb->snippets)->finish(); } if (NSnippetHelper::$outputAllowed) {';
556: }
557:
558:
559:
560: 561: 562:
563: public function macroCapture($content, $modifiers)
564: {
565: $name = NLatteFilter::fetchToken($content);
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: 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: 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')
603: . "\n?>$content<?php\n}}";
604: return '';
605: }
606:
607:
608:
609: 610: 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: 621:
622: public function macroAttr($content)
623: {
624: return preg_replace('#\)\s+#', ')->', $content . ' ');
625: }
626:
627:
628:
629: 630: 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:
660: return strpos($content, '/') ? 'NEnvironment::getHttpResponse()->setHeader("Content-Type", "' . $content . '")' : '';
661: }
662:
663:
664:
665: 666: 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: 677:
678: public function macroWidget($content)
679: {
680: $pair = NLatteFilter::fetchToken($content);
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);
690: return ($widget[0] === '$' ? "if (is_object($widget)) {$widget}->$method($param); else " : '')
691: . "\$control->getWidget($widget)->$method($param)";
692: }
693:
694:
695:
696: 697: 698:
699: public function macroLink($content, $modifiers)
700: {
701: return NLatteFilter::formatModifiers('$control->link(' . $this->formatLink($content) .')', $modifiers);
702: }
703:
704:
705:
706: 707: 708:
709: public function macroPlink($content, $modifiers)
710: {
711: return NLatteFilter::formatModifiers('$presenter->link(' . $this->formatLink($content) .')', $modifiers);
712: }
713:
714:
715:
716: 717: 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: 728:
729: private function formatLink($content)
730: {
731: return NLatteFilter::formatString(NLatteFilter::fetchToken($content)) . NLatteFilter::formatArray($content, ', ');
732: }
733:
734:
735:
736: 737: 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) {
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: 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: 767:
768: public function macroEscape($content)
769: {
770: return $this->filter->escape;
771: }
772:
773:
774:
775: 776: 777:
778: public function macroModifiers($content, $modifiers)
779: {
780: return NLatteFilter::formatModifiers($content, $modifiers);
781: }
782:
783:
784:
785:
786:
787:
788:
789: 790: 791: 792: 793: 794: 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: 809: 810: 811: 812: 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: 826: 827: 828: 829: 830:
831: public static function includeTemplate($destination, $params, $template)
832: {
833: if ($destination instanceof ITemplate) {
834: $tpl = $destination;
835:
836: } elseif ($destination == NULL) {
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);
850: return $tpl;
851: }
852:
853:
854:
855: 856: 857: 858: 859: 860: 861:
862: public static function initRuntime($template, $extends, $realFile)
863: {
864: $cb = (object) NULL;
865:
866:
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:
876: if (!empty($cb->caches)) {
877: end($cb->caches)->addFile($template->getFile());
878: }
879:
880: return $cb;
881: }
882:
883: }
884: