<?php
	/**
	* SmartTemplateParser Class
	* Used by SmartTemplate Class
	*
	* @desc Used by SmartTemplate Class
	* @author Philipp v. Criegern philipp@criegern.com
	* @author Manuel 'EndelWar' Dalla Lana endelwar@aregar.it
	* @version 1.2.1 03.07.2006
	*
	* CVS ID: $Id: class.smarttemplateparser.php,v 1.9 2006/07/02 22:17:15 endelwar Exp $
	*/
	class SmartTemplateParser
	{
		/**
		* The template itself
		*
		* @access private
		*/
		var $template;

		/**
		* The template filename used to extract the dirname for subtemplates
		*
		* @access private
		*/
		var $template_dir;

		/**
		* List of used SmartTemplate Extensions
		*
		* @access private
		*/
		var $extension_tagged  =  array();

		/**
		* Error messages
		*
		* @access public
		*/
		var $error;

		/**
		* SmartTemplateParser Constructor
		*
		* @param string $template_filename HTML Template Filename
		*/
		function SmartTemplateParser ( $template_filename )
		{
			// Load Template
			if ($hd = @fopen($template_filename, "r"))
			{
				if (filesize($template_filename))
				{
					$this->template = fread($hd, filesize($template_filename));
				}
				else
				{
					$this->template = "SmartTemplate Parser Error: File size is zero byte: '$template_filename'";
				}
				fclose($hd);
				// Extract the name of the template directory
				$this->template_dir = dirname($template_filename);
			}
			else
			{
				$this->template = "SmartTemplate Parser Error: File not found: '$template_filename'";
			}
		}

		/**
		* Main Template Parser
		*
		* @param string $compiled_template_filename Compiled Template Filename
		* @desc Creates Compiled PHP Template
		*/
		function compile( $compiled_template_filename = '' )
		{
			if (empty($this->template))
			{
				return;
			}
			/* Quick hack to allow subtemplates */
			if(eregi("<!-- INCLUDE", $this->template))
			{
				while ($this->count_subtemplates() > 0)
				{
					preg_match_all('/<!-- INCLUDE ([a-zA-Z0-9_.]+) -->/', $this->template, $tvar);
					foreach($tvar[1] as $subfile)
					{
						if(file_exists($this->template_dir . "/$subfile"))
						{
							$subst = implode('',file($this->template_dir . "/$subfile"));
						}
						else
						{
							$subst = 'SmartTemplate Parser Error: Subtemplate not found: \''.$subfile.'\'';
						}
						$this->template = str_replace("<!-- INCLUDE $subfile -->", $subst, $this->template);
					}
				}
			}
			//	END, ELSE Blocks
			$page  =  preg_replace("/<!-- ENDIF.+?-->/",  "<?php\n}\n?>",  $this->template);
			$page  =  preg_replace("/<!-- END[ a-zA-Z0-9_.]* -->/",  "<?php\n}\n\$_obj=\$_stack[--\$_stack_cnt];}\n?>",  $page);
			$page  =  str_replace("<!-- ELSE -->",  "<?php\n} else {\n?>",  $page);

			//	'BEGIN - END' Blocks
			if (preg_match_all('/<!-- BEGIN ([a-zA-Z0-9_.]+) -->/', $page, $var))
			{
				foreach ($var[1] as $tag)
				{
					list($parent, $block)  =  $this->var_name($tag);
					$code  =  "<?php\n"
							. "if (!empty(\$$parent"."['$block'])){\n"
							. "if (!is_array(\$$parent"."['$block']))\n"
							. "\$$parent"."['$block']=array(array('$block'=>\$$parent"."['$block']));\n"
							. "\$_tmp_arr_keys=array_keys(\$$parent"."['$block']);\n"
							. "if (\$_tmp_arr_keys[0]!='0')\n"
							. "\$$parent"."['$block']=array(0=>\$$parent"."['$block']);\n"
							. "\$_stack[\$_stack_cnt++]=\$_obj;\n"
							. "foreach (\$$parent"."['$block'] as \$rowcnt=>\$$block) {\n"
							. "\$$block"."['ROWCNT']=\$rowcnt;\n"
							. "\$$block"."['ALTROW']=\$rowcnt%2;\n"
							. "\$$block"."['ROWBIT']=\$rowcnt%2;\n"
							. "\$_obj=&\$$block;\n?>";
					$page  =  str_replace("<!-- BEGIN $tag -->",  $code,  $page);
				}
			}

			//	'IF nnn=mmm' Blocks
			if (preg_match_all('/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+)[ ]*([!=<>]+)[ ]*(["]?[^"]*["]?) -->/', $page, $var))
			{
				foreach ($var[2] as $cnt => $tag)
				{
					list($parent, $block)  =  $this->var_name($tag);
					$cmp   =  $var[3][$cnt];
					$val   =  $var[4][$cnt];
					$else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
					if ($cmp == '=')
					{
						$cmp  =  '==';
					}
					
					if (preg_match('/"([^"]*)"/',$val,$matches))
					{
						$code  =  "<?php\n$else"."if (\$$parent"."['$block'] $cmp \"".$matches[1]."\"){\n?>";
					}
					elseif (preg_match('/([^"]*)/',$val,$matches))
					{
						list($parent_right, $block_right)  =  $this->var_name($matches[1]);
						$code  =  "<?php\n$else"."if (\$$parent"."['$block'] $cmp \$$parent_right"."['$block_right']){\n?>";
					}
					
					$page  =  str_replace($var[0][$cnt],  $code,  $page);
				}
			}

			//	'IF nnn' Blocks
			if (preg_match_all('/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+) -->/', $page, $var))
			{
				foreach ($var[2] as $cnt => $tag)
				{
					$else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
					list($parent, $block)  =  $this->var_name($tag);
					$code  =  "<?php\n$else"."if (!empty(\$$parent"."['$block'])){\n?>";
					$page  =  str_replace($var[0][$cnt],  $code,  $page);
				}
			}

			//	Replace Scalars
			if (preg_match_all('/{([a-zA-Z0-9_. >]+)}/', $page, $var))
			{
				foreach ($var[1] as $fulltag)
				{
					//	Determin Command (echo / $obj[n]=)
					list($cmd, $tag)  =  $this->cmd_name($fulltag);

					list($block, $skalar)  =  $this->var_name($tag);
					$code  =  "<?php\n$cmd \$$block"."['$skalar'];\n?>\n";
					$page  =  str_replace('{'.$fulltag.'}',  $code,  $page);
				}
			}


			//	ROSI Special: Replace Translations
			if (preg_match_all('/<"([a-zA-Z0-9_.]+)">/', $page, $var))
			{
				foreach ($var[1] as $tag)
				{
					list($block, $skalar)  =  $this->var_name($tag);
					$code  =  "<?php\necho gettext('$skalar');\n?>\n";
					$page  =  str_replace('<"'.$tag.'">',  $code,  $page);
				}
			}


			//	Include Extensions
			$header = '';
			if (preg_match_all('/{([a-zA-Z0-9_]+):([^}]*)}/', $page, $var))
			{
				foreach ($var[2] as $cnt => $tag)
				{
					//	Determin Command (echo / $obj[n]=)
					list($cmd, $tag)  =  $this->cmd_name($tag);

					$extension  =  $var[1][$cnt];
					if (!isset($this->extension_tagged[$extension]))
					{
						$header  .=  "include_once \"smarttemplate_extensions/smarttemplate_extension_$extension.php\";\n";
						$this->extension_tagged[$extension]  =  true;
					}
					if (!strlen($tag))
					{
						$code  =  "<?php\n$cmd smarttemplate_extension_$extension();\n?>\n";
					}
					elseif (substr($tag, 0, 1) == '"')
					{
						$code  =  "<?php\n$cmd smarttemplate_extension_$extension($tag);\n?>\n";
					}
					elseif (strpos($tag, ','))
					{
						list($tag, $addparam)  =  explode(',', $tag, 2);
						list($block, $skalar)  =  $this->var_name($tag);
						if (preg_match('/^([a-zA-Z_]+)/', $addparam, $match))
						{
							$nexttag   =  $match[1];
							list($nextblock, $nextskalar)  =  $this->var_name($nexttag);
							$addparam  =  substr($addparam, strlen($nexttag));
							$code  =  "<?php\n$cmd smarttemplate_extension_$extension(\$$block"."['$skalar'],\$$nextblock"."['$nextskalar']"."$addparam);\n?>\n";
						}
						else
						{
							$code  =  "<?php\n$cmd smarttemplate_extension_$extension(\$$block"."['$skalar'],$addparam);\n?>\n";
						}
					}
					else
					{
						list($block, $skalar)  =  $this->var_name($tag);
						$code  =  "<?php\n$cmd smarttemplate_extension_$extension(\$$block"."['$skalar']);\n?>\n";
					}
					$page  =  str_replace($var[0][$cnt],  $code,  $page);
				}
			}

			//	Add Include Header
			if (isset($header) && !empty($header))
			{
				$page  =  "<?php\n$header\n?>$page";
			}

			//	Store Code to Temp Dir
			if (strlen($compiled_template_filename))
			{
		        if ($hd  =  fopen($compiled_template_filename,  "w"))
		        {
			        fwrite($hd,  $page);
			        fclose($hd);
			        return true;
			    }
			    else
			    {
			    	$this->error  =  "Could not write compiled file.";
			        return false;
			    }
			}
			else
			{
				return $page;
			}
		}


		/**
		* Splits Template-Style Variable Names into an Array-Name/Key-Name Components
		* {example}               :  array( "_obj",                   "example" )  ->  $_obj['example']
		* {example.value}         :  array( "_obj['example']",        "value" )    ->  $_obj['example']['value']
		* {example.0.value}       :  array( "_obj['example'][0]",     "value" )    ->  $_obj['example'][0]['value']
		* {top.example}           :  array( "_stack[0]",              "example" )  ->  $_stack[0]['example']
		* {parent.example}        :  array( "_stack[$_stack_cnt-1]",  "example" )  ->  $_stack[$_stack_cnt-1]['example']
		* {parent.parent.example} :  array( "_stack[$_stack_cnt-2]",  "example" )  ->  $_stack[$_stack_cnt-2]['example']
		*
		* @param string $tag Variale Name used in Template
		* @return array  Array Name, Key Name
		* @access private
		* @desc Splits Template-Style Variable Names into an Array-Name/Key-Name Components
		*/
		function var_name($tag)
		{
			$parent_level  =  0;
			while (substr($tag, 0, 7) == 'parent.')
			{
				$tag  =  substr($tag, 7);
				$parent_level++;
			}
			if (substr($tag, 0, 4) == 'top.')
			{
				$obj  =  '_stack[0]';
				$tag  =  substr($tag,4);
			}
			elseif ($parent_level)
			{
				$obj  =  '_stack[$_stack_cnt-'.$parent_level.']';
			}
			else
			{
				$obj  =  '_obj';
			}
			while (is_int(strpos($tag, '.')))
			{
				list($parent, $tag)  =  explode('.', $tag, 2);
				if (is_numeric($parent))
				{
					$obj  .=  "[" . $parent . "]";
				}
				else
				{
					$obj  .=  "['" . $parent . "']";
				}
			}
			$ret = array($obj, $tag);
			return $ret;
		}


		/**
		* Determine Template Command from Variable Name
		* {variable}             :  array( "echo",              "variable" )  ->  echo $_obj['variable']
		* {variable > new_name}  :  array( "_obj['new_name']=", "variable" )  ->  $_obj['new_name']= $_obj['variable']
		*
		* @param string $tag Variale Name used in Template
		* @return array  Array Command, Variable
		* @access private
		* @desc Determine Template Command from Variable Name
		*/
		function cmd_name($tag)
		{
			if (preg_match('/^(.+) > ([a-zA-Z0-9_.]+)$/', $tag, $tagvar))
			{
				$tag  =  $tagvar[1];
				list($newblock, $newskalar)  =  $this->var_name($tagvar[2]);
				$cmd  =  "\$$newblock"."['$newskalar']=";
			}
			else
			{
				$cmd  =  "echo";
			}
			$ret = array($cmd, $tag);
			return $ret;
		}

		/**
		* @return int Number of subtemplate included
		* @access private
		* @desc Count number of subtemplates included in current template
		*/
		function count_subtemplates()
		{
			preg_match_all('/<!-- INCLUDE ([a-zA-Z0-9_.]+) -->/', $this->template, $tvar);
			$count_subtemplates = count($tvar[1]);
			$ret = intval($count_subtemplates);
			return $ret;
		}
	}
?>