Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • 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

Classes

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