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