Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • Compiler
  • Engine
  • HtmlNode
  • MacroNode
  • MacroTokens
  • Object
  • Parser
  • PhpWriter
  • Token

Interfaces

  • ILoader
  • IMacro

Exceptions

  • CompileException
  • RegexpException
  • RuntimeException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Latte;
  9: 
 10: 
 11: /**
 12:  * Templating engine Latte.
 13:  *
 14:  * @author     David Grudl
 15:  */
 16: class Engine extends Object
 17: {
 18:     const VERSION = '2.3.1';
 19: 
 20:     /** Content types */
 21:     const CONTENT_HTML = 'html',
 22:         CONTENT_XHTML = 'xhtml',
 23:         CONTENT_XML = 'xml',
 24:         CONTENT_JS = 'js',
 25:         CONTENT_CSS = 'css',
 26:         CONTENT_URL = 'url',
 27:         CONTENT_ICAL = 'ical',
 28:         CONTENT_TEXT = 'text';
 29: 
 30:     /** @var callable[] */
 31:     public $onCompile = array();
 32: 
 33:     /** @var Parser */
 34:     private $parser;
 35: 
 36:     /** @var Compiler */
 37:     private $compiler;
 38: 
 39:     /** @var ILoader */
 40:     private $loader;
 41: 
 42:     /** @var string */
 43:     private $contentType = self::CONTENT_HTML;
 44: 
 45:     /** @var string */
 46:     private $tempDirectory;
 47: 
 48:     /** @var bool */
 49:     private $autoRefresh = TRUE;
 50: 
 51:     /** @var array run-time filters */
 52:     private $filters = array(
 53:         NULL => array(), // dynamic
 54:         'bytes' => 'Latte\Runtime\Filters::bytes',
 55:         'capitalize' => 'Latte\Runtime\Filters::capitalize',
 56:         'datastream' => 'Latte\Runtime\Filters::dataStream',
 57:         'date' => 'Latte\Runtime\Filters::date',
 58:         'escapecss' => 'Latte\Runtime\Filters::escapeCss',
 59:         'escapehtml' => 'Latte\Runtime\Filters::escapeHtml',
 60:         'escapehtmlcomment' => 'Latte\Runtime\Filters::escapeHtmlComment',
 61:         'escapeical' => 'Latte\Runtime\Filters::escapeICal',
 62:         'escapejs' => 'Latte\Runtime\Filters::escapeJs',
 63:         'escapeurl' => 'rawurlencode',
 64:         'escapexml' => 'Latte\Runtime\Filters::escapeXML',
 65:         'firstupper' => 'Latte\Runtime\Filters::firstUpper',
 66:         'implode' => 'implode',
 67:         'indent' => 'Latte\Runtime\Filters::indent',
 68:         'lower' => 'Latte\Runtime\Filters::lower',
 69:         'nl2br' => 'Latte\Runtime\Filters::nl2br',
 70:         'number' => 'number_format',
 71:         'repeat' => 'str_repeat',
 72:         'replace' => 'Latte\Runtime\Filters::replace',
 73:         'replacere' => 'Latte\Runtime\Filters::replaceRe',
 74:         'safeurl' => 'Latte\Runtime\Filters::safeUrl',
 75:         'strip' => 'Latte\Runtime\Filters::strip',
 76:         'striptags' => 'strip_tags',
 77:         'substr' => 'Latte\Runtime\Filters::substring',
 78:         'trim' => 'Latte\Runtime\Filters::trim',
 79:         'truncate' => 'Latte\Runtime\Filters::truncate',
 80:         'upper' => 'Latte\Runtime\Filters::upper',
 81:     );
 82: 
 83: 
 84:     /**
 85:      * Renders template to output.
 86:      * @return void
 87:      */
 88:     public function render($name, array $params = array())
 89:     {
 90:         $class = $this->getTemplateClass($name);
 91:         if (!class_exists($class, FALSE)) {
 92:             $this->loadCacheFile($name);
 93:         }
 94: 
 95:         $template = new $class($params, $this, $name);
 96:         $template->render();
 97:     }
 98: 
 99: 
100:     /**
101:      * Renders template to string.
102:      * @return string
103:      */
104:     public function renderToString($name, array $params = array())
105:     {
106:         ob_start();
107:         try {
108:             $this->render($name, $params);
109:         } catch (\Exception $e) {
110:             ob_end_clean();
111:             throw $e;
112:         }
113:         return ob_get_clean();
114:     }
115: 
116: 
117:     /**
118:      * Compiles template to PHP code.
119:      * @return string
120:      */
121:     public function compile($name)
122:     {
123:         foreach ($this->onCompile ?: array() as $cb) {
124:             call_user_func(Helpers::checkCallback($cb), $this);
125:         }
126:         $this->onCompile = array();
127: 
128:         $source = $this->getLoader()->getContent($name);
129: 
130:         try {
131:             $tokens = $this->getParser()->setContentType($this->contentType)
132:                 ->parse($source);
133: 
134:             $code = $this->getCompiler()->setContentType($this->contentType)
135:                 ->compile($tokens, $this->getTemplateClass($name));
136: 
137:         } catch (\Exception $e) {
138:             if (!$e instanceof CompileException) {
139:                 $e = new CompileException("Thrown exception '{$e->getMessage()}'", NULL, $e);
140:             }
141:             $line = isset($tokens) ? $this->getCompiler()->getLine() : $this->getParser()->getLine();
142:             throw $e->setSource($source, $line, $name);
143:         }
144: 
145:         if (!preg_match('#\n|\?#', $name)) {
146:             $code = "<?php\n// source: $name\n?>" . $code;
147:         }
148:         $code = Helpers::optimizePhp($code);
149:         return $code;
150:     }
151: 
152: 
153:     /**
154:      * @return void
155:      */
156:     private function loadCacheFile($name)
157:     {
158:         if (!$this->tempDirectory) {
159:             eval('?>' . $this->compile($name));
160:             return;
161:         }
162: 
163:         $file = $this->getCacheFile($name);
164: 
165:         if (!$this->isExpired($file, $name) && (@include $file) !== FALSE) { // @ - file may not exist
166:             return;
167:         }
168: 
169:         if (!is_dir($this->tempDirectory)) {
170:             @mkdir($this->tempDirectory); // @ - directory may already exist
171:         }
172: 
173:         $handle = fopen("$file.lock", 'c+');
174:         if (!$handle || !flock($handle, LOCK_EX)) {
175:             throw new \RuntimeException("Unable to acquire exclusive lock '$file.lock'.");
176:         }
177: 
178:         if (!is_file($file) || $this->isExpired($file, $name)) {
179:             $code = $this->compile($name);
180:             if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
181:                 @unlink("$file.tmp"); // @ - file may not exist
182:                 throw new \RuntimeException("Unable to create '$file'.");
183:             }
184:         }
185: 
186:         if ((include $file) === FALSE) {
187:             throw new \RuntimeException("Unable to load '$file'.");
188:         }
189: 
190:         flock($handle, LOCK_UN);
191:     }
192: 
193: 
194:     /**
195:      * @param  string
196:      * @param  string
197:      * @return bool
198:      */
199:     private function isExpired($file, $name)
200:     {
201:         return $this->autoRefresh && $this->getLoader()->isExpired($name, (int) @filemtime($file)); // @ - file may not exist
202:     }
203: 
204: 
205:     /**
206:      * @return string
207:      */
208:     public function getCacheFile($name)
209:     {
210:         $file = $this->getTemplateClass($name);
211:         if (preg_match('#\b\w.{10,50}$#', $name, $m)) {
212:             $file = trim(preg_replace('#\W+#', '-', $m[0]), '-') . '-' . $file;
213:         }
214:         return $this->tempDirectory . '/' . $file . '.php';
215:     }
216: 
217: 
218:     /**
219:      * @return string
220:      */
221:     public function getTemplateClass($name)
222:     {
223:         return 'Template' . md5("$this->tempDirectory\00$name");
224:     }
225: 
226: 
227:     /**
228:      * Registers run-time filter.
229:      * @param  string|NULL
230:      * @param  callable
231:      * @return self
232:      */
233:     public function addFilter($name, $callback)
234:     {
235:         if ($name == NULL) { // intentionally ==
236:             array_unshift($this->filters[NULL], $callback);
237:         } else {
238:             $this->filters[strtolower($name)] = $callback;
239:         }
240:         return $this;
241:     }
242: 
243: 
244:     /**
245:      * Returns all run-time filters.
246:      * @return callable[]
247:      */
248:     public function getFilters()
249:     {
250:         return $this->filters;
251:     }
252: 
253: 
254:     /**
255:      * Call a run-time filter.
256:      * @param  string  filter name
257:      * @param  array   arguments
258:      * @return mixed
259:      */
260:     public function invokeFilter($name, array $args)
261:     {
262:         $lname = strtolower($name);
263:         if (!isset($this->filters[$lname])) {
264:             $args2 = $args;
265:             array_unshift($args2, $lname);
266:             foreach ($this->filters[NULL] as $filter) {
267:                 $res = call_user_func_array(Helpers::checkCallback($filter), $args2);
268:                 if ($res !== NULL) {
269:                     return $res;
270:                 } elseif (isset($this->filters[$lname])) {
271:                     return call_user_func_array(Helpers::checkCallback($this->filters[$lname]), $args);
272:                 }
273:             }
274:             throw new \LogicException("Filter '$name' is not defined.");
275:         }
276:         return call_user_func_array(Helpers::checkCallback($this->filters[$lname]), $args);
277:     }
278: 
279: 
280:     /**
281:      * Adds new macro.
282:      * @return self
283:      */
284:     public function addMacro($name, IMacro $macro)
285:     {
286:         $this->getCompiler()->addMacro($name, $macro);
287:         return $this;
288:     }
289: 
290: 
291:     /**
292:      * @return self
293:      */
294:     public function setContentType($type)
295:     {
296:         $this->contentType = $type;
297:         return $this;
298:     }
299: 
300: 
301:     /**
302:      * Sets path to temporary directory.
303:      * @return self
304:      */
305:     public function setTempDirectory($path)
306:     {
307:         $this->tempDirectory = $path;
308:         return $this;
309:     }
310: 
311: 
312:     /**
313:      * Sets auto-refresh mode.
314:      * @return self
315:      */
316:     public function setAutoRefresh($on = TRUE)
317:     {
318:         $this->autoRefresh = (bool) $on;
319:         return $this;
320:     }
321: 
322: 
323:     /**
324:      * @return Parser
325:      */
326:     public function getParser()
327:     {
328:         if (!$this->parser) {
329:             $this->parser = new Parser;
330:         }
331:         return $this->parser;
332:     }
333: 
334: 
335:     /**
336:      * @return Compiler
337:      */
338:     public function getCompiler()
339:     {
340:         if (!$this->compiler) {
341:             $this->compiler = new Compiler;
342:             Macros\CoreMacros::install($this->compiler);
343:             Macros\BlockMacros::install($this->compiler);
344:         }
345:         return $this->compiler;
346:     }
347: 
348: 
349:     /**
350:      * @return self
351:      */
352:     public function setLoader(ILoader $loader)
353:     {
354:         $this->loader = $loader;
355:         return $this;
356:     }
357: 
358: 
359:     /**
360:      * @return ILoader
361:      */
362:     public function getLoader()
363:     {
364:         if (!$this->loader) {
365:             $this->loader = new Loaders\FileLoader;
366:         }
367:         return $this->loader;
368:     }
369: 
370: }
371: 
Nette 2.3.1 API API documentation generated by ApiGen 2.8.0