Packages

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

Classes

  • NFileTemplate
  • NTemplate
  • NTemplateHelpers

Interfaces

  • IFileTemplate
  • ITemplate

Exceptions

  • NTemplateException
  • 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, 2011 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\Templating
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * Template.
 17:  *
 18:  * @author     David Grudl
 19:  * @package Nette\Templating
 20:  */
 21: class NTemplate extends NObject implements ITemplate
 22: {
 23:     /** @var bool */
 24:     public $warnOnUndefined = TRUE;
 25: 
 26:     /** @var array of function(Template $sender); Occurs before a template is compiled - implement to customize the filters */
 27:     public $onPrepareFilters = array();
 28: 
 29:     /** @var string */
 30:     private $source;
 31: 
 32:     /** @var array */
 33:     private $params = array();
 34: 
 35:     /** @var array compile-time filters */
 36:     private $filters = array();
 37: 
 38:     /** @var array run-time helpers */
 39:     private $helpers = array();
 40: 
 41:     /** @var array */
 42:     private $helperLoaders = array();
 43: 
 44:     /** @var ICacheStorage */
 45:     private $cacheStorage;
 46: 
 47: 
 48: 
 49:     /**
 50:      * Sets template source code.
 51:      * @param  string
 52:      * @return NTemplate  provides a fluent interface
 53:      */
 54:     public function setSource($source)
 55:     {
 56:         $this->source = $source;
 57:         return $this;
 58:     }
 59: 
 60: 
 61: 
 62:     /**
 63:      * Returns template source code.
 64:      * @return source
 65:      */
 66:     public function getSource()
 67:     {
 68:         return $this->source;
 69:     }
 70: 
 71: 
 72: 
 73:     /********************* rendering ****************d*g**/
 74: 
 75: 
 76: 
 77:     /**
 78:      * Renders template to output.
 79:      * @return void
 80:      */
 81:     public function render()
 82:     {
 83:         $cache = new NCache($storage = $this->getCacheStorage(), 'Nette.Template');
 84:         $cached = $compiled = $cache->load($this->source);
 85: 
 86:         if ($compiled === NULL) {
 87:             $compiled = $this->compile();
 88:             $cache->save($this->source, $compiled, array(NCache::CONSTS => 'NFramework::REVISION'));
 89:             $cache->release();
 90:             $cached = $cache->load($this->source);
 91:         }
 92: 
 93:         if ($cached !== NULL && $storage instanceof NPhpFileStorage) {
 94:             NLimitedScope::load($cached['file'], $this->getParams());
 95:         } else {
 96:             NLimitedScope::evaluate($compiled, $this->getParams());
 97:         }
 98:     }
 99: 
100: 
101: 
102:     /**
103:      * Renders template to file.
104:      * @param  string
105:      * @return void
106:      */
107:     public function save($file)
108:     {
109:         if (file_put_contents($file, $this->__toString(TRUE)) === FALSE) {
110:             throw new IOException("Unable to save file '$file'.");
111:         }
112:     }
113: 
114: 
115: 
116:     /**
117:      * Renders template to string.
118:      * @param  bool  can throw exceptions? (hidden parameter)
119:      * @return string
120:      */
121:     public function __toString()
122:     {
123:         $args = func_get_args();
124:         ob_start();
125:         try {
126:             $this->render();
127:             return ob_get_clean();
128: 
129:         } catch (Exception $e) {
130:             ob_end_clean();
131:             if ($args && $args[0]) {
132:                 throw $e;
133:             } else {
134:                 NDebugger::toStringException($e);
135:             }
136:         }
137:     }
138: 
139: 
140: 
141:     /**
142:      * Applies filters on template content.
143:      * @return string
144:      */
145:     public function compile()
146:     {
147:         if (!$this->filters) {
148:             $this->onPrepareFilters($this);
149:         }
150: 
151:         $code = $this->getSource();
152:         foreach ($this->filters as $filter) {
153:             $code = self::extractPhp($code, $blocks);
154:             $code = $filter->invoke($code);
155:             $code = strtr($code, $blocks); // put PHP code back
156:         }
157: 
158:         return self::optimizePhp($code);
159:     }
160: 
161: 
162: 
163:     /********************* template filters & helpers ****************d*g**/
164: 
165: 
166: 
167:     /**
168:      * Registers callback as template compile-time filter.
169:      * @param  callback
170:      * @return void
171:      */
172:     public function registerFilter($callback)
173:     {
174:         $callback = callback($callback);
175:         if (in_array($callback, $this->filters)) {
176:             throw new InvalidStateException("Filter '$callback' was registered twice.");
177:         }
178:         $this->filters[] = $callback;
179:     }
180: 
181: 
182: 
183:     /**
184:      * Returns all registered compile-time filters.
185:      * @return array
186:      */
187:     final public function getFilters()
188:     {
189:         return $this->filters;
190:     }
191: 
192: 
193: 
194:     /**
195:      * Registers callback as template run-time helper.
196:      * @param  string
197:      * @param  callback
198:      * @return void
199:      */
200:     public function registerHelper($name, $callback)
201:     {
202:         $this->helpers[strtolower($name)] = callback($callback);
203:     }
204: 
205: 
206: 
207:     /**
208:      * Registers callback as template run-time helpers loader.
209:      * @param  callback
210:      * @return void
211:      */
212:     public function registerHelperLoader($callback)
213:     {
214:         $this->helperLoaders[] = callback($callback);
215:     }
216: 
217: 
218: 
219:     /**
220:      * Returns all registered run-time helpers.
221:      * @return array
222:      */
223:     final public function getHelpers()
224:     {
225:         return $this->helpers;
226:     }
227: 
228: 
229: 
230:     /**
231:      * Call a template run-time helper. Do not call directly.
232:      * @param  string  helper name
233:      * @param  array   arguments
234:      * @return mixed
235:      */
236:     public function __call($name, $args)
237:     {
238:         $lname = strtolower($name);
239:         if (!isset($this->helpers[$lname])) {
240:             foreach ($this->helperLoaders as $loader) {
241:                 $helper = $loader->invoke($lname);
242:                 if ($helper) {
243:                     $this->registerHelper($lname, $helper);
244:                     return $this->helpers[$lname]->invokeArgs($args);
245:                 }
246:             }
247:             return parent::__call($name, $args);
248:         }
249: 
250:         return $this->helpers[$lname]->invokeArgs($args);
251:     }
252: 
253: 
254: 
255:     /**
256:      * Sets translate adapter.
257:      * @param  ITranslator
258:      * @return NTemplate  provides a fluent interface
259:      */
260:     public function setTranslator(ITranslator $translator = NULL)
261:     {
262:         $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
263:         return $this;
264:     }
265: 
266: 
267: 
268:     /********************* template parameters ****************d*g**/
269: 
270: 
271: 
272:     /**
273:      * Adds new template parameter.
274:      * @param  string  name
275:      * @param  mixed   value
276:      * @return void
277:      */
278:     public function add($name, $value)
279:     {
280:         if (array_key_exists($name, $this->params)) {
281:             throw new InvalidStateException("The variable '$name' already exists.");
282:         }
283: 
284:         $this->params[$name] = $value;
285:     }
286: 
287: 
288: 
289:     /**
290:      * Sets all parameters.
291:      * @param  array
292:      * @return NTemplate  provides a fluent interface
293:      */
294:     public function setParams(array $params)
295:     {
296:         $this->params = $params + $this->params;
297:         return $this;
298:     }
299: 
300: 
301: 
302:     /**
303:      * Returns array of all parameters.
304:      * @return array
305:      */
306:     public function getParams()
307:     {
308:         $this->params['template'] = $this;
309:         return $this->params;
310:     }
311: 
312: 
313: 
314:     /**
315:      * Sets a template parameter. Do not call directly.
316:      * @param  string  name
317:      * @param  mixed   value
318:      * @return void
319:      */
320:     public function __set($name, $value)
321:     {
322:         $this->params[$name] = $value;
323:     }
324: 
325: 
326: 
327:     /**
328:      * Returns a template parameter. Do not call directly.
329:      * @param  string  name
330:      * @return mixed  value
331:      */
332:     public function &__get($name)
333:     {
334:         if ($this->warnOnUndefined && !array_key_exists($name, $this->params)) {
335:             trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
336:         }
337: 
338:         return $this->params[$name];
339:     }
340: 
341: 
342: 
343:     /**
344:      * Determines whether parameter is defined. Do not call directly.
345:      * @param  string    name
346:      * @return bool
347:      */
348:     public function __isset($name)
349:     {
350:         return isset($this->params[$name]);
351:     }
352: 
353: 
354: 
355:     /**
356:      * Removes a template parameter. Do not call directly.
357:      * @param  string    name
358:      * @return void
359:      */
360:     public function __unset($name)
361:     {
362:         unset($this->params[$name]);
363:     }
364: 
365: 
366: 
367:     /********************* caching ****************d*g**/
368: 
369: 
370: 
371:     /**
372:      * Set cache storage.
373:      * @param  NCache
374:      * @return void
375:      */
376:     public function setCacheStorage(ICacheStorage $storage)
377:     {
378:         $this->cacheStorage = $storage;
379:     }
380: 
381: 
382: 
383:     /**
384:      * @return ICacheStorage
385:      */
386:     public function getCacheStorage()
387:     {
388:         if ($this->cacheStorage === NULL) {
389:             return new NDevNullStorage;
390:         }
391:         return $this->cacheStorage;
392:     }
393: 
394: 
395: 
396:     /********************* tools ****************d*g**/
397: 
398: 
399: 
400:     /**
401:      * Extracts all blocks of PHP code.
402:      * @param  string
403:      * @param  array
404:      * @return string
405:      */
406:     private static function extractPhp($source, & $blocks)
407:     {
408:         $res = '';
409:         $blocks = array();
410:         $tokens = token_get_all($source);
411:         foreach ($tokens as $n => $token) {
412:             if (is_array($token)) {
413:                 if ($token[0] === T_INLINE_HTML || $token[0] === T_CLOSE_TAG) {
414:                     $res .= $token[1];
415:                     continue;
416: 
417:                 } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
418:                     $php = & $res;
419:                     $token[1] = '<<?php ?>?';
420: 
421:                 } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
422:                     $res .= $id = "<?php \x01@php:p" . count($blocks) . "@\x02";
423:                     $php = & $blocks[$id];
424:                 }
425:                 $php .= $token[1];
426: 
427:             } else {
428:                 $php .= $token;
429:             }
430:         }
431:         return $res;
432:     }
433: 
434: 
435: 
436:     /**
437:      * Removes unnecessary blocks of PHP code.
438:      * @param  string
439:      * @return string
440:      */
441:     public static function optimizePhp($source, $lineLength = 80, $existenceOfThisParameterSolvesDamnBugInPHP535 = NULL)
442:     {
443:         $res = $php = '';
444:         $lastChar = ';';
445:         $tokens = new ArrayIterator(token_get_all($source));
446:         foreach ($tokens as $key => $token) {
447:             if (is_array($token)) {
448:                 if ($token[0] === T_INLINE_HTML) {
449:                     $lastChar = '';
450:                     $res .= $token[1];
451: 
452:                 } elseif ($token[0] === T_CLOSE_TAG) {
453:                     $next = isset($tokens[$key + 1]) ? $tokens[$key + 1] : NULL;
454:                     if (substr($res, -1) !== '<' && preg_match('#^<\?php\s*$#', $php)) {
455:                         $php = ''; // removes empty (?php ?), but retains ((?php ?)?php
456: 
457:                     } elseif (is_array($next) && $next[0] === T_OPEN_TAG) { // remove ?)(?php
458:                         if (!strspn($lastChar, ';{}:/')) {
459:                             $php .= $lastChar = ';';
460:                         }
461:                         if (substr($next[1], -1) === "\n") {
462:                             $php .= "\n";
463:                         }
464:                         $tokens->next();
465: 
466:                     } elseif ($next) {
467:                         $res .= preg_replace('#;?(\s)*$#', '$1', $php) . $token[1]; // remove last semicolon before ?)
468:                         if (strlen($res) - strrpos($res, "\n") > $lineLength
469:                             && (!is_array($next) || strpos($next[1], "\n") === FALSE)
470:                         ) {
471:                             $res .= "\n";
472:                         }
473:                         $php = '';
474: 
475:                     } else { // remove last ?)
476:                         if (!strspn($lastChar, '};')) {
477:                             $php .= ';';
478:                         }
479:                     }
480: 
481:                 } elseif ($token[0] === T_ELSE || $token[0] === T_ELSEIF) {
482:                     if ($tokens[$key + 1] === ':' && $lastChar === '}') {
483:                         $php .= ';'; // semicolon needed in if(): ... if() ... else:
484:                     }
485:                     $lastChar = '';
486:                     $php .= $token[1];
487: 
488:                 } else {
489:                     if (!in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_OPEN_TAG))) {
490:                         $lastChar = '';
491:                     }
492:                     $php .= $token[1];
493:                 }
494:             } else {
495:                 $php .= $lastChar = $token;
496:             }
497:         }
498:         return $res . $php;
499:     }
500: 
501: }
502: 
Nette Framework 2.0beta1 (for PHP 5.2) API API documentation generated by ApiGen 2.3.0