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