<?php
/**
 * Project:     Smarty: the PHP compiling template engine
 * File:        smarty_internal_utility.php
 * SVN:         $Id: $
 * 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
 * For questions, help, comments, discussion, etc., please join the
 * Smarty mailing list. Send a blank e-mail to
 * smarty-discussion-subscribe@googlegroups.com
 *
 * @link       http://www.smarty.net/
 * @copyright  2008 New Digital Group, Inc.
 * @author     Monte Ohrt <monte at ohrt dot com>
 * @author     Uwe Tews
 * @package    Smarty
 * @subpackage PluginsInternal
 * @version    3-SVN$Rev: 3286 $
 */

/**
 * Utility class
 *
 * @package    Smarty
 * @subpackage Security
 */
class Smarty_Internal_Utility {
	/**
	 * private constructor to prevent calls creation of new instances
	 */
	final private function __construct() {
		// intentionally left blank
	}
	
	/**
	 * Compile all template files
	 *
	 * @param  string $extension template file name extension
	 * @param  bool $force_compile force all to recompile
	 * @param  int $time_limit set maximum execution time
	 * @param  int $max_errors set maximum allowed errors
	 * @param  Smarty $smarty Smarty instance
	 *
	 * @return integer number of template files compiled
	 */
	public static function compileAllTemplates( $extension, $force_compile, $time_limit, $max_errors, Smarty $smarty ) {
		// switch off time limit
		if ( function_exists( 'set_time_limit' ) ) {
			@set_time_limit( $time_limit );
		}
		$smarty->force_compile = $force_compile;
		$_count                = 0;
		$_error_count          = 0;
		// loop over array of template directories
		foreach ( $smarty->getTemplateDir() as $_dir ) {
			$_compileDirs = new RecursiveDirectoryIterator( $_dir );
			$_compile     = new RecursiveIteratorIterator( $_compileDirs );
			foreach ( $_compile as $_fileinfo ) {
				$_file = $_fileinfo->getFilename();
				if ( substr( basename( $_fileinfo->getPathname() ), 0, 1 ) == '.' || strpos( $_file, '.svn' ) !== false ) {
					continue;
				}
				if ( ! substr_compare( $_file, $extension, - strlen( $extension ) ) == 0 ) {
					continue;
				}
				if ( $_fileinfo->getPath() == substr( $_dir, 0, - 1 ) ) {
					$_template_file = $_file;
				} else {
					$_template_file = substr( $_fileinfo->getPath(), strlen( $_dir ) ) . DS . $_file;
				}
				echo '<br>', $_dir, '---', $_template_file;
				flush();
				$_start_time = microtime( true );
				try {
					$_tpl = $smarty->createTemplate( $_template_file, null, null, null, false );
					if ( $_tpl->mustCompile() ) {
						$_tpl->compileTemplateSource();
						$_count ++;
						echo ' compiled in  ', microtime( true ) - $_start_time, ' seconds';
						flush();
					} else {
						echo ' is up to date';
						flush();
					}
				} catch ( Exception $e ) {
					echo 'Error: ', $e->getMessage(), "<br><br>";
					$_error_count ++;
				}
				// free memory
				$smarty->template_objects       = [];
				$_tpl->smarty->template_objects = [];
				$_tpl                           = null;
				if ( $max_errors !== null && $_error_count == $max_errors ) {
					echo '<br><br>too many errors';
					exit();
				}
			}
		}
		
		return $_count;
	}
	
	/**
	 * Compile all config files
	 *
	 * @param  string $extension config file name extension
	 * @param  bool $force_compile force all to recompile
	 * @param  int $time_limit set maximum execution time
	 * @param  int $max_errors set maximum allowed errors
	 * @param  Smarty $smarty Smarty instance
	 *
	 * @return integer number of config files compiled
	 */
	public static function compileAllConfig( $extension, $force_compile, $time_limit, $max_errors, Smarty $smarty ) {
		// switch off time limit
		if ( function_exists( 'set_time_limit' ) ) {
			@set_time_limit( $time_limit );
		}
		$smarty->force_compile = $force_compile;
		$_count                = 0;
		$_error_count          = 0;
		// loop over array of template directories
		foreach ( $smarty->getConfigDir() as $_dir ) {
			$_compileDirs = new RecursiveDirectoryIterator( $_dir );
			$_compile     = new RecursiveIteratorIterator( $_compileDirs );
			foreach ( $_compile as $_fileinfo ) {
				$_file = $_fileinfo->getFilename();
				if ( substr( basename( $_fileinfo->getPathname() ), 0, 1 ) == '.' || strpos( $_file, '.svn' ) !== false ) {
					continue;
				}
				if ( ! substr_compare( $_file, $extension, - strlen( $extension ) ) == 0 ) {
					continue;
				}
				if ( $_fileinfo->getPath() == substr( $_dir, 0, - 1 ) ) {
					$_config_file = $_file;
				} else {
					$_config_file = substr( $_fileinfo->getPath(), strlen( $_dir ) ) . DS . $_file;
				}
				echo '<br>', $_dir, '---', $_config_file;
				flush();
				$_start_time = microtime( true );
				try {
					$_config = new Smarty_Internal_Config( $_config_file, $smarty );
					if ( $_config->mustCompile() ) {
						$_config->compileConfigSource();
						$_count ++;
						echo ' compiled in  ', microtime( true ) - $_start_time, ' seconds';
						flush();
					} else {
						echo ' is up to date';
						flush();
					}
				} catch ( Exception $e ) {
					echo 'Error: ', $e->getMessage(), "<br><br>";
					$_error_count ++;
				}
				if ( $max_errors !== null && $_error_count == $max_errors ) {
					echo '<br><br>too many errors';
					exit();
				}
			}
		}
		
		return $_count;
	}
	
	/**
	 * Delete compiled template file
	 *
	 * @param  string $resource_name template name
	 * @param  string $compile_id compile id
	 * @param  integer $exp_time expiration time
	 * @param  Smarty $smarty Smarty instance
	 *
	 * @return integer number of template files deleted
	 */
	public static function clearCompiledTemplate( $resource_name, $compile_id, $exp_time, Smarty $smarty ) {
		$_compile_dir = realpath( $smarty->getCompileDir() ) . '/';
		if ( $_compile_dir == '/' ) { //We should never want to delete this!
			return 0;
		}
		$_compile_id = isset( $compile_id ) ? preg_replace( '![^\w\|]+!', '_', $compile_id ) : null;
		$_dir_sep    = $smarty->use_sub_dirs ? '/' : '^';
		if ( isset( $resource_name ) ) {
			$_save_stat      = $smarty->caching;
			$smarty->caching = false;
			$tpl             = new $smarty->template_class( $resource_name, $smarty );
			$smarty->caching = $_save_stat;
			
			// remove from template cache
			$tpl->source; // have the template registered before unset()
			if ( $smarty->allow_ambiguous_resources ) {
				$_templateId = $tpl->source->unique_resource . $tpl->cache_id . $tpl->compile_id;
			} else {
				$_templateId = $smarty->joined_template_dir . '#' . $resource_name . $tpl->cache_id . $tpl->compile_id;
			}
			if ( isset( $_templateId[150] ) ) {
				$_templateId = sha1( $_templateId );
			}
			unset( $smarty->template_objects[ $_templateId ] );
			
			if ( $tpl->source->exists ) {
				$_resource_part_1        = basename( str_replace( '^', '/', $tpl->compiled->filepath ) );
				$_resource_part_1_length = strlen( $_resource_part_1 );
			} else {
				return 0;
			}
			
			$_resource_part_2        = str_replace( '.php', '.cache.php', $_resource_part_1 );
			$_resource_part_2_length = strlen( $_resource_part_2 );
		}
		$_dir = $_compile_dir;
		if ( $smarty->use_sub_dirs && isset( $_compile_id ) ) {
			$_dir .= $_compile_id . $_dir_sep;
		}
		if ( isset( $_compile_id ) ) {
			$_compile_id_part        = str_replace( '\\', '/', $_compile_dir . $_compile_id . $_dir_sep );
			$_compile_id_part_length = strlen( $_compile_id_part );
		}
		$_count = 0;
		try {
			$_compileDirs = new RecursiveDirectoryIterator( $_dir );
			// NOTE: UnexpectedValueException thrown for PHP >= 5.3
		} catch ( Exception $e ) {
			return 0;
		}
		$_compile = new RecursiveIteratorIterator( $_compileDirs, RecursiveIteratorIterator::CHILD_FIRST );
		foreach ( $_compile as $_file ) {
			if ( substr( basename( $_file->getPathname() ), 0, 1 ) == '.' || strpos( $_file, '.svn' ) !== false ) {
				continue;
			}
			
			$_filepath = str_replace( '\\', '/', (string) $_file );
			
			if ( $_file->isDir() ) {
				if ( ! $_compile->isDot() ) {
					// delete folder if empty
					@rmdir( $_file->getPathname() );
				}
			} else {
				$unlink = false;
				if ( ( ! isset( $_compile_id ) || ( isset( $_filepath[ $_compile_id_part_length ] ) && $a = ! strncmp( $_filepath, $_compile_id_part, $_compile_id_part_length ) ) )
				     && ( ! isset( $resource_name )
				          || ( isset( $_filepath[ $_resource_part_1_length ] )
				               && substr_compare( $_filepath, $_resource_part_1, - $_resource_part_1_length, $_resource_part_1_length ) == 0 )
				          || ( isset( $_filepath[ $_resource_part_2_length ] )
				               && substr_compare( $_filepath, $_resource_part_2, - $_resource_part_2_length, $_resource_part_2_length ) == 0 ) )
				) {
					if ( isset( $exp_time ) ) {
						if ( time() - @filemtime( $_filepath ) >= $exp_time ) {
							$unlink = true;
						}
					} else {
						$unlink = true;
					}
				}
				
				if ( $unlink && @unlink( $_filepath ) ) {
					$_count ++;
				}
			}
		}
		// clear compiled cache
		Smarty_Resource::$sources   = [];
		Smarty_Resource::$compileds = [];
		
		return $_count;
	}
	
	/**
	 * Return array of tag/attributes of all tags used by an template
	 *
	 * @param Smarty_Internal_Template $template
	 *
	 * @throws Exception
	 * @throws SmartyException
	 * @return array                    of tag/attributes
	 */
	public static function getTags( Smarty_Internal_Template $template ) {
		$template->smarty->get_used_tags = true;
		$template->compileTemplateSource();
		
		return $template->used_tags;
	}
}
