PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Nette/Templates/BaseTemplate.php

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