PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/nette-dev/Templates/BaseTemplate.php

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