Packages

  • Nette
    • Application
      • Application\Diagnostics
      • Application\Responses
      • Application\Routers
      • Application\UI
    • Caching
      • Caching\Storages
    • ComponentModel
    • Config
      • Config\Extensions
    • Database
      • Database\Diagnostics
      • Database\Drivers
      • Database\Reflection
      • Database\Table
    • DI
      • DI\Diagnostics
    • Diagnostics
    • Forms
      • Forms\Controls
      • Forms\Rendering
    • Http
    • Iterators
    • Latte
      • Latte\Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Security\Diagnostics
    • Templating
    • Utils
      • Utils\PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

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