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
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • BlockMacros
  • BlockMacrosRuntime
  • CoreMacros
  • MacroSet
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Latte (http://latte.nette.org)
  5:  * Copyright (c) 2008 David Grudl (http://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->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:      * {/if ...}
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:      * {else}
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:      * n:ifcontent
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:      * n:ifcontent
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:      * {_$var |modifiers}
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:      * {include "file" [,] [params]}
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:      * {use class MacroSet}
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:      * {capture $variable}
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:      * {/capture}
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:      * {foreach ...}
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:      * {breakIf ...}
268:      * {continueIf ...}
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:      * n:class="..."
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:      * n:attr="..."
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:      * {dump ...}
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:      * {debugbreak ...}
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:      * {var ...}
326:      * {default ...}
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:      * {contentType ...}
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:         // temporary solution
410:         if (strpos($node->args, '/')) {
411:             return $writer->write('header(%var)', "Content-Type: $node->args");
412:         }
413:     }
414: 
415: 
416:     /**
417:      * {status ...}
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: 
Nette 2.3.4 API API documentation generated by ApiGen 2.8.0