Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • BlockMacros
  • CoreMacros
  • MacroSet
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Latte (https://latte.nette.org)
  5:  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
  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:  * Basic macros for Latte.
 18:  *
 19:  * - {if ?} ... {elseif ?} ... {else} ... {/if}
 20:  * - {ifset ?} ... {elseifset ?} ... {/ifset}
 21:  * - {for ?} ... {/for}
 22:  * - {foreach ?} ... {/foreach}
 23:  * - {$variable} with escaping
 24:  * - {=expression} echo with escaping
 25:  * - {?expression} evaluate PHP statement
 26:  * - {_expression} echo translation with escaping
 27:  * - {attr ?} HTML element attributes
 28:  * - {capture ?} ... {/capture} capture block to parameter
 29:  * - {var var => value} set template parameter
 30:  * - {default var => value} set default template parameter
 31:  * - {dump $var}
 32:  * - {debugbreak}
 33:  * - {contentType ...} HTTP Content-Type header
 34:  * - {status ...} HTTP status
 35:  * - {l} {r} to display { }
 36:  *
 37:  * @author     David Grudl
 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:      * Finishes template parsing.
 90:      * @return array(prolog, epilog)
 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:     /********************* macros ****************d*g**/
102: 
103: 
104:     /**
105:      * {if ...}
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:      * {/if ...}
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:      * {else}
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:      * n:ifcontent
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:      * n:ifcontent
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:      * {_$var |modifiers}
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:      * {include "file" [,] [params]}
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:      * {use class MacroSet}
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:      * {capture $variable}
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:      * {/capture}
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:      * {foreach ...}
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:      * {breakIf ...}
269:      * {continueIf ...}
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:      * n:class="..."
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:      * n:attr="..."
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:      * {dump ...}
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:      * {debugbreak ...}
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:      * {var ...}
322:      * {default ...}
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:      * {contentType ...}
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:         // temporary solution
406:         if (strpos($node->args, '/')) {
407:             return $writer->write('header(%var)', "Content-Type: $node->args");
408:         }
409:     }
410: 
411: 
412:     /**
413:      * {status ...}
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: 
Nette 2.2 API documentation generated by ApiGen 2.8.0