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: