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