1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Latte\Macros;
9:
10: use Latte;
11: use Latte\CompileException;
12: use Latte\MacroNode;
13: use Latte\PhpWriter;
14:
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:
37: class CoreMacros extends MacroSet
38: {
39:
40:
41: public static function install(Latte\Compiler $compiler)
42: {
43: $me = new static($compiler);
44:
45: $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf'));
46: $me->addMacro('elseif', '} elseif (%node.args) {');
47: $me->addMacro('else', array($me, 'macroElse'));
48: $me->addMacro('ifset', 'if (isset(%node.args)) {', '}');
49: $me->addMacro('elseifset', '} elseif (isset(%node.args)) {');
50: $me->addMacro('ifcontent', array($me, 'macroIfContent'), array($me, 'macroEndIfContent'));
51:
52: $me->addMacro('switch', '$_l->switch[] = (%node.args); if (FALSE) {', '} array_pop($_l->switch)');
53: $me->addMacro('case', '} elseif (end($_l->switch) === (%node.args)) {');
54:
55: $me->addMacro('foreach', '', array($me, 'macroEndForeach'));
56: $me->addMacro('for', 'for (%node.args) {', '}');
57: $me->addMacro('while', 'while (%node.args) {', '}');
58: $me->addMacro('continueIf', array($me, 'macroBreakContinueIf'));
59: $me->addMacro('breakIf', array($me, 'macroBreakContinueIf'));
60: $me->addMacro('first', 'if ($iterator->isFirst(%node.args)) {', '}');
61: $me->addMacro('last', 'if ($iterator->isLast(%node.args)) {', '}');
62: $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)) {', '}');
63:
64: $me->addMacro('var', 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: $me->addMacro('contentType', array($me, 'macroContentType'));
79: $me->addMacro('status', array($me, 'macroStatus'));
80: $me->addMacro('php', array($me, 'macroExpr'));
81:
82: $me->addMacro('class', NULL, NULL, array($me, 'macroClass'));
83: $me->addMacro('attr', NULL, NULL, array($me, 'macroAttr'));
84: }
85:
86:
87: 88: 89: 90:
91: public function finalize()
92: {
93: return array('list($_b, $_g, $_l) = $template->initialize('
94: . var_export($this->getCompiler()->getTemplateId(), TRUE) . ', '
95: . var_export($this->getCompiler()->getContentType(), TRUE)
96: . ')');
97: }
98:
99:
100:
101:
102:
103: 104: 105:
106: public function macroIf(MacroNode $node, PhpWriter $writer)
107: {
108: if ($node->data->capture = ($node->args === '')) {
109: return 'ob_start()';
110: }
111: if ($node->prefix === $node::PREFIX_TAG) {
112: return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)) {' : 'if ($_l->ifs[] = (%node.args)) {');
113: }
114: return $writer->write('if (%node.args) {');
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) ? '{ $_l->else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_l->else; }' : 'ob_end_clean();')
131: );
132: }
133: return '}';
134: }
135:
136:
137: 138: 139:
140: public function macroElse(MacroNode $node, PhpWriter $writer)
141: {
142: $ifNode = $node->parentNode;
143: if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
144: if (isset($ifNode->data->else)) {
145: throw new CompileException('Macro {if} supports only one {else}.');
146: }
147: $ifNode->data->else = TRUE;
148: return 'ob_start()';
149: }
150: return '} else {';
151: }
152:
153:
154: 155: 156:
157: public function macroIfContent(MacroNode $node, PhpWriter $writer)
158: {
159: if (!$node->prefix) {
160: throw new CompileException("Unknown macro {{$node->name}}, use n:{$node->name} attribute.");
161: } elseif ($node->prefix !== MacroNode::PREFIX_NONE) {
162: throw new CompileException("Unknown attribute n:{$node->prefix}-{$node->name}, use n:{$node->name} attribute.");
163: }
164:
165: return $writer->write('ob_start()');
166: }
167:
168:
169: 170: 171:
172: public function macroEndIfContent(MacroNode $node, PhpWriter $writer)
173: {
174: preg_match('#(^.*?>)(.*)(<.*\z)#s', $node->content, $parts);
175: $node->content = $parts[1]
176: . '<?php ob_start() ?>'
177: . $parts[2]
178: . '<?php $_l->ifcontent = ob_get_contents(); ob_end_flush() ?>'
179: . $parts[3];
180: return 'rtrim($_l->ifcontent) === "" ? ob_end_clean() : ob_end_flush()';
181: }
182:
183:
184: 185: 186:
187: public function macroTranslate(MacroNode $node, PhpWriter $writer)
188: {
189: if ($node->closing) {
190: return $writer->write('echo %modify($template->translate(ob_get_clean()))');
191:
192: } elseif ($node->isEmpty = ($node->args !== '')) {
193: return $writer->write('echo %modify($template->translate(%node.args))');
194:
195: } else {
196: return 'ob_start()';
197: }
198: }
199:
200:
201: 202: 203:
204: public function macroInclude(MacroNode $node, PhpWriter $writer)
205: {
206: $code = $writer->write('$_b->templates[%var]->renderChildTemplate(%node.word, %node.array? + $template->getParameters())',
207: $this->getCompiler()->getTemplateId());
208:
209: if ($node->modifiers) {
210: return $writer->write('ob_start(); %raw; echo %modify(ob_get_clean())', $code);
211: } else {
212: return $code;
213: }
214: }
215:
216:
217: 218: 219:
220: public function macroUse(MacroNode $node, PhpWriter $writer)
221: {
222: call_user_func(Latte\Helpers::checkCallback(array($node->tokenizer->fetchWord(), 'install')), $this->getCompiler())
223: ->initialize();
224: }
225:
226:
227: 228: 229:
230: public function macroCapture(MacroNode $node, PhpWriter $writer)
231: {
232: $variable = $node->tokenizer->fetchWord();
233: if (substr($variable, 0, 1) !== '$') {
234: throw new CompileException("Invalid capture block variable '$variable'");
235: }
236: $node->data->variable = $variable;
237: return 'ob_start()';
238: }
239:
240:
241: 242: 243:
244: public function macroCaptureEnd(MacroNode $node, PhpWriter $writer)
245: {
246: return $node->data->variable . $writer->write(' = %modify(ob_get_clean())');
247: }
248:
249:
250: 251: 252:
253: public function macroEndForeach(MacroNode $node, PhpWriter $writer)
254: {
255: if ($node->modifiers !== '|noiterator' && preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) {
256: $node->openingCode = '<?php $iterations = 0; foreach ($iterator = $_l->its[] = new Latte\Runtime\CachingIterator('
257: . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . ') { ?>';
258: $node->closingCode = '<?php $iterations++; } array_pop($_l->its); $iterator = end($_l->its) ?>';
259: } else {
260: $node->openingCode = '<?php $iterations = 0; foreach (' . $writer->formatArgs() . ') { ?>';
261: $node->closingCode = '<?php $iterations++; } ?>';
262: }
263: }
264:
265:
266: 267: 268: 269:
270: public function macroBreakContinueIf(MacroNode $node, PhpWriter $writer)
271: {
272: $cmd = str_replace('If', '', $node->name);
273: if ($node->parentNode && $node->parentNode->prefix === $node::PREFIX_NONE) {
274: return $writer->write("if (%node.args) { echo \"</{$node->parentNode->htmlNode->name}>\\n\"; $cmd; }");
275: }
276: return $writer->write("if (%node.args) $cmd");
277: }
278:
279:
280: 281: 282:
283: public function macroClass(MacroNode $node, PhpWriter $writer)
284: {
285: if (isset($node->htmlNode->attrs['class'])) {
286: throw new CompileException('It is not possible to combine class with n:class.');
287: }
288: return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\', %escape(implode(" ", array_unique($_l->tmp))), \'"\'');
289: }
290:
291:
292: 293: 294:
295: public function macroAttr(MacroNode $node, PhpWriter $writer)
296: {
297: return $writer->write('echo Latte\Runtime\Filters::htmlAttributes(%node.array)');
298: }
299:
300:
301: 302: 303:
304: public function macroDump(MacroNode $node, PhpWriter $writer)
305: {
306: $args = $writer->formatArgs();
307: return $writer->write(
308: 'Tracy\Debugger::barDump(' . ($args ? "($args)" : 'get_defined_vars()'). ', %var)',
309: $args ?: 'variables'
310: );
311: }
312:
313:
314: 315: 316:
317: public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
318: {
319: return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
320: . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
321: }
322:
323:
324: 325: 326: 327:
328: public function macroVar(MacroNode $node, PhpWriter $writer)
329: {
330: if ($node->args === '' && $node->parentNode && $node->parentNode->name === 'switch') {
331: return '} else {';
332: }
333:
334: $var = TRUE;
335: $tokens = $writer->preprocess();
336: $res = new Latte\MacroTokens;
337: while ($tokens->nextToken()) {
338: if ($var && $tokens->isCurrent(Latte\MacroTokens::T_SYMBOL, Latte\MacroTokens::T_VARIABLE)) {
339: if ($node->name === 'default') {
340: $res->append("'" . ltrim($tokens->currentValue(), '$') . "'");
341: } else {
342: $res->append('$' . ltrim($tokens->currentValue(), '$'));
343: }
344: $var = NULL;
345:
346: } elseif ($tokens->isCurrent('=', '=>') && $tokens->depth === 0) {
347: $res->append($node->name === 'default' ? '=>' : '=');
348: $var = FALSE;
349:
350: } elseif ($tokens->isCurrent(',') && $tokens->depth === 0) {
351: if ($var === NULL) {
352: $res->append($node->name === 'default' ? '=>NULL' : '=NULL');
353: }
354: $res->append($node->name === 'default' ? ',' : ';');
355: $var = TRUE;
356:
357: } elseif ($var === NULL && $node->name === 'default' && !$tokens->isCurrent(Latte\MacroTokens::T_WHITESPACE)) {
358: throw new CompileException("Unexpected '{$tokens->currentValue()}' in {default $node->args}");
359:
360: } else {
361: $res->append($tokens->currentToken());
362: }
363: }
364: if ($var === NULL) {
365: $res->append($node->name === 'default' ? '=>NULL' : '=NULL');
366: }
367: $out = $writer->quoteFilter($res)->joinAll();
368: return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
369: }
370:
371:
372: 373: 374: 375:
376: public function macroExpr(MacroNode $node, PhpWriter $writer)
377: {
378: return $writer->write(($node->name === '=' ? 'echo ' : '') . '%modify(%node.args)');
379: }
380:
381:
382: 383: 384:
385: public function macroContentType(MacroNode $node, PhpWriter $writer)
386: {
387: if (strpos($node->args, 'xhtml') !== FALSE) {
388: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XHTML);
389:
390: } elseif (strpos($node->args, 'html') !== FALSE) {
391: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_HTML);
392:
393: } elseif (strpos($node->args, 'xml') !== FALSE) {
394: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XML);
395:
396: } elseif (strpos($node->args, 'javascript') !== FALSE) {
397: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_JS);
398:
399: } elseif (strpos($node->args, 'css') !== FALSE) {
400: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_CSS);
401:
402: } elseif (strpos($node->args, 'calendar') !== FALSE) {
403: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_ICAL);
404:
405: } else {
406: $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_TEXT);
407: }
408:
409:
410: if (strpos($node->args, '/')) {
411: return $writer->write('header(%var)', "Content-Type: $node->args");
412: }
413: }
414:
415:
416: 417: 418:
419: public function macroStatus(MacroNode $node, PhpWriter $writer)
420: {
421: return $writer->write((substr($node->args, -1) === '?' ? 'if (!headers_sent()) ' : '') .
422: 'header((isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : "HTTP/1.1") . " " . %0.var, TRUE, %0.var)', (int) $node->args
423: );
424: }
425:
426: }
427: