PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/nette/nette/Nette/Latte/Macros/CoreMacros.php

https://bitbucket.org/iiic/iszp
PHP | 413 lines | 220 code | 91 blank | 102 comment | 36 complexity | d3f0f741efc6f89a8f49d7ddc2196219 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. *
  5. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.txt that was distributed with this source code.
  9. */
  10. namespace Nette\Latte\Macros;
  11. use Nette,
  12. Nette\Latte,
  13. Nette\Latte\CompileException,
  14. Nette\Latte\MacroNode,
  15. Nette\Latte\PhpWriter;
  16. /**
  17. * Basic macros for Latte.
  18. *
  19. * - {if ?} ... {elseif ?} ... {else} ... {/if}
  20. * - {ifset ?} ... {elseifset ?} ... {/ifset}
  21. * - {for ?} ... {/for}
  22. * - {foreach ?} ... {/foreach}
  23. * - {$variable} with escaping
  24. * - {!$variable} without escaping
  25. * - {=expression} echo with escaping
  26. * - {!=expression} echo without escaping
  27. * - {?expression} evaluate PHP statement
  28. * - {_expression} echo translation with escaping
  29. * - {!_expression} echo translation without escaping
  30. * - {attr ?} HTML element attributes
  31. * - {capture ?} ... {/capture} capture block to parameter
  32. * - {var var => value} set template parameter
  33. * - {default var => value} set default template parameter
  34. * - {dump $var}
  35. * - {debugbreak}
  36. * - {l} {r} to display { }
  37. *
  38. * @author David Grudl
  39. */
  40. class CoreMacros extends MacroSet
  41. {
  42. public static function install(Latte\Compiler $compiler)
  43. {
  44. $me = new static($compiler);
  45. $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf'));
  46. $me->addMacro('elseif', 'elseif (%node.args):');
  47. $me->addMacro('else', array($me, 'macroElse'));
  48. $me->addMacro('ifset', 'if (isset(%node.args)):', 'endif');
  49. $me->addMacro('elseifset', 'elseif (isset(%node.args)):');
  50. $me->addMacro('foreach', '', array($me, 'macroEndForeach'));
  51. $me->addMacro('for', 'for (%node.args):', 'endfor');
  52. $me->addMacro('while', 'while (%node.args):', 'endwhile');
  53. $me->addMacro('continueIf', 'if (%node.args) continue');
  54. $me->addMacro('breakIf', 'if (%node.args) break');
  55. $me->addMacro('first', 'if ($iterator->isFirst(%node.args)):', 'endif');
  56. $me->addMacro('last', 'if ($iterator->isLast(%node.args)):', 'endif');
  57. $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)):', 'endif');
  58. $me->addMacro('var', array($me, 'macroVar'));
  59. $me->addMacro('assign', array($me, 'macroVar')); // deprecated
  60. $me->addMacro('default', array($me, 'macroVar'));
  61. $me->addMacro('dump', array($me, 'macroDump'));
  62. $me->addMacro('debugbreak', array($me, 'macroDebugbreak'));
  63. $me->addMacro('l', '?>{<?php');
  64. $me->addMacro('r', '?>}<?php');
  65. $me->addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate'));
  66. $me->addMacro('=', array($me, 'macroExpr'));
  67. $me->addMacro('?', array($me, 'macroExpr'));
  68. $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd'));
  69. $me->addMacro('include', array($me, 'macroInclude'));
  70. $me->addMacro('use', array($me, 'macroUse'));
  71. $me->addMacro('class', NULL, NULL, array($me, 'macroClass'));
  72. $me->addMacro('attr', array($me, 'macroOldAttr'), '', array($me, 'macroAttr'));
  73. $me->addMacro('href', NULL); // TODO: placeholder
  74. }
  75. /**
  76. * Finishes template parsing.
  77. * @return array(prolog, epilog)
  78. */
  79. public function finalize()
  80. {
  81. return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, '
  82. . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')');
  83. }
  84. /********************* macros ****************d*g**/
  85. /**
  86. * {if ...}
  87. */
  88. public function macroIf(MacroNode $node, PhpWriter $writer)
  89. {
  90. if ($node->data->capture = ($node->args === '')) {
  91. return 'ob_start()';
  92. }
  93. if ($node->prefix === $node::PREFIX_TAG) {
  94. return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)):' : 'if ($_l->ifs[] = (%node.args)):');
  95. }
  96. return $writer->write('if (%node.args):');
  97. }
  98. /**
  99. * {/if ...}
  100. */
  101. public function macroEndIf(MacroNode $node, PhpWriter $writer)
  102. {
  103. if ($node->data->capture) {
  104. if ($node->args === '') {
  105. throw new CompileException('Missing condition in {if} macro.');
  106. }
  107. return $writer->write('if (%node.args) '
  108. . (isset($node->data->else) ? '{ ob_end_clean(); ob_end_flush(); }' : 'ob_end_flush();')
  109. . ' else '
  110. . (isset($node->data->else) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_else; }' : 'ob_end_clean();')
  111. );
  112. }
  113. return 'endif';
  114. }
  115. /**
  116. * {else}
  117. */
  118. public function macroElse(MacroNode $node, PhpWriter $writer)
  119. {
  120. $ifNode = $node->parentNode;
  121. if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
  122. if (isset($ifNode->data->else)) {
  123. throw new CompileException("Macro {if} supports only one {else}.");
  124. }
  125. $ifNode->data->else = TRUE;
  126. return 'ob_start()';
  127. }
  128. return 'else:';
  129. }
  130. /**
  131. * {_$var |modifiers}
  132. */
  133. public function macroTranslate(MacroNode $node, PhpWriter $writer)
  134. {
  135. if ($node->closing) {
  136. return $writer->write('echo %modify($template->translate(ob_get_clean()))');
  137. } elseif ($node->isEmpty = ($node->args !== '')) {
  138. return $writer->write('echo %modify($template->translate(%node.args))');
  139. } else {
  140. return 'ob_start()';
  141. }
  142. }
  143. /**
  144. * {include "file" [,] [params]}
  145. */
  146. public function macroInclude(MacroNode $node, PhpWriter $writer)
  147. {
  148. $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])',
  149. $this->getCompiler()->getTemplateId());
  150. if ($node->modifiers) {
  151. return $writer->write('echo %modify(%raw->__toString(TRUE))', $code);
  152. } else {
  153. return $code . '->render()';
  154. }
  155. }
  156. /**
  157. * {use class MacroSet}
  158. */
  159. public function macroUse(MacroNode $node, PhpWriter $writer)
  160. {
  161. Nette\Callback::create($node->tokenizer->fetchWord(), 'install')
  162. ->invoke($this->getCompiler())
  163. ->initialize();
  164. }
  165. /**
  166. * {capture $variable}
  167. */
  168. public function macroCapture(MacroNode $node, PhpWriter $writer)
  169. {
  170. $variable = $node->tokenizer->fetchWord();
  171. if (substr($variable, 0, 1) !== '$') {
  172. throw new CompileException("Invalid capture block variable '$variable'");
  173. }
  174. $node->data->variable = $variable;
  175. return 'ob_start()';
  176. }
  177. /**
  178. * {/capture}
  179. */
  180. public function macroCaptureEnd(MacroNode $node, PhpWriter $writer)
  181. {
  182. return $node->data->variable . $writer->write(" = %modify(ob_get_clean())");
  183. }
  184. /**
  185. * {foreach ...}
  186. */
  187. public function macroEndForeach(MacroNode $node, PhpWriter $writer)
  188. {
  189. if (preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) {
  190. $node->openingCode = '<?php $iterations = 0; foreach ($iterator = $_l->its[] = new Nette\Iterators\CachingIterator('
  191. . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . '): ?>';
  192. $node->closingCode = '<?php $iterations++; endforeach; array_pop($_l->its); $iterator = end($_l->its) ?>';
  193. } else {
  194. $node->openingCode = '<?php $iterations = 0; foreach (' . $writer->formatArgs() . '): ?>';
  195. $node->closingCode = '<?php $iterations++; endforeach ?>';
  196. }
  197. }
  198. /**
  199. * n:class="..."
  200. */
  201. public function macroClass(MacroNode $node, PhpWriter $writer)
  202. {
  203. return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\' . %escape(implode(" ", array_unique($_l->tmp))) . \'"\'');
  204. }
  205. /**
  206. * n:attr="..."
  207. */
  208. public function macroAttr(MacroNode $node, PhpWriter $writer)
  209. {
  210. return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()');
  211. }
  212. /**
  213. * {attr ...}
  214. * @deprecated
  215. */
  216. public function macroOldAttr(MacroNode $node)
  217. {
  218. return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->');
  219. }
  220. /**
  221. * {dump ...}
  222. */
  223. public function macroDump(MacroNode $node, PhpWriter $writer)
  224. {
  225. $args = $writer->formatArgs();
  226. return 'Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(" . $writer->write('%var', $args) . " => $args)" : 'get_defined_vars()')
  227. . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))';
  228. }
  229. /**
  230. * {debugbreak ...}
  231. */
  232. public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
  233. {
  234. return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
  235. . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
  236. }
  237. /**
  238. * {var ...}
  239. * {default ...}
  240. */
  241. public function macroVar(MacroNode $node, PhpWriter $writer)
  242. {
  243. $out = '';
  244. $var = TRUE;
  245. $tokenizer = $writer->preprocess();
  246. while ($token = $tokenizer->fetchToken()) {
  247. if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) {
  248. if ($node->name === 'default') {
  249. $out .= "'" . ltrim($token['value'], "$") . "'";
  250. } else {
  251. $out .= '$' . ltrim($token['value'], "$");
  252. }
  253. $var = NULL;
  254. } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) {
  255. $out .= $node->name === 'default' ? '=>' : '=';
  256. $var = FALSE;
  257. } elseif ($token['value'] === ',' && $token['depth'] === 0) {
  258. $out .= $node->name === 'default' ? ',' : ';';
  259. $var = TRUE;
  260. } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) {
  261. throw new CompileException("Unexpected '$token[value]' in {default $node->args}");
  262. } else {
  263. $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
  264. }
  265. }
  266. return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
  267. }
  268. /**
  269. * {= ...}
  270. * {? ...}
  271. */
  272. public function macroExpr(MacroNode $node, PhpWriter $writer)
  273. {
  274. return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)');
  275. }
  276. /********************* run-time helpers ****************d*g**/
  277. /**
  278. * Includes subtemplate.
  279. * @param mixed included file name or template
  280. * @param array parameters
  281. * @param Nette\Templating\ITemplate current template
  282. * @return Nette\Templating\Template
  283. */
  284. public static function includeTemplate($destination, array $params, Nette\Templating\ITemplate $template)
  285. {
  286. if ($destination instanceof Nette\Templating\ITemplate) {
  287. $tpl = $destination;
  288. } elseif ($destination == NULL) { // intentionally ==
  289. throw new Nette\InvalidArgumentException("Template file name was not specified.");
  290. } elseif ($template instanceof Nette\Templating\IFileTemplate) {
  291. if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') {
  292. $destination = dirname($template->getFile()) . '/' . $destination;
  293. }
  294. $tpl = clone $template;
  295. $tpl->setFile($destination);
  296. } else {
  297. throw new Nette\NotSupportedException('Macro {include "filename"} is supported only with Nette\Templating\IFileTemplate.');
  298. }
  299. $tpl->setParameters($params); // interface?
  300. return $tpl;
  301. }
  302. /**
  303. * Initializes local & global storage in template.
  304. * @return \stdClass
  305. */
  306. public static function initRuntime(Nette\Templating\ITemplate $template, $templateId)
  307. {
  308. // local storage
  309. if (isset($template->_l)) {
  310. $local = $template->_l;
  311. unset($template->_l);
  312. } else {
  313. $local = new \stdClass;
  314. }
  315. $local->templates[$templateId] = $template;
  316. // global storage
  317. if (!isset($template->_g)) {
  318. $template->_g = new \stdClass;
  319. }
  320. return array($local, $template->_g);
  321. }
  322. }