Template.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. declare(strict_types=1);
  3. namespace netico\Bones;
  4. /**
  5. * This basic template system gets you off to a start.
  6. *
  7. * @package bones
  8. * @link https://git.lattuga.net/netico/code-library/src/master/Framework
  9. * @copyright Copyright (c) 2016, 2022 netico <netico@riseup.net>
  10. * @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License
  11. * @author netico <netico@riseup.net>
  12. *
  13. */
  14. class Template
  15. {
  16. private $blocks;
  17. private $vars;
  18. private $nb;
  19. private $template;
  20. /**
  21. * It requires the path to the template file.
  22. * You can refer to the TEMPLATES constant.
  23. *
  24. * @param string $template File path to template file.
  25. * @return void
  26. */
  27. public function __construct($template)
  28. {
  29. $this->template = $template;
  30. $this->nb = array();
  31. $this->blocks = array();
  32. $this->vars = array();
  33. }
  34. /**
  35. * The method adds a variable to the template.
  36. * This example describes how variables can be used within templates.
  37. *
  38. * PHP code:
  39. *
  40. * $t = new template("template.tpl");
  41. * $t->add("foo", "Barletta");
  42. * $t->output();
  43. *
  44. * HTML code:
  45. *
  46. * I love {foo}
  47. *
  48. * Output:
  49. *
  50. * I love Barletta
  51. *
  52. * @param string $key
  53. * @param string $value
  54. * @return void
  55. */
  56. public function add($key, $value)
  57. {
  58. $m = array();
  59. if (preg_match("/" . preg_quote('.', '/') . "/s", $key, $m) > 0) {
  60. $a = preg_split("/" . preg_quote('.', '/') . "/s", $key);
  61. $c = $a[0];
  62. $d = $a[1];
  63. $iter = $this->iter;
  64. $this->nb["$c"][$iter]["$d"] = array("key" => "$d", "value" => "$value");
  65. }
  66. if (preg_match("/" . preg_quote('.', '/') . "/s", $key, $m) < 1) {
  67. $this->vars["$key"]["key"] = $key;
  68. $this->vars["$key"]["value"] = $value;
  69. }
  70. }
  71. /**
  72. * This method provides support for repeating sections.
  73. *
  74. * PHP code:
  75. *
  76. * $t->section("planets");
  77. * $t->add("planets.name", "Neptune");
  78. * $t->add("planets.group", "Gas giant");
  79. * $t->section("planets");
  80. * $t->add("planets.name", "Saturn");
  81. * $t->add("planets.group", "Gas giant");
  82. *
  83. * HTML code
  84. *
  85. * <!-- open(planets) -->;
  86. * <p>Name: {name} - Group: {group}</p>
  87. * <!-- close(planets) -->;
  88. *
  89. * Output:
  90. *
  91. * Name: Neptune - Group: Gas giant
  92. * Name: Saturn - Group: Gas giant
  93. *
  94. * @param string $key
  95. * @return void
  96. */
  97. public function section($key)
  98. {
  99. $c = $this->load();
  100. $open = "/" . preg_quote('<!-- open(', '/') . $key . preg_quote(') -->', '/') . "/si";
  101. $close = "/" . preg_quote('<!-- close(', '/') . $key . preg_quote(') -->', '/') . "/si";
  102. $a = preg_split($open, $c);
  103. $c = $a[1];
  104. $a = preg_split($close, $c);
  105. if (empty($this->blocks["$key"]) == true) {
  106. $iter = 0;
  107. }
  108. if (empty($this->blocks["$key"]) == false) {
  109. $iter = $this->iter;
  110. }
  111. $str = '<!-- open(' . $key . ') -->';
  112. $str = $a[0];
  113. $str .= '<!-- close(' . $key . ') -->';
  114. $this->blocks["$key"]["content"] = $str;
  115. $this->iter = ($iter + 1);
  116. }
  117. /**
  118. * This method prints the HTML code.
  119. * Read https://stackoverflow.com/questions/6225351/how-to-minify-php-page-html-output,
  120. * that explains how to minify HTML code.
  121. *
  122. * @param string $mime
  123. * @return void
  124. */
  125. public function output($mime)
  126. {
  127. switch ($mime) {
  128. // TODO: Does it work?
  129. case 'XML':
  130. header('Content-Type: text/xml');
  131. break;
  132. case 'HTML':
  133. default:
  134. header('Content-Type: text/html');
  135. break;
  136. }
  137. echo $this->sanitize($this->string());
  138. }
  139. /**
  140. * This method also provides support for repeating sections.
  141. * The first parameter is an array constructed from the results of an SQL query.
  142. * The second parameter indicates the section with the name used in the template.
  143. *
  144. * PHP code:
  145. *
  146. * $db = new \netico\Bones\SQLite();
  147. * $db->sql("SELECT [id], [title], [text] FROM pages ORDER BY [id] ASC;");
  148. * $s->rs($db->data, "pages");
  149. *
  150. * HTML code:
  151. *
  152. * <!-- open(pages) -->
  153. * <h2><a name="{id}">{id}. {title}</a></h2>
  154. * <div>{text}</div>
  155. * <!-- close(pages) -->
  156. *
  157. * @param array $res
  158. * @param string $section
  159. * @return void
  160. */
  161. public function rs($res, $section)
  162. {
  163. $columns = array();
  164. foreach ($res as $r) {
  165. foreach ($r as $k => $v) {
  166. $columns[$k] = true;
  167. }
  168. }
  169. foreach ($res as $key => $v) {
  170. $this->section($section);
  171. foreach ($columns as $k => $v) {
  172. $this->add("$section.$k", $res["$key"]["$k"]);
  173. }
  174. }
  175. }
  176. private function load()
  177. {
  178. $t = $this->template;
  179. $fd = fopen("$t", "r");
  180. $r = fread($fd, filesize("$t"));
  181. fclose($fd);
  182. return $r;
  183. }
  184. private function replace($key, $value, $content)
  185. {
  186. $pattern = '/{' . preg_quote($key, '/') . '}/';
  187. return preg_replace($pattern, $value, $content);
  188. }
  189. private function string()
  190. {
  191. ksort($this->blocks);
  192. ksort($this->nb);
  193. $nca = array();
  194. foreach ($this->nb as $k => $r) {
  195. $content = $this->blocks["$k"]["content"];
  196. $nc = (string) "";
  197. // Repetitions
  198. foreach ($r as $repetition => $ra) {
  199. $tmp = $content;
  200. // section
  201. foreach ($ra as $key => $ka) {
  202. $pcre = $ka["key"];
  203. $value = $ka["value"];
  204. if (empty($pcre) != true && empty($value) != true) {
  205. $tmp = $this->replace($pcre, $value, $tmp);
  206. }
  207. }
  208. unset($key);
  209. $nc .= $tmp;
  210. }
  211. unset($repetition);
  212. $nca["$k"]["content"] = $nc;
  213. }
  214. $text = $this->load();
  215. foreach ($nca as $k => $v) {
  216. $content = $this->blocks["$k"]["content"];
  217. $content = '/' . preg_quote($content, '/') . '/';
  218. $nc = $v["content"];
  219. $text = preg_replace($content, $nc, $text);
  220. }
  221. foreach ($this->vars as $k => $v) {
  222. $pcre = $v["key"];
  223. $value = $v["value"];
  224. $text = $this->replace($pcre, $value, $text);
  225. }
  226. return $text;
  227. }
  228. private function sanitize($buffer)
  229. {
  230. $search = array(
  231. '/\>[^\S ]+/s', // strip whitespaces after tags, except space
  232. '/[^\S ]+\</s', // strip whitespaces before tags, except space
  233. '/(\s)+/s', // shorten multiple whitespace sequences
  234. '/<!--(.|\s)*?-->/' // Remove HTML comments
  235. );
  236. $replace = array(
  237. '>',
  238. '<',
  239. '\\1',
  240. ''
  241. );
  242. $buffer = preg_replace($search, $replace, $buffer);
  243. return $buffer;
  244. }
  245. }