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
  • PHP

Classes

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