PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/Nette/Templating/Template.php

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