PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Latte/Macros/CoreMacros.php

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