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