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