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\MacroNode;
 12: use Latte\PhpWriter;
 13: use Latte\CompileException;
 14: 
 15: 
 16: /**
 17:  * Block macros.
 18:  */
 19: class BlockMacros extends MacroSet
 20: {
 21:     /** @var array */
 22:     private $namedBlocks = array();
 23: 
 24:     /** @var bool */
 25:     private $extends;
 26: 
 27: 
 28:     public static function install(Latte\Compiler $compiler)
 29:     {
 30:         $me = new static($compiler);
 31:         $me->addMacro('include', array($me, 'macroInclude'));
 32:         $me->addMacro('includeblock', array($me, 'macroIncludeBlock'));
 33:         $me->addMacro('extends', array($me, 'macroExtends'));
 34:         $me->addMacro('layout', array($me, 'macroExtends'));
 35:         $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 36:         $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 37:         $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 38:         $me->addMacro('snippetArea', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 39:         $me->addMacro('ifset', array($me, 'macroIfset'), '}');
 40:         $me->addMacro('elseifset', array($me, 'macroIfset'), '}');
 41:     }
 42: 
 43: 
 44:     /**
 45:      * Initializes before template parsing.
 46:      * @return void
 47:      */
 48:     public function initialize()
 49:     {
 50:         $this->namedBlocks = array();
 51:         $this->extends = NULL;
 52:     }
 53: 
 54: 
 55:     /**
 56:      * Finishes template parsing.
 57:      * @return array(prolog, epilog)
 58:      */
 59:     public function finalize()
 60:     {
 61:         // try close last block
 62:         $last = $this->getCompiler()->getMacroNode();
 63:         if ($last && $last->name === 'block') {
 64:             $this->getCompiler()->closeMacro($last->name);
 65:         }
 66: 
 67:         $epilog = $prolog = array();
 68: 
 69:         if ($this->namedBlocks) {
 70:             foreach ($this->namedBlocks as $name => $code) {
 71:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
 72:                 $snippet = $name[0] === '_';
 73:                 $prolog[] = "//\n// block $name\n//\n"
 74:                     . "if (!function_exists(\$_b->blocks[" . var_export($name, TRUE) . "][] = '$func')) { "
 75:                     . "function $func(\$_b, \$_args) { foreach (\$_args as \$__k => \$__v) \$\$__k = \$__v"
 76:                     . ($snippet ? '; $_control->redrawControl(' . var_export((string) substr($name, 1), TRUE) . ', FALSE)' : '')
 77:                     . "\n?>$code<?php\n}}";
 78:             }
 79:             $prolog[] = "//\n// end of blocks\n//";
 80:         }
 81: 
 82:         if ($this->namedBlocks || $this->extends) {
 83:             $prolog[] = '// template extending';
 84: 
 85:             $prolog[] = '$_l->extends = '
 86:                 . ($this->extends ? $this->extends : 'empty($_g->extended) && isset($_control) && $_control instanceof Nette\Application\UI\Presenter ? $_control->findLayoutTemplateFile() : NULL')
 87:                 . '; $_g->extended = TRUE;';
 88: 
 89:             $prolog[] = 'if ($_l->extends) { ob_start(function () {});}';
 90:             if (!$this->namedBlocks) {
 91:                 $epilog[] = 'if ($_l->extends) { ob_end_clean(); return $template->renderChildTemplate($_l->extends, get_defined_vars());}';
 92:             }
 93:         }
 94: 
 95:         return array(implode("\n\n", $prolog), implode("\n", $epilog));
 96:     }
 97: 
 98: 
 99:     /********************* macros ****************d*g**/
100: 
101: 
102:     /**
103:      * {include #block}
104:      */
105:     public function macroInclude(MacroNode $node, PhpWriter $writer)
106:     {
107:         $destination = $node->tokenizer->fetchWord(); // destination [,] [params]
108:         if (!preg_match('~#|[\w-]+\z~A', $destination)) {
109:             return FALSE;
110:         }
111: 
112:         $destination = ltrim($destination, '#');
113:         $parent = $destination === 'parent';
114:         if ($destination === 'parent' || $destination === 'this') {
115:             for ($item = $node->parentNode; $item && $item->name !== 'block' && !isset($item->data->name); $item = $item->parentNode);
116:             if (!$item) {
117:                 throw new CompileException("Cannot include $destination block outside of any block.");
118:             }
119:             $destination = $item->data->name;
120:         }
121: 
122:         $name = strpos($destination, '$') === FALSE ? var_export($destination, TRUE) : $destination;
123:         if (isset($this->namedBlocks[$destination]) && !$parent) {
124:             $cmd = "call_user_func(reset(\$_b->blocks[$name]), \$_b, %node.array? + get_defined_vars())";
125:         } else {
126:             $cmd = 'Latte\Macros\BlockMacrosRuntime::callBlock' . ($parent ? 'Parent' : '') . "(\$_b, $name, %node.array? + " . ($parent ? 'get_defined_vars' : '$template->getParameters') . '())';
127:         }
128: 
129:         if ($node->modifiers) {
130:             return $writer->write("ob_start(function () {}); $cmd; echo %modify(ob_get_clean())");
131:         } else {
132:             return $writer->write($cmd);
133:         }
134:     }
135: 
136: 
137:     /**
138:      * {includeblock "file"}
139:      */
140:     public function macroIncludeBlock(MacroNode $node, PhpWriter $writer)
141:     {
142:         if ($node->modifiers) {
143:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
144:         }
145:         return $writer->write(
146:             'ob_start(function () {}); $_g->includingBlock = isset($_g->includingBlock) ? ++$_g->includingBlock : 1; $_b->templates[%var]->renderChildTemplate(%node.word, %node.array? + get_defined_vars()); $_g->includingBlock--; echo rtrim(ob_get_clean())',
147:             $this->getCompiler()->getTemplateId()
148:         );
149:     }
150: 
151: 
152:     /**
153:      * {extends auto | none | $var | "file"}
154:      */
155:     public function macroExtends(MacroNode $node, PhpWriter $writer)
156:     {
157:         if ($node->modifiers) {
158:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
159:         }
160:         if (!$node->args) {
161:             throw new CompileException("Missing destination in {{$node->name}}");
162:         }
163:         if (!empty($node->parentNode)) {
164:             throw new CompileException("{{$node->name}} must be placed outside any macro.");
165:         }
166:         if ($this->extends !== NULL) {
167:             throw new CompileException("Multiple {{$node->name}} declarations are not allowed.");
168:         }
169:         if ($node->args === 'none') {
170:             $this->extends = 'FALSE';
171:         } elseif ($node->args === 'auto') {
172:             $this->extends = '$_presenter->findLayoutTemplateFile()';
173:         } else {
174:             $this->extends = $writer->write('%node.word%node.args');
175:         }
176:         return;
177:     }
178: 
179: 
180:     /**
181:      * {block [[#]name]}
182:      * {snippet [name [,]] [tag]}
183:      * {snippetArea [name]}
184:      * {define [#]name}
185:      */
186:     public function macroBlock(MacroNode $node, PhpWriter $writer)
187:     {
188:         $name = $node->tokenizer->fetchWord();
189: 
190:         if ($node->name === '#') {
191:             trigger_error('Shortcut {#block} is deprecated.', E_USER_DEPRECATED);
192: 
193:         } elseif ($node->name === 'block' && $name === FALSE) { // anonymous block
194:             return $node->modifiers === '' ? '' : 'ob_start(function () {})';
195:         }
196: 
197:         $node->data->name = $name = ltrim($name, '#');
198:         if ($name == NULL) {
199:             if ($node->name === 'define') {
200:                 throw new CompileException('Missing block name.');
201:             }
202: 
203:         } elseif (strpos($name, '$') !== FALSE) { // dynamic block/snippet
204:             if ($node->name === 'snippet') {
205:                 for ($parent = $node->parentNode; $parent && !($parent->name === 'snippet' || $parent->name === 'snippetArea'); $parent = $parent->parentNode);
206:                 if (!$parent) {
207:                     throw new CompileException('Dynamic snippets are allowed only inside static snippet/snippetArea.');
208:                 }
209:                 $parent->data->dynamic = TRUE;
210:                 $node->data->leave = TRUE;
211:                 $node->closingCode = "<?php \$_l->dynSnippets[\$_l->dynSnippetId] = ob_get_flush() ?>";
212: 
213:                 if ($node->prefix) {
214:                     $node->attrCode = $writer->write("<?php echo ' id=\"' . (\$_l->dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)})) . '\"' ?>");
215:                     return $writer->write('ob_start()');
216:                 }
217:                 $tag = trim($node->tokenizer->fetchWord(), '<>');
218:                 $tag = $tag ? $tag : 'div';
219:                 $node->closingCode .= "\n</$tag>";
220:                 return $writer->write("?>\n<$tag id=\"<?php echo \$_l->dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)}) ?>\"><?php ob_start()");
221: 
222:             } else {
223:                 $node->data->leave = TRUE;
224:                 $fname = $writer->formatWord($name);
225:                 $node->closingCode = '<?php }} ' . ($node->name === 'define' ? '' : "call_user_func(reset(\$_b->blocks[$fname]), \$_b, get_defined_vars())") . ' ?>';
226:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
227:                 return "\n\n//\n// block $name\n//\n"
228:                     . "if (!function_exists(\$_b->blocks[$fname]['{$this->getCompiler()->getTemplateId()}'] = '$func')) { "
229:                     . "function $func(\$_b, \$_args) { foreach (\$_args as \$__k => \$__v) \$\$__k = \$__v";
230:             }
231:         }
232: 
233:         // static snippet/snippetArea
234:         if ($node->name === 'snippet' || $node->name === 'snippetArea') {
235:             if ($node->prefix && isset($node->htmlNode->attrs['id'])) {
236:                 throw new CompileException('Cannot combine HTML attribute id with n:snippet.');
237:             }
238:             $node->data->name = $name = '_' . $name;
239:         }
240: 
241:         if (isset($this->namedBlocks[$name])) {
242:             throw new CompileException("Cannot redeclare static {$node->name} '$name'");
243:         }
244: 
245:         $prolog = $this->namedBlocks ? '' : "if (\$_l->extends) { ob_end_clean(); return \$template->renderChildTemplate(\$_l->extends, get_defined_vars()); }\n";
246:         $this->namedBlocks[$name] = TRUE;
247: 
248:         $include = 'call_user_func(reset($_b->blocks[%var]), $_b, ' . (($node->name === 'snippet' || $node->name === 'snippetArea') ? '$template->getParameters()' : 'get_defined_vars()') . ')';
249:         if ($node->modifiers) {
250:             $include = "ob_start(function () {}); $include; echo %modify(ob_get_clean())";
251:         }
252: 
253:         if ($node->name === 'snippet') {
254:             if ($node->prefix) {
255:                 $node->attrCode = $writer->write('<?php echo \' id="\' . $_control->getSnippetId(%var) . \'"\' ?>', (string) substr($name, 1));
256:                 return $writer->write($prolog . $include, $name);
257:             }
258:             $tag = trim($node->tokenizer->fetchWord(), '<>');
259:             $tag = $tag ? $tag : 'div';
260:             return $writer->write("$prolog ?>\n<$tag id=\"<?php echo \$_control->getSnippetId(%var) ?>\"><?php $include ?>\n</$tag><?php ",
261:                 (string) substr($name, 1), $name
262:             );
263: 
264:         } elseif ($node->name === 'define') {
265:             return $prolog;
266: 
267:         } else { // block, snippetArea
268:             return $writer->write($prolog . $include, $name);
269:         }
270:     }
271: 
272: 
273:     /**
274:      * {/block}
275:      * {/snippet}
276:      * {/snippetArea}
277:      * {/define}
278:      */
279:     public function macroBlockEnd(MacroNode $node, PhpWriter $writer)
280:     {
281:         if (isset($node->data->name)) { // block, snippet, define
282:             if ($node->name === 'snippet' && $node->prefix === MacroNode::PREFIX_NONE // n:snippet -> n:inner-snippet
283:                 && preg_match('#^.*? n:\w+>\n?#s', $node->content, $m1) && preg_match('#[ \t]*<[^<]+\z#s', $node->content, $m2)
284:             ) {
285:                 $node->openingCode = $m1[0] . $node->openingCode;
286:                 $node->content = substr($node->content, strlen($m1[0]), -strlen($m2[0]));
287:                 $node->closingCode .= $m2[0];
288:             }
289: 
290:             if (empty($node->data->leave)) {
291:                 if ($node->name === 'snippetArea' && empty($node->data->dynamic)) {
292:                     $node->content = "<?php \$_control->snippetMode = isset(\$_snippetMode) && \$_snippetMode; ?>{$node->content}<?php \$_control->snippetMode = FALSE; ?>";
293:                 }
294:                 if (!empty($node->data->dynamic)) {
295:                     $node->content .= '<?php if (isset($_l->dynSnippets)) return $_l->dynSnippets; ?>';
296:                 }
297:                 if ($node->name === 'snippetArea') {
298:                     $node->content .= '<?php return FALSE; ?>';
299:                 }
300:                 $this->namedBlocks[$node->data->name] = $tmp = preg_replace('#^\n+|(?<=\n)[ \t]+\z#', '', $node->content);
301:                 $node->content = substr_replace($node->content, $node->openingCode . "\n", strspn($node->content, "\n"), strlen($tmp));
302:                 $node->openingCode = '<?php ?>';
303:             }
304: 
305:         } elseif ($node->modifiers) { // anonymous block with modifier
306:             return $writer->write('echo %modify(ob_get_clean())');
307:         }
308:     }
309: 
310: 
311:     /**
312:      * {ifset #block}
313:      * {elseifset #block}
314:      */
315:     public function macroIfset(MacroNode $node, PhpWriter $writer)
316:     {
317:         if ($node->modifiers) {
318:             trigger_error("Modifiers are not allowed in {{$node->name}}", E_USER_WARNING);
319:         }
320:         if (!preg_match('~#|[\w-]+\z~A', $node->args)) {
321:             return FALSE;
322:         }
323:         $list = array();
324:         while (($name = $node->tokenizer->fetchWord()) !== FALSE) {
325:             $list[] = preg_match('~#|[\w-]+\z~A', $name)
326:                 ? '$_b->blocks["' . ltrim($name, '#') . '"]'
327:                 : $writer->formatArgs(new Latte\MacroTokens($name));
328:         }
329:         return ($node->name === 'elseifset' ? '} else' : '')
330:             . 'if (isset(' . implode(', ', $list) . ')) {';
331:     }
332: 
333: }
334: 
Nette 2.3-20161221 API API documentation generated by ApiGen 2.8.0