init
[garradin.git] / include / libs / template_lite / class.parser.php
1 <?php
2
3 class Template_Syntax_Exception extends Exception
4 {
5 }
6
7 class Template_Parser
8 {
9 const ATTR_STATE_NAME = 0;
10 const ATTR_STATE_SEPARATOR = 1;
11 const ATTR_STATE_VALUE = 2;
12
13 const RESERVED_TPL_VAR_NAME = '(?:smarty|tpl|templatelite)';
14
15 // Methods to extend and rewrite
16 // These are actually just rewriting your template to the same code, for testing purpose
17 public function processString($content)
18 {
19 if (is_array($content))
20 {
21 return '"'.implode('', $content).'"';
22 }
23 else
24 {
25 return '"'.$content.'"';
26 }
27 }
28
29 public function processModifier($name, $content, $arguments, $map_array)
30 {
31 return "$content|$name";
32 }
33
34 public function processVariable($name)
35 {
36 return '$'.$name;
37 }
38
39 // These are parser methods, you should not extend or rewrite them
40
41 public function __construct()
42 {
43 // matches double quoted strings:
44 // "foobar"
45 // "foo\"bar"
46 // "foobar" . "foo\"bar"
47 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
48
49 // matches single quoted strings:
50 // 'foobar'
51 // 'foo\'bar'
52 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
53
54 // matches single or double quoted strings
55 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
56
57 // matches bracket portion of vars
58 // [0]
59 // [foo]
60 // [$bar]
61 // [#bar#]
62 $this->_var_bracket_regexp = '\[[\$|\#]?\w+\#?\]';
63 // $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
64
65 // matches section vars:
66 // %foo.bar%
67 $this->_svar_regexp = '\%\w+\.\w+\%';
68
69 // matches $ vars (not objects):
70 // $foo
71 // $foo[0]
72 // $foo[$bar]
73 // $foo[5][blah]
74 # $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*';
75 $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*';
76
77 // matches config vars:
78 // #foo#
79 // #foobar123_foo#
80 $this->_cvar_regexp = '\#[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*\#';
81
82 // matches valid variable syntax:
83 // $foo
84 // 'text'
85 // "text"
86 $this->_var_regexp = '(?:(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . ')|' . $this->_qstr_regexp . ')';
87
88 // matches valid modifier syntax:
89 // |foo
90 // |@foo
91 // |foo:"bar"
92 // |foo:$bar
93 // |foo:"bar":$foobar
94 // |foo|bar
95 $this->_mod_regexp = '(?:\|@?[0-9a-zA-Z_]+(?::(?>-?\w+|' . $this->_dvar_regexp . '|' . $this->_qstr_regexp .'))*)';
96
97 // matches valid function name:
98 // foo123
99 // _foo_bar
100 $this->_func_regexp = '(?:[a-zA-Z_]+\:\:)?[a-zA-Z_]+';
101 // $this->_func_regexp = '[a-zA-Z_]\w*';
102 }
103
104 protected function parseArguments($args_str)
105 {
106 $result = array();
107 $last_value = '';
108 $state = self::ATTR_STATE_NAME;
109
110 preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+))+|[=]/x', $args_str, $match);
111
112 foreach ($match[0] as $value)
113 {
114 if ($state == self::ATTR_STATE_NAME)
115 {
116 if (!is_string($value))
117 throw new Template_Syntax_Exception("Invalid attribute name '".$value."'.");
118
119 $attr_name = $value;
120 $state = self::ATTR_STATE_SEPARATOR;
121 }
122 elseif ($state == self::ATTR_STATE_SEPARATOR)
123 {
124 if ($value != '=')
125 throw new Template_Syntax_Exception("Expecting '=' after '".$last_value."'");
126
127 $state = self::ATTR_STATE_VALUE;
128 }
129 elseif ($state == self::ATTR_STATE_VALUE)
130 {
131 if ($value == '=')
132 throw new Template_Syntax_Exception("Unexpected '=' after '".$last_value."'");
133
134 if ($value == 'yes' || $value == 'on' || $value == 'true')
135 $value = true;
136 elseif ($value == 'no' || $value == 'off' || $value == 'false')
137 $value = false;
138 elseif ($value == 'null')
139 $value = null;
140
141 if (preg_match_all('/(?:(' . $this->_var_regexp . '|' . $this->_svar_regexp . ')(' . $this->_mod_regexp . '*))(?:\s+(.*))?/xs', $value, $variables))
142 {
143 list($value) = $this->parseVariables($variables[1], $variables[2]);
144 $result[$attr_name] = $value;
145 }
146 else
147 {
148 $result[$attr_name] = $value;
149 }
150
151 $state = self::ATTR_STATE_NAME;
152 }
153
154 $last_value = $value;
155 }
156
157 if ($state == self::ATTR_STATE_SEPARATOR)
158 throw new Template_Syntax_Exception("Expecting '=' after '".$last_value."'");
159 elseif ($state == self::ATTR_STATE_VALUE)
160 throw new Template_Syntax_Exception("Missing attribute value after '".$last_value."'");
161
162 return $result;
163 }
164
165 protected function parseVariables($variables, $modifiers)
166 {
167 $result = array();
168
169 foreach($variables as $key => $value)
170 {
171 $tag_variable = trim($variables[$key]);
172 /*
173 if(!empty($this->default_modifiers) && !preg_match('!(^|\|)templatelite:nodefaults($|\|)!',$modifiers[$key]))
174 {
175 $_default_mod_string = implode('|',(array)$this->default_modifiers);
176 $modifiers[$key] = empty($modifiers[$key]) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers[$key];
177 }*/
178
179 if (empty($modifiers[$key]))
180 {
181 $result[] = $this->parseVariable($tag_variable);
182 }
183 else
184 {
185 $result[] = $this->parseModifier($this->parseVariable($tag_variable), $modifiers[$key]);
186 }
187 }
188
189 return $result;
190 }
191
192 protected function parseVariable($variable)
193 {
194 // replace variable with value
195 if ($variable[0] == '$')
196 {
197 // replace the variable
198 #return $this->_compile_variable($variable);
199 return $this->processVariable(substr($variable, 1));
200 }
201 elseif ($variable[0] == '#')
202 {
203 // replace the config variable
204 #return $this->_compile_config($variable);
205 return $this->processConfigVariable(substr($variable, 1));
206 }
207 elseif ($variable[0] == '"')
208 {
209 // Parse classic string
210 $variable = substr($variable, 1, -1);
211 $result = array();
212
213 // replace all quoted variables by simple variables
214 $variable = preg_replace('!`('.$this->_dvar_regexp.')`!', '\\1', $variable);
215
216 // Split string between variables, if they are not escaped
217 // (will parse "hi $name" but no "hi \$name"
218 $parts = preg_split('!(^|[^\\])('.$this->_dvar_regexp.')!', $variable, -1,
219 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE | PREG_SPLIT_NO_EMPTY);
220
221 foreach ($parts as $key=>$part)
222 {
223 if ($part[0][0] == '$')
224 $result[] = $this->processVariable(substr($part[0], 1));
225 else
226 $result[] = $part[0];
227 }
228
229 return $this->processString($result);
230
231 /*
232 // expand the quotes to pull any variables out of it
233 // fortunately variables inside of a quote aren't fancy, no modifiers, no quotes
234 // just get everything from the $ to the ending space and parse it
235 // if the $ is escaped, then we won't expand it
236 //preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . ')/', $variable, $expand); // old match
237 // preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . '[^\\\])/', $variable, $_expand);
238 if (($pos = strpos($variable, '$')) !== false)
239 {
240 while ($pos !== false)
241 {
242 }
243 }
244
245 foreach($expand as $key => $value)
246 {
247 $expand[$key] = trim($value);
248 if (($pos = strpos($expand[$key], '$')) > 0)
249 {
250 $expand[$key] = substr($expand[$key], $pos);
251 }
252 }
253
254 $result = $variable;
255 foreach($expand as $value)
256 {
257 $value = trim($value);
258 $result = str_replace($value, '" . ' . $this->parseVariable($value) . ' . "', $result);
259 }
260 $result = str_replace("`", "", $result);
261
262 return $result;
263 */
264 }
265 elseif ($variable[0] == "'")
266 {
267 // return the value just as it is
268 return $this->processString(substr($variable, 1, -1));
269 }
270 elseif ($variable[0] == '%')
271 {
272 return $this->parseSection($variable);
273 }
274 else
275 {
276 // return it as is; i believe that there was a reason before that i did not just return it as is,
277 // but i forgot what that reason is ...
278 // the reason i return the variable 'as is' right now is so that unquoted literals are allowed
279 return $this->processString($variable);
280 }
281 }
282
283 protected function parseModifier($variable, $modifiers)
284 {
285 $mods = array(); // stores all modifiers
286 $args = array(); // modifier arguments
287
288 preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifiers, $match);
289 list(, $mods, $args) = $match;
290
291 $count_mods = count($mods);
292 for ($i = 0, $for_max = $count_mods; $i < $for_max; $i++)
293 {
294 preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $args[$i], $match);
295 $arg = $match[1];
296
297 if ($mods[$i]{0} == '@')
298 {
299 $mods[$i] = substr($mods[$i], 1);
300 $map_array = 0;
301 }
302 else
303 {
304 $map_array = 1;
305 }
306
307 foreach($arg as $key => $value)
308 {
309 $arg[$key] = $this->parseVariable($value);
310 }
311
312 //$variable = $this->callCompiler('modifier', array('name' => $mods[$i], 'content' => $variable, 'misc' => $map_array, 'args' => $arg));
313 $variable = $this->processModifier($mods[$i], $variable, $arg, $map_array);
314 /*
315 if ($this->_plugin_exists($_mods[$i], "modifier") || function_exists($_mods[$i]))
316 {
317 if (count($_arg) > 0)
318 {
319 $_arg = ', '.implode(', ', $_arg);
320 }
321 else
322 {
323 $_arg = '';
324 }
325
326 $php_function = "PHP";
327 if ($this->_plugin_exists($_mods[$i], "modifier"))
328 {
329 $php_function = "plugin";
330 }
331 $variable = "\$this->_run_modifier($variable, '$_mods[$i]', '$php_function', $_map_array$_arg)";
332 }
333 else
334 {
335 $variable = "\$this->trigger_error(\"'" . $_mods[$i] . "' modifier does not exist\", E_USER_NOTICE, __FILE__, __LINE__);";
336 }*/
337 }
338
339 return $variable;
340 }
341
342 }
343
344 ?>