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