Namespaces

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

Classes

  • BlockMacros
  • BlockMacrosRuntime
  • 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: 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:      * Finishes template parsing.
 89:      * @return array(prolog, epilog)
 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:     /********************* macros ****************d*g**/
101: 
102: 
103:     /**
104:      * {if ...}
105:      */
106:     public function macroIf(MacroNode $node, PhpWriter $writer)
107:     {
108:         if ($node->modifiers) {
109:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
110:         }
111:         if ($node->data->capture = ($node->args === '')) {
112:             return 'ob_start(function () {})';
113:         }
114:         if ($node->prefix === $node::PREFIX_TAG) {
115:             return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)) {' : 'if ($_l->ifs[] = (%node.args)) {');
116:         }
117:         return $writer->write('if (%node.args) {');
118:     }
119: 
120: 
121:     /**
122:      * {/if ...}
123:      */
124:     public function macroEndIf(MacroNode $node, PhpWriter $writer)
125:     {
126:         if ($node->data->capture) {
127:             if ($node->args === '') {
128:                 throw new CompileException('Missing condition in {if} macro.');
129:             }
130:             return $writer->write('if (%node.args) '
131:                 . (isset($node->data->else) ? '{ ob_end_clean(); echo ob_get_clean(); }' : 'echo ob_get_clean();')
132:                 . ' else '
133:                 . (isset($node->data->else) ? '{ $_l->else = ob_get_clean(); ob_end_clean(); echo $_l->else; }' : 'ob_end_clean();')
134:             );
135:         }
136:         return '}';
137:     }
138: 
139: 
140:     /**
141:      * {else}
142:      */
143:     public function macroElse(MacroNode $node, PhpWriter $writer)
144:     {
145:         if ($node->modifiers) {
146:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
147:         } elseif ($node->args) {
148:             $hint = substr($node->args, 0, 2) === 'if' ? ', did you mean {elseif}?' : '';
149:             trigger_error("Arguments are not allowed in {{$node->name}}$hint", E_USER_WARNING);
150:         }
151:         $ifNode = $node->parentNode;
152:         if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
153:             if (isset($ifNode->data->else)) {
154:                 throw new CompileException('Macro {if} supports only one {else}.');
155:             }
156:             $ifNode->data->else = TRUE;
157:             return 'ob_start(function () {})';
158:         }
159:         return '} else {';
160:     }
161: 
162: 
163:     /**
164:      * n:ifcontent
165:      */
166:     public function macroIfContent(MacroNode $node, PhpWriter $writer)
167:     {
168:         if (!$node->prefix) {
169:             throw new CompileException("Unknown macro {{$node->name}}, use n:{$node->name} attribute.");
170:         } elseif ($node->prefix !== MacroNode::PREFIX_NONE) {
171:             throw new CompileException("Unknown attribute n:{$node->prefix}-{$node->name}, use n:{$node->name} attribute.");
172:         }
173: 
174:         return $writer->write('ob_start(function () {})');
175:     }
176: 
177: 
178:     /**
179:      * n:ifcontent
180:      */
181:     public function macroEndIfContent(MacroNode $node, PhpWriter $writer)
182:     {
183:         preg_match('#(^.*?>)(.*)(<.*\z)#s', $node->content, $parts);
184:         $node->content = $parts[1]
185:             . '<?php ob_start() ?>'
186:             . $parts[2]
187:             . '<?php $_l->ifcontent = ob_get_flush() ?>'
188:             . $parts[3];
189:         return 'if (rtrim($_l->ifcontent) === "") ob_end_clean(); else echo ob_get_clean()';
190:     }
191: 
192: 
193:     /**
194:      * {_$var |modifiers}
195:      */
196:     public function macroTranslate(MacroNode $node, PhpWriter $writer)
197:     {
198:         if ($node->closing) {
199:             return $writer->write('echo %modify($template->translate(ob_get_clean()))');
200: 
201:         } elseif ($node->isEmpty = ($node->args !== '')) {
202:             return $writer->write('echo %modify($template->translate(%node.args))');
203: 
204:         } else {
205:             return 'ob_start(function () {})';
206:         }
207:     }
208: 
209: 
210:     /**
211:      * {include "file" [,] [params]}
212:      */
213:     public function macroInclude(MacroNode $node, PhpWriter $writer)
214:     {
215:         $code = $writer->write('$_b->templates[%var]->renderChildTemplate(%node.word, %node.array? + $template->getParameters())',
216:             $this->getCompiler()->getTemplateId());
217: 
218:         if ($node->modifiers) {
219:             return $writer->write('ob_start(function () {}); %raw; echo %modify(ob_get_clean())', $code);
220:         } else {
221:             return $code;
222:         }
223:     }
224: 
225: 
226:     /**
227:      * {use class MacroSet}
228:      */
229:     public function macroUse(MacroNode $node, PhpWriter $writer)
230:     {
231:         if ($node->modifiers) {
232:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
233:         }
234:         call_user_func(Latte\Helpers::checkCallback(array($node->tokenizer->fetchWord(), 'install')), $this->getCompiler())
235:             ->initialize();
236:     }
237: 
238: 
239:     /**
240:      * {capture $variable}
241:      */
242:     public function macroCapture(MacroNode $node, PhpWriter $writer)
243:     {
244:         $variable = $node->tokenizer->fetchWord();
245:         if (substr($variable, 0, 1) !== '$') {
246:             throw new CompileException("Invalid capture block variable '$variable'");
247:         }
248:         $node->data->variable = $variable;
249:         return 'ob_start(function () {})';
250:     }
251: 
252: 
253:     /**
254:      * {/capture}
255:      */
256:     public function macroCaptureEnd(MacroNode $node, PhpWriter $writer)
257:     {
258:         return $node->data->variable . $writer->write(' = %modify(ob_get_clean())');
259:     }
260: 
261: 
262:     /**
263:      * {foreach ...}
264:      */
265:     public function macroEndForeach(MacroNode $node, PhpWriter $writer)
266:     {
267:         if ($node->modifiers && $node->modifiers !== '|noiterator') {
268:             trigger_error('Only modifier |noiterator is allowed here.', E_USER_WARNING);
269:         }
270:         if ($node->modifiers !== '|noiterator' && preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) {
271:             $node->openingCode = '<?php $iterations = 0; foreach ($iterator = $_l->its[] = new Latte\Runtime\CachingIterator('
272:             . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . ') { ?>';
273:             $node->closingCode = '<?php $iterations++; } array_pop($_l->its); $iterator = end($_l->its) ?>';
274:         } else {
275:             $node->openingCode = '<?php $iterations = 0; foreach (' . $writer->formatArgs() . ') { ?>';
276:             $node->closingCode = '<?php $iterations++; } ?>';
277:         }
278:     }
279: 
280: 
281:     /**
282:      * {breakIf ...}
283:      * {continueIf ...}
284:      */
285:     public function macroBreakContinueIf(MacroNode $node, PhpWriter $writer)
286:     {
287:         if ($node->modifiers) {
288:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
289:         }
290:         $cmd = str_replace('If', '', $node->name);
291:         if ($node->parentNode && $node->parentNode->prefix === $node::PREFIX_NONE) {
292:             return $writer->write("if (%node.args) { echo \"</{$node->parentNode->htmlNode->name}>\\n\"; $cmd; }");
293:         }
294:         return $writer->write("if (%node.args) $cmd");
295:     }
296: 
297: 
298:     /**
299:      * n:class="..."
300:      */
301:     public function macroClass(MacroNode $node, PhpWriter $writer)
302:     {
303:         if (isset($node->htmlNode->attrs['class'])) {
304:             throw new CompileException('It is not possible to combine class with n:class.');
305:         }
306:         return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\', %escape(implode(" ", array_unique($_l->tmp))), \'"\'');
307:     }
308: 
309: 
310:     /**
311:      * n:attr="..."
312:      */
313:     public function macroAttr(MacroNode $node, PhpWriter $writer)
314:     {
315:         return $writer->write('echo Latte\Runtime\Filters::htmlAttributes(%node.array)');
316:     }
317: 
318: 
319:     /**
320:      * {dump ...}
321:      */
322:     public function macroDump(MacroNode $node, PhpWriter $writer)
323:     {
324:         if ($node->modifiers) {
325:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
326:         }
327:         $args = $writer->formatArgs();
328:         return $writer->write(
329:             'Tracy\Debugger::barDump(' . ($args ? "($args)" : 'get_defined_vars()'). ', %var)',
330:             $args ?: 'variables'
331:         );
332:     }
333: 
334: 
335:     /**
336:      * {debugbreak ...}
337:      */
338:     public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
339:     {
340:         if ($node->modifiers) {
341:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
342:         }
343:         return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
344:             . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
345:     }
346: 
347: 
348:     /**
349:      * {var ...}
350:      * {default ...}
351:      */
352:     public function macroVar(MacroNode $node, PhpWriter $writer)
353:     {
354:         if ($node->modifiers) {
355:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
356:         }
357:         if ($node->args === '' && $node->parentNode && $node->parentNode->name === 'switch') {
358:             return '} else {';
359:         }
360: 
361:         $var = TRUE;
362:         $tokens = $writer->preprocess();
363:         $res = new Latte\MacroTokens;
364:         while ($tokens->nextToken()) {
365:             if ($var && $tokens->isCurrent(Latte\MacroTokens::T_SYMBOL, Latte\MacroTokens::T_VARIABLE)) {
366:                 if ($node->name === 'default') {
367:                     $res->append("'" . ltrim($tokens->currentValue(), '$') . "'");
368:                 } else {
369:                     $res->append('$' . ltrim($tokens->currentValue(), '$'));
370:                 }
371:                 $var = NULL;
372: 
373:             } elseif ($tokens->isCurrent('=', '=>') && $tokens->depth === 0) {
374:                 $res->append($node->name === 'default' ? '=>' : '=');
375:                 $var = FALSE;
376: 
377:             } elseif ($tokens->isCurrent(',') && $tokens->depth === 0) {
378:                 if ($var === NULL) {
379:                     $res->append($node->name === 'default' ? '=>NULL' : '=NULL');
380:                 }
381:                 $res->append($node->name === 'default' ? ',' : ';');
382:                 $var = TRUE;
383: 
384:             } elseif ($var === NULL && $node->name === 'default' && !$tokens->isCurrent(Latte\MacroTokens::T_WHITESPACE)) {
385:                 throw new CompileException("Unexpected '{$tokens->currentValue()}' in {default $node->args}");
386: 
387:             } else {
388:                 $res->append($tokens->currentToken());
389:             }
390:         }
391:         if ($var === NULL) {
392:             $res->append($node->name === 'default' ? '=>NULL' : '=NULL');
393:         }
394:         $out = $writer->quoteFilter($res)->joinAll();
395:         return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
396:     }
397: 
398: 
399:     /**
400:      * {= ...}
401:      * {? ...}
402:      */
403:     public function macroExpr(MacroNode $node, PhpWriter $writer)
404:     {
405:         return $writer->write(($node->name === '=' ? 'echo ' : '') . '%modify(%node.args)');
406:     }
407: 
408: 
409:     /**
410:      * {contentType ...}
411:      */
412:     public function macroContentType(MacroNode $node, PhpWriter $writer)
413:     {
414:         if ($node->modifiers) {
415:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
416:         }
417:         if (strpos($node->args, 'xhtml') !== FALSE) {
418:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XHTML);
419: 
420:         } elseif (strpos($node->args, 'html') !== FALSE) {
421:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_HTML);
422: 
423:         } elseif (strpos($node->args, 'xml') !== FALSE) {
424:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XML);
425: 
426:         } elseif (strpos($node->args, 'javascript') !== FALSE) {
427:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_JS);
428: 
429:         } elseif (strpos($node->args, 'css') !== FALSE) {
430:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_CSS);
431: 
432:         } elseif (strpos($node->args, 'calendar') !== FALSE) {
433:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_ICAL);
434: 
435:         } else {
436:             $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_TEXT);
437:         }
438: 
439:         // temporary solution
440:         if (strpos($node->args, '/')) {
441:             return $writer->write('header(%var)', "Content-Type: $node->args");
442:         }
443:     }
444: 
445: 
446:     /**
447:      * {status ...}
448:      */
449:     public function macroStatus(MacroNode $node, PhpWriter $writer)
450:     {
451:         if ($node->modifiers) {
452:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
453:         }
454:         return $writer->write((substr($node->args, -1) === '?' ? 'if (!headers_sent()) ' : '') .
455:             'header((isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : "HTTP/1.1") . " " . %0.var, TRUE, %0.var)', (int) $node->args
456:         );
457:     }
458: 
459: }
460: 
Nette 2.3-20161221 API API documentation generated by ApiGen 2.8.0