1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Latte\Macros;
13:
14: use Nette,
15: Nette\Latte,
16: Nette\Latte\ParseException,
17: Nette\Latte\MacroNode;
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: class CoreMacros extends MacroSet
46: {
47:
48:
49: public static function install(Latte\Compiler $compiler)
50: {
51: $me = new static($compiler);
52:
53: $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf'));
54: $me->addMacro('elseif', 'elseif (%node.args):');
55: $me->addMacro('else', array($me, 'macroElse'));
56: $me->addMacro('ifset', 'if (isset(%node.args)):', 'endif');
57: $me->addMacro('elseifset', 'elseif (isset(%node.args)):');
58:
59: $me->addMacro('foreach', array($me, 'macroForeach'), '$iterations++; endforeach; array_pop($_l->its); $iterator = end($_l->its)');
60: $me->addMacro('for', 'for (%node.args):', 'endfor');
61: $me->addMacro('while', 'while (%node.args):', 'endwhile');
62: $me->addMacro('continueIf', 'if (%node.args) continue');
63: $me->addMacro('breakIf', 'if (%node.args) break');
64: $me->addMacro('first', 'if ($iterator->isFirst(%node.args)):', 'endif');
65: $me->addMacro('last', 'if ($iterator->isLast(%node.args)):', 'endif');
66: $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)):', 'endif');
67:
68: $me->addMacro('var', array($me, 'macroVar'));
69: $me->addMacro('assign', array($me, 'macroVar'));
70: $me->addMacro('default', array($me, 'macroVar'));
71: $me->addMacro('dump', array($me, 'macroDump'));
72: $me->addMacro('debugbreak', array($me, 'macroDebugbreak'));
73: $me->addMacro('l', '?>{<?php');
74: $me->addMacro('r', '?>}<?php');
75:
76: $me->addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate'));
77: $me->addMacro('=', array($me, 'macroExpr'));
78: $me->addMacro('?', array($me, 'macroExpr'));
79:
80: $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd'));
81: $me->addMacro('include', array($me, 'macroInclude'));
82: $me->addMacro('use', array($me, 'macroUse'));
83:
84: $me->addMacro('@href', NULL, NULL);
85: $me->addMacro('@class', array($me, 'macroClass'));
86: $me->addMacro('@attr', array($me, 'macroAttr'));
87: $me->addMacro('attr', array($me, 'macroOldAttr'));
88: }
89:
90:
91:
92: 93: 94: 95:
96: public function finalize()
97: {
98: return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, '
99: . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')');
100: }
101:
102:
103:
104:
105:
106:
107:
108: 109: 110:
111: public function macroIf(MacroNode $node, $writer)
112: {
113: if ($node->data->capture = ($node->args === '')) {
114: return 'ob_start()';
115: }
116: return $writer->write('if (%node.args):');
117: }
118:
119:
120:
121: 122: 123:
124: public function macroEndIf(MacroNode $node, $writer)
125: {
126: if ($node->data->capture) {
127: if ($node->args === '') {
128: throw new ParseException('Missing condition in {if} macro.');
129: }
130: return $writer->write('if (%node.args) '
131: . (isset($node->data->else) ? '{ ob_end_clean(); ob_end_flush(); }' : 'ob_end_flush();')
132: . ' else '
133: . (isset($node->data->else) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_else; }' : 'ob_end_clean();')
134: );
135: }
136: return 'endif';
137: }
138:
139:
140:
141: 142: 143:
144: public function macroElse(MacroNode $node, $writer)
145: {
146: $ifNode = $node->parentNode;
147: if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
148: if (isset($ifNode->data->else)) {
149: throw new ParseException("Macro {if} supports only one {else}.");
150: }
151: $ifNode->data->else = TRUE;
152: return 'ob_start()';
153: }
154: return 'else:';
155: }
156:
157:
158:
159: 160: 161:
162: public function macroTranslate(MacroNode $node, $writer)
163: {
164: if ($node->closing) {
165: return $writer->write('echo %modify($template->translate(ob_get_clean()))');
166:
167: } elseif ($node->isEmpty = ($node->args !== '')) {
168: return $writer->write('echo %modify($template->translate(%node.args))');
169:
170: } else {
171: return 'ob_start()';
172: }
173: }
174:
175:
176:
177: 178: 179:
180: public function macroInclude(MacroNode $node, $writer)
181: {
182: $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])',
183: $this->getCompiler()->getTemplateId());
184:
185: if ($node->modifiers) {
186: return $writer->write('echo %modify(%raw->__toString(TRUE))', $code);
187: } else {
188: return $code . '->render()';
189: }
190: }
191:
192:
193:
194: 195: 196:
197: public function macroUse(MacroNode $node, $writer)
198: {
199: call_user_func(array($node->tokenizer->fetchWord(), 'install'), $this->getCompiler())
200: ->initialize();
201: }
202:
203:
204:
205: 206: 207:
208: public function macroCapture(MacroNode $node, $writer)
209: {
210: $variable = $node->tokenizer->fetchWord();
211: if (substr($variable, 0, 1) !== '$') {
212: throw new ParseException("Invalid capture block variable '$variable'");
213: }
214: $node->data->variable = $variable;
215: return 'ob_start()';
216: }
217:
218:
219:
220: 221: 222:
223: public function macroCaptureEnd(MacroNode $node, $writer)
224: {
225: return $writer->write("{$node->data->variable} = %modify(ob_get_clean())");
226: }
227:
228:
229:
230: 231: 232:
233: public function macroForeach(MacroNode $node, $writer)
234: {
235: return '$iterations = 0; foreach ($iterator = $_l->its[] = new Nette\Iterators\CachingIterator('
236: . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . '):';
237: }
238:
239:
240:
241: 242: 243:
244: public function macroClass(MacroNode $node, $writer)
245: {
246: return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\' . %escape(implode(" ", array_unique($_l->tmp))) . \'"\'');
247: }
248:
249:
250:
251: 252: 253:
254: public function macroAttr(MacroNode $node, $writer)
255: {
256: return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()');
257: }
258:
259:
260:
261: 262: 263: 264:
265: public function macroOldAttr(MacroNode $node)
266: {
267: return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->');
268: }
269:
270:
271:
272: 273: 274:
275: public function macroDump(MacroNode $node, $writer)
276: {
277: $args = $writer->formatArgs();
278: return $writer->write('Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(%var => $args)" : 'get_defined_vars()')
279: . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))', $args);
280: }
281:
282:
283:
284: 285: 286:
287: public function macroDebugbreak(MacroNode $node, $writer)
288: {
289: return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
290: . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
291: }
292:
293:
294:
295: 296: 297: 298:
299: public function macroVar(MacroNode $node, $writer)
300: {
301: $out = '';
302: $var = TRUE;
303: $tokenizer = $writer->preprocess();
304: while ($token = $tokenizer->fetchToken()) {
305: if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) {
306: if ($node->name === 'default') {
307: $out .= "'" . ltrim($token['value'], "$") . "'";
308: } else {
309: $out .= '$' . ltrim($token['value'], "$");
310: }
311: $var = NULL;
312:
313: } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) {
314: $out .= $node->name === 'default' ? '=>' : '=';
315: $var = FALSE;
316:
317: } elseif ($token['value'] === ',' && $token['depth'] === 0) {
318: $out .= $node->name === 'default' ? ',' : ';';
319: $var = TRUE;
320:
321: } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) {
322: throw new ParseException("Unexpected '$token[value]' in {default $node->args}");
323:
324: } else {
325: $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
326: }
327: }
328: return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
329: }
330:
331:
332:
333: 334: 335: 336:
337: public function macroExpr(MacroNode $node, $writer)
338: {
339: return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)');
340: }
341:
342:
343:
344:
345:
346:
347:
348: 349: 350: 351: 352: 353: 354:
355: public static function includeTemplate($destination, $params, $template)
356: {
357: if ($destination instanceof Nette\Templating\ITemplate) {
358: $tpl = $destination;
359:
360: } elseif ($destination == NULL) {
361: throw new Nette\InvalidArgumentException("Template file name was not specified.");
362:
363: } else {
364: $tpl = clone $template;
365: if ($template instanceof Nette\Templating\IFileTemplate) {
366: if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') {
367: $destination = dirname($template->getFile()) . '/' . $destination;
368: }
369: $tpl->setFile($destination);
370: }
371: }
372:
373: $tpl->setParameters($params);
374: return $tpl;
375: }
376:
377:
378:
379: 380: 381: 382: 383: 384:
385: public static function initRuntime($template, $templateId)
386: {
387:
388: if (isset($template->_l)) {
389: $local = $template->_l;
390: unset($template->_l);
391: } else {
392: $local = (object) NULL;
393: }
394: $local->templates[$templateId] = $template;
395:
396:
397: if (!isset($template->_g)) {
398: $template->_g = (object) NULL;
399: }
400:
401: return array($local, $template->_g);
402: }
403:
404: }
405: