Packages

  • 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

  • Overview
  • Package
  • 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:  * @package Nette\Latte\Macros
  7:  */
  8: 
  9: 
 10: 
 11: /**
 12:  * Macros for UI.
 13:  *
 14:  * - {link destination ...} control link
 15:  * - {plink destination ...} presenter link
 16:  * - {snippet ?} ... {/snippet ?} control snippet
 17:  * - {contentType ...} HTTP Content-Type header
 18:  * - {status ...} HTTP status
 19:  *
 20:  * @author     David Grudl
 21:  * @package Nette\Latte\Macros
 22:  */
 23: class UIMacros extends MacroSet
 24: {
 25:     /** @var array */
 26:     private $namedBlocks = array();
 27: 
 28:     /** @var bool */
 29:     private $extends;
 30: 
 31: 
 32:     public static function install(LatteCompiler $compiler)
 33:     {
 34:         $me = new self($compiler);
 35:         $me->addMacro('include', array($me, 'macroInclude'));
 36:         $me->addMacro('includeblock', array($me, 'macroIncludeBlock'));
 37:         $me->addMacro('extends', array($me, 'macroExtends'));
 38:         $me->addMacro('layout', array($me, 'macroExtends'));
 39:         $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 40:         $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 41:         $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
 42:         $me->addMacro('ifset', array($me, 'macroIfset'), '}');
 43: 
 44:         $me->addMacro('widget', array($me, 'macroControl')); // deprecated - use control
 45:         $me->addMacro('control', array($me, 'macroControl'));
 46: 
 47:         $me->addMacro('href', NULL, NULL, create_function('MacroNode $node, PhpWriter $writer', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('me'=>$me)).'-1], EXTR_REFS);
 48:             return \' ?> href="<?php \' . $me->macroLink($node, $writer) . \' ?>"<?php \';
 49:         '));
 50:         $me->addMacro('plink', array($me, 'macroLink'));
 51:         $me->addMacro('link', array($me, 'macroLink'));
 52:         $me->addMacro('ifCurrent', array($me, 'macroIfCurrent'), '}'); // deprecated; use n:class="$presenter->linkCurrent ? ..."
 53: 
 54:         $me->addMacro('contentType', array($me, 'macroContentType'));
 55:         $me->addMacro('status', array($me, 'macroStatus'));
 56:     }
 57: 
 58: 
 59:     /**
 60:      * Initializes before template parsing.
 61:      * @return void
 62:      */
 63:     public function initialize()
 64:     {
 65:         $this->namedBlocks = array();
 66:         $this->extends = NULL;
 67:     }
 68: 
 69: 
 70:     /**
 71:      * Finishes template parsing.
 72:      * @return array(prolog, epilog)
 73:      */
 74:     public function finalize()
 75:     {
 76:         // try close last block
 77:         try {
 78:             $this->getCompiler()->writeMacro('/block');
 79:         } catch (CompileException $e) {
 80:         }
 81: 
 82:         $epilog = $prolog = array();
 83: 
 84:         if ($this->namedBlocks) {
 85:             foreach ($this->namedBlocks as $name => $code) {
 86:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
 87:                 $snippet = $name[0] === '_';
 88:                 $prolog[] = "//\n// block $name\n//\n"
 89:                     . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { "
 90:                     . "function $func(\$_l, \$_args) { foreach (\$_args as \$__k => \$__v) \$\$__k = \$__v"
 91:                     . ($snippet ? '; $_control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '')
 92:                     . "\n?>$code<?php\n}}";
 93:             }
 94:             $prolog[] = "//\n// end of blocks\n//";
 95:         }
 96: 
 97:         if ($this->namedBlocks || $this->extends) {
 98:             $prolog[] = "// template extending and snippets support";
 99: 
100:             $prolog[] = '$_l->extends = '
101:                 . ($this->extends ? $this->extends : 'empty($template->_extended) && isset($_control) && $_control instanceof Presenter ? $_control->findLayoutTemplateFile() : NULL')
102:                 . '; $template->_extended = $_extended = TRUE;';
103: 
104:             $prolog[] = '
105: if ($_l->extends) {
106:     ' . ($this->namedBlocks ? 'ob_start();' : 'return CoreMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render();') . '
107: 
108: } elseif (!empty($_control->snippetMode)) {
109:     return UIMacros::renderSnippets($_control, $_l, get_defined_vars());
110: }';
111:         } else {
112:             $prolog[] = '
113: // snippets support
114: if (!empty($_control->snippetMode)) {
115:     return UIMacros::renderSnippets($_control, $_l, get_defined_vars());
116: }';
117:         }
118: 
119:         return array(implode("\n\n", $prolog), implode("\n", $epilog));
120:     }
121: 
122: 
123:     /********************* macros ****************d*g**/
124: 
125: 
126:     /**
127:      * {include #block}
128:      */
129:     public function macroInclude(MacroNode $node, PhpWriter $writer)
130:     {
131:         $destination = $node->tokenizer->fetchWord(); // destination [,] [params]
132:         if (substr($destination, 0, 1) !== '#') {
133:             return FALSE;
134:         }
135: 
136:         $destination = ltrim($destination, '#');
137:         $parent = $destination === 'parent';
138:         if ($destination === 'parent' || $destination === 'this') {
139:             for ($item = $node->parentNode; $item && $item->name !== 'block' && !isset($item->data->name); $item = $item->parentNode);
140:             if (!$item) {
141:                 throw new CompileException("Cannot include $destination block outside of any block.");
142:             }
143:             $destination = $item->data->name;
144:         }
145: 
146:         $name = Strings::contains($destination, '$') ? $destination : var_export($destination, TRUE);
147:         if (isset($this->namedBlocks[$destination]) && !$parent) {
148:             $cmd = "call_user_func(reset(\$_l->blocks[$name]), \$_l, %node.array? + get_defined_vars())";
149:         } else {
150:             $cmd = 'UIMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, %node.array? + " . ($parent ? 'get_defined_vars' : '$template->getParameters') . '())';
151:         }
152: 
153:         if ($node->modifiers) {
154:             return $writer->write("ob_start(); $cmd; echo %modify(ob_get_clean())");
155:         } else {
156:             return $writer->write($cmd);
157:         }
158:     }
159: 
160: 
161:     /**
162:      * {includeblock "file"}
163:      */
164:     public function macroIncludeBlock(MacroNode $node, PhpWriter $writer)
165:     {
166:         return $writer->write('CoreMacros::includeTemplate(%node.word, %node.array? + get_defined_vars(), $_l->templates[%var])->render()',
167:             $this->getCompiler()->getTemplateId());
168:     }
169: 
170: 
171:     /**
172:      * {extends auto | none | $var | "file"}
173:      */
174:     public function macroExtends(MacroNode $node, PhpWriter $writer)
175:     {
176:         if (!$node->args) {
177:             throw new CompileException('Missing destination in {' . $node->name . '}');
178:         }
179:         if (!empty($node->parentNode)) {
180:             throw new CompileException('{' . $node->name . '} must be placed outside any macro.');
181:         }
182:         if ($this->extends !== NULL) {
183:             throw new CompileException('Multiple {' . $node->name . '} declarations are not allowed.');
184:         }
185:         if ($node->args === 'none') {
186:             $this->extends = 'FALSE';
187:         } elseif ($node->args === 'auto') {
188:             $this->extends = '$_presenter->findLayoutTemplateFile()';
189:         } else {
190:             $this->extends = $writer->write('%node.word%node.args');
191:         }
192:         return;
193:     }
194: 
195: 
196:     /**
197:      * {block [[#]name]}
198:      * {snippet [name [,]] [tag]}
199:      * {define [#]name}
200:      */
201:     public function macroBlock(MacroNode $node, PhpWriter $writer)
202:     {
203:         $name = $node->tokenizer->fetchWord();
204: 
205:         if ($node->name === 'block' && $name === FALSE) { // anonymous block
206:             return $node->modifiers === '' ? '' : 'ob_start()';
207:         }
208: 
209:         $node->data->name = $name = ltrim($name, '#');
210:         if ($name == NULL) {
211:             if ($node->name !== 'snippet') {
212:                 throw new CompileException("Missing block name.");
213:             }
214: 
215:         } elseif (Strings::contains($name, '$')) { // dynamic block/snippet
216:             if ($node->name === 'snippet') {
217:                 for ($parent = $node->parentNode; $parent && $parent->name !== 'snippet'; $parent = $parent->parentNode);
218:                 if (!$parent) {
219:                     throw new CompileException("Dynamic snippets are allowed only inside static snippet.");
220:                 }
221:                 $parent->data->dynamic = TRUE;
222:                 $node->data->leave = TRUE;
223:                 $node->closingCode = "<?php \$_dynSnippets[\$_dynSnippetId] = ob_get_flush() ?>";
224: 
225:                 if ($node->htmlNode) {
226:                     $node->attrCode = $writer->write("<?php echo ' id=\"' . (\$_dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)})) . '\"' ?>");
227:                     return $writer->write('ob_start()');
228:                 }
229:                 $tag = trim($node->tokenizer->fetchWord(), '<>');
230:                 $tag = $tag ? $tag : 'div';
231:                 $node->closingCode .= "\n</$tag>";
232:                 return $writer->write("?>\n<$tag id=\"<?php echo \$_dynSnippetId = \$_control->getSnippetId({$writer->formatWord($name)}) ?>\"><?php ob_start()");
233: 
234:             } else {
235:                 $node->data->leave = TRUE;
236:                 $fname = $writer->formatWord($name);
237:                 $node->closingCode = "<?php }} " . ($node->name === 'define' ? '' : "call_user_func(reset(\$_l->blocks[$fname]), \$_l, get_defined_vars())") . " ?>";
238:                 $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
239:                 return "\n\n//\n// block $name\n//\n"
240:                     . "if (!function_exists(\$_l->blocks[$fname]['{$this->getCompiler()->getTemplateId()}'] = '$func')) { "
241:                     . "function $func(\$_l, \$_args) { foreach (\$_args as \$__k => \$__v) \$\$__k = \$__v";
242:             }
243:         }
244: 
245:         // static block/snippet
246:         if ($node->name === 'snippet') {
247:             $node->data->name = $name = '_' . $name;
248:         }
249: 
250:         if (isset($this->namedBlocks[$name])) {
251:             throw new CompileException("Cannot redeclare static block '$name'");
252:         }
253: 
254:         $prolog = $this->namedBlocks ? '' : "if (\$_l->extends) { ob_end_clean(); return CoreMacros::includeTemplate(\$_l->extends, get_defined_vars(), \$template)->render(); }\n";
255:         $this->namedBlocks[$name] = TRUE;
256: 
257:         $include = 'call_user_func(reset($_l->blocks[%var]), $_l, ' . ($node->name === 'snippet' ? '$template->getParameters()' : 'get_defined_vars()') . ')';
258:         if ($node->modifiers) {
259:             $include = "ob_start(); $include; echo %modify(ob_get_clean())";
260:         }
261: 
262:         if ($node->name === 'snippet') {
263:             if ($node->htmlNode) {
264:                 $node->attrCode = $writer->write('<?php echo \' id="\' . $_control->getSnippetId(%var) . \'"\' ?>', (string) substr($name, 1));
265:                 return $writer->write($prolog . $include, $name);
266:             }
267:             $tag = trim($node->tokenizer->fetchWord(), '<>');
268:             $tag = $tag ? $tag : 'div';
269:             return $writer->write("$prolog ?>\n<$tag id=\"<?php echo \$_control->getSnippetId(%var) ?>\"><?php $include ?>\n</$tag><?php ",
270:                 (string) substr($name, 1), $name
271:             );
272: 
273:         } elseif ($node->name === 'define') {
274:             return $prolog;
275: 
276:         } else {
277:             return $writer->write($prolog . $include, $name);
278:         }
279:     }
280: 
281: 
282:     /**
283:      * {/block}
284:      * {/snippet}
285:      * {/define}
286:      */
287:     public function macroBlockEnd(MacroNode $node, PhpWriter $writer)
288:     {
289:         if (isset($node->data->name)) { // block, snippet, define
290:             if ($node->name === 'snippet' && $node->htmlNode && !$node->prefix // n:snippet -> n:inner-snippet
291:                 && preg_match('#^.*? n:\w+>\n?#s', $node->content, $m1) && preg_match('#[ \t]*<[^<]+\z#s', $node->content, $m2))
292:             {
293:                 $node->openingCode = $m1[0] . $node->openingCode;
294:                 $node->content = substr($node->content, strlen($m1[0]), -strlen($m2[0]));
295:                 $node->closingCode .= $m2[0];
296:             }
297: 
298:             if (empty($node->data->leave)) {
299:                 if (!empty($node->data->dynamic)) {
300:                     $node->content .= '<?php if (isset($_dynSnippets)) return $_dynSnippets; ?>';
301:                 }
302:                 $this->namedBlocks[$node->data->name] = $tmp = rtrim(ltrim($node->content, "\n"), " \t");
303:                 $node->content = substr_replace($node->content, $node->openingCode . "\n", strspn($node->content, "\n"), strlen($tmp));
304:                 $node->openingCode = "<?php ?>";
305:             }
306: 
307:         } elseif ($node->modifiers) { // anonymous block with modifier
308:             return $writer->write('echo %modify(ob_get_clean())');
309:         }
310:     }
311: 
312: 
313:     /**
314:      * {ifset #block}
315:      */
316:     public function macroIfset(MacroNode $node, PhpWriter $writer)
317:     {
318:         if (!Strings::contains($node->args, '#')) {
319:             return FALSE;
320:         }
321:         $list = array();
322:         while (($name = $node->tokenizer->fetchWord()) !== FALSE) {
323:             $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name;
324:         }
325:         return 'if (isset(' . implode(', ', $list) . ')) {';
326:     }
327: 
328: 
329:     /**
330:      * {control name[:method] [params]}
331:      */
332:     public function macroControl(MacroNode $node, PhpWriter $writer)
333:     {
334:         $pair = $node->tokenizer->fetchWord();
335:         if ($pair === FALSE) {
336:             throw new CompileException("Missing control name in {control}");
337:         }
338:         $pair = explode(':', $pair, 2);
339:         $name = $writer->formatWord($pair[0]);
340:         $method = isset($pair[1]) ? ucfirst($pair[1]) : '';
341:         $method = Strings::match($method, '#^\w*\z#') ? "render$method" : "{\"render$method\"}";
342:         $param = $writer->formatArray();
343:         if (!Strings::contains($node->args, '=>')) {
344:             $param = substr($param, 6, -1); // removes array()
345:         }
346:         return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '')
347:             . '$_ctrl = $_control->getComponent(' . $name . '); '
348:             . 'if ($_ctrl instanceof IRenderable) $_ctrl->validateControl(); '
349:             . "\$_ctrl->$method($param)";
350:     }
351: 
352: 
353:     /**
354:      * {link destination [,] [params]}
355:      * {plink destination [,] [params]}
356:      * n:href="destination [,] [params]"
357:      */
358:     public function macroLink(MacroNode $node, PhpWriter $writer)
359:     {
360:         return $writer->write('echo %escape(%modify(' . ($node->name === 'plink' ? '$_presenter' : '$_control') . '->link(%node.word, %node.array?)))');
361:     }
362: 
363: 
364:     /**
365:      * {ifCurrent destination [,] [params]}
366:      */
367:     public function macroIfCurrent(MacroNode $node, PhpWriter $writer)
368:     {
369:         return $writer->write(($node->args ? 'try { $_presenter->link(%node.word, %node.array?); } catch (InvalidLinkException $e) {}' : '')
370:             . '; if ($_presenter->getLastCreatedRequestFlag("current")) {');
371:     }
372: 
373: 
374:     /**
375:      * {contentType ...}
376:      */
377:     public function macroContentType(MacroNode $node, PhpWriter $writer)
378:     {
379:         if (Strings::contains($node->args, 'xhtml')) {
380:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_XHTML);
381: 
382:         } elseif (Strings::contains($node->args, 'html')) {
383:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_HTML);
384: 
385:         } elseif (Strings::contains($node->args, 'xml')) {
386:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_XML);
387: 
388:         } elseif (Strings::contains($node->args, 'javascript')) {
389:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_JS);
390: 
391:         } elseif (Strings::contains($node->args, 'css')) {
392:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_CSS);
393: 
394:         } elseif (Strings::contains($node->args, 'calendar')) {
395:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_ICAL);
396: 
397:         } else {
398:             $this->getCompiler()->setContentType(LatteCompiler::CONTENT_TEXT);
399:         }
400: 
401:         // temporary solution
402:         if (Strings::contains($node->args, '/')) {
403:             return $writer->write('$netteHttpResponse->setHeader("Content-Type", %var)', $node->args);
404:         }
405:     }
406: 
407: 
408:     /**
409:      * {status ...}
410:      */
411:     public function macroStatus(MacroNode $node, PhpWriter $writer)
412:     {
413:         return $writer->write((substr($node->args, -1) === '?' ? 'if (!$netteHttpResponse->isSent()) ' : '') .
414:             '$netteHttpResponse->setCode(%var)', (int) $node->args
415:         );
416:     }
417: 
418: 
419:     /********************* run-time writers ****************d*g**/
420: 
421: 
422:     /**
423:      * Calls block.
424:      * @return void
425:      */
426:     public static function callBlock(stdClass $context, $name, array $params)
427:     {
428:         if (empty($context->blocks[$name])) {
429:             throw new InvalidStateException("Cannot include undefined block '$name'.");
430:         }
431:         $block = reset($context->blocks[$name]);
432:         $block($context, $params);
433:     }
434: 
435: 
436:     /**
437:      * Calls parent block.
438:      * @return void
439:      */
440:     public static function callBlockParent(stdClass $context, $name, array $params)
441:     {
442:         if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) {
443:             throw new InvalidStateException("Cannot include undefined parent block '$name'.");
444:         }
445:         $block($context, $params);
446:         prev($context->blocks[$name]);
447:     }
448: 
449: 
450:     public static function renderSnippets(Control $control, stdClass $local, array $params)
451:     {
452:         $control->snippetMode = FALSE;
453:         $payload = $control->getPresenter()->getPayload();
454:         if (isset($local->blocks)) {
455:             foreach ($local->blocks as $name => $function) {
456:                 if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) {
457:                     continue;
458:                 }
459:                 ob_start();
460:                 $function = reset($function);
461:                 $snippets = $function($local, $params);
462:                 $payload->snippets[$id = $control->getSnippetId(substr($name, 1))] = ob_get_clean();
463:                 if ($snippets) {
464:                     $payload->snippets += $snippets;
465:                     unset($payload->snippets[$id]);
466:                 }
467:             }
468:         }
469:         $control->snippetMode = TRUE;
470:         if ($control instanceof IRenderable) {
471:             $queue = array($control);
472:             do {
473:                 foreach (array_shift($queue)->getComponents() as $child) {
474:                     if ($child instanceof IRenderable) {
475:                         if ($child->isControlInvalid()) {
476:                             $child->snippetMode = TRUE;
477:                             $child->render();
478:                             $child->snippetMode = FALSE;
479:                         }
480:                     } elseif ($child instanceof IComponentContainer) {
481:                         $queue[] = $child;
482:                     }
483:                 }
484:             } while ($queue);
485:         }
486:     }
487: 
488: }
489: 
Nette Framework 2.0.18 (for PHP 5.2, un-prefixed) API documentation generated by ApiGen 2.8.0