1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Templating;
9:
10: use Nette,
11: Nette\Caching,
12: Nette\Utils\Callback;
13:
14:
15: 16: 17: 18: 19:
20: class Template extends Nette\Object implements ITemplate
21: {
22:
23: public $onPrepareFilters = array();
24:
25:
26: private $source;
27:
28:
29: private $params = array();
30:
31:
32: private $filters = array();
33:
34:
35: private $helpers = array();
36:
37:
38: private $helperLoaders = array();
39:
40:
41: private $cacheStorage;
42:
43:
44: 45: 46: 47: 48:
49: public function setSource($source)
50: {
51: $this->source = $source;
52: return $this;
53: }
54:
55:
56: 57: 58: 59:
60: public function getSource()
61: {
62: return $this->source;
63: }
64:
65:
66:
67:
68:
69: 70: 71: 72:
73: public function render()
74: {
75: $cache = new Caching\Cache($storage = $this->getCacheStorage(), 'Nette.Template');
76: $cached = $compiled = $cache->load($this->source);
77:
78: if ($compiled === NULL) {
79: $compiled = $this->compile();
80: $cache->save($this->source, $compiled, array(Caching\Cache::CONSTS => 'Nette\Framework::REVISION'));
81: $cached = $cache->load($this->source);
82: }
83:
84: if ($cached !== NULL && $storage instanceof Caching\Storages\PhpFileStorage) {
85: Nette\Utils\LimitedScope::load($cached['file'], $this->getParameters());
86: } else {
87: Nette\Utils\LimitedScope::evaluate($compiled, $this->getParameters());
88: }
89: }
90:
91:
92: 93: 94: 95: 96:
97: public function save($file)
98: {
99: if (file_put_contents($file, $this->__toString(TRUE)) === FALSE) {
100: throw new Nette\IOException("Unable to save file '$file'.");
101: }
102: }
103:
104:
105: 106: 107: 108: 109:
110: public function __toString()
111: {
112: ob_start();
113: try {
114: $this->render();
115: return ob_get_clean();
116:
117: } catch (\Exception $e) {
118: ob_end_clean();
119: if (func_get_args() && func_get_arg(0)) {
120: throw $e;
121: } else {
122: trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
123: }
124: }
125: }
126:
127:
128: 129: 130: 131:
132: public function compile()
133: {
134: if (!$this->filters) {
135: $this->onPrepareFilters($this);
136: }
137:
138: $code = $this->getSource();
139: foreach ($this->filters as $filter) {
140: $code = self::extractPhp($code, $blocks);
141: $code = call_user_func($filter, $code);
142: $code = strtr($code, $blocks);
143: }
144:
145: return Helpers::optimizePhp($code);
146: }
147:
148:
149:
150:
151:
152: 153: 154: 155: 156:
157: public function registerFilter($callback)
158: {
159: $this->filters[] = Callback::check($callback);
160: return $this;
161: }
162:
163:
164: 165: 166: 167:
168: public function getFilters()
169: {
170: return $this->filters;
171: }
172:
173:
174: 175: 176: 177: 178: 179:
180: public function registerHelper($name, $callback)
181: {
182: $this->helpers[strtolower($name)] = $callback;
183: return $this;
184: }
185:
186:
187: 188: 189: 190: 191:
192: public function registerHelperLoader($callback)
193: {
194: array_unshift($this->helperLoaders, $callback);
195: return $this;
196: }
197:
198:
199: 200: 201: 202:
203: public function getHelpers()
204: {
205: return $this->helpers;
206: }
207:
208:
209: 210: 211: 212:
213: public function getHelperLoaders()
214: {
215: return $this->helperLoaders;
216: }
217:
218:
219: 220: 221: 222: 223: 224:
225: public function __call($name, $args)
226: {
227: $lname = strtolower($name);
228: if (!isset($this->helpers[$lname])) {
229: foreach ($this->helperLoaders as $loader) {
230: $helper = Callback::invoke($loader, $lname);
231: if ($helper) {
232: $this->registerHelper($lname, $helper);
233: return Callback::invokeArgs($this->helpers[$lname], $args);
234: }
235: }
236: return parent::__call($name, $args);
237: }
238:
239: return Callback::invokeArgs($this->helpers[$lname], $args);
240: }
241:
242:
243: 244: 245: 246:
247: public function setTranslator(Nette\Localization\ITranslator $translator = NULL)
248: {
249: $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
250: return $this;
251: }
252:
253:
254:
255:
256:
257: 258: 259: 260:
261: public function add($name, $value)
262: {
263: if (array_key_exists($name, $this->params)) {
264: throw new Nette\InvalidStateException("The variable '$name' already exists.");
265: }
266:
267: $this->params[$name] = $value;
268: return $this;
269: }
270:
271:
272: 273: 274: 275: 276:
277: public function setParameters(array $params)
278: {
279: $this->params = $params + $this->params;
280: return $this;
281: }
282:
283:
284: 285: 286: 287:
288: public function getParameters()
289: {
290: $this->params['template'] = $this;
291: return $this->params;
292: }
293:
294:
295: 296: 297: 298:
299: public function __set($name, $value)
300: {
301: $this->params[$name] = $value;
302: }
303:
304:
305: 306: 307: 308:
309: public function &__get($name)
310: {
311: if (!array_key_exists($name, $this->params)) {
312: trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
313: }
314:
315: return $this->params[$name];
316: }
317:
318:
319: 320: 321: 322:
323: public function __isset($name)
324: {
325: return isset($this->params[$name]);
326: }
327:
328:
329: 330: 331: 332: 333:
334: public function __unset($name)
335: {
336: unset($this->params[$name]);
337: }
338:
339:
340:
341:
342:
343: 344: 345: 346:
347: public function setCacheStorage(Caching\IStorage $storage)
348: {
349: $this->cacheStorage = $storage;
350: return $this;
351: }
352:
353:
354: 355: 356:
357: public function getCacheStorage()
358: {
359: if ($this->cacheStorage === NULL) {
360: return new Caching\Storages\DevNullStorage;
361: }
362: return $this->cacheStorage;
363: }
364:
365:
366:
367:
368:
369: 370: 371: 372: 373: 374:
375: private static function ($source, & $blocks)
376: {
377: $res = '';
378: $blocks = array();
379: $tokens = token_get_all($source);
380: foreach ($tokens as $n => $token) {
381: if (is_array($token)) {
382: if ($token[0] === T_INLINE_HTML) {
383: $res .= $token[1];
384: continue;
385:
386: } elseif ($token[0] === T_CLOSE_TAG) {
387: if ($php !== $res) {
388: $res .= str_repeat("\n", substr_count($php, "\n"));
389: }
390: $res .= $token[1];
391: continue;
392:
393: } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
394: $php = & $res;
395: $token[1] = '<<?php ?>?';
396:
397: } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
398: $res .= $id = "<?php \x01@php:p" . count($blocks) . "@\x02";
399: $php = & $blocks[$id];
400: }
401: $php .= $token[1];
402:
403: } else {
404: $php .= $token;
405: }
406: }
407: return $res;
408: }
409:
410: }
411: