PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lib/engines/template_engine.php

http://textmotion.googlecode.com/
PHP | 307 lines | 227 code | 25 blank | 55 comment | 26 complexity | 967a7efae5309151583b355743456899 MD5 | raw file
Possible License(s): MIT, CC0-1.0
  1. <?php
  2. /**
  3. * Template Engine
  4. * Translates a set of specially formatted strings into PHP code.
  5. * ---
  6. * Written by Jose Carlos Nieto <xiam@menteslibres.org>
  7. * Copyright (c) 2007 Astrata Software S.A. de C.V.
  8. *
  9. * Licensed under The MIT License
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @author Jose Carlos Nieto <xiam@menteslibres.org>
  13. * @copyright Copyright (c) 2007-2008, Astrata Software S.A. de C.V.
  14. * @link http://opensource.astrata.com.mx Astrata Open Source Projects
  15. * @version $Revision: $
  16. * @modifiedby $LastChangedBy: $
  17. * @lastmodified $Date: $
  18. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  19. *
  20. */
  21. require_once 'simple_parser_engine.php';
  22. $GLOBALS['_TEMPLATE_GLOBALS'] = array();
  23. class template_engine extends simple_parser_engine {
  24. /**
  25. * Key based array of variables that will be extracted into the template's current symbol table
  26. * @public array
  27. */
  28. public $vars = array();
  29. /**
  30. * Directory where to store cached templates.
  31. * @public string
  32. */
  33. public $cache_dir = '/tmp';
  34. /**
  35. * Set to true if you want this class to save a cached version of the template.
  36. * @public boolean
  37. */
  38. public $enable_cache = true;
  39. /**
  40. * We want to evaluate only the first level of enclosed text
  41. * @public boolean
  42. */
  43. public $use_stack = true;
  44. /**
  45. * Using '\' as a general scape character
  46. * @public string
  47. */
  48. public $escape_char = '\\';
  49. /**
  50. * Template delimiters
  51. * @public array
  52. */
  53. public $delimiters = array(
  54. array('<!--{', '}-->'),
  55. array('{', '}', '[^\n\r]')
  56. );
  57. /**
  58. * Escapes HTML tags.
  59. * @param mixed $data A reference to the variable that will be escaped
  60. * @returns escaped variable
  61. */
  62. static function escape(&$data) {
  63. if (is_array($data)) {
  64. while (list($k) = each($data)) {
  65. template::escape($data[$k]);
  66. }
  67. } else {
  68. $data = htmlspecialchars($data);
  69. }
  70. return $data;
  71. }
  72. /**
  73. * Inserts variables into the current template symbol table.
  74. * @param mixed $name A string with the name of the variable or an array that contains a key based array of variables.
  75. * @param string $value Value of the variable.
  76. * @param boolean $escape True if you want HTML entities to be escaped.
  77. */
  78. public function set($name, $value = null, $escape = true) {
  79. if (is_array($name)) {
  80. foreach ($name as $n => $v)
  81. $this->set($n, $v, $escape);
  82. } else {
  83. $this->vars[$name] = $escape ? $this->escape($value) : $value;
  84. }
  85. }
  86. /**
  87. * Inserts variables into the global template symbol table.
  88. * @param mixed $name A string with the name of the variable or an array that contains a key based array of variables.
  89. * @param string $value Value of the variable.
  90. * @param boolean $escape True if you want HTML entities to be escaped.
  91. */
  92. static function set_global($name, $value = null, $escape = true) {
  93. if (is_array($name)) {
  94. foreach ($name as $n => $v) {
  95. template::set_global($n, $v, $escape);
  96. }
  97. } else {
  98. $a =& $GLOBALS['_TEMPLATE_GLOBALS'];
  99. $b = null;
  100. $name = explode('.', $name);
  101. while ($name) {
  102. $n = array_shift($name);
  103. $b =& $a[$n];
  104. if (!isset($b)) {
  105. $b = array();
  106. }
  107. $a =& $b;
  108. }
  109. $a = $escape ? template::escape($value) : $value;
  110. }
  111. }
  112. public function get_global($name) {
  113. $public =& $GLOBALS['_TEMPLATE_GLOBALS'][$name];
  114. return isset($public) ? $public : null;
  115. }
  116. /**
  117. * Reads an entire file from disk.
  118. * @param string $path Full path of the file to read.
  119. * @param string &$buff Reference where to store the file contents.
  120. */
  121. public function load_file($path, &$buff) {
  122. if (file_exists($path)) {
  123. $buff = file_get_contents($path);
  124. } else {
  125. trigger_error("Not readable $path.", E_USER_ERROR);
  126. }
  127. }
  128. /**
  129. * Executes the template
  130. *
  131. */
  132. public function execute() {
  133. extract($GLOBALS['_TEMPLATE_GLOBALS'], EXTR_SKIP);
  134. extract($this->vars, EXTR_SKIP);
  135. ob_start();
  136. include $this->cache_file;
  137. $this->result = ob_get_clean();
  138. }
  139. /**
  140. * Executes the template and returns the result
  141. * @returns The result of the executed template
  142. */
  143. public function output() {
  144. $this->execute();
  145. return $this->result;
  146. }
  147. /**
  148. * Executes the template and echoes the result
  149. */
  150. public function write_output($content_type = null) {
  151. $output = $this->output();
  152. if ($content_type) {
  153. header('Content-Type: '.$content_type.'; charset=utf-8');
  154. header('Content-Length: '.strlen($output).'');
  155. }
  156. echo $output;
  157. }
  158. /**
  159. * Writes the template buffer into its cache file
  160. * @param string $buff the data that will be written
  161. */
  162. public function cache_write($buff) {
  163. $fh = fopen($this->cache_file, 'w') or trigger_error("Couldn't write file {$this->cache_file}!", E_USER_ERROR);
  164. fwrite($fh, $buff);
  165. fclose($fh);
  166. }
  167. /**
  168. * Translates specially formatted strings into PHP code
  169. * @param string $code String to be evaluated
  170. * @param array $e Enclosing tags
  171. */
  172. public function eval_enclosed($code, $e) {
  173. $enclosed = substr($code, strlen($e[0]), -1*strlen($e[1]));
  174. if (preg_match('/^[^\n]+$/', $code)) {
  175. if (preg_match('/^[A-Z0-9_]+$/', $enclosed)) {
  176. return "<?php echo $enclosed ?>";
  177. } else {
  178. switch ($e[0]) {
  179. case '<!--{':
  180. switch($enclosed) {
  181. case 'else':
  182. return "<?php } else { ?>";
  183. break;
  184. case '/':
  185. return "<?php } ?>";
  186. break;
  187. default:
  188. if (preg_match('/else.*/', $enclosed)) {
  189. return "<?php } $enclosed { ?>";
  190. } else {
  191. return "<?php $enclosed { ?>";
  192. }
  193. break;
  194. }
  195. break;
  196. case '{':
  197. if (preg_match('/^[A-Z0-9\'"\s]+:.+$/i', $enclosed)) {
  198. // javascript tuple
  199. return $code;
  200. } else {
  201. switch ($enclosed{0}) {
  202. case '%':
  203. // TODO: use regular expressions
  204. if (substr($enclosed, 1, 5) == 'using') {
  205. return "<?php extract(\$this->using(array".substr($enclosed, 6)."), EXTR_OVERWRITE) ?>";
  206. } else {
  207. return "<?php \$this->".substr($enclosed, 1)."; ?>";
  208. }
  209. break;
  210. case '=':
  211. // as is
  212. return "<?php echo ".substr($enclosed, 1)." ?>";
  213. break;
  214. case ':':
  215. // language
  216. if ($enclosed{1} == '(') {
  217. return "<?php echo __".substr($enclosed, 1)." ?>";
  218. } else {
  219. return "<?php echo _"."_(\"".substr($enclosed, 1)."\") ?>";
  220. }
  221. break;
  222. default:
  223. // escaped
  224. return $enclosed{0} == ' ' ? '{'.$enclosed.'}' : "<?php echo htmlspecialchars($enclosed) ?>";
  225. break;
  226. }
  227. }
  228. break;
  229. }
  230. }
  231. } else {
  232. return $code;
  233. }
  234. }
  235. public function clean_cache() {
  236. $dh = opendir($this->cache_dir);
  237. while (($f = readdir($dh)) !== false) {
  238. if ($f{0} != '.') {
  239. $f = $this->cache_dir.'/'.$f;
  240. if (time()-filemtime($f) > 3600*24) {
  241. unlink($f);
  242. }
  243. }
  244. }
  245. closedir($dh);
  246. }
  247. public function __construct(&$param = null) {
  248. }
  249. /**
  250. * Reads a template file from disk and converts it into PHP
  251. * @param string $file Full path to the template file
  252. */
  253. public function load_template($file) {
  254. $this->vars = array();
  255. if (!file_exists($file))
  256. trigger_error("Template file \"$file\" does not exists.", E_USER_ERROR);
  257. $this->cache_name = substr(md5($file), 0, 16).".".filemtime($file).'.'.filesize($file);
  258. $this->cache_file = $this->cache_dir.$this->cache_name;
  259. if (!file_exists($this->cache_file) || !$this->enable_cache) {
  260. // reading from disk
  261. $this->load_file($file, $buff);
  262. // included files
  263. while (preg_match('/{((include|require)\(\'([^\']+)\'\))}/', $buff, $match)) {
  264. $contents = file_get_contents(dirname($file).'/'.$match[3]);
  265. $buff = str_replace($match[0], $contents, $buff);
  266. }
  267. $this->parse($buff);
  268. $this->cache_write($buff);
  269. }
  270. }
  271. }
  272. ?>