PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/shopaholic/lib/Nette/Templates/Template.php

http://github.com/jakubkulhan/shopaholic
PHP | 472 lines | 225 code | 109 blank | 138 comment | 33 complexity | a1e5e222d810b27b88ee34118f5115ae MD5 | raw file
Possible License(s): WTFPL
  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. * @version $Id: Template.php 298 2009-05-03 16:13:22Z david@grudl.com $
  18. */
  19. require_once dirname(__FILE__) . '/../Object.php';
  20. require_once dirname(__FILE__) . '/../Templates/IFileTemplate.php';
  21. /**
  22. * Template.
  23. *
  24. * @author David Grudl
  25. * @copyright Copyright (c) 2004, 2009 David Grudl
  26. * @package Nette\Templates
  27. */
  28. class Template extends Object implements IFileTemplate
  29. {
  30. /** @var bool */
  31. public $warnOnUndefined = TRUE;
  32. /** @var string */
  33. private $file;
  34. /** @var array */
  35. private $params = array();
  36. /** @var array */
  37. private $filters = array();
  38. /** @var array */
  39. private $helpers = array();
  40. /** @var array */
  41. private $helperLoaders = array();
  42. /** @var int */
  43. public static $cacheExpire = FALSE;
  44. /** @var ICacheStorage */
  45. private static $cacheStorage;
  46. /**
  47. * Sets the path to the template file.
  48. * @param string template file path
  49. * @return void
  50. */
  51. public function setFile($file)
  52. {
  53. $this->file = $file;
  54. }
  55. /**
  56. * Returns the path to the template file.
  57. * @return string template file path
  58. */
  59. public function getFile()
  60. {
  61. return $this->file;
  62. }
  63. /**
  64. * Creates subtemplate.
  65. * @param string file name
  66. * @param array parameters (optional)
  67. * @return Template
  68. */
  69. public function subTemplate($file, array $params = NULL)
  70. {
  71. if ($file instanceof self) {
  72. $tpl = $file;
  73. } elseif ($file == NULL) { // intentionally ==
  74. throw new InvalidArgumentException("Template file name was not specified.");
  75. } else {
  76. $tpl = clone $this;
  77. if ($file[0] !== '/' && $file[1] !== ':') {
  78. $file = dirname($this->file) . '/' . $file;
  79. }
  80. $tpl->setFile($file);
  81. }
  82. if ($params === NULL) {
  83. $tpl->params = & $this->params;
  84. } else {
  85. $tpl->params = & $params;
  86. $tpl->params += $this->params;
  87. }
  88. return $tpl;
  89. }
  90. /**
  91. * Registers callback as template filter.
  92. * @param callback
  93. * @return void
  94. */
  95. public function registerFilter($callback)
  96. {
  97. fixCallback($callback);
  98. if (in_array($callback, $this->filters, TRUE)) {
  99. is_callable($callback, TRUE, $textual);
  100. throw new InvalidStateException("Filter '$textual' was registered twice.");
  101. }
  102. $this->filters[] = $callback;
  103. }
  104. /********************* rendering ****************d*g**/
  105. /**
  106. * Renders template to output.
  107. * @return void
  108. */
  109. public function render()
  110. {
  111. if ($this->file == NULL) { // intentionally ==
  112. throw new InvalidStateException("Template file name was not specified.");
  113. } elseif (!is_file($this->file) || !is_readable($this->file)) {
  114. throw new FileNotFoundException("Missing template file '$this->file'.");
  115. }
  116. $this->params['template'] = $this;
  117. if (!count($this->filters)) {
  118. LimitedScope::load($this->file, $this->params);
  119. return;
  120. }
  121. $cache = new Cache($this->getCacheStorage(), 'Nette.Template');
  122. $key = md5($this->file) . count($this->filters) . '.' . basename($this->file);
  123. $cached = $content = $cache[$key];
  124. if ($content === NULL) {
  125. $content = file_get_contents($this->file);
  126. foreach ($this->filters as $filter) {
  127. if (!is_callable($filter)) {
  128. $able = is_callable($filter, TRUE, $textual);
  129. throw new InvalidStateException("Filter '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
  130. }
  131. // remove PHP code
  132. $res = '';
  133. $blocks = array();
  134. unset($php);
  135. foreach (token_get_all($content) as $token) {
  136. if (is_array($token)) {
  137. if ($token[0] === T_INLINE_HTML) {
  138. $res .= $token[1];
  139. unset($php);
  140. } else {
  141. if (!isset($php)) {
  142. $res .= $php = "\x01@php:p" . count($blocks) . "@\x02";
  143. $php = & $blocks[$php];
  144. }
  145. $php .= $token[1];
  146. }
  147. } else {
  148. $php .= $token;
  149. }
  150. }
  151. try {
  152. $content = call_user_func($filter, $res);
  153. } catch (Exception $e) {
  154. is_callable($filter, TRUE, $textual);
  155. throw new InvalidStateException("Filter $textual: " . $e->getMessage() . " (in file $this->file)", 0, $e);
  156. }
  157. $content = strtr($content, $blocks); // put PHP code back
  158. }
  159. $content = "<?php\n// template $this->file\n?>$content";
  160. $cache->save(
  161. $key,
  162. $content,
  163. array(
  164. Cache::FILES => $this->file,
  165. Cache::EXPIRE => self::$cacheExpire,
  166. )
  167. );
  168. $cached = $cache[$key];
  169. }
  170. if ($cached !== NULL && self::$cacheStorage instanceof TemplateCacheStorage) {
  171. LimitedScope::load($cached['file'], $this->params);
  172. fclose($cached['handle']);
  173. } else {
  174. LimitedScope::evaluate($content, $this->params);
  175. }
  176. }
  177. /**
  178. * Renders template to string.
  179. * @param bool can throw exceptions? (hidden parameter)
  180. * @return string
  181. */
  182. public function __toString()
  183. {
  184. ob_start();
  185. try {
  186. $this->render();
  187. return ob_get_clean();
  188. } catch (Exception $e) {
  189. ob_end_clean();
  190. if (func_num_args() && func_get_arg(0)) {
  191. throw $e;
  192. } else {
  193. trigger_error($e->getMessage(), E_USER_WARNING);
  194. return '';
  195. }
  196. }
  197. }
  198. /**
  199. * Converts to SimpleXML. (experimental)
  200. * @return SimpleXMLElement
  201. */
  202. public function toXml()
  203. {
  204. $dom = new DOMDocument;
  205. $dom->loadHTML('<html><meta http-equiv="Content-Type" content="text/html;charset=utf-8">' . str_replace("\r", '', $this->__toString()) . '</html>');
  206. return simplexml_import_dom($dom)->body;
  207. //return simplexml_load_string('<xml>' . $this->__toString() . '</xml>');
  208. }
  209. /********************* template helpers ****************d*g**/
  210. /**
  211. * Registers callback as template helper.
  212. * @param string
  213. * @param callback
  214. * @return void
  215. */
  216. public function registerHelper($name, $callback)
  217. {
  218. fixCallback($callback);
  219. if (!is_callable($callback)) {
  220. $able = is_callable($callback, TRUE, $textual);
  221. throw new InvalidArgumentException("Helper handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
  222. }
  223. $this->helpers[strtolower($name)] = $callback;
  224. }
  225. /**
  226. * Registers callback as template helpers loader.
  227. * @param callback
  228. * @return void
  229. */
  230. public function registerHelperLoader($callback)
  231. {
  232. fixCallback($callback);
  233. if (!is_callable($callback)) {
  234. $able = is_callable($callback, TRUE, $textual);
  235. throw new InvalidArgumentException("Helper loader '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
  236. }
  237. $this->helperLoaders[] = $callback;
  238. }
  239. /**
  240. * Call a template helper. Do not call directly.
  241. * @param string helper name
  242. * @param array arguments
  243. * @return mixed
  244. */
  245. public function __call($name, $args)
  246. {
  247. $name = strtolower($name);
  248. if (!isset($this->helpers[$name])) {
  249. foreach ($this->helperLoaders as $loader) {
  250. $helper = call_user_func($loader, $name);
  251. if ($helper) {
  252. $this->registerHelper($name, $helper);
  253. return call_user_func_array($helper, $args);
  254. }
  255. }
  256. throw new InvalidStateException("The helper '$name' was not registered.");
  257. }
  258. return call_user_func_array($this->helpers[$name], $args);
  259. }
  260. /**
  261. * Sets translate adapter.
  262. * @param ITranslator
  263. * @return void
  264. */
  265. public function setTranslator(ITranslator $translator = NULL)
  266. {
  267. $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
  268. }
  269. /********************* template parameters ****************d*g**/
  270. /**
  271. * Adds new template parameter.
  272. * @param string name
  273. * @param mixed value
  274. * @return void
  275. */
  276. public function add($name, $value)
  277. {
  278. if (array_key_exists($name, $this->params)) {
  279. throw new InvalidStateException("The variable '$name' exists yet.");
  280. }
  281. $this->params[$name] = $value;
  282. }
  283. /**
  284. * Adds new template as parameter.
  285. * @param string name
  286. * @param string|Template file name or Template object
  287. * @return Template
  288. */
  289. public function addTemplate($name, $file)
  290. {
  291. $tpl = $this->subTemplate($file);
  292. $this->add($name, $tpl);
  293. return $tpl;
  294. }
  295. /**
  296. * Returns array of all parameters.
  297. * @return array
  298. */
  299. public function getParams()
  300. {
  301. return $this->params;
  302. }
  303. /**
  304. * Sets a template parameter. Do not call directly.
  305. * @param string name
  306. * @param mixed value
  307. * @return void
  308. */
  309. public function __set($name, $value)
  310. {
  311. $this->params[$name] = $value;
  312. }
  313. /**
  314. * Returns a template parameter. Do not call directly.
  315. * @param string name
  316. * @return mixed value
  317. */
  318. public function &__get($name)
  319. {
  320. if ($this->warnOnUndefined && !array_key_exists($name, $this->params)) {
  321. trigger_error("The variable '$name' does not exist in template '$this->file'", E_USER_WARNING);
  322. }
  323. return $this->params[$name];
  324. }
  325. /**
  326. * Determines whether parameter is defined. Do not call directly.
  327. * @param string name
  328. * @return bool
  329. */
  330. public function __isset($name)
  331. {
  332. return isset($this->params[$name]);
  333. }
  334. /**
  335. * Removes a template parameter. Do not call directly.
  336. * @param string name
  337. * @return void
  338. */
  339. public function __unset($name)
  340. {
  341. unset($this->params[$name]);
  342. }
  343. /********************* caching ****************d*g**/
  344. /**
  345. * Set cache storage.
  346. * @param Cache
  347. * @return void
  348. */
  349. public static function setCacheStorage(ICacheStorage $storage)
  350. {
  351. self::$cacheStorage = $storage;
  352. }
  353. /**
  354. * @return ICacheStorage
  355. */
  356. public static function getCacheStorage()
  357. {
  358. if (self::$cacheStorage === NULL) {
  359. self::$cacheStorage = new TemplateCacheStorage(Environment::getVariable('cacheBase'));
  360. }
  361. return self::$cacheStorage;
  362. }
  363. }