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