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