Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
7.9 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. <?php
  2. require_once 'object/TemplateEngine.php';
  3. define("KEY_NOT_EXISTS", '^R_key_not_Exists^');
  4. class Template implements ITemplateEngine {
  5. static $name ="internal";
  6. var $r;
  7. var $search;
  8. var $replace;
  9. var $stack = array();
  10. var $nodes = array();
  11. var $done = false;
  12. var $d = false;
  13. var $lang = null;
  14. var $debug = false;
  15. private function _preg_error() {
  16. switch (preg_last_error()) {
  17. case PREG_INTERNAL_ERROR: echo('PREG_INTERNAL_ERROR');
  18. break;
  19. case PREG_BACKTRACK_LIMIT_ERROR: echo('PREG_BACKTRACK_LIMIT_ERROR');
  20. break;
  21. case PREG_RECURSION_LIMIT_ERROR: echo('PREG_RECURSION_LIMIT_ERROR');
  22. break;
  23. case PREG_BAD_UTF8_ERROR: echo('PREG_BAD_UTF8_ERROR');
  24. break;
  25. // This is only valid for php > 5.3, not certain how to code around it for unit tests
  26. // case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break;
  27. default:
  28. //die("Unknown preg error.");
  29. return;
  30. }
  31. echo "<hr><pre>";
  32. debug_print_backtrace();
  33. die();
  34. }
  35. private function _push_stack() {
  36. $this->stack[] = array($this->r, $this->nodes);
  37. }
  38. private function _pop_stack() {
  39. list($this->r, $this->nodes) = array_pop($this->stack);
  40. }
  41. private function _get_var($name, $retNoKey = false) {
  42. $keys = array_map('trim', explode(".", $name));
  43. if ($retNoKey && !array_key_exists($keys[0], $this->r))
  44. return KEY_NOT_EXISTS;
  45. $val = $this->r;
  46. foreach ($keys as $k) {
  47. $val = (isset($val[$k]) ? $val[$k] : null);
  48. }
  49. return $val;
  50. }
  51. /**
  52. * IF node
  53. *
  54. * {{ if <$var> }}...[{{ else }} ...] {{ endif }}
  55. * {{ if <$var>==<val|$var> }}...[{{ else }} ...]{{ endif }}
  56. * {{ if <$var>!=<val|$var> }}...[{{ else }} ...]{{ endif }}
  57. */
  58. private function _replcb_if($args) {
  59. if (strpos($args[2], "==") > 0) {
  60. list($a, $b) = array_map("trim", explode("==", $args[2]));
  61. $a = $this->_get_var($a);
  62. if ($b[0] == "$")
  63. $b = $this->_get_var($b);
  64. $val = ($a == $b);
  65. } else if (strpos($args[2], "!=") > 0) {
  66. list($a, $b) = array_map("trim", explode("!=", $args[2]));
  67. $a = $this->_get_var($a);
  68. if ($b[0] == "$")
  69. $b = $this->_get_var($b);
  70. $val = ($a != $b);
  71. } else {
  72. $val = $this->_get_var($args[2]);
  73. }
  74. $x = preg_split("|{{ *else *}}|", $args[3]);
  75. return ( $val ? $x[0] : (isset($x[1]) ? $x[1] : ""));
  76. }
  77. /**
  78. * FOR node
  79. *
  80. * {{ for <$var> as $name }}...{{ endfor }}
  81. * {{ for <$var> as $key=>$name }}...{{ endfor }}
  82. */
  83. private function _replcb_for($args) {
  84. $m = array_map('trim', explode(" as ", $args[2]));
  85. $x = explode("=>", $m[1]);
  86. if (count($x) == 1) {
  87. $varname = $x[0];
  88. $keyname = "";
  89. } else {
  90. list($keyname, $varname) = $x;
  91. }
  92. if ($m[0] == "" || $varname == "" || is_null($varname))
  93. die("template error: 'for " . $m[0] . " as " . $varname . "'");
  94. //$vals = $this->r[$m[0]];
  95. $vals = $this->_get_var($m[0]);
  96. $ret = "";
  97. if (!is_array($vals))
  98. return $ret;
  99. foreach ($vals as $k => $v) {
  100. $this->_push_stack();
  101. $r = $this->r;
  102. $r[$varname] = $v;
  103. if ($keyname != '')
  104. $r[$keyname] = (($k === 0) ? '0' : $k);
  105. $ret .= $this->replace($args[3], $r);
  106. $this->_pop_stack();
  107. }
  108. return $ret;
  109. }
  110. /**
  111. * INC node
  112. *
  113. * {{ inc <templatefile> [with $var1=$var2] }}{{ endinc }}
  114. */
  115. private function _replcb_inc($args) {
  116. if (strpos($args[2], "with")) {
  117. list($tplfile, $newctx) = array_map('trim', explode("with", $args[2]));
  118. } else {
  119. $tplfile = trim($args[2]);
  120. $newctx = null;
  121. }
  122. if ($tplfile[0] == "$")
  123. $tplfile = $this->_get_var($tplfile);
  124. $this->_push_stack();
  125. $r = $this->r;
  126. if (!is_null($newctx)) {
  127. list($a, $b) = array_map('trim', explode("=", $newctx));
  128. $r[$a] = $this->_get_var($b);
  129. }
  130. $this->nodes = Array();
  131. $tpl = get_markup_template($tplfile);
  132. $ret = $this->replace($tpl, $r);
  133. $this->_pop_stack();
  134. return $ret;
  135. }
  136. /**
  137. * DEBUG node
  138. *
  139. * {{ debug $var [$var [$var [...]]] }}{{ enddebug }}
  140. *
  141. * replace node with <pre>var_dump($var, $var, ...);</pre>
  142. */
  143. private function _replcb_debug($args) {
  144. $vars = array_map('trim', explode(" ", $args[2]));
  145. $vars[] = $args[1];
  146. $ret = "<pre>";
  147. foreach ($vars as $var) {
  148. $ret .= htmlspecialchars(var_export($this->_get_var($var), true));
  149. $ret .= "\n";
  150. }
  151. $ret .= "</pre>";
  152. return $ret;
  153. }
  154. private function _replcb_node($m) {
  155. $node = $this->nodes[$m[1]];
  156. if (method_exists($this, "_replcb_" . $node[1])) {
  157. $s = call_user_func(array($this, "_replcb_" . $node[1]), $node);
  158. } else {
  159. $s = "";
  160. }
  161. $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s);
  162. return $s;
  163. }
  164. private function _replcb($m) {
  165. //var_dump(array_map('htmlspecialchars', $m));
  166. $this->done = false;
  167. $this->nodes[] = (array) $m;
  168. return "||" . (count($this->nodes) - 1) . "||";
  169. }
  170. private function _build_nodes($s) {
  171. $this->done = false;
  172. while (!$this->done) {
  173. $this->done = true;
  174. $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*({{ *else *}}[^{]*)?){{ *end\1 *}}|', array($this, "_replcb"), $s);
  175. if ($s == Null)
  176. $this->_preg_error();
  177. }
  178. //({{ *else *}}[^{]*)?
  179. krsort($this->nodes);
  180. return $s;
  181. }
  182. private function var_replace($s) {
  183. $m = array();
  184. /** regexp:
  185. * \$ literal $
  186. * (\[)? optional open square bracket
  187. * ([a-zA-Z0-9-_]+\.?)+ var name, followed by optional
  188. * dot, repeated at least 1 time
  189. * (|[a-zA-Z0-9-_:]+)* pipe followed by filter name and args, zero or many
  190. * (?(1)\]) if there was opened square bracket
  191. * (subgrup 1), match close bracket
  192. */
  193. if (preg_match_all('/\$(\[)?([a-zA-Z0-9-_]+\.?)+(\|[a-zA-Z0-9-_:]+)*(?(1)\])/', $s, $m)) {
  194. foreach ($m[0] as $var) {
  195. $exp = str_replace(array("[", "]"), array("", ""), $var);
  196. $exptks = explode("|", $exp);
  197. $varn = $exptks[0];
  198. unset($exptks[0]);
  199. $val = $this->_get_var($varn, true);
  200. if ($val != KEY_NOT_EXISTS) {
  201. /* run filters */
  202. /*
  203. * Filter are in form of:
  204. * filtername:arg:arg:arg
  205. *
  206. * "filtername" is function name
  207. * "arg"s are optional, var value is appended to the end
  208. * if one "arg"==='x' , is replaced with var value
  209. *
  210. * examples:
  211. * $item.body|htmlspecialchars // escape html chars
  212. * $item.body|htmlspecialchars|strtoupper // escape html and uppercase result
  213. * $item.created|date:%Y %M %j // format date (created is a timestamp)
  214. * $item.body|str_replace:cat:dog // replace all "cat" with "dog"
  215. * $item.body|str_replace:cat:dog:x:1 // replace one "cat" with "dog"
  216. */
  217. foreach ($exptks as $filterstr) {
  218. $filter = explode(":", $filterstr);
  219. $filtername = $filter[0];
  220. unset($filter[0]);
  221. $valkey = array_search("x", $filter);
  222. if ($valkey === false) {
  223. $filter[] = $val;
  224. } else {
  225. $filter[$valkey] = $val;
  226. }
  227. if (function_exists($filtername)) {
  228. $val = call_user_func_array($filtername, $filter);
  229. }
  230. }
  231. $s = str_replace($var, $val, $s);
  232. }
  233. }
  234. }
  235. return $s;
  236. }
  237. // TemplateEngine interface
  238. public function replace_macros($s, $r) {
  239. $this->r = $r;
  240. // remove comments block
  241. $s = preg_replace('/{#(.*?\s*?)*?#}/', "", $s);
  242. $s = $this->_build_nodes($s);
  243. $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s);
  244. if ($s == Null)
  245. $this->_preg_error();
  246. // replace strings recursively (limit to 10 loops)
  247. $os = "";
  248. $count = 0;
  249. while ($os != $s && $count < 10) {
  250. $os = $s;
  251. $count++;
  252. $s = $this->var_replace($s);
  253. }
  254. return template_unescape($s);
  255. }
  256. public function get_template_file($file, $root='') {
  257. $a = get_app();
  258. $template_file = get_template_file($a, $file, $root);
  259. $content = file_get_contents($template_file);
  260. return $content;
  261. }
  262. }
  263. function template_escape($s) {
  264. return str_replace(array('$', '{{'), array('!_Doll^Ars1Az_!', '!_DoubLe^BraceS4Rw_!'), $s);
  265. }
  266. function template_unescape($s) {
  267. return str_replace(array('!_Doll^Ars1Az_!', '!_DoubLe^BraceS4Rw_!'), array('$', '{{'), $s);
  268. }