Namespaces

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

Classes

  • DefaultHelpers
  • FileTemplate
  • Template

Interfaces

  • IFileTemplate
  • ITemplate

Exceptions

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