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