Source for file BaseTemplate.php

Documentation is available at BaseTemplate.php

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