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