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
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Compiler
  • Engine
  • Helpers
  • HtmlNode
  • MacroNode
  • MacroTokens
  • Parser
  • PhpHelpers
  • PhpWriter
  • Token
  • TokenIterator
  • Tokenizer

Interfaces

  • ILoader
  • IMacro

Traits

  • Strict

Exceptions

  • CompileException
  • RegexpException
  • RuntimeException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  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
 15: {
 16:     use Strict;
 17: 
 18:     const VERSION = '2.4.8';
 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_ICAL = 'ical',
 27:         CONTENT_TEXT = 'text';
 28: 
 29:     /** @var callable[] */
 30:     public $onCompile = [];
 31: 
 32:     /** @var Parser */
 33:     private $parser;
 34: 
 35:     /** @var Compiler */
 36:     private $compiler;
 37: 
 38:     /** @var ILoader */
 39:     private $loader;
 40: 
 41:     /** @var Runtime\FilterExecutor */
 42:     private $filters;
 43: 
 44:     /** @var array */
 45:     private $providers = [];
 46: 
 47:     /** @var string */
 48:     private $contentType = self::CONTENT_HTML;
 49: 
 50:     /** @var string */
 51:     private $tempDirectory;
 52: 
 53:     /** @var bool */
 54:     private $autoRefresh = true;
 55: 
 56: 
 57:     public function __construct()
 58:     {
 59:         $this->filters = new Runtime\FilterExecutor;
 60:     }
 61: 
 62: 
 63:     /**
 64:      * Renders template to output.
 65:      * @return void
 66:      */
 67:     public function render($name, array $params = [], $block = null)
 68:     {
 69:         $this->createTemplate($name, $params + ['_renderblock' => $block])
 70:             ->render();
 71:     }
 72: 
 73: 
 74:     /**
 75:      * Renders template to string.
 76:      * @return string
 77:      */
 78:     public function renderToString($name, array $params = [], $block = null)
 79:     {
 80:         $template = $this->createTemplate($name, $params + ['_renderblock' => $block]);
 81:         return $template->capture([$template, 'render']);
 82:     }
 83: 
 84: 
 85:     /**
 86:      * Creates template object.
 87:      * @return Runtime\Template
 88:      */
 89:     public function createTemplate($name, array $params = [])
 90:     {
 91:         $class = $this->getTemplateClass($name);
 92:         if (!class_exists($class, false)) {
 93:             $this->loadTemplate($name);
 94:         }
 95:         return new $class($this, $params, $this->filters, $this->providers, $name);
 96:     }
 97: 
 98: 
 99:     /**
100:      * Compiles template to PHP code.
101:      * @return string
102:      */
103:     public function compile($name)
104:     {
105:         foreach ($this->onCompile ?: [] as $cb) {
106:             call_user_func(Helpers::checkCallback($cb), $this);
107:         }
108:         $this->onCompile = [];
109: 
110:         $source = $this->getLoader()->getContent($name);
111: 
112:         try {
113:             $tokens = $this->getParser()->setContentType($this->contentType)
114:                 ->parse($source);
115: 
116:             $code = $this->getCompiler()->setContentType($this->contentType)
117:                 ->compile($tokens, $this->getTemplateClass($name));
118: 
119:         } catch (\Exception $e) {
120:             if (!$e instanceof CompileException) {
121:                 $e = new CompileException("Thrown exception '{$e->getMessage()}'", 0, $e);
122:             }
123:             $line = isset($tokens) ? $this->getCompiler()->getLine() : $this->getParser()->getLine();
124:             throw $e->setSource($source, $line, $name);
125:         }
126: 
127:         if (!preg_match('#\n|\?#', $name)) {
128:             $code = "<?php\n// source: $name\n?>" . $code;
129:         }
130:         $code = PhpHelpers::reformatCode($code);
131:         return $code;
132:     }
133: 
134: 
135:     /**
136:      * Compiles template to cache.
137:      * @param  string
138:      * @return void
139:      * @throws \LogicException
140:      */
141:     public function warmupCache($name)
142:     {
143:         if (!$this->tempDirectory) {
144:             throw new \LogicException('Path to temporary directory is not set.');
145:         }
146: 
147:         $class = $this->getTemplateClass($name);
148:         if (!class_exists($class, false)) {
149:             $this->loadTemplate($name);
150:         }
151:     }
152: 
153: 
154:     /**
155:      * @return void
156:      */
157:     private function loadTemplate($name)
158:     {
159:         if (!$this->tempDirectory) {
160:             $code = $this->compile($name);
161:             if (@eval('?>' . $code) === false) { // @ is escalated to exception
162:                 throw (new CompileException('Error in template: ' . error_get_last()['message']))
163:                     ->setSource($code, error_get_last()['line'], "$name (compiled)");
164:             }
165:             return;
166:         }
167: 
168:         $file = $this->getCacheFile($name);
169: 
170:         if (!$this->isExpired($file, $name) && (@include $file) !== false) { // @ - file may not exist
171:             return;
172:         }
173: 
174:         if (!is_dir($this->tempDirectory) && !@mkdir($this->tempDirectory) && !is_dir($this->tempDirectory)) { // @ - dir may already exist
175:             throw new \RuntimeException("Unable to create directory '$this->tempDirectory'. " . error_get_last()['message']);
176:         }
177: 
178:         $handle = @fopen("$file.lock", 'c+'); // @ is escalated to exception
179:         if (!$handle) {
180:             throw new \RuntimeException("Unable to create file '$file.lock'. " . error_get_last()['message']);
181:         } elseif (!@flock($handle, LOCK_EX)) { // @ is escalated to exception
182:             throw new \RuntimeException("Unable to acquire exclusive lock on '$file.lock'. " . error_get_last()['message']);
183:         }
184: 
185:         if (!is_file($file) || $this->isExpired($file, $name)) {
186:             $code = $this->compile($name);
187:             if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
188:                 @unlink("$file.tmp"); // @ - file may not exist
189:                 throw new \RuntimeException("Unable to create '$file'.");
190:             } elseif (function_exists('opcache_invalidate')) {
191:                 @opcache_invalidate($file, true); // @ can be restricted
192:             }
193:         }
194: 
195:         if ((include $file) === false) {
196:             throw new \RuntimeException("Unable to load '$file'.");
197:         }
198: 
199:         flock($handle, LOCK_UN);
200:         fclose($handle);
201:         @unlink("$file.lock"); // @ file may become locked on Windows
202:     }
203: 
204: 
205:     /**
206:      * @param  string
207:      * @param  string
208:      * @return bool
209:      */
210:     private function isExpired($file, $name)
211:     {
212:         return $this->autoRefresh && $this->getLoader()->isExpired($name, (int) @filemtime($file)); // @ - file may not exist
213:     }
214: 
215: 
216:     /**
217:      * @return string
218:      */
219:     public function getCacheFile($name)
220:     {
221:         $hash = substr($this->getTemplateClass($name), 8);
222:         $base = preg_match('#([/\\\\][\w@.-]{3,35}){1,3}\z#', $name, $m)
223:             ? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1)) . '--'
224:             : '';
225:         return "$this->tempDirectory/$base$hash.php";
226:     }
227: 
228: 
229:     /**
230:      * @return string
231:      */
232:     public function getTemplateClass($name)
233:     {
234:         $key = $this->getLoader()->getUniqueId($name) . "\00" . self::VERSION;
235:         return 'Template' . substr(md5($key), 0, 10);
236:     }
237: 
238: 
239:     /**
240:      * Registers run-time filter.
241:      * @param  string|null
242:      * @param  callable
243:      * @return static
244:      */
245:     public function addFilter($name, $callback)
246:     {
247:         $this->filters->add($name, $callback);
248:         return $this;
249:     }
250: 
251: 
252:     /**
253:      * Returns all run-time filters.
254:      * @return string[]
255:      */
256:     public function getFilters()
257:     {
258:         return $this->filters->getAll();
259:     }
260: 
261: 
262:     /**
263:      * Call a run-time filter.
264:      * @param  string  filter name
265:      * @param  array   arguments
266:      * @return mixed
267:      */
268:     public function invokeFilter($name, array $args)
269:     {
270:         return call_user_func_array($this->filters->$name, $args);
271:     }
272: 
273: 
274:     /**
275:      * Adds new macro.
276:      * @return static
277:      */
278:     public function addMacro($name, IMacro $macro)
279:     {
280:         $this->getCompiler()->addMacro($name, $macro);
281:         return $this;
282:     }
283: 
284: 
285:     /**
286:      * Adds new provider.
287:      * @return static
288:      */
289:     public function addProvider($name, $value)
290:     {
291:         $this->providers[$name] = $value;
292:         return $this;
293:     }
294: 
295: 
296:     /**
297:      * Returns all providers.
298:      * @return array
299:      */
300:     public function getProviders()
301:     {
302:         return $this->providers;
303:     }
304: 
305: 
306:     /**
307:      * @return static
308:      */
309:     public function setContentType($type)
310:     {
311:         $this->contentType = $type;
312:         return $this;
313:     }
314: 
315: 
316:     /**
317:      * Sets path to temporary directory.
318:      * @return static
319:      */
320:     public function setTempDirectory($path)
321:     {
322:         $this->tempDirectory = $path;
323:         return $this;
324:     }
325: 
326: 
327:     /**
328:      * Sets auto-refresh mode.
329:      * @return static
330:      */
331:     public function setAutoRefresh($on = true)
332:     {
333:         $this->autoRefresh = (bool) $on;
334:         return $this;
335:     }
336: 
337: 
338:     /**
339:      * @return Parser
340:      */
341:     public function getParser()
342:     {
343:         if (!$this->parser) {
344:             $this->parser = new Parser;
345:         }
346:         return $this->parser;
347:     }
348: 
349: 
350:     /**
351:      * @return Compiler
352:      */
353:     public function getCompiler()
354:     {
355:         if (!$this->compiler) {
356:             $this->compiler = new Compiler;
357:             Macros\CoreMacros::install($this->compiler);
358:             Macros\BlockMacros::install($this->compiler);
359:         }
360:         return $this->compiler;
361:     }
362: 
363: 
364:     /**
365:      * @return static
366:      */
367:     public function setLoader(ILoader $loader)
368:     {
369:         $this->loader = $loader;
370:         return $this;
371:     }
372: 
373: 
374:     /**
375:      * @return ILoader
376:      */
377:     public function getLoader()
378:     {
379:         if (!$this->loader) {
380:             $this->loader = new Loaders\FileLoader;
381:         }
382:         return $this->loader;
383:     }
384: }
385: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0