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