PageRenderTime 60ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/Nette/Templates/Filters/LatteMacros.php

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