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