]> git.mxchange.org Git - friendica.git/blob - include/template_processor.php
Merge remote-tracking branch 'refs/remotes/friendica/develop' into develop
[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                 $val = $this->r;
59                 foreach ($keys as $k) {
60                         $val = (isset($val[$k]) ? $val[$k] : null);
61                 }
62                 return $val;
63         }
64
65         /**
66          * IF node
67          * 
68          * {{ if <$var> }}...[{{ else }} ...] {{ endif }}
69          * {{ if <$var>==<val|$var> }}...[{{ else }} ...]{{ endif }}
70          * {{ if <$var>!=<val|$var> }}...[{{ else }} ...]{{ endif }}
71          */
72         private function _replcb_if($args) {
73                 if (strpos($args[2], "==") > 0) {
74                         list($a, $b) = array_map("trim", explode("==", $args[2]));
75                         $a = $this->_get_var($a);
76                         if ($b[0] == "$")
77                                 $b = $this->_get_var($b);
78                         $val = ($a == $b);
79                 } else if (strpos($args[2], "!=") > 0) {
80                         list($a, $b) = array_map("trim", explode("!=", $args[2]));
81                         $a = $this->_get_var($a);
82                         if ($b[0] == "$")
83                                 $b = $this->_get_var($b);
84                         $val = ($a != $b);
85                 } else {
86                         $val = $this->_get_var($args[2]);
87                 }
88                 $x = preg_split("|{{ *else *}}|", $args[3]);
89                 return ( $val ? $x[0] : (isset($x[1]) ? $x[1] : ""));
90         }
91
92         /**
93          * FOR node
94          * 
95          * {{ for <$var> as $name }}...{{ endfor }}
96          * {{ for <$var> as $key=>$name }}...{{ endfor }}
97          */
98         private function _replcb_for($args) {
99                 $m = array_map('trim', explode(" as ", $args[2]));
100                 $x = explode("=>", $m[1]);
101                 if (count($x) == 1) {
102                         $varname = $x[0];
103                         $keyname = "";
104                 } else {
105                         list($keyname, $varname) = $x;
106                 }
107                 if ($m[0] == "" || $varname == "" || is_null($varname))
108                         die("template error: 'for " . $m[0] . " as " . $varname . "'");
109                 //$vals = $this->r[$m[0]];
110                 $vals = $this->_get_var($m[0]);
111                 $ret = "";
112                 if (!is_array($vals))
113                         return $ret;
114                 foreach ($vals as $k => $v) {
115                         $this->_push_stack();
116                         $r = $this->r;
117                         $r[$varname] = $v;
118                         if ($keyname != '')
119                                 $r[$keyname] = (($k === 0) ? '0' : $k);
120                         $ret .= $this->replace($args[3], $r);
121                         $this->_pop_stack();
122                 }
123                 return $ret;
124         }
125
126         /**
127          * INC node
128          * 
129          * {{ inc <templatefile> [with $var1=$var2] }}{{ endinc }}
130          */
131         private function _replcb_inc($args) {
132                 if (strpos($args[2], "with")) {
133                         list($tplfile, $newctx) = array_map('trim', explode("with", $args[2]));
134                 } else {
135                         $tplfile = trim($args[2]);
136                         $newctx = null;
137                 }
138
139                 if ($tplfile[0] == "$")
140                         $tplfile = $this->_get_var($tplfile);
141
142                 $this->_push_stack();
143                 $r = $this->r;
144                 if (!is_null($newctx)) {
145                         list($a, $b) = array_map('trim', explode("=", $newctx));
146                         $r[$a] = $this->_get_var($b);
147                 }
148                 $this->nodes = Array();
149                 $tpl = get_markup_template($tplfile);
150                 $ret = $this->replace($tpl, $r);
151                 $this->_pop_stack();
152                 return $ret;
153         }
154
155         /**
156          * DEBUG node
157          * 
158          * {{ debug $var [$var [$var [...]]] }}{{ enddebug }}
159          * 
160          * replace node with <pre>var_dump($var, $var, ...);</pre>
161          */
162         private function _replcb_debug($args) {
163                 $vars = array_map('trim', explode(" ", $args[2]));
164                 $vars[] = $args[1];
165
166                 $ret = "<pre>";
167                 foreach ($vars as $var) {
168                         $ret .= htmlspecialchars(var_export($this->_get_var($var), true));
169                         $ret .= "\n";
170                 }
171                 $ret .= "</pre>";
172                 return $ret;
173         }
174
175         private function _replcb_node($m) {
176                 $node = $this->nodes[$m[1]];
177                 if (method_exists($this, "_replcb_" . $node[1])) {
178                         $s = call_user_func(array($this, "_replcb_" . $node[1]), $node);
179                 } else {
180                         $s = "";
181                 }
182                 $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s);
183                 return $s;
184         }
185
186         private function _replcb($m) {
187                 //var_dump(array_map('htmlspecialchars', $m));
188                 $this->done = false;
189                 $this->nodes[] = (array) $m;
190                 return "||" . (count($this->nodes) - 1) . "||";
191         }
192
193         private function _build_nodes($s) {
194                 $this->done = false;
195                 while (!$this->done) {
196                         $this->done = true;
197                         $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*({{ *else *}}[^{]*)?){{ *end\1 *}}|', array($this, "_replcb"), $s);
198                         if ($s == Null)
199                                 $this->_preg_error();
200                 }
201                 //({{ *else *}}[^{]*)?
202                 krsort($this->nodes);
203                 return $s;
204         }
205
206         private function var_replace($s) {
207                 $m = array();
208                 /** regexp:
209                  * \$                                           literal $
210                  * (\[)?                                        optional open square bracket
211                  * ([a-zA-Z0-9-_]+\.?)+         var name, followed by optional
212                  *                                                      dot, repeated at least 1 time
213                  * (|[a-zA-Z0-9-_:]+)*          pipe followed by filter name and args, zero or many
214                  * (?(1)\])                                     if there was opened square bracket
215                  *                                                      (subgrup 1), match close bracket
216                  */
217                 if (preg_match_all('/\$(\[)?([a-zA-Z0-9-_]+\.?)+(\|[a-zA-Z0-9-_:]+)*(?(1)\])/', $s, $m)) {
218                         foreach ($m[0] as $var) {
219
220                                 $exp = str_replace(array("[", "]"), array("", ""), $var);
221                                 $exptks = explode("|", $exp);
222
223                                 $varn = $exptks[0];
224                                 unset($exptks[0]);
225                                 $val = $this->_get_var($varn, true);
226                                 if ($val != KEY_NOT_EXISTS) {
227                                         /* run filters */
228                                         /*
229                                          * Filter are in form of:
230                                          * filtername:arg:arg:arg
231                                          * 
232                                          * "filtername" is function name
233                                          * "arg"s are optional, var value is appended to the end
234                                          *                      if one "arg"==='x' , is replaced with var value
235                                          * 
236                                          * examples:
237                                          * $item.body|htmlspecialchars                          // escape html chars
238                                          * $item.body|htmlspecialchars|strtoupper   // escape html and uppercase result
239                                          * $item.created|date:%Y %M %j                          // format date (created is a timestamp)
240                                          * $item.body|str_replace:cat:dog                       // replace all "cat" with "dog"
241                                          * $item.body|str_replace:cat:dog:x:1           // replace one "cat" with "dog"
242                                          
243                                          */
244                                         foreach ($exptks as $filterstr) {
245                                                 $filter = explode(":", $filterstr);
246                                                 $filtername = $filter[0];
247                                                 unset($filter[0]);
248                                                 $valkey = array_search("x", $filter);
249                                                 if ($valkey === false) {
250                                                         $filter[] = $val;
251                                                 } else {
252                                                         $filter[$valkey] = $val;
253                                                 }
254                                                 if (function_exists($filtername)) {
255                                                         $val = call_user_func_array($filtername, $filter);
256                                                 }
257                                         }
258                                         $s = str_replace($var, $val, $s);
259                                 }
260                         }
261                 }
262
263                 return $s;
264         }
265
266         // TemplateEngine interface
267         public function replace_macros($s, $r) {
268                 $this->r = $r;
269
270                 // remove comments block
271                 $s = preg_replace('/{#(.*?\s*?)*?#}/', "", $s);
272
273                 $s = $this->_build_nodes($s);
274
275                 $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s);
276                 if ($s == Null)
277                         $this->_preg_error();
278
279                 // replace strings recursively (limit to 10 loops)
280                 $os = "";
281                 $count = 0;
282                 while ($os != $s && $count < 10) {
283                         $os = $s;
284                         $count++;
285                         $s = $this->var_replace($s);
286                 }
287                 return template_unescape($s);
288         }
289         
290         public function get_template_file($file, $root='') {
291                 $a = get_app();
292                 $template_file = get_template_file($a, $file, $root);
293                 $content = file_get_contents($template_file);
294                 return $content;                
295         }
296         
297 }
298
299
300 function template_escape($s) {
301
302         return str_replace(array('$', '{{'), array('!_Doll^Ars1Az_!', '!_DoubLe^BraceS4Rw_!'), $s);
303 }
304
305 function template_unescape($s) {
306
307         return str_replace(array('!_Doll^Ars1Az_!', '!_DoubLe^BraceS4Rw_!'), array('$', '{{'), $s);
308 }
309