PageRenderTime 231ms CodeModel.GetById 121ms app.highlight 51ms RepoModel.GetById 49ms app.codeStats 1ms

/template_engines_bench/libs/smarty-light/src/class.compiler.php

https://github.com/limb-php-framework/limb-tools
PHP | 800 lines | 647 code | 51 blank | 102 comment | 127 complexity | ef663209fe749a742c3f18037e1288f0 MD5 | raw file
  1<?php
  2/*
  3 * Project:	Smarty-Light, a smarter template engine
  4 * File:	class.compiler.php
  5 * Author:	Paul Lockaby <paul@paullockaby.com>
  6 * Version:	2.2.11
  7 * Copyright:	2003,2004,2005 by Paul Lockaby
  8 * Credit:	This work is a light version of Smarty: the PHP compiling
  9 *		template engine, v2.5.0-CVS. Smarty was originally
 10 *		programmed by Monte Ohrt and Andrei Zmievski and can be
 11 *		found at http://smarty.php.net
 12 *
 13 * This library is free software; you can redistribute it and/or
 14 * modify it under the terms of the GNU Lesser General Public
 15 * License as published by the Free Software Foundation; either
 16 * version 2.1 of the License, or (at your option) any later version.
 17 *
 18 * This library is distributed in the hope that it will be useful,
 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 21 * Lesser General Public License for more details.
 22 *
 23 * You should have received a copy of the GNU Lesser General Public
 24 * License along with this library; if not, write to the Free Software
 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 26 *
 27 * You may contact the author of Smarty-Light by e-mail at:
 28 * paul@paullockaby.com
 29 *
 30 * The latest version of Smarty-Light can be obtained from:
 31 * http://www.paullockaby.com/projects/smarty-light
 32 *
 33 */
 34
 35class compiler extends template {
 36	// public configuration variables
 37	var $left_tag			= "";
 38	var $right_tag			= "";
 39	var $plugin_dir			= "";
 40	var $template_dir		= "";
 41
 42	// private internal variables
 43	var $_vars			=	array();	// stores all internal assigned variables
 44	var $_confs			=	array();	// stores all internal config variables
 45	var $_plugins			=	array();	// stores all internal plugins
 46	var $_linenum			=	0;		// the current line number in the file we are processing
 47	var $_file			=	"";		// the current file we are processing
 48	var $_literal			=	array();	// stores all literal blocks
 49	var $_foreachelse_stack		=	array();
 50	var $_for_stack			=	0;
 51	var $_switch_stack		=	array();
 52	var $_tag_stack			=	array();
 53	var $_require_stack		=	array();	// stores all files that are "required" inside of the template
 54	var $_php_blocks		=	array();	// stores all of the php blocks
 55	var $_error_level		=	null;
 56	var $_sl_md5			=	'39fc70570b8b60cbc1b85839bf242aff';
 57
 58	var $_db_qstr_regexp		=	null;		// regexps are setup in the constructor
 59	var $_si_qstr_regexp		=	null;
 60	var $_qstr_regexp		=	null;
 61	var $_func_regexp		=	null;
 62	var $_var_bracket_regexp	=	null;
 63	var $_dvar_regexp		=	null;
 64	var $_cvar_regexp		=	null;
 65	var $_mod_regexp		=	null;
 66	var $_var_regexp		=	null;
 67
 68	function compiler() {
 69		// matches double quoted strings:
 70		// "foobar"
 71		// "foo\"bar"
 72		// "foobar" . "foo\"bar"
 73		$this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
 74
 75		// matches single quoted strings:
 76		// 'foobar'
 77		// 'foo\'bar'
 78		$this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
 79
 80		// matches single or double quoted strings
 81		$this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
 82
 83		// matches bracket portion of vars
 84		// [0]
 85		// [foo]
 86		// [$bar]
 87		// [#bar#]
 88		$this->_var_bracket_regexp = '\[[\$|\#]?\w+\#?\]';
 89				
 90		// matches $ vars (not objects):
 91		// $foo
 92		// $foo[0]
 93		// $foo[$bar]
 94		// $foo[5][blah]
 95		$this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*';
 96
 97		// matches config vars:
 98		// #foo#
 99		// #foobar123_foo#
100		$this->_cvar_regexp = '\#[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*\#';
101
102		// matches valid variable syntax:
103		// $foo
104		// 'text'
105		// "text"
106		$this->_var_regexp = '(?:(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . ')|' . $this->_qstr_regexp . ')';
107
108		// matches valid modifier syntax:
109		// |foo
110		// |@foo
111		// |foo:"bar"
112		// |foo:$bar
113		// |foo:"bar":$foobar
114		// |foo|bar
115		$this->_mod_regexp = '(?:\|@?[0-9a-zA-Z_]+(?::(?>-?\w+|' . $this->_dvar_regexp . '|' . $this->_qstr_regexp .'))*)';		
116
117		// matches valid function name:
118		// foo123
119		// _foo_bar
120		$this->_func_regexp = '[a-zA-Z0-9_]+';
121	}
122
123	function _compile_file($file_contents) {
124		$ldq = preg_quote($this->left_tag);
125		$rdq = preg_quote($this->right_tag);
126		$_match		= array();		// a temp variable for the current regex match
127		$tags		= array();		// all original tags
128		$text		= array();		// all original text
129		$compiled_text	= "";			// stores the compiled result
130		$compiled_tags	= array();		// all tags and stuff
131
132		// remove all comments
133		$file_contents = preg_replace("!{$ldq}\*.*?\*{$rdq}!se","",$file_contents);
134
135		// replace all php start and end tags
136		$file_contents = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $file_contents);
137
138		// remove literal blocks
139		preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $file_contents, $_match);
140		$this->_literal = $_match[1];
141		$file_contents = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", stripslashes($ldq . "literal" . $rdq), $file_contents);
142
143		// remove php blocks
144		preg_match_all("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", $file_contents, $_match);
145		$this->_php_blocks = $_match[1];
146		$file_contents = preg_replace("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", stripslashes($ldq . "php" . $rdq), $file_contents);
147
148		// gather all template tags
149		preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $file_contents, $_match);
150		$tags = $_match[1];
151
152		// put all of the non-template tag text blocks into an array, using the template tags as delimiters
153		$text = preg_split("!{$ldq}.*?{$rdq}!s", $file_contents);
154
155		// compile template tags
156		for ($i = 0, $for_max = count($tags); $i < $for_max; $i++) {
157			$this->_linenum += substr_count($text[$i], "\n");
158			$compiled_tags[] = $this->_compile_tag($tags[$i]);
159			$this->_linenum += substr_count($tags[$i], "\n");
160		}
161
162		// build the compiled template by replacing and interleaving text blocks and compiled tags
163		for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++)
164			$compiled_text .= $text[$i].$compiled_tags[$i];
165		$compiled_text .= $text[$i];
166
167		foreach ($this->_require_stack as $key => $value)
168			$compiled_text = '<?php require_once(\''. $this->_get_plugin_dir() . $key . '\'); $this->register_' . $value[0] . '("' . $value[1] . '", "' . $value[2] . '"); ?>' . $compiled_text;
169
170		// remove unnecessary close/open tags
171		$compiled_text = preg_replace('!\?>\n?<\?php!', '', $compiled_text);
172
173		return $compiled_text;
174	}
175
176	function _compile_tag($tag) {
177		$_match		= array();		// stores the tags
178		$_result	= "";			// the compiled tag
179		$_variable	= "";			// the compiled variable
180
181		// extract the tag command, modifier and arguments
182		preg_match_all('/(?:(' . $this->_var_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)(?:\s*[,\.]\s*)?)(?:\s+(.*))?/xs', $tag, $_match);
183
184		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} == '"') {
185			$_result = $this->_parse_variables($_match[1], $_match[2]);
186			return "<?php echo $_result; ?>\n";
187		}
188		// process a function
189		$tag_command = $_match[1][0];
190		$tag_modifiers = !empty($_match[2][0]) ? $_match[2][0] : null;
191		$tag_arguments = !empty($_match[3][0]) ? $_match[3][0] : null;
192		$_result = $this->_parse_function($tag_command, $tag_modifiers, $tag_arguments);
193		return $_result;
194	}
195
196	function _parse_function($function, $modifiers, $arguments) {
197		switch ($function) {
198			case 'include':
199				$_args = $this->_parse_arguments($arguments);
200				if (!isset($_args['file']))
201					$this->trigger_error("missing 'file' attribute in 'include'", E_USER_ERROR, __FILE__, __LINE__);
202				if (!isset($_args['assign']))
203					return '<?php echo $this->_fetch_compile(' . $_args['file'] . '); ?>';
204				else
205					return '<?php $this->assign("' . $this->_dequote($_args['assign']) . '", $this->_fetch_compile(' . $_args['file'] . ')); ?>';
206				break;
207			case 'insert':
208				$_args = $this->_parse_arguments($arguments);
209				if (!isset($_args['name']))
210					$this->trigger_error("missing 'name' attribute in 'insert'", E_USER_ERROR, __FILE__, __LINE__);
211				foreach ($_args as $key => $value) {
212					if (is_bool($value))
213						$value = $value ? 'true' : 'false';
214					$arg_list[] = "'$key' => $value";
215				}
216				return '<?php echo $this->_run_insert(array(' . implode(', ', (array)$arg_list) . ')); ?>';
217				break;
218			case 'ldelim':
219				return $this->left_tag;
220				break;
221			case 'rdelim':
222				return $this->right_tag;
223				break;
224			case 'literal':
225				list (,$literal) = each($this->_literal);
226				$this->_linenum += substr_count($literal, "\n");
227				return "<?php echo '" . str_replace("'", "\'", str_replace("\\", "\\\\", $literal)) . "'; ?>\n";
228				break;
229			case 'php':
230				list (,$php_block) = each($this->_php_blocks);
231				$this->_linenum += substr_count($php_block, "\n");
232				return '<?php ' . $php_block . ' ?>';
233				break;
234			case 'foreach':
235				array_push($this->_foreachelse_stack, false);
236				$_args = $this->_parse_arguments($arguments);
237				if (!isset($_args['from']))
238					$this->trigger_error("missing 'from' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);
239				if (!isset($_args['value']) && !isset($_args['item']))
240					$this->trigger_error("missing 'value' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);
241				if (isset($_args['value'])) {
242					$_args['value'] = $this->_dequote($_args['value']);
243				} elseif (isset($_args['item'])) {
244					$_args['value'] = $this->_dequote($_args['item']);
245				}
246				isset($_args['key']) ? $_args['key'] = "\$this->_vars['".$this->_dequote($_args['key'])."'] => " : $_args['key'] = '';
247				$_result = '<?php if (count((array)' . $_args['from'] . ')): foreach ((array)' . $_args['from'] . ' as ' . $_args['key'] . '$this->_vars[\'' . $_args['value'] . '\']): ?>';
248				return $_result;
249				break;
250			case 'foreachelse':
251				$this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
252				return "<?php endforeach; else: ?>";
253				break;
254			case '/foreach':
255				if (array_pop($this->_foreachelse_stack))
256					return "<?php endif; ?>";
257				else
258					return "<?php endforeach; endif; ?>";
259				break;
260			case 'for':
261				$this->_for_stack++;
262				$_args = $this->_parse_arguments($arguments);
263				if (!isset($_args['start']))
264					$this->trigger_error("missing 'start' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);
265				if (!isset($_args['stop']))
266					$this->trigger_error("missing 'stop' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);
267				if (!isset($_args['step']))
268					$_args['step'] = 1;
269				$_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'] . ')): ?>';
270				if (isset($_args['value']))
271					$_result .= '<?php $this->assign(\'' . $this->_dequote($_args['value']) . '\', $for' . $this->_for_stack . '); ?>';
272				return $_result;
273				break;
274			case '/for':
275				$this->_for_stack--;
276				return "<?php endfor; ?>";
277				break;
278			case 'if':
279				return $this->_compile_if($arguments);
280				break;
281			case 'else':
282				return "<?php else: ?>";
283				break;
284			case 'elseif':
285				return $this->_compile_if($arguments, true);
286				break;
287			case '/if':
288				return "<?php endif; ?>";
289				break;
290			case 'assign':
291		        	$_args = $this->_parse_arguments($arguments);
292				if (!isset($_args['name']))
293					$this->trigger_error("missing 'name' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);
294				if (!isset($_args['value']))
295					$this->trigger_error("missing 'value' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);
296				return '<?php $this->assign(\'' . $this->_dequote($_args['name']) . '\', ' . $_args['value'] . '); ?>';
297				break;
298			case 'switch':
299				$_args = $this->_parse_arguments($arguments);
300				if (!isset($_args['from']))
301					$this->trigger_error("missing 'from' attribute in 'switch'", E_USER_ERROR, __FILE__, __LINE__);
302				array_push($this->_switch_stack, array("matched" => false, "var" => $this->_dequote($_args['from'])));
303				return;
304				break;
305			case '/switch':
306				array_pop($this->_switch_stack);
307				return '<?php break; endswitch; ?>';
308				break;
309			case 'case':
310				if (count($this->_switch_stack) > 0) {
311					$_result = "<?php ";
312					$_args = $this->_parse_arguments($arguments);
313					$_index = count($this->_switch_stack) - 1;
314					if (!$this->_switch_stack[$_index]["matched"]) {
315						$_result .= 'switch(' . $this->_switch_stack[$_index]["var"] . '): ';
316						$this->_switch_stack[$_index]["matched"] = true;
317					} else {
318						$_result .= 'break; ';
319					}
320					if (!empty($_args['value']))
321						$_result .= 'case '.$_args['value'].': ';
322					else
323						$_result .= 'default: ';
324					return $_result . ' ?>';
325				} else {
326					$this->trigger_error("unexpected 'case', 'case' can only be in a 'switch'", E_USER_ERROR, __FILE__, __LINE__);
327				}
328				break;
329			case 'config_load':
330				$_args = $this->_parse_arguments($arguments);
331				if (empty($_args['file']))
332					$this->trigger_error("missing 'file' attribute in 'config_load' tag", E_USER_ERROR, __FILE__, __LINE__);
333				isset($_args['section']) ? null : $_args['section'] = 'null';
334				isset($_args['var']) ? null : $_args['var'] = 'null';
335				return '<?php $this->config_load(' . $_args['file'] . ', ' . $_args['section'] . ', ' . $_args['var'] . '); ?>';
336				break;
337			default:
338				$_result = "";
339				if ($this->_compile_custom_block($function, $modifiers, $arguments, $_result)) {
340					return $_result;
341				} elseif ($this->_compile_custom_function($function, $modifiers, $arguments, $_result)) {
342					return $_result;
343				} else {
344					$this->trigger_error($function." function does not exist", E_USER_ERROR, __FILE__, __LINE__);
345				}
346				break;
347		}
348	}
349
350	function _compile_custom_function($function, $modifiers, $arguments, &$_result) {
351		if ($function = $this->_plugin_exists($function, "function")) {
352			$_args = $this->_parse_arguments($arguments);
353			foreach($_args as $key => $value) {
354				if (is_bool($value))
355					$value = $value ? 'true' : 'false';
356				if (is_null($value))
357					$value = 'null';
358				$_args[$key] = "'$key' => $value";
359			}
360			$_result = '<?php echo ';
361			if (!empty($modifiers)) {
362				$_result .= $this->_parse_modifier($function . '(array(' . implode(',', (array)$_args) . '), $this)', $modifiers) . '; ';
363			} else {
364				$_result .= $function . '(array(' . implode(',', (array)$_args) . '), $this);';
365			}
366			$_result .= '?>';
367			return true;
368		} else {
369			return false;
370		}
371	}
372
373	function _compile_custom_block($function, $modifiers, $arguments, &$_result) {
374		if ($function{0} == '/') {
375			$start_tag = false;
376			$function = substr($function, 1);
377		} else {
378			$start_tag = true;
379		}
380
381		if ($function = $this->_plugin_exists($function, "block")) {
382			if ($start_tag) {
383				$_args = $this->_parse_arguments($arguments);
384				foreach($_args as $key => $value) {
385					if (is_bool($value))
386						$value = $value ? 'true' : 'false';
387					if (is_null($value))
388						$value = 'null';
389					$_args[$key] = "'$key' => $value";
390				}
391				$_result = "<?php \$this->_tag_stack[] = array('$function', array(".implode(',', (array)$_args).")); ";
392				$_result .= $function . '(array(' . implode(',', (array)$_args) .'), null, $this); ';
393				$_result .= 'ob_start(); ?>';
394			} else {
395				$_result .= '<?php $this->_block_content = ob_get_contents(); ob_end_clean(); ';
396				$_result .= '$this->_block_content = ' . $function . '($this->_tag_stack[count($this->_tag_stack) - 1][1], $this->_block_content, $this); ';
397				if (!empty($modifiers)) {
398					$_result .= '$this->_block_content = ' . $this->_parse_modifier('$this->_block_content', $modifiers) . '; ';
399				}
400				$_result .= 'echo $this->_block_content; array_pop($this->_tag_stack); ?>';
401			}
402			return true;
403		} else {
404			return false;
405		}
406	}
407
408	function _compile_if($arguments, $elseif = false) {
409		$_result	= "";
410		$_match		= array();
411		$_args		= array();
412		$_is_arg_stack	= array();
413
414		// extract arguments from the equation
415		preg_match_all('/(?>(' . $this->_var_regexp . '|\/?' . $this->_func_regexp . ')(?:' . $this->_mod_regexp . '*)?|\-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\%|\+|\-|\/|\*|\@|\b\w+\b|\S+)/x', $arguments, $_match);
416		$_args = $_match[0];
417
418		// make sure we have balanced parenthesis
419		$_args_count = array_count_values($_args);
420		if(isset($_args_count['(']) && $_args_count['('] != $_args_count[')']) {
421			$this->trigger_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
422		}
423
424		for ($i = 0, $for_max = count($_args); $i < $for_max; $i++) {
425			$_arg = &$_args[$i];
426			switch (strtolower($_arg)) {
427				case '!':
428				case '%':
429				case '!==':
430				case '==':
431				case '===':
432				case '>':
433				case '<':
434				case '!=':
435				case '<>':
436				case '<<':
437				case '>>':
438				case '<=':
439				case '>=':
440				case '&&':
441				case '||':
442				case '^':
443				case '&':
444				case '~':
445				case ')':
446				case ',':
447				case '+':
448				case '-':
449				case '*':
450				case '/':
451				case '@':
452					break;					
453				case 'eq':
454					$_arg = '==';
455					break;
456				case 'ne':
457				case 'neq':
458					$_arg = '!=';
459					break;
460				case 'lt':
461					$_arg = '<';
462					break;
463				case 'le':
464				case 'lte':
465					$_arg = '<=';
466					break;
467				case 'gt':
468					$_arg = '>';
469					break;
470				case 'ge':
471				case 'gte':
472					$_arg = '>=';
473					break;
474				case 'and':
475					$_arg = '&&';
476					break;
477				case 'or':
478					$_arg = '||';
479					break;
480				case 'not':
481					$_arg = '!';
482					break;
483				case 'mod':
484					$_arg = '%';
485					break;
486				case '(':
487					array_push($_is_arg_stack, $i);
488					break;
489				default:
490					preg_match('/(?:(' . $this->_var_regexp . '|' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)(?:\s*[,\.]\s*)?)(?:\s+(.*))?/xs', $_arg, $_match);
491					if ($_match[0]{0} == '$' || ($_match[0]{0} == '#' && $_match[0]{strlen($_match[0]) - 1} == '#') || $_match[0]{0} == "'" || $_match[0]{0} == '"') {
492						// process a variable
493						$_arg = $this->_parse_variables(array($_match[1]), array($_match[2]));
494					} elseif (is_numeric($_arg)) {
495						// pass the number through
496					} elseif (function_exists($_match[0]) || $_match[0] == "empty" || $_match[0] == "isset" || $_match[0] == "unset" || strtolower($_match[0]) == "true" || strtolower($_match[0]) == "false" || strtolower($_match[0]) == "null") {
497						// pass the function through
498					} else {
499						$this->trigger_error("unidentified token '$_arg'", E_USER_ERROR, __FILE__, __LINE__);
500					}
501					break;
502			}
503		}
504
505		if ($elseif) {
506			return '<?php elseif ('.implode(' ', $_args).'): ?>';
507		} else {
508			return '<?php if ('.implode(' ', $_args).'): ?>';
509		}
510		return $_result;
511	}
512
513	function _dequote($string) {
514		if (($string{0} == "'" || $string{0} == '"') && $string{strlen($string)-1} == $string{0})
515			return substr($string, 1, -1);
516		else
517			return $string;
518	}
519
520	function _parse_arguments($arguments) {
521		$_match		= array();
522		$_result	= array();
523		$_variables	= array();
524		preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+))+|[=]/x', $arguments, $_match);
525		/*
526		   Parse state:
527		     0 - expecting attribute name
528		     1 - expecting '='
529		     2 - expecting attribute value (not '=')
530		*/
531		$state = 0;
532		foreach($_match[0] as $value) {
533			switch($state) {
534				case 0:
535					// valid attribute name
536					if (is_string($value)) {
537						$a_name = $value;
538						$state = 1;
539					} else {
540						$this->trigger_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
541					}
542					break;
543				case 1:
544					if ($value == '=') {
545						$state = 2;
546					} else {
547						$this->trigger_error("expecting '=' after '$last_value'", E_USER_ERROR, __FILE__, __LINE__);
548					}
549					break;
550				case 2:
551					if ($value != '=') {
552						if ($value == 'yes' || $value == 'on' || $value == 'true') {
553							$value = true;
554						} elseif ($value == 'no' || $value == 'off' || $value == 'false') {
555							$value = false;
556						} elseif ($value == 'null') {
557							$value = null;
558						}
559
560						if(!preg_match_all('/(?:(' . $this->_var_regexp . ')(' . $this->_mod_regexp . '*))(?:\s+(.*))?/xs', $value, $_variables)) {
561							$_result[$a_name] = $value;
562						} else {
563							$_result[$a_name] = $this->_parse_variables($_variables[1], $_variables[2]);
564						}
565						$state = 0;
566					} else {
567						$this->trigger_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
568					}
569					break;
570			}
571			$last_value = $value;
572		}
573		if($state != 0) {
574			if($state == 1) {
575				$this->trigger_error("expecting '=' after attribute name '$last_value'", E_USER_ERROR, __FILE__, __LINE__);
576			} else {
577				$this->trigger_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
578			}
579		}
580		return $_result;
581	}
582
583	function _parse_variables($variables, $modifiers) {
584		$_result = "";
585		foreach($variables as $key => $value) {
586			$tag_variable = trim($variables[$key]);
587			if (empty($modifiers[$key])) {
588				$_result .= $this->_parse_variable($tag_variable).'.';
589			} else {
590				$_result .= $this->_parse_modifier($this->_parse_variable($tag_variable), $modifiers[$key]).'.';
591			}
592		}
593		return substr($_result, 0, -1);
594	}
595
596	function _parse_variable($variable) {
597		// replace variable with value
598		if ($variable{0} == "\$") {
599			// replace the variable
600			return $this->_compile_variable($variable);
601		} elseif ($variable{0} == '#') {
602			// replace the config variable
603			return $this->_compile_config($variable);
604		} elseif ($variable{0} == '"') {
605			// expand the quotes to pull any variables out of it
606			// fortunately variables inside of a quote aren't fancy, no modifiers, no quotes
607			//   just get everything from the $ to the ending space and parse it
608			// if the $ is escaped, then we won't expand it
609			$_result = "";
610			preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . ')/', substr($variable, 1, -1), $_expand);
611			$_expand = array_unique($_expand[0]);
612			foreach($_expand as $key => $value) {
613				$_expand[$key] = trim($value);
614				if (strpos($_expand[$key], '$') > 0) {
615					$_expand[$key] = substr($_expand[$key], strpos($_expand[$key], '$'));
616				}
617			}
618			$_result = $variable;
619			foreach($_expand as $value) {
620				$value = trim($value);
621				$_result = str_replace($value, '" . ' . $this->_parse_variable($value) . ' . "', $_result);
622			}
623			return $_result;
624		} elseif ($variable{0} == "'") {
625			// return the value just as it is
626			return $variable;
627		} else {
628			// return it as is; i believe that there was a reason before that i did not just return it as is,
629			// but i forgot what that reason is ...
630			// the reason i return the variable 'as is' right now is so that unquoted literals are allowed
631			return $variable;
632		}
633	}
634
635	function _compile_variable($variable) {
636		$_result	= "";
637
638		// remove the $
639		$variable = substr($variable, 1);
640
641		// get [foo] and .foo and (...) pieces			
642		preg_match_all('!(?:^\w+)|(?:' . $this->_var_bracket_regexp . ')|\.\$?\w+|\S+!', $variable, $_match);
643		$variable = $_match[0];
644		$var_name = array_shift($variable);
645
646		if ($var_name == '_TPL') {
647			if ($variable[0]{0} == '[') {
648				switch($variable[0]) {
649					case '[GET]':
650						$_result = "\$_GET";
651						break;
652					case '[POST]':
653						$_result = "\$_POST";
654						break;
655					case '[COOKIE]':
656						$_result = "\$_COOKIE";
657						break;
658					case '[ENV]':
659						$_result = "\$_ENV";
660						break;
661					case '[SERVER]':
662						$_result = "\$_SERVER";
663						break;
664					case '[SESSION]':
665						$_result = "\$_SESSION";
666						break;
667					case '[NOW]':
668						$_result = "time()";
669						break;
670					default:
671						$this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid $_TPL reference', E_USER_ERROR, __FILE__, __LINE__);
672						break;
673				}
674				array_shift($variable);
675			} else {
676				$this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid $_TPL reference', E_USER_ERROR, __FILE__, __LINE__);
677			}
678		} else {
679			$_result = "\$this->_vars['$var_name']";
680		}
681
682		foreach ($variable as $var) {
683			if ($var{0} == '[') {
684				$var = substr($var, 1, -1);
685				if (is_numeric($var)) {
686					$_result .= "[$var]";
687				} elseif ($var{0} == '$') {
688					$_result .= "[" . $this->_compile_variable($var) . "]";
689				} elseif ($var{0} == '#') {
690					$_result .= "[" . $this->_compile_config($var) . "]";
691				} else {
692					$_result .= "['$var']";
693				}
694			} else {
695				$this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
696			}
697		}
698		return $_result;
699	}
700
701	function _compile_config($variable) {
702		$_result	= "";
703
704		// remove the beginning and ending #
705		$variable = substr($variable, 1, -1);
706
707		// get [foo] and .foo and (...) pieces			
708		preg_match_all('!(?:^\w+)|(?:' . $this->_var_bracket_regexp . ')|\.\$?\w+|\S+!', $variable, $_match);
709		$variable = $_match[0];
710		$var_name = array_shift($variable);
711
712		$_result = "\$this->_confs['$var_name']";
713		foreach ($variable as $var) {
714			if ($var{0} == '[') {
715				$var = substr($var, 1, -1);
716				if (is_numeric($var)) {
717					$_result .= "[$var]";
718				} elseif ($var{0} == '$') {
719					$_result .= "[" . $this->_compile_variable($var) . "]";
720				} elseif ($var{0} == '#') {
721					$_result .= "[" . $this->_compile_config($var) . "]";
722				} else {
723					$_result .= "['$var']";
724				}
725			} else {
726				$this->trigger_error('#' . $var_name.implode('', $variable) . '# is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
727			}
728		}
729		return $_result;
730	}
731
732	function _parse_modifier($variable, $modifiers) {
733		$_match		= array();
734		$_mods		= array();		// stores all modifiers
735		$_args		= array();		// modifier arguments
736
737		preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifiers, $_match);
738		list(, $_mods, $_args) = $_match;
739
740		for ($i = 0, $for_max = count($_mods); $i < $for_max; $i++) {
741			preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $_args[$i], $_match);
742			$_arg = $_match[1];
743
744			if ($_mods[$i]{0} == '@') {
745				$_mods[$i] = substr($_mods[$i], 1);
746				$_map_array = 0;
747			} else {
748				$_map_array = 1;
749			}
750
751			foreach($_arg as $key => $value)
752				$_arg[$key] = $this->_parse_variable($value);
753
754			if ($this->_plugin_exists($_mods[$i], "modifier")) {
755				if (count($_arg) > 0)
756					$_arg = ', '.implode(', ', $_arg);
757				else
758					$_arg = '';
759				$variable = "\$this->_run_modifier($variable, '$_mods[$i]', $_map_array$_arg)";
760			} else {
761				$variable = "\$this->trigger_error(\"'" . $_mods[$i] . "' modifier does not exist\", E_USER_NOTICE, __FILE__, __LINE__);";
762			}
763		}
764		return $variable;
765	}
766
767	function _plugin_exists($function, $type) {
768		// check for object functions
769		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]))
770			return '$this->_plugins[\'' . $type . '\'][\'' . $function . '\'][0]->' . $this->_plugins[$type][$function][1];
771		// check for standard functions
772		if (isset($this->_plugins[$type][$function]) && function_exists($this->_plugins[$type][$function]))
773			return $this->_plugins[$type][$function];
774		// check for a plugin in the plugin directory
775		if (file_exists($this->_get_plugin_dir() . $type . '.' . $function . '.php')) {
776			require_once($this->_get_plugin_dir() . $type . '.' . $function . '.php');
777			if (function_exists('tpl_' . $type . '_' . $function)) {
778				$this->_require_stack[$type . '.' . $function . '.php'] = array($type, $function, 'tpl_' . $type . '_' . $function);
779				return ('tpl_' . $type . '_' . $function);
780			}
781		}
782		return false;
783	}
784
785	function _get_plugin_dir() {
786		static $plugin_dir;
787		if (isset($plugin_dir)) return $plugin_dir;
788
789		$this->plugin_dir = $this->_get_dir($this->plugin_dir);
790		if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $this->plugin_dir)) {
791			// path is relative
792			$plugin_dir = dirname(__FILE__).DIRECTORY_SEPARATOR.$this->plugin_dir;
793		} else {
794			// path is absolute
795			$plugin_dir = str_replace('\\', '\\\\', $this->plugin_dir);
796		}
797		return $plugin_dir;
798	}
799}
800?>