<?php


/**
 * Project: RainTPL, compile HTML template to PHP
 *  
 * File: RainTPL.class.php
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @link http://www.raintpl.com
 * @author Federico Ulfo <info@rainelemental.net>
 * @version 1.1
 * @copyright 2006 - 2007 Federico Ulfo | www.RainElemental.net
 * @package RainTPL
 */



/**
 * Class for compiling HTML template into PHP. This class compile the HTML template PHP file.
 * 
 * Tag Implemented:  {loop name="name_of_loop"}{/loop}, {if condition="here_condition"}{else}{/if}, {noparse}{/noparse}, {ignore}{/ignore}{include="file name.html"}, {$var}
 * 
 * 
 * @version 1.1
 * @access private
 *
 */

class RainTPLCompile{
	
	/**
	 * True se nel template sono stati usati plugins
	 * @access private
	 * @var bool
	 */
	
	var $plugins_used = false;
	
	
	/**
	 * versione del package RainTPL
	 * @access private
	 * @var string
	 */
	var $version = '1.0';
	

	/**
	 * Directory dove si trovano i templates
	 * @access private
	 * @var string
	 */
	var $tpl_dir = "themes";	
		
	
	/**
	 * variabile che contiene l'espression regolare per recuperare i tag nei template
	 * @access private
	 * @var string
	 */
	var $split_pattern = '/(\{(?:loop(?:\s+)name="(?:.*?)")\})|(\{(?:\/loop)\})|(\{(?:if(?:\s+)condition="(?:.*?)")\})|(\{(?:elseif(?:\s+)condition="(?:.*?)")\})|(\{(?:else)\})|(\{(?:\/if)\})|(\{noparse\})|(\{\/noparse\})|(\{ignore\})|(\{\/ignore\})|(\{include="(?:.*?)"\})/';
	
	
	/**
	 * Compila un template e scrive il file compilato
	 *
	 * @access private
	 * @param string $file File template
	 * @param string $template_directory Directory dove si trova il template 
	 */
	
	function compileFile( $template_name, $template_directory = 'THEMES' ){

		//setto la directory dei template
		$this->tpl_dir = $template_directory;
		
		//if directory $this->tpl_dir/compiled doesn't exist, create its and compile all file into $this->tpl_dir
		if( !file_exists( $this->tpl_dir . '/compiled/' ) )
			mkdir( $this->tpl_dir . '/compiled/', 0755 );
				
		if( $compiled_file = glob( $this->tpl_dir . '/compiled/' . $template_name . '*.php' ) )
			foreach( $compiled_file as $file_name )
				if (!preg_match("#_def#is",$file_name)) {
					unlink( $file_name );
				}

		//leggo il template
		$template_code = file_get_contents( $this->tpl_dir . '/' . $template_name . '.' . TPL_EXT );
		
		//se i tag php sono disabilitati nei template
		if( !RAINTPL_PHP_ENABLED ){
			$template_code = str_replace( "<?", "&lt;?", $template_code );
			$template_code = str_replace( "?>", "?&gt;", $template_code );
		}

		//compilo il template
		$template_compiled = $this->compileTemplate( $template_code, $template_name );
		
		//scrivo il file compilato
		fwrite( $fp = fopen( $this->tpl_dir . "/compiled/" . $template_name . "_" . filemtime( $this->tpl_dir . '/' . $template_name . '.' . TPL_EXT ) . ".php", 'w' ), $template_compiled );	
		
		//chiudo l'handler del file
		fclose( $fp );

	}
	

	/**
	 * Compilo il template e lo scrivo su file
	 *
	 * @param string $template_code template code
	 * @param string template_name nome del template
	 * @param string $file template file
	 */
	function compileTemplate( $template_code, $template_name ){
	
		$this->left_delimiter = addcslashes( LEFT_DELIMITER, "{} [] () ? ! <> # - + * % $ @");
		$this->right_delimiter = addcslashes( RIGHT_DELIMITER, "{} [] () ? ! <> # - + * % $ @");

		if( $this->left_delimiter != "\{" )
			$this->split_pattern = str_replace( "\{", $this->left_delimiter, $this->split_pattern );
		if( $this->right_delimiter != "\}" )
			$this->split_pattern = str_replace( "\}", $this->right_delimiter, $this->split_pattern );
	
		//divido il codice del template nei vari tag
		$parsed_code = preg_split ( $this->split_pattern, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
		
		//compilo il codice parsato in codice php
		$compiled_code = $this->compileCode( $parsed_code );

		//inizializzo la stringa del template compilato
		$template_compiled = "";
		
		//includo i plugins se sono stati usati
		if( $this->plugins_used )
			foreach( $this->plugins_used as $plugin => $params )
				$template_compiled .= "<?php include_once( \"includes/" . RAINTPL_PLUGINS_DIR . "/{$plugin}.php\" ); ?>\n";

		$template_compiled .= "\n<?php \$var['rain'] = \$GLOBALS; ?>\n";
		//creo il codice da inserire nel template
		$template_compiled .= "<!-- $template_name | generated by RainTPL v " . $this->version . " | www.RainTPL.com -->" . "\n" .
			        			$compiled_code . "\n" .
			        			"<!--/ $template_name -->" . "\n";
		
		return $template_compiled;
	  
		
	}
	
	
	/**
	 * Compilo il codice parsato in php
	 *
	 * @access private
	 * @param string $parsed_code Array che contiene codice html e tag
	 * @return string codice compilato
	 */
	function compileCode( $parsed_code ){

		//inizializzo tutte le variabili
		$parent_loop[ $level = 0 ] = $loop_name = $compiled_code = $compiled_return_code = null;
	 	$open_if = $comment_is_open = $ignore_is_open = 0;

	 	//leggo tutti gli elementi dell'array del codice parsato
	 	while( $html = array_shift( $parsed_code ) ){
	 		
	 		//calcolo lo spazio per l'indentazione del codice compilato
	 		for( $space_counter = 0, $space = ""; $space_counter < $level + $open_if; $space_counter++, $space .= "	" );
	 		
	 		//Chiudo il tag ignore
	 		if( !$comment_is_open and preg_match('#'. $this->left_delimiter . '\/ignore' . $this->right_delimiter.'#is', $html ) )
	 			$ignore_is_open = false;	
	 			
	 		//tutto il codice tra il tag ignore ed /ignore viene cancellato
	 		elseif( $ignore_is_open ){
	 			//non faccio niente
	 		}
	 		
	 		//Chiudo il tag noparse
	 		elseif( preg_match('#'. $this->left_delimiter . '\/noparse' . $this->right_delimiter.'#is', $html ) )
	 			$comment_is_open = false;	
	 			
	 		//tutto il codice tra il tag noparse e /noparse non viene compilato
	 		elseif( $comment_is_open ){
 				$compiled_code .= $html;
	 		}

	 		//Apro il tag ignore
	 		elseif( preg_match('#'. $this->left_delimiter . 'ignore' . $this->right_delimiter.'#is', $html ) )
	 			$ignore_is_open = true;

	 		//apro il tag noparse
	 		elseif( preg_match('#'. $this->left_delimiter . 'noparse' . $this->right_delimiter.'#is', $html ) )
	 			$comment_is_open = true;
	 		
			//inserisco il comando include
			elseif( preg_match( '#(?:' . $this->left_delimiter . 'include="(.*?)"' . $this->right_delimiter . ')#is', $html, $code ) ){
			
				//sostituisco le variabili che si trovano nel tag include
				$include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $this_loop_name = $parent_loop[ $level ] );

				//inserisco il codice php per includere il file dinamicamente con il RainTPL
				$compiled_code .= "<?php\n" .
								 "\$RainTPL_include_obj = new RainTPL();\n" .
								 "\$RainTPL_include_obj->assign( \$var );\n" .
								 "\$RainTPL_directory_template_temp = \$RainTPL_include_obj->tpl_dir;" . "\n" . 
								 ( ( $this_loop_name ) ? "\$RainTPL_include_obj->assign( \"key\", \$key_" . $this_loop_name . " );\n" . "\$RainTPL_include_obj->assign( \"value\", \$value_" . $this_loop_name . " );\n" : null ) .
								 "\$this->tpl_dir = \$GLOBALS[ 'RainTPL_tpl_dir' ] = \$RainTPL_include_obj->tpl_dir . \"/\" . dirname( \"$include_var\" );" . "\n" .
								 "\$RainTPL_include_obj->draw( basename( \"$include_var\" ) );" . "\n" .
								 "\$this->tpl_dir = \$GLOBALS[ 'RainTPL_tpl_dir' ] = \$RainTPL_directory_template_temp;" . "\n" .
								 "?>";
				 
			}
			
	 		//apro il tag loop
	 		elseif( preg_match( '#(?:' . $this->left_delimiter . 'loop(?:\s+)name="(.*?)"' . $this->right_delimiter . ')#is', $html, $code ) ){
	 			
	 			//incremento la variabile level, che tiene conto del numero di tag loop aperti
	 			$level++;
	 			
	 			//inserisco nell'array parent_loop il nome di questo loop
				$parent_loop[ $level ] = $level;

				$var = $this->var_replace( '$' . $code[ 1 ], "","", "","", $level-1 );
					
				//creo le variabili php per il loop
				$counter = "\$counter$level";
				$key = "\$key$level";
				$value = "\$value$level";
				
				//scrivo il codice per aprire il tag loop
				$compiled_code .=  "<?php" . "\n" .
										$space . "	if( isset( $var ) ){" . "\n" .
										$space . "		$counter = 0;" . "\n" .
										$space . "		foreach( $var as $key => $value ){ " . "\n" .
										"?>";

			}
			
			//chiudo il tag loop
			elseif( preg_match('#'. $this->left_delimiter . '\/loop' . $this->right_delimiter.'#is', $html ) ){
				//carico la variabile counter
				$counter = "\$counter$level";
				
				//diminuisco il livello del loop
				$level--;				
				
				//scrivo il codice per chiudere il tag loop
				$compiled_code .=  "<?php" . "\n" .
										$space . "		$counter++;" . "\n" .
										$space . "	}" . "\n" .
										$space . "}" . "\n" .
										"?>";
			}
			
			//apro il tag if
			elseif( preg_match( '#(?:' . $this->left_delimiter . 'if(?:\s+)condition="(.*?)"' . $this->right_delimiter . ')#is', $html, $code ) ){
				
				//incremento open_if (serve per l'indentazione del codice)
				$open_if++;
				
				//recupero l'attributo condition del tag if
				$condition = $code[ 1 ];
				
				//sostituisco le variabili all'interno della condizione del tag if, le variabili nell'if vengono inserite senza i delimitatori
				$parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = '', $tag_right_delimiter = '', $php_left_delimiter = null, $php_right_delimiter = null, $parent_loop[ $level ] );				
				
				//scrivo il codice php del tag if
				$compiled_code .=   "<?php" . "\n" .
										 $space . "	if( $parsed_condition ){" . "\n" .
										 "?>";
			}
			
			//apro il tag elseif
			elseif( preg_match( '#(?:' . $this->left_delimiter . 'elseif(?:\s+)condition="(.*?)"' . $this->right_delimiter . ')#is', $html, $code ) ){
				
				//incremento open_if
				$open_if++;
				
				//recupero l'attributo condition del tag elseif
				$condition = $code[ 1 ];
				
				//sostituisco le variabili all'interno della condizione del tag elseif, le variabili nell'elseif vengono inserite senza i delimitatori
				$parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = '', $tag_right_delimiter = '', $php_left_delimiter = null, $php_right_delimiter = null, $parent_loop[ $level ] );				
				
				//scrivo il codice php del tag elseif
				$compiled_code .=   "<?php" . "\n" .
										 $space . "}" . "\n" .
										 $space . "	elseif( $parsed_condition ){" . "\n" .
										 "?>";
			}
			
			//apro il tag else
			elseif( preg_match('#'. $this->left_delimiter . 'else' . $this->right_delimiter.'#is', $html ) ){

				//scrivo il codice php del tag else
				$compiled_code .=   "<?php" . "\n" .
										 $space . "}" . "\n" .
										 $space . "else{" . "\n" .
										 "?>";

			}
						
			//chido il tag if
			elseif( preg_match('#'. $this->left_delimiter . '\/if' . $this->right_delimiter.'#is', $html ) ){
				
				//decremento $open_if
				$open_if--;
				
				//scrivo il codice php del tag /if
				$compiled_code .=   "<?php" . "\n" .
										 $space . "}" . "\n" .
										 "?>";
				
			}
			
			//scrivo il codice html, cio tutto il codice che non  incluso tra tag
			else{
				
				//sostituisco i percorsi di immagini e collegamenti a fogli di stile
				$html = $this->path_replace( $html );
				
				//eseguo la sostituzione delle variabili nel codice
				$compiled_code .= $this->var_replace( $html, $this->left_delimiter, $this->right_delimiter, $php_left_delimiter = '<?php echo ', $php_right_delimiter = ';?>', $parent_loop[ $level ] );
				
				
			}
		}
		
		//restituisco il codice compilato
		return $compiled_code;
	}
	
	
	
	/**
	 * Sostituisce il percorso dei fogli di stile, dell'src delle immagini e del tag background
	 *
	 * @param string $html 
	 * @return string html sostituito
	 */
	function path_replace( $html ){

		//sostituisco il percorso di img src="http://..." con src="http://...#"
		$reg_exp = '/src=(?:")http\:\/\/([^"]+)(?:")/i';
		$reg_sub = 'src=@http://$1@';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
	
		$reg_exp = '/src=(?:")([^"]+)#(?:")/i';
		$reg_sub = "src=@$1@";
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		
		//sostituisco il percorso di img src="..." con src="template_directory/..."
		$reg_exp = '/src="(.*?)"/';
		$reg_sub = 'src="' . $this->tpl_dir . '/\\1"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );

		//sostituisco il percorso di img src="...#" con src="...#"
		$reg_exp = '/src=(?:\@)([^"]+)(?:\@)/i';
		$reg_sub = 'src="$1"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		
		
		//sostituisco il percorso di img src="http://..." con src="http://...#"
		$reg_exp = '/background=(?:")http\:\/\/([^"]+)(?:")/i';
		$reg_sub = 'background=@http://$1@';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
	
		$reg_exp = '/background=(?:")([^"]+)#(?:")/i';
		$reg_sub = "background=@$1@";
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		
		//sostituisco il percorso di img src="..." con src="template_directory/..."
		$reg_exp = '/background="(.*?)"/';
		$reg_sub = 'background="' . $this->tpl_dir . '/\\1"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );

		//sostituisco il percorso di img src="...#" con src="...#"
		$reg_exp = '/background=(?:\@)([^"]+)(?:\@)/i';
		$reg_sub = 'background="$1"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		


		//sostituisco il percorso di img src="http://..." con src="http://...#"
		$reg_exp = '/<link(.*?)href=(?:")http\:\/\/([^"]+)(?:")/i';
		$reg_sub = '<link$1href=@http://$2@';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
	
		$reg_exp = '/<link(.*?)href=(?:")([^"]+)#(?:")/i';
		$reg_sub = "<link$1href=@$2@";
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		
		//sostituisco il percorso di img src="..." con src="template_directory/..."
		$reg_exp = '/<link(.*?)href="(.*?)"/';
		$reg_sub = '<link$1href="' . $this->tpl_dir . '/$2"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );

		//sostituisco il percorso di img src="...#" con src="...#"
		$reg_exp = '/<link(.*?)href=(?:\@)([^"]+)(?:\@)/i';
		$reg_sub = '<link$1href="$2"';
		$html = preg_replace( $reg_exp, $reg_sub, $html );
		
		return $html;
	}



	/**
	 * Sostituisco le variabili con il codice php
	 *
	 * @param string $html Html da sostituire
	 * @param string $tag_left_delimiter Delimitatore sinistro del tag delle variabili. Di default  {
	 * @param string $tag_right_delimiter Delimitatore destro del tag delle variabili. Di default  }
	 * @param string $php_left_delimiter Delimitatore sinistro della sostituzione php. Es. <?php=
	 * @param string $php_right_delimiter  Delimitatore destro della sostituzione php. Es. ;?>
	 * @param string $loop_name Nome del loop
	 * @return string Codice sostituito
	 */
	function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_name = null ){

		
		//Non funziona sempre questa preg
		//preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:(?:\.{1}\${0,1}\w+)*(?:\[(?:\w+)\])*(?:\-\>\${0,1}\w+)*)*)(.*?)' . $tag_right_delimiter . '/', $html, $matches );
		preg_match_all( '#' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}(?:\w+))*(?:\[\${0,1}(?:\w+)\])*(?:\-\>\${0,1}(?:\w+))*)(.*?)' . $tag_right_delimiter . '#is', $html, $matches );
		//echo "<pre>"; print_r( $matches ); echo "</pre>"; exit;

		for( $i = 0; $i < count( $matches[ 0 ] ); $i++ ){
			
			//inserisco nella variabile $tag il tag completo, es: {$news.title|substr:0,100}
			$tag = $matches[ 0 ][ $i ];
			
			
			//inserisco in $var la variabile es: news.title
			$var = $matches[ 1 ][ $i ];
			
			//inserisco in $extra_var le eventuali funzioni associate alla variabile, es: substr:0,100
			$extra_var = $matches[ 2 ][ $i ];
			
			
			$function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
			
			//divido la variabile se  un array (es. $news.title o $news[title]) o un oggetto (es. $news->title)
			$temp = preg_split( "/\.|\[|\-\>/", $var );
			
			//prendo il nome della variabile
			$var_name = $temp[ 0 ];
			
			//prendo gli elementi dell'array successivi al nome
			$variable_path = substr( $var, strlen( $var_name ) );
			
			//trasformo le parentesi [ e ] in [" e in "]
			$variable_path = str_replace( '[', '["', $variable_path );
			$variable_path = str_replace( ']', '"]', $variable_path );
			
			//trasformo .$variabile in ["$variabile"]
			$variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
			//trasformo [variabile] in ["variabile]
			$variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
			
			//se alla funzione  associata una funzione
			if( $function_var ){
				
				//divido la funzione dai parametri (es: substr:0,100)
				$function_split = explode( ':', $function_var, 2 );
				
				//prendo la funzione
				$function = $function_split[ 0 ];
				
				//prendo i parametri
				$params = ( isset( $function_split[ 1 ] ) ) ? $function_split[ 1 ] : null;

				//se la funzione $function non esiste allora cerco se esiste nella directory plugins
				if( !function_exists( $function ) ){
					if( !file_exists( "includes/" . RAINTPL_PLUGINS_DIR . "/" . $function . ".php" ) )
						echo "includes/" . RAINTPL_PLUGINS_DIR . "/" . $function . ".php <br><br> errore nella riga";
					else
						$this->plugins_used[ $function ] = $params;
				}
				
			}
			else
				$function = $params = null;
			
			//if it is inside a loop
			if( $loop_name ){

				//verify the variable name
				if( $var_name == 'key' )
					$php_var = '$key' . $loop_name;
				elseif( $var_name == 'value' )
					$php_var = '$value' . $loop_name . $variable_path;
				elseif( $var_name == 'counter' )
					$php_var = '$counter' . $loop_name;
				else
					$php_var = '$var["' . $var_name . '"]' . $variable_path;
			}
			else
				$php_var = '$var["' . $var_name . '"]' . $variable_path;
				
			if( isset( $function ) )
				$php_var = $php_left_delimiter . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
			else
				$php_var = $php_left_delimiter . $php_var . $extra_var . $php_right_delimiter;



			$html = str_replace( $tag, $php_var, $html );

		}
		
		
		
		return $html;	
	}

}
	
?>