1: <?php
2:
3: 4: 5: 6: 7:
8:
9:
10:
11: 12: 13: 14: 15: 16:
17: class NTemplate extends NObject implements ITemplate
18: {
19:
20: public $onPrepareFilters = array();
21:
22:
23: private $source;
24:
25:
26: private $params = array();
27:
28:
29: private $filters = array();
30:
31:
32: private $helpers = array();
33:
34:
35: private $helperLoaders = array();
36:
37:
38: private $cacheStorage;
39:
40:
41: 42: 43: 44: 45:
46: public function setSource($source)
47: {
48: $this->source = $source;
49: return $this;
50: }
51:
52:
53: 54: 55: 56:
57: public function getSource()
58: {
59: return $this->source;
60: }
61:
62:
63:
64:
65:
66: 67: 68: 69:
70: public function render()
71: {
72: $cache = new NCache($storage = $this->getCacheStorage(), 'Nette.Template');
73: $cached = $compiled = $cache->load($this->source);
74:
75: if ($compiled === NULL) {
76: $compiled = $this->compile();
77: $cache->save($this->source, $compiled, array(NCache::CONSTS => 'NFramework::REVISION'));
78: $cached = $cache->load($this->source);
79: }
80:
81: if ($cached !== NULL && $storage instanceof NPhpFileStorage) {
82: NLimitedScope::load($cached['file'], $this->getParameters());
83: } else {
84: NLimitedScope::evaluate($compiled, $this->getParameters());
85: }
86: }
87:
88:
89: 90: 91: 92: 93:
94: public function save($file)
95: {
96: if (file_put_contents($file, $this->__toString(TRUE)) === FALSE) {
97: throw new IOException("Unable to save file '$file'.");
98: }
99: }
100:
101:
102: 103: 104: 105: 106:
107: public function __toString()
108: {
109: ob_start();
110: try {
111: $this->render();
112: return ob_get_clean();
113:
114: } catch (Exception $e) {
115: ob_end_clean();
116: if (func_num_args()) {
117: throw $e;
118: }
119: trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
120: }
121: }
122:
123:
124: 125: 126: 127:
128: public function compile()
129: {
130: if (!$this->filters) {
131: $this->onPrepareFilters($this);
132: }
133:
134: $code = $this->getSource();
135: foreach ($this->filters as $filter) {
136: $code = self::extractPhp($code, $blocks);
137: $code = $filter->invoke($code);
138: $code = strtr($code, $blocks);
139: }
140:
141: return NTemplateHelpers::optimizePhp($code);
142: }
143:
144:
145:
146:
147:
148: 149: 150: 151: 152:
153: public function registerFilter($callback)
154: {
155: $callback = new NCallback($callback);
156: if (in_array($callback, $this->filters, TRUE)) {
157: throw new InvalidStateException("Filter '$callback' was registered twice.");
158: }
159: $this->filters[] = $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)] = new NCallback($callback);
183: return $this;
184: }
185:
186:
187: 188: 189: 190: 191:
192: public function registerHelperLoader($callback)
193: {
194: $this->helperLoaders[] = new NCallback($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 = $loader->invoke($lname);
231: if ($helper) {
232: $this->registerHelper($lname, $helper);
233: return $this->helpers[$lname]->invokeArgs($args);
234: }
235: }
236: return parent::__call($name, $args);
237: }
238:
239: return $this->helpers[$lname]->invokeArgs($args);
240: }
241:
242:
243: 244: 245: 246:
247: public function setTranslator(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 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: function setParams(array $params)
297: {
298: trigger_error(__METHOD__ . '() is deprecated; use setParameters() instead.', E_USER_WARNING);
299: return $this->setParameters($params);
300: }
301:
302:
303:
304: function getParams()
305: {
306: trigger_error(__METHOD__ . '() is deprecated; use getParameters() instead.', E_USER_WARNING);
307: return $this->getParameters();
308: }
309:
310:
311: 312: 313: 314:
315: public function __set($name, $value)
316: {
317: $this->params[$name] = $value;
318: }
319:
320:
321: 322: 323: 324:
325: public function &__get($name)
326: {
327: if (!array_key_exists($name, $this->params)) {
328: trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
329: }
330:
331: return $this->params[$name];
332: }
333:
334:
335: 336: 337: 338:
339: public function __isset($name)
340: {
341: return isset($this->params[$name]);
342: }
343:
344:
345: 346: 347: 348: 349:
350: public function __unset($name)
351: {
352: unset($this->params[$name]);
353: }
354:
355:
356:
357:
358:
359: 360: 361: 362:
363: public function setCacheStorage(ICacheStorage $storage)
364: {
365: $this->cacheStorage = $storage;
366: return $this;
367: }
368:
369:
370: 371: 372:
373: public function getCacheStorage()
374: {
375: if ($this->cacheStorage === NULL) {
376: return new NDevNullStorage;
377: }
378: return $this->cacheStorage;
379: }
380:
381:
382:
383:
384:
385: 386: 387: 388: 389: 390:
391: private static function ($source, & $blocks)
392: {
393: $res = '';
394: $blocks = array();
395: $tokens = token_get_all($source);
396: foreach ($tokens as $n => $token) {
397: if (is_array($token)) {
398: if ($token[0] === T_INLINE_HTML) {
399: $res .= $token[1];
400: continue;
401:
402: } elseif ($token[0] === T_CLOSE_TAG) {
403: if ($php !== $res) {
404: $res .= str_repeat("\n", substr_count($php, "\n"));
405: }
406: $res .= $token[1];
407: continue;
408:
409: } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
410: $php = & $res;
411: $token[1] = '<<?php ?>?';
412:
413: } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
414: $res .= $id = "<?php \x01@php:p" . count($blocks) . "@\x02";
415: $php = & $blocks[$id];
416: }
417: $php .= $token[1];
418:
419: } else {
420: $php .= $token;
421: }
422: }
423: return $res;
424: }
425:
426: }
427: