Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • none

Classes

  • CacheMacro
  • CoreMacros
  • FormMacros
  • MacroSet
  • UIMacros
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Latte\Macros;
  9: 
 10: use Nette;
 11: use Nette\Latte;
 12: use Nette\Latte\CompileException;
 13: use Nette\Latte\MacroNode;
 14: use Nette\Latte\PhpWriter;
 15: 
 16: 
 17: /**
 18:  * Basic macros for Latte.
 19:  *
 20:  * - {if ?} ... {elseif ?} ... {else} ... {/if}
 21:  * - {ifset ?} ... {elseifset ?} ... {/ifset}
 22:  * - {for ?} ... {/for}
 23:  * - {foreach ?} ... {/foreach}
 24:  * - {$variable} with escaping
 25:  * - {!$variable} without escaping
 26:  * - {=expression} echo with escaping
 27:  * - {!=expression} echo without escaping
 28:  * - {?expression} evaluate PHP statement
 29:  * - {_expression} echo translation with escaping
 30:  * - {!_expression} echo translation without escaping
 31:  * - {attr ?} HTML element attributes
 32:  * - {capture ?} ... {/capture} capture block to parameter
 33:  * - {var var => value} set template parameter
 34:  * - {default var => value} set default template parameter
 35:  * - {dump $var}
 36:  * - {debugbreak}
 37:  * - {l} {r} to display { }
 38:  *
 39:  * @author     David Grudl
 40:  */
 41: class CoreMacros extends MacroSet
 42: {
 43: 
 44: 
 45:     public static function install(Latte\Compiler $compiler)
 46:     {
 47:         $me = new static($compiler);
 48: 
 49:         $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf'));
 50:         $me->addMacro('elseif', '} elseif (%node.args) {');
 51:         $me->addMacro('else', array($me, 'macroElse'));
 52:         $me->addMacro('ifset', 'if (isset(%node.args)) {', '}');
 53:         $me->addMacro('elseifset', '} elseif (isset(%node.args)) {');
 54:         $me->addMacro('ifcontent', array($me, 'macroIfContent'), array($me, 'macroEndIfContent'));
 55: 
 56:         $me->addMacro('switch', '$_l->switch[] = (%node.args); if (FALSE) {', '} array_pop($_l->switch)');
 57:         $me->addMacro('case', '} elseif (end($_l->switch) === (%node.args)) {');
 58: 
 59:         $me->addMacro('foreach', '', array($me, 'macroEndForeach'));
 60:         $me->addMacro('for', 'for (%node.args) {', '}');
 61:         $me->addMacro('while', 'while (%node.args) {', '}');
 62:         $me->addMacro('continueIf', array($me, 'macroBreakContinueIf'));
 63:         $me->addMacro('breakIf', array($me, 'macroBreakContinueIf'));
 64:         $me->addMacro('first', 'if ($iterator->isFirst(%node.args)) {', '}');
 65:         $me->addMacro('last', 'if ($iterator->isLast(%node.args)) {', '}');
 66:         $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)) {', '}');
 67: 
 68:         $me->addMacro('var', array($me, 'macroVar'));
 69:         $me->addMacro('assign', array($me, 'macroVar')); // deprecated
 70:         $me->addMacro('default', array($me, 'macroVar'));
 71:         $me->addMacro('dump', array($me, 'macroDump'));
 72:         $me->addMacro('debugbreak', array($me, 'macroDebugbreak'));
 73:         $me->addMacro('l', '?>{<?php');
 74:         $me->addMacro('r', '?>}<?php');
 75: 
 76:         $me->addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate'));
 77:         $me->addMacro('=', array($me, 'macroExpr'));
 78:         $me->addMacro('?', array($me, 'macroExpr'));
 79: 
 80:         $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd'));
 81:         $me->addMacro('include', array($me, 'macroInclude'));
 82:         $me->addMacro('use', array($me, 'macroUse'));
 83: 
 84:         $me->addMacro('class', NULL, NULL, array($me, 'macroClass'));
 85:         $me->addMacro('attr', array($me, 'macroOldAttr'), '', array($me, 'macroAttr'));
 86:         $me->addMacro('href', NULL); // placeholder
 87:     }
 88: 
 89: 
 90:     /**
 91:      * Finishes template parsing.
 92:      * @return array(prolog, epilog)
 93:      */
 94:     public function finalize()
 95:     {
 96:         return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, '
 97:             . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')');
 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) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_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 $_ifcontent = ob_get_contents(); ob_end_flush() ?>'
180:             . $parts[3];
181:         return 'rtrim($_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('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])',
208:             $this->getCompiler()->getTemplateId());
209: 
210:         if ($node->modifiers) {
211:             return $writer->write('echo %modify(%raw->__toString(TRUE))', $code);
212:         } else {
213:             return $code . '->render()';
214:         }
215:     }
216: 
217: 
218:     /**
219:      * {use class MacroSet}
220:      */
221:     public function macroUse(MacroNode $node, PhpWriter $writer)
222:     {
223:         Nette\Utils\Callback::invoke(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 Nette\Iterators\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 Nette\Utils\Html::el(NULL, %node.array)->attributes()');
296:     }
297: 
298: 
299:     /**
300:      * {attr ...}
301:      * @deprecated
302:      */
303:     public function macroOldAttr(MacroNode $node)
304:     {
305:         trigger_error('Macro {attr} is deprecated; use n:attr="..." instead.', E_USER_DEPRECATED);
306:         return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->');
307:     }
308: 
309: 
310:     /**
311:      * {dump ...}
312:      */
313:     public function macroDump(MacroNode $node, PhpWriter $writer)
314:     {
315:         $args = $writer->formatArgs();
316:         return 'Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(" . $writer->write('%var', $args) . " => $args)" : 'get_defined_vars()')
317:             . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))';
318:     }
319: 
320: 
321:     /**
322:      * {debugbreak ...}
323:      */
324:     public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
325:     {
326:         return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
327:             . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
328:     }
329: 
330: 
331:     /**
332:      * {var ...}
333:      * {default ...}
334:      */
335:     public function macroVar(MacroNode $node, PhpWriter $writer)
336:     {
337:         if ($node->args === '' && $node->parentNode && $node->parentNode->name === 'switch') {
338:             return '} else {';
339: 
340:         } elseif ($node->name === 'assign') {
341:             trigger_error('Macro {assign} is deprecated; use {var} instead.', E_USER_DEPRECATED);
342:         }
343: 
344:         $var = TRUE;
345:         $tokens = $writer->preprocess();
346:         $res = new Latte\MacroTokens;
347:         while ($tokens->nextToken()) {
348:             if ($var && $tokens->isCurrent(Latte\MacroTokens::T_SYMBOL, Latte\MacroTokens::T_VARIABLE)) {
349:                 if ($node->name === 'default') {
350:                     $res->append("'" . ltrim($tokens->currentValue(), '$') . "'");
351:                 } else {
352:                     $res->append('$' . ltrim($tokens->currentValue(), '$'));
353:                 }
354:                 $var = NULL;
355: 
356:             } elseif ($tokens->isCurrent('=', '=>') && $tokens->depth === 0) {
357:                 $res->append($node->name === 'default' ? '=>' : '=');
358:                 $var = FALSE;
359: 
360:             } elseif ($tokens->isCurrent(',') && $tokens->depth === 0) {
361:                 $res->append($node->name === 'default' ? ',' : ';');
362:                 $var = TRUE;
363: 
364:             } elseif ($var === NULL && $node->name === 'default' && !$tokens->isCurrent(Latte\MacroTokens::T_WHITESPACE)) {
365:                 throw new CompileException("Unexpected '{$tokens->currentValue()}' in {default $node->args}");
366: 
367:             } else {
368:                 $res->append($tokens->currentToken());
369:             }
370:         }
371:         $out = $writer->quoteFilter($res)->joinAll();
372:         return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
373:     }
374: 
375: 
376:     /**
377:      * {= ...}
378:      * {? ...}
379:      */
380:     public function macroExpr(MacroNode $node, PhpWriter $writer)
381:     {
382:         return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)');
383:     }
384: 
385: 
386:     /********************* run-time helpers ****************d*g**/
387: 
388: 
389:     /**
390:      * Includes subtemplate.
391:      * @param  mixed      included file name or template
392:      * @param  array      parameters
393:      * @param  Nette\Templating\ITemplate  current template
394:      * @return Nette\Templating\Template
395:      */
396:     public static function includeTemplate($destination, array $params, Nette\Templating\ITemplate $template)
397:     {
398:         if ($destination instanceof Nette\Templating\ITemplate) {
399:             $tpl = $destination;
400: 
401:         } elseif ($destination == NULL) { // intentionally ==
402:             throw new Nette\InvalidArgumentException("Template file name was not specified.");
403: 
404:         } elseif ($template instanceof Nette\Templating\IFileTemplate) {
405:             if (!preg_match('#/|\\\\|[a-z]:#iA', $destination)) {
406:                 $destination = dirname($template->getFile()) . '/' . $destination;
407:             }
408:             $tpl = clone $template;
409:             $tpl->setFile($destination);
410: 
411:         } else {
412:             throw new Nette\NotSupportedException('Macro {include "filename"} is supported only with Nette\Templating\IFileTemplate.');
413:         }
414: 
415:         $tpl->setParameters($params); // interface?
416:         return $tpl;
417:     }
418: 
419: 
420:     /**
421:      * Initializes local & global storage in template.
422:      * @return \stdClass
423:      */
424:     public static function initRuntime(Nette\Templating\ITemplate $template, $templateId)
425:     {
426:         // local storage
427:         if (isset($template->_l)) {
428:             $local = $template->_l;
429:             unset($template->_l);
430:         } else {
431:             $local = new \stdClass;
432:         }
433:         $local->templates[$templateId] = $template;
434: 
435:         // global storage
436:         if (!isset($template->_g)) {
437:             $template->_g = new \stdClass;
438:         }
439: 
440:         return array($local, $template->_g);
441:     }
442: 
443: }
444: 
Nette 2.1 API documentation generated by ApiGen 2.8.0