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