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