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