init
[garradin.git] / include / libs / template_lite / class.compiler.php
1 <?php
2 /*
3 * Project: template_lite, a smarter template engine
4 * File: class.compiler.php
5 * Author: Paul Lockaby <paul@paullockaby.com>, Mark Dickenson <akapanamajack@sourceforge.net>
6 * Copyright: 2003,2004,2005 by Paul Lockaby, 2005,2006 Mark Dickenson
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * The latest version of template_lite can be obtained from:
23 * http://templatelite.sourceforge.net
24 *
25 */
26
27 class Template_Lite_Compiler extends Template_Lite {
28 // public configuration variables
29 var $left_delimiter = "";
30 var $right_delimiter = "";
31 var $plugins_dir = "";
32 var $template_dir = "";
33 var $reserved_template_varname = "";
34 var $default_modifiers = array();
35
36 var $php_extract_vars = true; // Set this to false if you do not want the $this->_tpl variables to be extracted for use by PHP code inside the template.
37
38 // private internal variables
39 var $_vars = array(); // stores all internal assigned variables
40 var $_confs = array(); // stores all internal config variables
41 var $_plugins = array(); // stores all internal plugins
42 var $_linenum = 0; // the current line number in the file we are processing
43 var $_file = ""; // the current file we are processing
44 var $_literal = array(); // stores all literal blocks
45 var $_foreachelse_stack = array();
46 var $_for_stack = 0;
47 var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
48 var $_switch_stack = array();
49 var $_tag_stack = array();
50 var $_require_stack = array(); // stores all files that are "required" inside of the template
51 var $_php_blocks = array(); // stores all of the php blocks
52 var $_error_level = null;
53 var $_sl_md5 = '39fc70570b8b60cbc1b85839bf242aff';
54
55 var $_db_qstr_regexp = null; // regexps are setup in the constructor
56 var $_si_qstr_regexp = null;
57 var $_qstr_regexp = null;
58 var $_func_regexp = null;
59 var $_var_bracket_regexp = null;
60 var $_dvar_regexp = null;
61 var $_cvar_regexp = null;
62 var $_svar_regexp = null;
63 var $_mod_regexp = null;
64 var $_var_regexp = null;
65 var $_obj_params_regexp = null;
66 var $_templatelite_vars = array();
67
68 function Template_Lite_compiler()
69 {
70 // matches double quoted strings:
71 // "foobar"
72 // "foo\"bar"
73 // "foobar" . "foo\"bar"
74 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
75
76 // matches single quoted strings:
77 // 'foobar'
78 // 'foo\'bar'
79 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
80
81 // matches single or double quoted strings
82 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
83
84 // matches bracket portion of vars
85 // [0]
86 // [foo]
87 // [$bar]
88 // [#bar#]
89 $this->_var_bracket_regexp = '\[[\$|\#]?\w+\#?\]';
90 // $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
91
92 // matches section vars:
93 // %foo.bar%
94 $this->_svar_regexp = '\%\w+\.\w+\%';
95
96 // matches $ vars (not objects):
97 // $foo
98 // $foo[0]
99 // $foo[$bar]
100 // $foo[5][blah]
101 // $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*';
102 $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*';
103
104 // matches config vars:
105 // #foo#
106 // #foobar123_foo#
107 $this->_cvar_regexp = '\#[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*\#';
108
109 // matches valid variable syntax:
110 // $foo
111 // 'text'
112 // "text"
113 $this->_var_regexp = '(?:(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . ')|' . $this->_qstr_regexp . ')';
114
115 // matches valid modifier syntax:
116 // |foo
117 // |@foo
118 // |foo:"bar"
119 // |foo:$bar
120 // |foo:"bar":$foobar
121 // |foo|bar
122 $this->_mod_regexp = '(?:\|@?[0-9a-zA-Z_]+(?::(?>-?\w+|' . $this->_dvar_regexp . '|' . $this->_qstr_regexp .'))*)';
123
124 // matches valid function name:
125 // foo123
126 // _foo_bar
127 $this->_func_regexp = '[a-zA-Z_]+';
128 // $this->_func_regexp = '[a-zA-Z_]\w*';
129
130 $this->_const_regexp = '(?:[a-zA-Z_]+\\\\)*(?:[a-zA-Z_]+::)?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
131 }
132
133 function _compile_file($file_contents)
134 {
135 // Remove BOM from templates
136 if(substr($file_contents, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
137 $file_contents = substr($file_contents, 3);
138 }
139
140 $ldq = preg_quote($this->left_delimiter);
141 $rdq = preg_quote($this->right_delimiter);
142 $_match = array(); // a temp variable for the current regex match
143 $tags = array(); // all original tags
144 $text = array(); // all original text
145 $compiled_text = '<?php /* '.$this->_version.' '.strftime("%Y-%m-%d %H:%M:%S %Z").' */ ?>'; // stores the compiled result
146 $compiled_tags = array(); // all tags and stuff
147
148 $this->_require_stack = array();
149
150 $this->_load_filters();
151
152 if (count($this->_plugins['prefilter']) > 0)
153 {
154 foreach ($this->_plugins['prefilter'] as $function)
155 {
156 if ($function === false)
157 {
158 continue;
159 }
160 $file_contents = $function($file_contents, $this);
161 }
162 }
163
164 // remove all comments
165 $file_contents = preg_replace("!{$ldq}\*.*?\*{$rdq}!s","",$file_contents);
166
167 // replace all php start and end tags
168 $file_contents = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>', $file_contents);
169
170 // remove literal blocks
171 preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $file_contents, $_match);
172 $this->_literal = $_match[1];
173 $file_contents = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", stripslashes($ldq . "literal" . $rdq), $file_contents);
174
175 // remove php blocks
176 preg_match_all("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", $file_contents, $_match);
177 $this->_php_blocks = $_match[1];
178 $file_contents = preg_replace("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", stripslashes($ldq . "php" . $rdq), $file_contents);
179
180 // gather all template tags
181 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $file_contents, $_match);
182 $tags = $_match[1];
183
184 // put all of the non-template tag text blocks into an array, using the template tags as delimiters
185 $text = preg_split("!{$ldq}.*?{$rdq}!s", $file_contents);
186
187 // compile template tags
188 $count_tags = count($tags);
189 for ($i = 0, $for_max = $count_tags; $i < $for_max; $i++)
190 {
191 $this->_linenum += substr_count($text[$i], "\n");
192 $compiled_tags[] = $this->_compile_tag($tags[$i]);
193 $this->_linenum += substr_count($tags[$i], "\n");
194 }
195
196 // build the compiled template by replacing and interleaving text blocks and compiled tags
197 $count_compiled_tags = count($compiled_tags);
198 for ($i = 0, $for_max = $count_compiled_tags; $i < $for_max; $i++)
199 {
200 if ($compiled_tags[$i] == '') {
201 $text[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text[$i+1]);
202 }
203 $compiled_text .= $text[$i].$compiled_tags[$i];
204 }
205 $compiled_text .= $text[$i];
206
207 foreach ($this->_require_stack as $key => $value)
208 {
209 $compiled_text = '<?php require_once(\''. $this->_get_plugin_dir($key) . $key . '\'); $this->register_' . $value[0] . '("' . $value[1] . '", "' . $value[2] . '", false); ?>' . $compiled_text;
210 }
211
212 // remove unnecessary close/open tags
213 $compiled_text = preg_replace('!\?>\n?<\?php!', '', $compiled_text);
214
215 if (count($this->_plugins['postfilter']) > 0)
216 {
217 foreach ($this->_plugins['postfilter'] as $function)
218 {
219 if ($function === false)
220 {
221 continue;
222 }
223 $compiled_text = $function($compiled_text, $this);
224 }
225 }
226
227 return $compiled_text;
228 }
229
230 function _compile_tag($tag)
231 {
232 $_match = array(); // stores the tags
233 $_result = ""; // the compiled tag
234 $_variable = ""; // the compiled variable
235
236 // extract the tag command, modifier and arguments
237 preg_match_all('/(?:(' . $this->_const_regexp . '|' . $this->_var_regexp . '|' . $this->_svar_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)(?:\s*[,\.]\s*)?)(?:\s+(.*))?/xs', $tag, $_match);
238
239 if ($_match[1][0][0] == '$' || ($_match[1][0][0] == '#' && $_match[1][0][strlen($_match[1][0]) - 1] == '#') || $_match[1][0][0] == "'" || $_match[1][0][0] == '"' || $_match[1][0][0] == '%')
240 {
241 $_result = $this->_parse_variables($_match[1], $_match[2]);
242 return "<?php echo $_result; ?>";
243 }
244 elseif (defined($_match[1][0]))
245 {
246 return "<?php echo constant('".$_match[1][0]."'); ?>";
247 }
248 // process a function
249 $tag_command = $_match[1][0];
250 $tag_modifiers = !empty($_match[2][0]) ? $_match[2][0] : null;
251 $tag_arguments = !empty($_match[3][0]) ? $_match[3][0] : null;
252 $_result = $this->_parse_function($tag_command, $tag_modifiers, $tag_arguments);
253 return $_result;
254 }
255
256 function _parse_function($function, $modifiers, $arguments)
257 {
258 switch ($function) {
259 case 'include':
260 if (!function_exists('compile_include'))
261 {
262 require_once(TEMPLATE_LITE_DIR . "internal/compile.include.php");
263 }
264 return compile_include($arguments, $this);
265 break;
266 case 'insert':
267 $_args = $this->_parse_arguments($arguments);
268 if (!isset($_args['name']))
269 {
270 throw new Template_Exception("missing 'name' attribute in 'insert'", $this);
271 }
272 foreach ($_args as $key => $value)
273 {
274 if (is_bool($value))
275 {
276 $value = $value ? 'true' : 'false';
277 }
278 $arg_list[] = "'$key' => $value";
279 }
280 return '<?php echo $this->_run_insert(array(' . implode(', ', (array)$arg_list) . ')); ?>';
281 break;
282 case 'ldelim':
283 return $this->left_delimiter;
284 break;
285 case 'rdelim':
286 return $this->right_delimiter;
287 break;
288 case 'literal':
289 list (,$literal) = each($this->_literal);
290 $this->_linenum += substr_count($literal, "\n");
291 return "<?php echo '" . str_replace("'", "\'", str_replace("\\", "\\\\", $literal)) . "'; ?>";
292 break;
293 case 'php':
294 list (,$php_block) = each($this->_php_blocks);
295 $this->_linenum += substr_count($php_block, "\n");
296 $php_extract = '';
297 if($this->php_extract_vars)
298 {
299 if (strnatcmp(PHP_VERSION, '4.3.0') >= 0)
300 {
301 $php_extract = '<?php extract($this->_vars, EXTR_REFS); ?>' . "\n";
302 }
303 else
304 {
305 $php_extract = '<?php extract($this->_vars); ?>' . "\n";
306 }
307 }
308 return $php_extract . '<?php '.$php_block.' ?>';
309 break;
310 case 'foreach':
311 array_push($this->_foreachelse_stack, false);
312 $_args = $this->_parse_arguments($arguments);
313 if (!isset($_args['from']))
314 {
315 throw new Template_Exception("missing 'from' attribute in 'foreach'", $this);
316 }
317 if (!isset($_args['value']) && !isset($_args['item']))
318 {
319 throw new Template_Exception("missing 'value' attribute in 'foreach'", $this);
320 }
321 if (isset($_args['value']))
322 {
323 $_args['value'] = $this->_dequote($_args['value']);
324 }
325 elseif (isset($_args['item']))
326 {
327 $_args['value'] = $this->_dequote($_args['item']);
328 }
329 isset($_args['key']) ? $_args['key'] = "\$this->_vars['".$this->_dequote($_args['key'])."'] => " : $_args['key'] = '';
330 $_result = '<?php if (count((array)' . $_args['from'] . ')): ?>';
331 if (isset($_args['name']))
332 {
333 $foreach_props = '$this->_templatelite_vars[\'foreach\'][' . $_args['name'] . ']';
334 $_result .= '<?php ' . $foreach_props . '[\'iteration\'] = 0; ' . $foreach_props . '[\'total\'] = count((array)' . $_args['from'] . '); ?>';
335 }
336 $_result .= '<?php foreach ((array)' . $_args['from'] . ' as ' . $_args['key'] . '$this->_vars[\'' . $_args['value'] . '\']): ?>';
337 if (isset($_args['name']))
338 {
339 $_result .= '<?php ' . $foreach_props . '[\'iteration\']++; '
340 . $foreach_props . '[\'first\'] = ' . $foreach_props . '[\'iteration\'] == 1 ? true : false; '
341 . $foreach_props . '[\'last\'] = ' . $foreach_props . '[\'iteration\'] == ' . $foreach_props . '[\'total\'] ? true : false; '
342 . '?>';
343 }
344 return $_result;
345 break;
346 case 'foreachelse':
347 $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
348 return "<?php endforeach; else: ?>";
349 break;
350 case '/foreach':
351 if (array_pop($this->_foreachelse_stack))
352 {
353 return "<?php endif; ?>";
354 }
355 else
356 {
357 return "<?php endforeach; endif; ?>";
358 }
359 break;
360 case 'for':
361 $this->_for_stack++;
362 $_args = $this->_parse_arguments($arguments);
363 if (!isset($_args['start']))
364 {
365 throw new Template_Exception("missing 'start' attribute in 'for'", $this);
366 }
367 if (!isset($_args['stop']))
368 {
369 throw new Template_Exception("missing 'stop' attribute in 'for'", $this);
370 }
371 if (!isset($_args['step']))
372 {
373 $_args['step'] = 1;
374 }
375 $_result = '<?php for($for' . $this->_for_stack . ' = ' . $_args['start'] . '; ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ($for' . $this->_for_stack . ' < ' . $_args['stop'] . ') : ($for' . $this->_for_stack . ' > ' . $_args['stop'] . ')); $for' . $this->_for_stack . ' += ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ' . $_args['step'] . ' : -' . $_args['step'] . ')): ?>';
376 if (isset($_args['value']))
377 {
378 $_result .= '<?php $this->assign(\'' . $this->_dequote($_args['value']) . '\', $for' . $this->_for_stack . '); ?>';
379 }
380 return $_result;
381 break;
382 case '/for':
383 $this->_for_stack--;
384 return "<?php endfor; ?>";
385 break;
386 case 'section':
387 array_push($this->_sectionelse_stack, false);
388 if (!function_exists('compile_section_start'))
389 {
390 require_once(TEMPLATE_LITE_DIR . "internal/compile.section_start.php");
391 }
392 return compile_section_start($arguments, $this);
393 break;
394 case 'sectionelse':
395 $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
396 return "<?php endfor; else: ?>";
397 break;
398 case '/section':
399 if (array_pop($this->_sectionelse_stack))
400 {
401 return "<?php endif; ?>";
402 }
403 else
404 {
405 return "<?php endfor; endif; ?>";
406 }
407 break;
408 case 'while':
409 $_args = $this->_compile_if($arguments, false, true);
410 return '<?php while(' . $_args . '): ?>';
411 break;
412 case '/while':
413 return "<?php endwhile; ?>";
414 break;
415 case 'if':
416 return $this->_compile_if($arguments);
417 break;
418 case 'else':
419 return "<?php else: ?>";
420 break;
421 case 'elseif':
422 return $this->_compile_if($arguments, true);
423 break;
424 case '/if':
425 return "<?php endif; ?>";
426 break;
427 case 'assign':
428 $_args = $this->_parse_arguments($arguments);
429 if (!isset($_args['var']))
430 {
431 throw new Template_Exception("missing 'var' attribute in 'assign'", $this);
432 }
433 if (!isset($_args['value']))
434 {
435 throw new Template_Exception("missing 'value' attribute in 'assign'", $this);
436 }
437 return '<?php $this->assign(\'' . $this->_dequote($_args['var']) . '\', ' . $_args['value'] . '); ?>';
438 break;
439 case 'switch':
440 $_args = $this->_parse_arguments($arguments);
441 if (!isset($_args['from']))
442 {
443 throw new Template_Exception("missing 'from' attribute in 'switch'", $this);
444 }
445 array_push($this->_switch_stack, array("matched" => false, "var" => $this->_dequote($_args['from'])));
446 return;
447 break;
448 case '/switch':
449 array_pop($this->_switch_stack);
450 return '<?php break; endswitch; ?>';
451 break;
452 case 'case':
453 if (count($this->_switch_stack) > 0)
454 {
455 $_result = "<?php ";
456 $_args = $this->_parse_arguments($arguments);
457 $_index = count($this->_switch_stack) - 1;
458 if (!$this->_switch_stack[$_index]["matched"])
459 {
460 $_result .= 'switch(' . $this->_switch_stack[$_index]["var"] . '): ';
461 $this->_switch_stack[$_index]["matched"] = true;
462 }
463 else
464 {
465 $_result .= 'break; ';
466 }
467 if (!empty($_args['value']))
468 {
469 $_result .= 'case '.$_args['value'].': ';
470 }
471 else
472 {
473 $_result .= 'default: ';
474 }
475 return $_result . ' ?>';
476 }
477 else
478 {
479 throw new Template_Exception("unexpected 'case', 'case' can only be in a 'switch'", $this);
480 }
481 break;
482 case 'config_load':
483 $_args = $this->_parse_arguments($arguments);
484 if (empty($_args['file']))
485 {
486 throw new Template_Exception("missing 'file' attribute in 'config_load' tag", $this);
487 }
488 isset($_args['section']) ? null : $_args['section'] = 'null';
489 isset($_args['var']) ? null : $_args['var'] = 'null';
490 return '<?php $this->config_load(' . $_args['file'] . ', ' . $_args['section'] . ', ' . $_args['var'] . '); ?>';
491 break;
492 default:
493 $_result = "";
494 if ($this->_compile_compiler_function($function, $arguments, $_result))
495 {
496 return $_result;
497 }
498 else if ($this->_compile_custom_block($function, $modifiers, $arguments, $_result))
499 {
500 return $_result;
501 }
502 elseif ($this->_compile_custom_function($function, $modifiers, $arguments, $_result))
503 {
504 return $_result;
505 }
506 else
507 {
508 throw new Template_Exception($function." function does not exist", $this);
509 }
510 break;
511 }
512 }
513
514 function _compile_compiler_function($function, $arguments, &$_result)
515 {
516 if ($function = $this->_plugin_exists($function, "compiler"))
517 {
518 $_args = $this->_parse_arguments($arguments);
519 $_result = '<?php ' . $function($_args, $this) . ' ?>';
520 return true;
521 }
522 else
523 {
524 return false;
525 }
526 }
527
528 function _compile_custom_function($function, $modifiers, $arguments, &$_result)
529 {
530 if (!function_exists('compile_compile_custom_function'))
531 {
532 require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_function.php");
533 }
534 return compile_compile_custom_function($function, $modifiers, $arguments, $_result, $this);
535 }
536
537 function _compile_custom_block($function, $modifiers, $arguments, &$_result)
538 {
539 if (!function_exists('compile_compile_custom_block'))
540 {
541 require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_block.php");
542 }
543 return compile_compile_custom_block($function, $modifiers, $arguments, $_result, $this);
544 }
545
546 function _compile_if($arguments, $elseif = false, $while = false)
547 {
548 if (!function_exists('compile_compile_if'))
549 {
550 require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_if.php");
551 }
552 return compile_compile_if($arguments, $elseif, $while, $this);
553 }
554
555 function _parse_is_expr($is_arg, $_arg)
556 {
557 if (!function_exists('compile_parse_is_expr'))
558 {
559 require_once(TEMPLATE_LITE_DIR . "internal/compile.parse_is_expr.php");
560 }
561 return compile_parse_is_expr($is_arg, $_arg, $this);
562 }
563
564 function _compile_config($variable)
565 {
566 if (!function_exists('compile_compile_config'))
567 {
568 require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_config.php");
569 }
570 return compile_compile_config($variable, $this);
571 }
572
573 function _dequote($string)
574 {
575 if (($string[0] == "'" || $string[0] == '"') && $string{strlen($string)-1} == $string[0])
576 {
577 return substr($string, 1, -1);
578 }
579 else
580 {
581 return $string;
582 }
583 }
584
585 function _parse_arguments($arguments)
586 {
587 $_match = array();
588 $_result = array();
589 $_variables = array();
590 preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+))+|[=]/x', $arguments, $_match);
591 /*
592 Parse state:
593 0 - expecting attribute name
594 1 - expecting '='
595 2 - expecting attribute value (not '=')
596 */
597 $state = 0;
598 foreach($_match[0] as $value)
599 {
600 switch($state) {
601 case 0:
602 // valid attribute name
603 if (is_string($value))
604 {
605 $a_name = $value;
606 $state = 1;
607 }
608 else
609 {
610 throw new Template_Exception("invalid attribute name: '$token'", $this);
611 }
612 break;
613 case 1:
614 if ($value == '=')
615 {
616 $state = 2;
617 }
618 else
619 {
620 throw new Template_Exception("expecting '=' after '$last_value'", $this);
621 }
622 break;
623 case 2:
624 if ($value != '=')
625 {
626 if ($value == 'yes' || $value == 'on' || $value == 'true')
627 {
628 $value = true;
629 }
630 elseif ($value == 'no' || $value == 'off' || $value == 'false')
631 {
632 $value = false;
633 }
634 elseif ($value == 'null')
635 {
636 $value = null;
637 }
638
639 if(!preg_match_all('/(?:(' . $this->_const_regexp . '|' . $this->_var_regexp . '|' . $this->_svar_regexp . ')(' . $this->_mod_regexp . '*))(?:\s+(.*))?/xs', $value, $_variables))
640 {
641 $_result[$a_name] = $value;
642 }
643 else
644 {
645 $_result[$a_name] = $this->_parse_variables($_variables[1], $_variables[2]);
646 }
647 $state = 0;
648 }
649 else
650 {
651 throw new Template_Exception("'=' cannot be an attribute value", $this);
652 }
653 break;
654 }
655 $last_value = $value;
656 }
657 if($state != 0)
658 {
659 if($state == 1)
660 {
661 throw new Template_Exception("expecting '=' after attribute name '$last_value'", $this);
662 }
663 else
664 {
665 throw new Template_Exception("missing attribute value", $this);
666 }
667 }
668 return $_result;
669 }
670
671 function _parse_variables($variables, $modifiers)
672 {
673 $_result = "";
674 foreach($variables as $key => $value)
675 {
676 $tag_variable = trim($variables[$key]);
677 if(!empty($this->default_modifiers) && !preg_match('!(^|\|)templatelite:nodefaults($|\|)!',$modifiers[$key]))
678 {
679 $_default_mod_string = implode('|',(array)$this->default_modifiers);
680 $modifiers[$key] = empty($modifiers[$key]) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers[$key];
681 }
682 if (empty($modifiers[$key]))
683 {
684 $_result .= $this->_parse_variable($tag_variable).'.';
685 }
686 else
687 {
688 $_result .= $this->_parse_modifier($this->_parse_variable($tag_variable), $modifiers[$key]).'.';
689 }
690 }
691 return substr($_result, 0, -1);
692 }
693
694 function _parse_variable($variable)
695 {
696 // replace variable with value
697 if ($variable[0] == "\$")
698 {
699 // replace the variable
700 return $this->_compile_variable($variable);
701 }
702 elseif ($variable[0] == '#')
703 {
704 // replace the config variable
705 return $this->_compile_config($variable);
706 }
707 elseif ($variable[0] == '"')
708 {
709 // expand the quotes to pull any variables out of it
710 // fortunately variables inside of a quote aren't fancy, no modifiers, no quotes
711 // just get everything from the $ to the ending space and parse it
712 // if the $ is escaped, then we won't expand it
713 $_result = "";
714 preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . ')/', substr($variable, 1, -1), $_expand); // old match
715 // preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . '[^\\\])/', $variable, $_expand);
716 $_expand = array_unique($_expand[0]);
717 foreach($_expand as $key => $value)
718 {
719 $_expand[$key] = trim($value);
720 if (strpos($_expand[$key], '$') > 0)
721 {
722 $_expand[$key] = substr($_expand[$key], strpos($_expand[$key], '$'));
723 }
724 }
725 $_result = $variable;
726 foreach($_expand as $value)
727 {
728 $value = trim($value);
729 $_result = str_replace($value, '" . ' . $this->_parse_variable($value) . ' . "', $_result);
730 }
731 $_result = str_replace("`", "", $_result);
732 return $_result;
733 }
734 elseif ($variable[0] == "'")
735 {
736 // return the value just as it is
737 return $variable;
738 }
739 elseif ($variable[0] == "%")
740 {
741 return $this->_parse_section_prop($variable);
742 }
743 elseif (defined($variable))
744 {
745 return '"".constant("'.addslashes($variable).'")';
746 }
747 else
748 {
749 // return it as is; i believe that there was a reason before that i did not just return it as is,
750 // but i forgot what that reason is ...
751 // the reason i return the variable 'as is' right now is so that unquoted literals are allowed
752 return $variable;
753 }
754 }
755
756 function _parse_section_prop($section_prop_expr)
757 {
758 $parts = explode('|', $section_prop_expr, 2);
759 $var_ref = $parts[0];
760 $modifiers = isset($parts[1]) ? $parts[1] : '';
761
762 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
763 $section_name = $match[1];
764 $prop_name = $match[2];
765
766 $output = "\$this->_sections['$section_name']['$prop_name']";
767
768 $this->_parse_modifier($output, $modifiers);
769
770 return $output;
771 }
772
773 function _compile_variable($variable)
774 {
775 $_result = "";
776
777 // remove the $
778 $variable = substr($variable, 1);
779
780 // get [foo] and .foo and (...) pieces
781 preg_match_all('!(?:^\w+)|(?:' . $this->_var_bracket_regexp . ')|\.\$?\w+|\S+!', $variable, $_match);
782 $variable = $_match[0];
783 $var_name = array_shift($variable);
784
785 if ($var_name == $this->reserved_template_varname)
786 {
787 if ($variable[0][0] == '[' || $variable[0][0] == '.')
788 {
789 $find = array("[", "]", ".");
790 switch(strtoupper(str_replace($find, "", $variable[0])))
791 {
792 case 'GET':
793 $_result = "\$_GET";
794 break;
795 case 'POST':
796 $_result = "\$_POST";
797 break;
798 case 'COOKIE':
799 $_result = "\$_COOKIE";
800 break;
801 case 'ENV':
802 $_result = "\$_ENV";
803 break;
804 case 'SERVER':
805 $_result = "\$_SERVER";
806 break;
807 case 'SESSION':
808 $_result = "\$_SESSION";
809 break;
810 case 'NOW':
811 $_result = "time()";
812 break;
813 case 'SECTION':
814 $_result = "\$this->_sections";
815 break;
816 case 'LDELIM':
817 $_result = "\$this->left_delimiter";
818 break;
819 case 'RDELIM':
820 $_result = "\$this->right_delimiter";
821 break;
822 case 'VERSION':
823 $_result = "\$this->_version";
824 break;
825 case 'CONFIG':
826 $_result = "\$this->_confs";
827 break;
828 case 'TEMPLATE':
829 $_result = "\$this->_file";
830 break;
831 case 'CONST':
832 $constant = str_replace($find, "", $_match[0][2]);
833 $_result = "constant('$constant')";
834 $variable = array();
835 break;
836 default:
837 $_var_name = str_replace($find, "", $variable[0]);
838 $_result = "\$this->_templatelite_vars['$_var_name']";
839 break;
840 }
841 array_shift($variable);
842 }
843 else
844 {
845 throw new Template_Exception('$' . $var_name.implode('', $variable) . ' is an invalid $templatelite reference', $this);
846 }
847 }
848 else
849 {
850 $_result = "\$this->_vars['$var_name']";
851 }
852
853 foreach ($variable as $var)
854 {
855 if ($var[0] == '[')
856 {
857 $var = substr($var, 1, -1);
858 if (is_numeric($var))
859 {
860 $_result .= "[$var]";
861 }
862 elseif ($var[0] == '$')
863 {
864 $_result .= "[" . $this->_compile_variable($var) . "]";
865 }
866 elseif ($var[0] == '#')
867 {
868 $_result .= "[" . $this->_compile_config($var) . "]";
869 }
870 else
871 {
872 // $_result .= "['$var']";
873 $parts = explode('.', $var);
874 $section = $parts[0];
875 $section_prop = isset($parts[1]) ? $parts[1] : 'index';
876 $_result .= "[\$this->_sections['$section']['$section_prop']]";
877 }
878 }
879 else if ($var[0] == '.')
880 {
881 if ($var[1] == '$')
882 {
883 $_result .= "[\$this->_TPL['" . substr($var, 2) . "']]";
884 }
885 else
886 {
887 $_result .= "['" . substr($var, 1) . "']";
888 }
889 }
890 else if (substr($var,0,2) == '->')
891 {
892 if(substr($var,2,2) == '__')
893 {
894 throw new Template_Exception('call to internal object members is not allowed', $this);
895 }
896 else if (substr($var, 2, 1) == '$')
897 {
898 $_output .= '->{(($var=$this->_TPL[\''.substr($var,3).'\']) && substr($var,0,2)!=\'__\') ? $_var : throw new Template_Exception("cannot access property \\"$var\\"", $this)}';
899 }
900 }
901 else
902 {
903 throw new Template_Exception('$' . $var_name.implode('', $variable) . ' is an invalid reference', $this);
904 }
905 }
906 return $_result;
907 }
908
909 function _parse_modifier($variable, $modifiers)
910 {
911 $_match = array();
912 $_mods = array(); // stores all modifiers
913 $_args = array(); // modifier arguments
914
915 preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifiers, $_match);
916 list(, $_mods, $_args) = $_match;
917
918 $count_mods = count($_mods);
919 for ($i = 0, $for_max = $count_mods; $i < $for_max; $i++)
920 {
921 preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $_args[$i], $_match);
922 $_arg = $_match[1];
923
924 if ($_mods[$i][0] == '@')
925 {
926 $_mods[$i] = substr($_mods[$i], 1);
927 $_map_array = 0;
928 } else {
929 $_map_array = 1;
930 }
931
932 foreach($_arg as $key => $value)
933 {
934 $_arg[$key] = $this->_parse_variable($value);
935 }
936
937 if ($this->_plugin_exists($_mods[$i], "modifier") || function_exists($_mods[$i]))
938 {
939 if (count($_arg) > 0)
940 {
941 $_arg = ', '.implode(', ', $_arg);
942 }
943 else
944 {
945 $_arg = '';
946 }
947
948 $php_function = "PHP";
949 if ($this->_plugin_exists($_mods[$i], "modifier"))
950 {
951 $php_function = "plugin";
952 }
953 $variable = "\$this->_run_modifier($variable, '$_mods[$i]', '$php_function', $_map_array$_arg)";
954 }
955 else
956 {
957 throw new Template_Exception("'" . $_mods[$i] . "' modifier does not exist", $this);
958 }
959 }
960 return $variable;
961 }
962
963 function _plugin_exists($function, $type)
964 {
965 // check for object functions
966 if (isset($this->_plugins[$type][$function]) && is_array($this->_plugins[$type][$function]) && is_object($this->_plugins[$type][$function][0]) && method_exists($this->_plugins[$type][$function][0], $this->_plugins[$type][$function][1]))
967 {
968 return '$this->_plugins[\'' . $type . '\'][\'' . $function . '\'][0]->' . $this->_plugins[$type][$function][1];
969 }
970 // check for standard functions
971 if (isset($this->_plugins[$type][$function]) && is_callable($this->_plugins[$type][$function]))
972 {
973 return $this->_plugins[$type][$function];
974 }
975 // check for a plugin in the plugin directory
976 if (file_exists($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php'))
977 {
978 require_once($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php');
979 if (function_exists('tpl_' . $type . '_' . $function))
980 {
981 $this->_require_stack[$type . '.' . $function . '.php'] = array($type, $function, 'tpl_' . $type . '_' . $function);
982 return ('tpl_' . $type . '_' . $function);
983 }
984 }
985 return false;
986 }
987
988 function _load_filters()
989 {
990 if (count($this->_plugins['prefilter']) > 0)
991 {
992 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter)
993 {
994 if (!function_exists($prefilter))
995 {
996 @include_once( $this->_get_plugin_dir("prefilter." . $filter_name . ".php") . "prefilter." . $filter_name . ".php");
997 }
998 }
999 }
1000 if (count($this->_plugins['postfilter']) > 0)
1001 {
1002 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter)
1003 {
1004 if (!function_exists($postfilter))
1005 {
1006 @include_once( $this->_get_plugin_dir("postfilter." . $filter_name . ".php") . "postfilter." . $filter_name . ".php");
1007 }
1008 }
1009 }
1010 }
1011 }
1012
1013 ?>