1: <?php
2:
3: 4: 5: 6: 7:
8:
9:
10:
11: 12: 13: 14: 15: 16:
17: class Template extends Object 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 Cache($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(Cache::CONSTS => 'Framework::REVISION'));
78: $cached = $cache->load($this->source);
79: }
80:
81: if ($cached !== NULL && $storage instanceof PhpFileStorage) {
82: LimitedScope::load($cached['file'], $this->getParameters());
83: } else {
84: LimitedScope::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_get_args() && func_get_arg(0)) {
117: throw $e;
118: } else {
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:
129: public function compile()
130: {
131: if (!$this->filters) {
132: $this->onPrepareFilters($this);
133: }
134:
135: $code = $this->getSource();
136: foreach ($this->filters as $filter) {
137: $code = self::extractPhp($code, $blocks);
138: $code = $filter->invoke($code);
139: $code = strtr($code, $blocks);
140: }
141:
142: return TemplateHelpers::optimizePhp($code);
143: }
144:
145:
146:
147:
148:
149: 150: 151: 152: 153:
154: public function registerFilter($callback)
155: {
156: $callback = new Callback($callback);
157: if (in_array($callback, $this->filters)) {
158: throw new InvalidStateException("Filter '$callback' was registered twice.");
159: }
160: $this->filters[] = $callback;
161: return $this;
162: }
163:
164:
165: 166: 167: 168:
169: public function getFilters()
170: {
171: return $this->filters;
172: }
173:
174:
175: 176: 177: 178: 179: 180:
181: public function registerHelper($name, $callback)
182: {
183: $this->helpers[strtolower($name)] = new Callback($callback);
184: return $this;
185: }
186:
187:
188: 189: 190: 191: 192:
193: public function registerHelperLoader($callback)
194: {
195: $this->helperLoaders[] = new Callback($callback);
196: return $this;
197: }
198:
199:
200: 201: 202: 203:
204: public function getHelpers()
205: {
206: return $this->helpers;
207: }
208:
209:
210: 211: 212: 213:
214: public function getHelperLoaders()
215: {
216: return $this->helperLoaders;
217: }
218:
219:
220: 221: 222: 223: 224: 225:
226: public function __call($name, $args)
227: {
228: $lname = strtolower($name);
229: if (!isset($this->helpers[$lname])) {
230: foreach ($this->helperLoaders as $loader) {
231: $helper = $loader->invoke($lname);
232: if ($helper) {
233: $this->registerHelper($lname, $helper);
234: return $this->helpers[$lname]->invokeArgs($args);
235: }
236: }
237: return parent::__call($name, $args);
238: }
239:
240: return $this->helpers[$lname]->invokeArgs($args);
241: }
242:
243:
244: 245: 246: 247:
248: public function setTranslator(ITranslator $translator = NULL)
249: {
250: $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
251: return $this;
252: }
253:
254:
255:
256:
257:
258: 259: 260: 261:
262: public function add($name, $value)
263: {
264: if (array_key_exists($name, $this->params)) {
265: throw new InvalidStateException("The variable '$name' already exists.");
266: }
267:
268: $this->params[$name] = $value;
269: return $this;
270: }
271:
272:
273: 274: 275: 276: 277:
278: public function setParameters(array $params)
279: {
280: $this->params = $params + $this->params;
281: return $this;
282: }
283:
284:
285: 286: 287: 288:
289: public function getParameters()
290: {
291: $this->params['template'] = $this;
292: return $this->params;
293: }
294:
295:
296:
297: function setParams(array $params)
298: {
299: trigger_error(__METHOD__ . '() is deprecated; use setParameters() instead.', E_USER_WARNING);
300: return $this->setParameters($params);
301: }
302:
303:
304:
305: function getParams()
306: {
307: trigger_error(__METHOD__ . '() is deprecated; use getParameters() instead.', E_USER_WARNING);
308: return $this->getParameters();
309: }
310:
311:
312: 313: 314: 315:
316: public function __set($name, $value)
317: {
318: $this->params[$name] = $value;
319: }
320:
321:
322: 323: 324: 325:
326: public function &__get($name)
327: {
328: if (!array_key_exists($name, $this->params)) {
329: trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
330: }
331:
332: return $this->params[$name];
333: }
334:
335:
336: 337: 338: 339:
340: public function __isset($name)
341: {
342: return isset($this->params[$name]);
343: }
344:
345:
346: 347: 348: 349: 350:
351: public function __unset($name)
352: {
353: unset($this->params[$name]);
354: }
355:
356:
357:
358:
359:
360: 361: 362: 363:
364: public function setCacheStorage(ICacheStorage $storage)
365: {
366: $this->cacheStorage = $storage;
367: return $this;
368: }
369:
370:
371: 372: 373:
374: public function getCacheStorage()
375: {
376: if ($this->cacheStorage === NULL) {
377: return new DevNullStorage;
378: }
379: return $this->cacheStorage;
380: }
381:
382:
383:
384:
385:
386: 387: 388: 389: 390: 391:
392: private static function ($source, & $blocks)
393: {
394: $res = '';
395: $blocks = array();
396: $tokens = token_get_all($source);
397: foreach ($tokens as $n => $token) {
398: if (is_array($token)) {
399: if ($token[0] === T_INLINE_HTML) {
400: $res .= $token[1];
401: continue;
402:
403: } elseif ($token[0] === T_CLOSE_TAG) {
404: if ($php !== $res) {
405: $res .= str_repeat("\n", substr_count($php, "\n"));
406: }
407: $res .= $token[1];
408: continue;
409:
410: } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
411: $php = & $res;
412: $token[1] = '<<?php ?>?';
413:
414: } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
415: $res .= $id = "<?php \x01@php:p" . count($blocks) . "@\x02";
416: $php = & $blocks[$id];
417: }
418: $php .= $token[1];
419:
420: } else {
421: $php .= $token;
422: }
423: }
424: return $res;
425: }
426:
427: }
428: