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 BaseTemplate 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 __toString()
93: {
94: ob_start();
95: try {
96: $this->render();
97: return ob_get_clean();
98:
99: } catch (\Exception $e) {
100: ob_end_clean();
101: if (func_num_args() && func_get_arg(0)) {
102: throw $e;
103: } else {
104: Nette\Debug::toStringException($e);
105: }
106: }
107: }
108:
109:
110:
111: 112: 113: 114: 115: 116:
117: protected function compile($content, $label = NULL)
118: {
119: if (!$this->filters) {
120: $this->onPrepareFilters($this);
121: }
122:
123: try {
124: foreach ($this->filters as $filter) {
125: $content = self::extractPhp($content, $blocks);
126: $content = $filter($content);
127: $content = strtr($content, $blocks);
128: }
129: } catch (\Exception $e) {
130: throw new \InvalidStateException("Filter $filter: " . $e->getMessage() . ($label ? " (in $label)" : ''), 0, $e);
131: }
132:
133: if ($label) {
134: $content = "<?php\n// $label\n//\n?>$content";
135: }
136:
137: return self::optimizePhp($content);
138: }
139:
140:
141:
142:
143:
144:
145:
146: 147: 148: 149: 150: 151:
152: public function registerHelper($name, $callback)
153: {
154: $this->helpers[strtolower($name)] = callback($callback);
155: }
156:
157:
158:
159: 160: 161: 162: 163:
164: public function registerHelperLoader($callback)
165: {
166: $this->helperLoaders[] = callback($callback);
167: }
168:
169:
170:
171: 172: 173: 174:
175: final public function getHelpers()
176: {
177: return $this->helpers;
178: }
179:
180:
181:
182: 183: 184: 185: 186: 187:
188: public function __call($name, $args)
189: {
190: $lname = strtolower($name);
191: if (!isset($this->helpers[$lname])) {
192: foreach ($this->helperLoaders as $loader) {
193: $helper = $loader($lname);
194: if ($helper) {
195: $this->registerHelper($lname, $helper);
196: return $this->helpers[$lname]->invokeArgs($args);
197: }
198: }
199: return parent::__call($name, $args);
200: }
201:
202: return $this->helpers[$lname]->invokeArgs($args);
203: }
204:
205:
206:
207: 208: 209: 210: 211:
212: public function setTranslator(Nette\ITranslator $translator = NULL)
213: {
214: $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
215: return $this;
216: }
217:
218:
219:
220:
221:
222:
223:
224: 225: 226: 227: 228: 229:
230: public function add($name, $value)
231: {
232: if (array_key_exists($name, $this->params)) {
233: throw new \InvalidStateException("The variable '$name' exists yet.");
234: }
235:
236: $this->params[$name] = $value;
237: }
238:
239:
240:
241: 242: 243: 244: 245:
246: public function setParams(array $params)
247: {
248: $this->params = $params;
249: return $this;
250: }
251:
252:
253:
254: 255: 256: 257:
258: public function getParams()
259: {
260: return $this->params;
261: }
262:
263:
264:
265: 266: 267: 268: 269: 270:
271: public function __set($name, $value)
272: {
273: $this->params[$name] = $value;
274: }
275:
276:
277:
278: 279: 280: 281: 282:
283: public function &__get($name)
284: {
285: if ($this->warnOnUndefined && !array_key_exists($name, $this->params)) {
286: trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
287: }
288:
289: return $this->params[$name];
290: }
291:
292:
293:
294: 295: 296: 297: 298:
299: public function __isset($name)
300: {
301: return isset($this->params[$name]);
302: }
303:
304:
305:
306: 307: 308: 309: 310:
311: public function __unset($name)
312: {
313: unset($this->params[$name]);
314: }
315:
316:
317:
318:
319:
320:
321:
322: 323: 324: 325: 326: 327:
328: private static function extractPhp($source, & $blocks)
329: {
330: $res = '';
331: $blocks = array();
332: $tokens = token_get_all($source);
333: foreach ($tokens as $n => $token) {
334: if (is_array($token)) {
335: if ($token[0] === T_INLINE_HTML) {
336: $res .= $token[1];
337: continue;
338:
339: } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
340: $php = & $res;
341: $token[1] = '<<?php ?>?';
342:
343: } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
344: $res .= $id = "\x01@php:p" . count($blocks) . "@\x02";
345: $php = & $blocks[$id];
346: }
347: $php .= $token[1];
348:
349: } else {
350: $php .= $token;
351: }
352: }
353: return $res;
354: }
355:
356:
357:
358: 359: 360: 361: 362:
363: public static function optimizePhp($source)
364: {
365: $res = $php = '';
366: $lastChar = ';';
367: $tokens = new \ArrayIterator(token_get_all($source));
368: foreach ($tokens as $key => $token) {
369: if (is_array($token)) {
370: if ($token[0] === T_INLINE_HTML) {
371: $lastChar = '';
372: $res .= $token[1];
373:
374: } elseif ($token[0] === T_CLOSE_TAG) {
375: $next = isset($tokens[$key + 1]) ? $tokens[$key + 1] : NULL;
376: if (substr($res, -1) !== '<' && preg_match('#^<\?php\s*$#', $php)) {
377: $php = '';
378:
379: } elseif (is_array($next) && $next[0] === T_OPEN_TAG) {
380: if ($lastChar !== ';' && $lastChar !== '{' && $lastChar !== '}' && $lastChar !== ':' && $lastChar !== '/' ) $php .= $lastChar = ';';
381: if (substr($next[1], -1) === "\n") $php .= "\n";
382: $tokens->next();
383:
384: } elseif ($next) {
385: $res .= preg_replace('#;?(\s)*$#', '$1', $php) . $token[1];
386: $php = '';
387:
388: } else {
389: if ($lastChar !== '}' && $lastChar !== ';') $php .= ';';
390: }
391:
392: } elseif ($token[0] === T_ELSE || $token[0] === T_ELSEIF) {
393: if ($tokens[$key + 1] === ':' && $lastChar === '}') $php .= ';';
394: $lastChar = '';
395: $php .= $token[1];
396:
397: } else {
398: if (!in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_OPEN_TAG))) $lastChar = '';
399: $php .= $token[1];
400: }
401: } else {
402: $php .= $lastChar = $token;
403: }
404: }
405: return $res . $php;
406: }
407:
408: }
409: