Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • 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 (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Latte\Macros;
  9: 
 10: use Nette,
 11:     Nette\Latte,
 12:     Nette\Latte\CompileException,
 13:     Nette\Latte\MacroNode,
 14:     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: 
 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', 'if (%node.args) continue');
 59:         $me->addMacro('breakIf', 'if (%node.args) break');
 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('assign', array($me, 'macroVar')); // deprecated
 66:         $me->addMacro('default', array($me, 'macroVar'));
 67:         $me->addMacro('dump', array($me, 'macroDump'));
 68:         $me->addMacro('debugbreak', array($me, 'macroDebugbreak'));
 69:         $me->addMacro('l', '?>{<?php');
 70:         $me->addMacro('r', '?>}<?php');
 71: 
 72:         $me->addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate'));
 73:         $me->addMacro('=', array($me, 'macroExpr'));
 74:         $me->addMacro('?', array($me, 'macroExpr'));
 75: 
 76:         $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd'));
 77:         $me->addMacro('include', array($me, 'macroInclude'));
 78:         $me->addMacro('use', array($me, 'macroUse'));
 79: 
 80:         $me->addMacro('class', NULL, NULL, array($me, 'macroClass'));
 81:         $me->addMacro('attr', array($me, 'macroOldAttr'), '', array($me, 'macroAttr'));
 82:         $me->addMacro('href', NULL); // placeholder
 83:     }
 84: 
 85: 
 86:     /**
 87:      * Finishes template parsing.
 88:      * @return array(prolog, epilog)
 89:      */
 90:     public function finalize()
 91:     {
 92:         return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, '
 93:             . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')');
 94:     }
 95: 
 96: 
 97:     /********************* macros ****************d*g**/
 98: 
 99: 
100:     /**
101:      * {if ...}
102:      */
103:     public function macroIf(MacroNode $node, PhpWriter $writer)
104:     {
105:         if ($node->data->capture = ($node->args === '')) {
106:             return 'ob_start()';
107:         }
108:         if ($node->prefix === $node::PREFIX_TAG) {
109:             return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)) {' : 'if ($_l->ifs[] = (%node.args)) {');
110:         }
111:         return $writer->write('if (%node.args) {');
112:     }
113: 
114: 
115:     /**
116:      * {/if ...}
117:      */
118:     public function macroEndIf(MacroNode $node, PhpWriter $writer)
119:     {
120:         if ($node->data->capture) {
121:             if ($node->args === '') {
122:                 throw new CompileException('Missing condition in {if} macro.');
123:             }
124:             return $writer->write('if (%node.args) '
125:                 . (isset($node->data->else) ? '{ ob_end_clean(); ob_end_flush(); }' : 'ob_end_flush();')
126:                 . ' else '
127:                 . (isset($node->data->else) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_else; }' : 'ob_end_clean();')
128:             );
129:         }
130:         return '}';
131:     }
132: 
133: 
134:     /**
135:      * {else}
136:      */
137:     public function macroElse(MacroNode $node, PhpWriter $writer)
138:     {
139:         $ifNode = $node->parentNode;
140:         if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
141:             if (isset($ifNode->data->else)) {
142:                 throw new CompileException('Macro {if} supports only one {else}.');
143:             }
144:             $ifNode->data->else = TRUE;
145:             return 'ob_start()';
146:         }
147:         return '} else {';
148:     }
149: 
150: 
151:     /**
152:      * {_$var |modifiers}
153:      */
154:     public function macroTranslate(MacroNode $node, PhpWriter $writer)
155:     {
156:         if ($node->closing) {
157:             return $writer->write('echo %modify($template->translate(ob_get_clean()))');
158: 
159:         } elseif ($node->isEmpty = ($node->args !== '')) {
160:             return $writer->write('echo %modify($template->translate(%node.args))');
161: 
162:         } else {
163:             return 'ob_start()';
164:         }
165:     }
166: 
167: 
168:     /**
169:      * {include "file" [,] [params]}
170:      */
171:     public function macroInclude(MacroNode $node, PhpWriter $writer)
172:     {
173:         $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])',
174:             $this->getCompiler()->getTemplateId());
175: 
176:         if ($node->modifiers) {
177:             return $writer->write('echo %modify(%raw->__toString(TRUE))', $code);
178:         } else {
179:             return $code . '->render()';
180:         }
181:     }
182: 
183: 
184:     /**
185:      * {use class MacroSet}
186:      */
187:     public function macroUse(MacroNode $node, PhpWriter $writer)
188:     {
189:         Nette\Callback::create($node->tokenizer->fetchWord(), 'install')
190:             ->invoke($this->getCompiler())
191:             ->initialize();
192:     }
193: 
194: 
195:     /**
196:      * {capture $variable}
197:      */
198:     public function macroCapture(MacroNode $node, PhpWriter $writer)
199:     {
200:         $variable = $node->tokenizer->fetchWord();
201:         if (substr($variable, 0, 1) !== '$') {
202:             throw new CompileException("Invalid capture block variable '$variable'");
203:         }
204:         $node->data->variable = $variable;
205:         return 'ob_start()';
206:     }
207: 
208: 
209:     /**
210:      * {/capture}
211:      */
212:     public function macroCaptureEnd(MacroNode $node, PhpWriter $writer)
213:     {
214:         return $node->data->variable . $writer->write(' = %modify(ob_get_clean())');
215:     }
216: 
217: 
218:     /**
219:      * {foreach ...}
220:      */
221:     public function macroEndForeach(MacroNode $node, PhpWriter $writer)
222:     {
223:         if ($node->modifiers !== '|noiterator' && preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) {
224:             $node->openingCode = '<?php $iterations = 0; foreach ($iterator = $_l->its[] = new Nette\Iterators\CachingIterator('
225:             . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . ') { ?>';
226:             $node->closingCode = '<?php $iterations++; } array_pop($_l->its); $iterator = end($_l->its) ?>';
227:         } else {
228:             $node->openingCode = '<?php $iterations = 0; foreach (' . $writer->formatArgs() . ') { ?>';
229:             $node->closingCode = '<?php $iterations++; } ?>';
230:         }
231:     }
232: 
233: 
234:     /**
235:      * n:class="..."
236:      */
237:     public function macroClass(MacroNode $node, PhpWriter $writer)
238:     {
239:         return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\' . %escape(implode(" ", array_unique($_l->tmp))) . \'"\'');
240:     }
241: 
242: 
243:     /**
244:      * n:attr="..."
245:      */
246:     public function macroAttr(MacroNode $node, PhpWriter $writer)
247:     {
248:         return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()');
249:     }
250: 
251: 
252:     /**
253:      * {attr ...}
254:      * @deprecated
255:      */
256:     public function macroOldAttr(MacroNode $node)
257:     {
258:         return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->');
259:     }
260: 
261: 
262:     /**
263:      * {dump ...}
264:      */
265:     public function macroDump(MacroNode $node, PhpWriter $writer)
266:     {
267:         $args = $writer->formatArgs();
268:         return 'Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(" . $writer->write('%var', $args) . " => $args)" : 'get_defined_vars()')
269:             . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))';
270:     }
271: 
272: 
273:     /**
274:      * {debugbreak ...}
275:      */
276:     public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
277:     {
278:         return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else')
279:             . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()');
280:     }
281: 
282: 
283:     /**
284:      * {var ...}
285:      * {default ...}
286:      */
287:     public function macroVar(MacroNode $node, PhpWriter $writer)
288:     {
289:         $out = '';
290:         $var = TRUE;
291:         $tokenizer = $writer->preprocess();
292:         while ($token = $tokenizer->fetchToken()) {
293:             if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) {
294:                 if ($node->name === 'default') {
295:                     $out .= "'" . ltrim($token['value'], "$") . "'";
296:                 } else {
297:                     $out .= '$' . ltrim($token['value'], "$");
298:                 }
299:                 $var = NULL;
300: 
301:             } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) {
302:                 $out .= $node->name === 'default' ? '=>' : '=';
303:                 $var = FALSE;
304: 
305:             } elseif ($token['value'] === ',' && $token['depth'] === 0) {
306:                 $out .= $node->name === 'default' ? ',' : ';';
307:                 $var = TRUE;
308: 
309:             } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) {
310:                 throw new CompileException("Unexpected '$token[value]' in {default $node->args}");
311: 
312:             } else {
313:                 $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
314:             }
315:         }
316:         return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
317:     }
318: 
319: 
320:     /**
321:      * {= ...}
322:      * {? ...}
323:      */
324:     public function macroExpr(MacroNode $node, PhpWriter $writer)
325:     {
326:         return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)');
327:     }
328: 
329: 
330:     /********************* run-time helpers ****************d*g**/
331: 
332: 
333:     /**
334:      * Includes subtemplate.
335:      * @param  mixed      included file name or template
336:      * @param  array      parameters
337:      * @param  Nette\Templating\ITemplate  current template
338:      * @return Nette\Templating\Template
339:      */
340:     public static function includeTemplate($destination, array $params, Nette\Templating\ITemplate $template)
341:     {
342:         if ($destination instanceof Nette\Templating\ITemplate) {
343:             $tpl = $destination;
344: 
345:         } elseif ($destination == NULL) { // intentionally ==
346:             throw new Nette\InvalidArgumentException("Template file name was not specified.");
347: 
348:         } elseif ($template instanceof Nette\Templating\IFileTemplate) {
349:             if (!preg_match('#/|\\\\|[a-z]:#iA', $destination)) {
350:                 $destination = dirname($template->getFile()) . '/' . $destination;
351:             }
352:             $tpl = clone $template;
353:             $tpl->setFile($destination);
354: 
355:         } else {
356:             throw new Nette\NotSupportedException('Macro {include "filename"} is supported only with Nette\Templating\IFileTemplate.');
357:         }
358: 
359:         $tpl->setParameters($params); // interface?
360:         return $tpl;
361:     }
362: 
363: 
364:     /**
365:      * Initializes local & global storage in template.
366:      * @return \stdClass
367:      */
368:     public static function initRuntime(Nette\Templating\ITemplate $template, $templateId)
369:     {
370:         // local storage
371:         if (isset($template->_l)) {
372:             $local = $template->_l;
373:             unset($template->_l);
374:         } else {
375:             $local = new \stdClass;
376:         }
377:         $local->templates[$templateId] = $template;
378: 
379:         // global storage
380:         if (!isset($template->_g)) {
381:             $template->_g = new \stdClass;
382:         }
383: 
384:         return array($local, $template->_g);
385:     }
386: 
387: }
388: 
Nette 2.0 API documentation generated by ApiGen 2.8.0