2012-04-22 03:59:45 +08:00
< ? php
/**
2015-08-16 21:55:31 +08:00
* RainTPL
*
* Realized by Federico Ulfo & maintained by the Rain Team
* Distributed under GNU / LGPL 3 License
2012-04-22 03:59:45 +08:00
*
2015-08-16 21:55:31 +08:00
* @ version 2.7 . 2
2012-04-22 03:59:45 +08:00
*/
2015-08-16 21:55:31 +08:00
/**
* RainTPL
*/
2012-04-22 03:59:45 +08:00
class RainTPL {
// -------------------------
// CONFIGURATION
// -------------------------
/**
* Template directory
*
* @ var string
*/
2012-04-30 01:15:06 +08:00
static $tpl_dir = 'tpl/' ;
2012-04-22 03:59:45 +08:00
/**
2015-08-16 21:55:31 +08:00
* Cache directory
*
* Is the directory where RainTPL will compile the template and save the cache
2012-04-22 03:59:45 +08:00
*
* @ var string
*/
2012-04-30 01:15:06 +08:00
static $cache_dir = 'tmp/' ;
2012-04-22 03:59:45 +08:00
/**
2015-08-16 21:55:31 +08:00
* Template base URL
*
* RainTPL will add this URL to the relative paths of element selected in $path_replace_list .
2012-04-22 03:59:45 +08:00
*
* @ var string
*/
static $base_url = null ;
/**
2015-08-16 21:55:31 +08:00
* Template extension
2012-04-22 03:59:45 +08:00
*
* @ var string
*/
static $tpl_ext = " html " ;
/**
2015-08-16 21:55:31 +08:00
* Should the path be replaced
*
* Path replace is a cool features that replace all relative paths of images ( & lt ; img src = " ... " & gt ;), stylesheet ( & lt ; link href = " ... " & gt ;), script ( & lt ; script src = " ... " & gt ;) and link ( & lt ; a href = " ... " & gt ;)
2012-04-22 03:59:45 +08:00
* Set true to enable the path replace .
*
2015-08-16 21:55:31 +08:00
* @ var boolean
2012-04-22 03:59:45 +08:00
*/
static $path_replace = true ;
/**
* You can set what the path_replace method will replace .
* Avaible options : a , img , link , script , input
*
* @ var array
*/
static $path_replace_list = array ( 'a' , 'img' , 'link' , 'script' , 'input' );
/**
* You can define in the black list what string are disabled into the template tags
*
2015-08-16 21:55:31 +08:00
* @ var array
2012-04-22 03:59:45 +08:00
*/
static $black_list = array ( '\$this' , 'raintpl::' , 'self::' , '_SESSION' , '_SERVER' , '_ENV' , 'eval' , 'exec' , 'unlink' , 'rmdir' );
/**
2015-08-16 21:55:31 +08:00
* Check template
2015-09-05 07:55:19 +08:00
*
2012-04-22 03:59:45 +08:00
* true : checks template update time , if changed it compile them
* false : loads the compiled template . Set false if server doesn ' t have write permission for cache_directory .
*
2015-08-16 21:55:31 +08:00
* @ var bool
2012-04-22 03:59:45 +08:00
*/
static $check_template_update = true ;
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
/**
2012-04-30 01:15:06 +08:00
* PHP tags < ? ?>
2015-08-16 21:55:31 +08:00
*
2012-04-22 03:59:45 +08:00
* True : php tags are enabled into the template
* False : php tags are disabled into the template and rendered as html
*
* @ var bool
*/
static $php_enabled = false ;
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
/**
2015-08-16 21:55:31 +08:00
* Debug mode flag
*
2012-04-22 03:59:45 +08:00
* True : debug mode is used , syntax errors are displayed directly in template . Execution of script is not terminated .
* False : exception is thrown on found error .
*
* @ var bool
*/
static $debug = false ;
// -------------------------
// -------------------------
// RAINTPL VARIABLES
// -------------------------
/**
* Is the array where RainTPL keep the variables assigned
*
* @ var array
*/
public $var = array ();
2015-08-16 21:55:31 +08:00
/**
* variables to keep the template directories and info
*
* @ var array
*/
2015-09-05 07:55:19 +08:00
protected $tpl = array (); //
2012-04-22 03:59:45 +08:00
2015-08-16 21:55:31 +08:00
/**
* static cache enabled / disabled
*
* @ var bool
*/
protected $cache = false ;
/**
* identify only one cache
*
* @ var string
*/
protected $cache_id = '' ;
/**
* takes all the config to create the md5 of the file
*
* @ var array the file
*/
2015-09-05 07:55:19 +08:00
protected static $config_name_sum = array ();
2012-04-22 03:59:45 +08:00
// -------------------------
2015-08-16 21:55:31 +08:00
/**
* default cache expire time = hour
*
* @ const int
*/
const CACHE_EXPIRE_TIME = 3600 ;
2012-04-22 03:59:45 +08:00
/**
* Assign variable
* eg . $t -> assign ( 'name' , 'mickey' );
*
2015-08-16 21:55:31 +08:00
* @ access public
* @ param mixed $variable_name Name of template variable or associative array name / value
* @ param mixed $value value assigned to this variable . Not set if variable_name is an associative array
2012-04-22 03:59:45 +08:00
*/
2015-08-16 21:55:31 +08:00
public function assign ( $variable , $value = null ){
2012-04-22 03:59:45 +08:00
if ( is_array ( $variable ) )
$this -> var += $variable ;
else
$this -> var [ $variable ] = $value ;
}
/**
* Draw the template
* eg . $html = $tpl -> draw ( 'demo' , TRUE ); // return template in string
* or $tpl -> draw ( $tpl_name ); // echo the template
*
2015-08-16 21:55:31 +08:00
* @ access public
* @ param string $tpl_name template to load
* @ param boolean $return_string true = return a string , false = echo the template
2012-04-22 03:59:45 +08:00
* @ return string
*/
2015-08-16 21:55:31 +08:00
public function draw ( $tpl_name , $return_string = false ){
2012-04-22 03:59:45 +08:00
try {
// compile the template if necessary and set the template filepath
$this -> check_template ( $tpl_name );
} catch ( RainTpl_Exception $e ) {
$output = $this -> printDebug ( $e );
die ( $output );
}
// Cache is off and, return_string is false
2015-09-05 07:55:19 +08:00
// Rain just echo the template
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
if ( ! $this -> cache && ! $return_string ){
extract ( $this -> var );
include $this -> tpl [ 'compiled_filename' ];
unset ( $this -> tpl );
}
2012-04-22 03:59:45 +08:00
// cache or return_string are enabled
2015-09-05 07:55:19 +08:00
// rain get the output buffer to save the output in the cache or to return it as string
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
else {
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//----------------------
// get the output buffer
//----------------------
ob_start ();
extract ( $this -> var );
include $this -> tpl [ 'compiled_filename' ];
$raintpl_contents = ob_get_clean ();
//----------------------
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// save the output in the cache
if ( $this -> cache )
file_put_contents ( $this -> tpl [ 'cache_filename' ], " <?php if(!class_exists('raintpl')) { exit;}?> " . $raintpl_contents );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// free memory
unset ( $this -> tpl );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// return or print the template
if ( $return_string ) return $raintpl_contents ; else echo $raintpl_contents ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
2012-04-22 03:59:45 +08:00
}
/**
* If exists a valid cache for this template it returns the cache
*
2015-08-16 21:55:31 +08:00
* @ access public
* @ param string $tpl_name Name of template ( set the same of draw )
* @ param int $expiration_time Set after how many seconds the cache expire and must be regenerated
* @ param string $cache_id Suffix to be used when writing file to cache ( optional )
2012-04-22 03:59:45 +08:00
* @ return string it return the HTML or null if the cache must be recreated
*/
2015-08-16 21:55:31 +08:00
public function cache ( $tpl_name , $expire_time = self :: CACHE_EXPIRE_TIME , $cache_id = '' ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// set the cache_id
$this -> cache_id = $cache_id ;
2012-04-22 03:59:45 +08:00
if ( ! $this -> check_template ( $tpl_name ) && file_exists ( $this -> tpl [ 'cache_filename' ] ) && ( time () - filemtime ( $this -> tpl [ 'cache_filename' ] ) < $expire_time ) )
return substr ( file_get_contents ( $this -> tpl [ 'cache_filename' ] ), 43 );
else {
//delete the cache of the selected template
2015-09-05 07:55:19 +08:00
if ( file_exists ( $this -> tpl [ 'cache_filename' ]))
unlink ( $this -> tpl [ 'cache_filename' ] );
2012-04-22 03:59:45 +08:00
$this -> cache = true ;
}
}
/**
* Configure the settings of RainTPL
*
2015-08-16 21:55:31 +08:00
* @ access public
* @ static
* @ param array | string $setting array of settings or setting name
* @ param mixed $value content to set in the setting ( optional )
2012-04-22 03:59:45 +08:00
*/
2015-08-16 21:55:31 +08:00
public static function configure ( $setting , $value = null ){
2012-04-22 03:59:45 +08:00
if ( is_array ( $setting ) )
foreach ( $setting as $key => $value )
self :: configure ( $key , $value );
else if ( property_exists ( __CLASS__ , $setting ) ){
self :: $$setting = $value ;
2015-09-05 07:55:19 +08:00
self :: $config_name_sum [ $setting ] = $value ; // take trace of all config
}
2012-04-22 03:59:45 +08:00
}
2015-08-16 21:55:31 +08:00
/**
* Check if has to compile the template
*
* @ access protected
* @ param string $tpl_name template name to check
* @ throws RainTpl_NotFoundException
* @ return bool return true if the template has changed
*/
2012-04-22 03:59:45 +08:00
protected function check_template ( $tpl_name ){
if ( ! isset ( $this -> tpl [ 'checked' ]) ){
2015-09-05 07:55:19 +08:00
$tpl_basename = basename ( $tpl_name ); // template basename
$tpl_basedir = strpos ( $tpl_name , " / " ) ? dirname ( $tpl_name ) . '/' : null ; // template basedirectory
$tpl_dir = PATH . self :: $tpl_dir . $tpl_basedir ; // template directory
$this -> tpl [ 'tpl_filename' ] = $tpl_dir . $tpl_basename . '.' . self :: $tpl_ext ; // template filename
$temp_compiled_filename = PATH . self :: $cache_dir . $tpl_basename . " . " . md5 ( $tpl_dir . serialize ( self :: $config_name_sum ));
$this -> tpl [ 'compiled_filename' ] = $temp_compiled_filename . '.rtpl.php' ; // cache filename
$this -> tpl [ 'cache_filename' ] = $temp_compiled_filename . '.s_' . $this -> cache_id . '.rtpl.php' ; // static cache filename
2012-04-22 03:59:45 +08:00
// if the template doesn't exsist throw an error
if ( self :: $check_template_update && ! file_exists ( $this -> tpl [ 'tpl_filename' ] ) ){
$e = new RainTpl_NotFoundException ( 'Template ' . $tpl_basename . ' not found!' );
throw $e -> setTemplateFile ( $this -> tpl [ 'tpl_filename' ]);
}
// file doesn't exsist, or the template was updated, Rain will compile the template
if ( ! file_exists ( $this -> tpl [ 'compiled_filename' ] ) || ( self :: $check_template_update && filemtime ( $this -> tpl [ 'compiled_filename' ]) < filemtime ( $this -> tpl [ 'tpl_filename' ] ) ) ){
2016-07-11 20:15:20 +08:00
$this -> compileFile ( $tpl_basedir , $this -> tpl [ 'tpl_filename' ], PATH . self :: $cache_dir , $this -> tpl [ 'compiled_filename' ] );
2012-04-22 03:59:45 +08:00
return true ;
}
$this -> tpl [ 'checked' ] = true ;
}
}
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
2015-08-16 21:55:31 +08:00
* execute stripslaches () on the xml block . Invoqued by preg_replace_callback function below
*
* @ access protected
* @ param string $capture
* @ return string
*/
2012-04-22 03:59:45 +08:00
protected function xml_reSubstitution ( $capture ) {
2015-09-05 07:55:19 +08:00
return " <?php echo '<?xml " . stripslashes ( $capture [ 1 ]) . " ?>'; ?> " ;
2012-04-30 01:15:06 +08:00
}
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
* Compile and write the compiled template file
2015-08-16 21:55:31 +08:00
*
2012-04-22 03:59:45 +08:00
* @ access protected
2015-08-16 21:55:31 +08:00
* @ param string $tpl_basedir
* @ param string $tpl_filename
* @ param string $cache_dir
* @ param string $compiled_filename
* @ throws RainTpl_Exception
* @ return void
2012-04-22 03:59:45 +08:00
*/
2016-07-11 20:15:20 +08:00
protected function compileFile ( $tpl_basedir , $tpl_filename , $cache_dir , $compiled_filename ){
2012-04-22 03:59:45 +08:00
//read template file
$this -> tpl [ 'source' ] = $template_code = file_get_contents ( $tpl_filename );
//xml substitution
$template_code = preg_replace ( " /< \ ?xml(.*?) \ ?>/s " , " ##XML \\ 1XML## " , $template_code );
//disable php tag
if ( ! self :: $php_enabled )
$template_code = str_replace ( array ( " <? " , " ?> " ), array ( " <? " , " ?> " ), $template_code );
//xml re-substitution
2012-04-30 01:15:06 +08:00
$template_code = preg_replace_callback ( " /##XML(.*?)XML##/s " , array ( $this , 'xml_reSubstitution' ), $template_code );
2012-04-22 03:59:45 +08:00
//compile template
$template_compiled = " <?php if(!class_exists('raintpl')) { exit;}?> " . $this -> compileTemplate ( $template_code , $tpl_basedir );
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
// fix the php-eating-newline-after-closing-tag-problem
$template_compiled = str_replace ( " ?> \n " , " ?> \n \n " , $template_compiled );
// create directories
if ( ! is_dir ( $cache_dir ) )
mkdir ( $cache_dir , 0755 , true );
if ( ! is_writable ( $cache_dir ) )
2016-05-13 02:07:58 +08:00
throw new RainTpl_Exception ( 'Cache directory ' . $cache_dir . ' doesn\'t have write permission. Set write permission or set RAINTPL_CHECK_TEMPLATE_UPDATE to false. More details on http://www.raintpl.com/Documentation/Documentation-for-PHP-developers/Configuration/' );
2012-04-22 03:59:45 +08:00
//write compiled file
file_put_contents ( $compiled_filename , $template_compiled );
}
/**
* Compile template
2015-08-16 21:55:31 +08:00
*
2012-04-22 03:59:45 +08:00
* @ access protected
2015-08-16 21:55:31 +08:00
* @ param string $template_code
* @ param string $tpl_basedir
* @ return string
2012-04-22 03:59:45 +08:00
*/
protected function compileTemplate ( $template_code , $tpl_basedir ){
//tag list
2015-09-05 07:55:19 +08:00
$tag_regexp = array ( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})' ,
'loop_close' => '(\{\/loop\})' ,
'if' => '(\{if(?: condition){0,1}="[^"]*"\})' ,
'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})' ,
'else' => '(\{else\})' ,
'if_close' => '(\{\/if\})' ,
'function' => '(\{function="[^"]*"\})' ,
'noparse' => '(\{noparse\})' ,
'noparse_close' => '(\{\/noparse\})' ,
'ignore' => '(\{ignore\}|\{\*)' ,
'ignore_close' => '(\{\/ignore\}|\*\})' ,
'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})' ,
'template_info' => '(\{\$template_info\})' ,
'function' => '(\{function="(\w*?)(?:.*?)"\})'
2012-04-22 03:59:45 +08:00
);
$tag_regexp = " / " . join ( " | " , $tag_regexp ) . " / " ;
//split the code with the tags regexp
$template_code = preg_split ( $tag_regexp , $template_code , - 1 , PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
//path replace (src of img, background and href of link)
$template_code = $this -> path_replace ( $template_code , $tpl_basedir );
//compile the code
$compiled_code = $this -> compileCode ( $template_code );
//return the compiled code
return $compiled_code ;
}
/**
* Compile the code
2015-08-16 21:55:31 +08:00
*
2012-04-22 03:59:45 +08:00
* @ access protected
2015-08-16 21:55:31 +08:00
* @ param string $parsed_code
* @ throws RainTpl_SyntaxException
* @ return string
2012-04-22 03:59:45 +08:00
*/
protected function compileCode ( $parsed_code ){
//variables initialization
$compiled_code = $open_if = $comment_is_open = $ignore_is_open = null ;
2015-09-05 07:55:19 +08:00
$loop_level = 0 ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//read all parsed code
while ( $html = array_shift ( $parsed_code ) ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//close ignore tag
2012-04-22 03:59:45 +08:00
if ( ! $comment_is_open && ( strpos ( $html , '{/ignore}' ) !== FALSE || strpos ( $html , '*}' ) !== FALSE ) )
2015-09-05 07:55:19 +08:00
$ignore_is_open = false ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//code between tag ignore id deleted
elseif ( $ignore_is_open ){
//ignore the code
}
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//close no parse tag
2012-04-22 03:59:45 +08:00
elseif ( strpos ( $html , '{/noparse}' ) !== FALSE )
2015-09-05 07:55:19 +08:00
$comment_is_open = false ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//code between tag noparse is not compiled
elseif ( $comment_is_open )
$compiled_code .= $html ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//ignore
2012-04-22 03:59:45 +08:00
elseif ( strpos ( $html , '{ignore}' ) !== FALSE || strpos ( $html , '{*' ) !== FALSE )
2015-09-05 07:55:19 +08:00
$ignore_is_open = true ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//noparse
elseif ( strpos ( $html , '{noparse}' ) !== FALSE )
$comment_is_open = true ;
2012-04-22 03:59:45 +08:00
//include tag
elseif ( preg_match ( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/' , $html , $code ) ){
//variables substitution
$include_var = $this -> var_replace ( $code [ 1 ], $left_delimiter = null , $right_delimiter = null , $php_left_delimiter = '".' , $php_right_delimiter = '."' , $loop_level );
// if the cache is active
if ( isset ( $code [ 2 ]) ){
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
//dynamic include
$compiled_code .= '<?php $tpl = new ' . get_class ( $this ) . ';' .
'if( $cache = $tpl->cache( $template = basename("' . $include_var . '") ) )' .
' echo $cache;' .
'else{' .
' $tpl_dir_temp = self::$tpl_dir;' .
' $tpl->assign( $this->var );' .
( ! $loop_level ? null : '$tpl->assign( "key", $key' . $loop_level . ' ); $tpl->assign( "value", $value' . $loop_level . ' );' ) .
' $tpl->draw( dirname("' . $include_var . '") . ( substr("' . $include_var . '",-1,1) != "/" ? "/" : "" ) . basename("' . $include_var . '") );' .
'} ?>' ;
}
else {
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
//dynamic include
$compiled_code .= '<?php $tpl = new ' . get_class ( $this ) . ';' .
'$tpl_dir_temp = self::$tpl_dir;' .
'$tpl->assign( $this->var );' .
( ! $loop_level ? null : '$tpl->assign( "key", $key' . $loop_level . ' ); $tpl->assign( "value", $value' . $loop_level . ' );' ) .
'$tpl->draw( dirname("' . $include_var . '") . ( substr("' . $include_var . '",-1,1) != "/" ? "/" : "" ) . basename("' . $include_var . '") );' .
'?>' ;
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
}
}
2015-09-05 07:55:19 +08:00
//loop
2012-04-22 03:59:45 +08:00
elseif ( preg_match ( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/' , $html , $code ) ){
2015-09-05 07:55:19 +08:00
//increase the loop counter
$loop_level ++ ;
2012-04-22 03:59:45 +08:00
//replace the variable in the loop
$var = $this -> var_replace ( '$' . $code [ 1 ], $tag_left_delimiter = null , $tag_right_delimiter = null , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level - 1 );
//loop variables
2015-09-05 07:55:19 +08:00
$counter = " \$ counter $loop_level " ; // count iteration
$key = " \$ key $loop_level " ; // key
$value = " \$ value $loop_level " ; // value
2012-04-22 03:59:45 +08:00
//loop code
$compiled_code .= " <?php $counter =-1; if( isset( $var ) && is_array( $var ) && sizeof( $var ) ) foreach( $var as $key => $value ) { $counter ++; ?> " ;
}
//close loop tag
elseif ( strpos ( $html , '{/loop}' ) !== FALSE ) {
//iterator
$counter = " \$ counter $loop_level " ;
//decrease the loop counter
$loop_level -- ;
//close loop code
$compiled_code .= " <?php } ?> " ;
}
//if
elseif ( preg_match ( '/\{if(?: condition){0,1}="([^"]*)"\}/' , $html , $code ) ){
//increase open if counter (for intendation)
$open_if ++ ;
//tag
$tag = $code [ 0 ];
//condition attribute
$condition = $code [ 1 ];
// check if there's any function disabled by black_list
$this -> function_check ( $tag );
//variable substitution into condition (no delimiter into the condition)
$parsed_condition = $this -> var_replace ( $condition , $tag_left_delimiter = null , $tag_right_delimiter = null , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level );
//if code
$compiled_code .= " <?php if( $parsed_condition ) { ?> " ;
}
//elseif
elseif ( preg_match ( '/\{elseif(?: condition){0,1}="([^"]*)"\}/' , $html , $code ) ){
//tag
$tag = $code [ 0 ];
//condition attribute
$condition = $code [ 1 ];
//variable substitution into condition (no delimiter into the condition)
$parsed_condition = $this -> var_replace ( $condition , $tag_left_delimiter = null , $tag_right_delimiter = null , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level );
//elseif code
$compiled_code .= " <?php }elseif( $parsed_condition ) { ?> " ;
}
//else
elseif ( strpos ( $html , '{else}' ) !== FALSE ) {
//else code
$compiled_code .= '<?php }else{ ?>' ;
}
//close if tag
elseif ( strpos ( $html , '{/if}' ) !== FALSE ) {
//decrease if counter
$open_if -- ;
// close if code
$compiled_code .= '<?php } ?>' ;
}
//function
elseif ( preg_match ( '/\{function="(\w*)(.*?)"\}/' , $html , $code ) ){
//tag
$tag = $code [ 0 ];
//function
$function = $code [ 1 ];
// check if there's any function disabled by black_list
$this -> function_check ( $tag );
if ( empty ( $code [ 2 ] ) )
$parsed_function = $function . " () " ;
else
// parse the function
$parsed_function = $function . $this -> var_replace ( $code [ 2 ], $tag_left_delimiter = null , $tag_right_delimiter = null , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level );
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
//if code
$compiled_code .= " <?php echo $parsed_function ; ?> " ;
}
// show all vars
elseif ( strpos ( $html , '{$template_info}' ) !== FALSE ) {
//tag
$tag = '{$template_info}' ;
//if code
$compiled_code .= '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>' ;
}
//all html code
else {
//variables substitution (es. {$title})
$html = $this -> var_replace ( $html , $left_delimiter = '\{' , $right_delimiter = '\}' , $php_left_delimiter = '<?php ' , $php_right_delimiter = ';?>' , $loop_level , $echo = true );
//const substitution (es. {#CONST#})
$html = $this -> const_replace ( $html , $left_delimiter = '\{' , $right_delimiter = '\}' , $php_left_delimiter = '<?php ' , $php_right_delimiter = ';?>' , $loop_level , $echo = true );
//functions substitution (es. {"string"|functions})
$compiled_code .= $this -> func_replace ( $html , $left_delimiter = '\{' , $right_delimiter = '\}' , $php_left_delimiter = '<?php ' , $php_right_delimiter = ';?>' , $loop_level , $echo = true );
}
}
if ( $open_if > 0 ) {
$e = new RainTpl_SyntaxException ( 'Error! You need to close an {if} tag in ' . $this -> tpl [ 'tpl_filename' ] . ' template' );
throw $e -> setTemplateFile ( $this -> tpl [ 'tpl_filename' ]);
}
return $compiled_code ;
}
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
2015-08-16 21:55:31 +08:00
* Reduce a path
*
* eg . www / library /../ filepath //file => www/filepath/file
*
* @ param string $path
* @ return string
2012-04-22 03:59:45 +08:00
*/
protected function reduce_path ( $path ){
$path = str_replace ( " :// " , " @not_replace@ " , $path );
$path = str_replace ( " // " , " / " , $path );
$path = str_replace ( " @not_replace@ " , " :// " , $path );
return preg_replace ( '/\w+\/\.\.\//' , '' , $path );
}
/**
2015-08-16 21:55:31 +08:00
* replace the path of image src , link href and a href
2015-09-05 07:55:19 +08:00
*
2012-04-22 03:59:45 +08:00
* url => template_dir / url
* url # => url
* http :// url => http :// url
*
2015-08-16 21:55:31 +08:00
* @ access protected
* @ param string $html
* @ param string $tpl_basedir
* @ return string html substitution
2012-04-22 03:59:45 +08:00
*/
protected function path_replace ( $html , $tpl_basedir ){
if ( self :: $path_replace ){
2012-04-30 01:15:06 +08:00
$tpl_dir = self :: $base_url . PATH . self :: $tpl_dir . $tpl_basedir ;
2012-04-22 03:59:45 +08:00
// reduce the path
$path = $this -> reduce_path ( $tpl_dir );
$exp = $sub = array ();
if ( in_array ( " img " , self :: $path_replace_list ) ){
$exp = array ( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i' , '/<img(.*?)src=(?:")([^"]+?)#(?:")/i' , '/<img(.*?)src="(.*?)"/' , '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
$sub = array ( '<img$1src=@$2://$3@' , '<img$1src=@$2@' , '<img$1src="' . $path . '$2"' , '<img$1src="$2"' );
}
if ( in_array ( " script " , self :: $path_replace_list ) ){
$exp = array_merge ( $exp , array ( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i' , '/<script(.*?)src=(?:")([^"]+?)#(?:")/i' , '/<script(.*?)src="(.*?)"/' , '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
$sub = array_merge ( $sub , array ( '<script$1src=@$2://$3@' , '<script$1src=@$2@' , '<script$1src="' . $path . '$2"' , '<script$1src="$2"' ) );
}
if ( in_array ( " link " , self :: $path_replace_list ) ){
$exp = array_merge ( $exp , array ( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i' , '/<link(.*?)href=(?:")([^"]+?)#(?:")/i' , '/<link(.*?)href="(.*?)"/' , '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
$sub = array_merge ( $sub , array ( '<link$1href=@$2://$3@' , '<link$1href=@$2@' , '<link$1href="' . $path . '$2"' , '<link$1href="$2"' ) );
}
if ( in_array ( " a " , self :: $path_replace_list ) ){
$exp = array_merge ( $exp , array ( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i' , '/<a(.*?)href="(.*?)"/' , '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
$sub = array_merge ( $sub , array ( '<a$1href=@$2$3@' , '<a$1href="' . self :: $base_url . '$2"' , '<a$1href="$2"' ) );
}
if ( in_array ( " input " , self :: $path_replace_list ) ){
$exp = array_merge ( $exp , array ( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i' , '/<input(.*?)src=(?:")([^"]+?)#(?:")/i' , '/<input(.*?)src="(.*?)"/' , '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
$sub = array_merge ( $sub , array ( '<input$1src=@$2://$3@' , '<input$1src=@$2@' , '<input$1src="' . $path . '$2"' , '<input$1src="$2"' ) );
}
return preg_replace ( $exp , $sub , $html );
}
else
return $html ;
}
2015-08-16 21:55:31 +08:00
/**
* replace constants
*
* @ access public
* @ param string $html
* @ param string $tag_left_delimiter
* @ param string $tag_right_delimiter
* @ param string $php_left_delimiter ( optional )
* @ param string $php_right_delimiter ( optional )
* @ param string $loop_level ( optional )
* @ param string $echo ( optional )
* @ return string
*/
public function const_replace ( $html , $tag_left_delimiter , $tag_right_delimiter , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level = null , $echo = null ){
2012-04-22 03:59:45 +08:00
// const
return preg_replace ( '/\{\#(\w+)\#{0,1}\}/' , $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter , $html );
}
2015-08-16 21:55:31 +08:00
/**
* replace functions / modifiers on constants and strings
*
* @ access public
* @ param string $html
* @ param string $tag_left_delimiter
* @ param string $tag_right_delimiter
* @ param string $php_left_delimiter ( optional )
* @ param string $php_right_delimiter ( optional )
* @ param string $loop_level ( optional )
* @ param string $echo ( optional )
* @ return string
*/
public function func_replace ( $html , $tag_left_delimiter , $tag_right_delimiter , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level = null , $echo = null ){
2012-04-22 03:59:45 +08:00
preg_match_all ( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/' , $html , $matches );
for ( $i = 0 , $n = count ( $matches [ 0 ]); $i < $n ; $i ++ ){
//complete tag ex: {$news.title|substr:0,100}
$tag = $matches [ 0 ][ $i ];
//variable name ex: news.title
$var = $matches [ 1 ][ $i ];
//function and parameters associate to the variable ex: substr:0,100
$extra_var = $matches [ 2 ][ $i ];
// check if there's any function disabled by black_list
$this -> function_check ( $tag );
$extra_var = $this -> var_replace ( $extra_var , null , null , null , null , $loop_level );
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
$is_init_variable = preg_match ( " /^( \ s*?) \ =[^=](.*?) $ / " , $extra_var );
//function associate to variable
$function_var = ( $extra_var and $extra_var [ 0 ] == '|' ) ? substr ( $extra_var , 1 ) : null ;
//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
$temp = preg_split ( " / \ .| \ [| \ - \ >/ " , $var );
//variable name
$var_name = $temp [ 0 ];
//variable path
$variable_path = substr ( $var , strlen ( $var_name ) );
//parentesis transform [ e ] in [" e in "]
$variable_path = str_replace ( '[' , '["' , $variable_path );
$variable_path = str_replace ( ']' , '"]' , $variable_path );
//transform .$variable in ["$variable"]
$variable_path = preg_replace ( '/\.\$(\w+)/' , '["$\\1"]' , $variable_path );
//transform [variable] in ["variable"]
$variable_path = preg_replace ( '/\.(\w+)/' , '["\\1"]' , $variable_path );
//if there's a function
if ( $function_var ){
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
// check if there's a function or a static method and separate, function by parameters
2012-04-22 03:59:45 +08:00
$function_var = str_replace ( " :: " , " @double_dot@ " , $function_var );
2015-09-05 07:55:19 +08:00
// get the position of the first :
if ( $dot_position = strpos ( $function_var , " : " ) ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// get the function and the parameters
$function = substr ( $function_var , 0 , $dot_position );
$params = substr ( $function_var , $dot_position + 1 );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
else {
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//get the function
$function = str_replace ( " @double_dot@ " , " :: " , $function_var );
$params = null ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// replace back the @double_dot@ with ::
$function = str_replace ( " @double_dot@ " , " :: " , $function );
$params = str_replace ( " @double_dot@ " , " :: " , $params );
2012-04-22 03:59:45 +08:00
}
else
$function = $params = null ;
$php_var = $var_name . $variable_path ;
// compile the variable for php
if ( isset ( $function ) ){
if ( $php_var )
$php_var = $php_left_delimiter . ( ! $is_init_variable && $echo ? 'echo ' : null ) . ( $params ? " ( $function ( $php_var , $params ) ) " : " $function ( $php_var ) " ) . $php_right_delimiter ;
else
$php_var = $php_left_delimiter . ( ! $is_init_variable && $echo ? 'echo ' : null ) . ( $params ? " ( $function ( $params ) ) " : " $function () " ) . $php_right_delimiter ;
}
else
$php_var = $php_left_delimiter . ( ! $is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter ;
$html = str_replace ( $tag , $php_var , $html );
}
return $html ;
}
2015-08-16 21:55:31 +08:00
/**
* replace variables
*
* @ access public
* @ param string $html
* @ param string $tag_left_delimiter
* @ param string $tag_right_delimiter
* @ param string $php_left_delimiter ( optional )
* @ param string $php_right_delimiter ( optional )
* @ param string $loop_level ( optional )
* @ param string $echo ( optional )
* @ return string
*/
public function var_replace ( $html , $tag_left_delimiter , $tag_right_delimiter , $php_left_delimiter = null , $php_right_delimiter = null , $loop_level = null , $echo = null ){
2012-04-22 03:59:45 +08:00
//all variables
if ( preg_match_all ( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/' , $html , $matches ) ){
2015-09-05 07:55:19 +08:00
for ( $parsed = array (), $i = 0 , $n = count ( $matches [ 0 ]); $i < $n ; $i ++ )
$parsed [ $matches [ 0 ][ $i ]] = array ( 'var' => $matches [ 1 ][ $i ], 'extra_var' => $matches [ 2 ][ $i ]);
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
foreach ( $parsed as $tag => $array ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//variable name ex: news.title
$var = $array [ 'var' ];
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//function and parameters associate to the variable ex: substr:0,100
$extra_var = $array [ 'extra_var' ];
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// check if there's any function disabled by black_list
$this -> function_check ( $tag );
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
$extra_var = $this -> var_replace ( $extra_var , null , null , null , null , $loop_level );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
$is_init_variable = preg_match ( " /^[a-z_A-Z \ . \ [ \ ]( \ - \ >)]*=[^=]* $ / " , $extra_var );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//function associate to variable
$function_var = ( $extra_var and $extra_var [ 0 ] == '|' ) ? substr ( $extra_var , 1 ) : null ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
$temp = preg_split ( " / \ .| \ [| \ - \ >/ " , $var );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//variable name
$var_name = $temp [ 0 ];
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//variable path
$variable_path = substr ( $var , strlen ( $var_name ) );
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
//parentesis transform [ e ] in [" e in "]
$variable_path = str_replace ( '[' , '["' , $variable_path );
$variable_path = str_replace ( ']' , '"]' , $variable_path );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//transform .$variable in ["$variable"] and .variable in ["variable"]
$variable_path = preg_replace ( '/\.(\${0,1}\w+)/' , '["\\1"]' , $variable_path );
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
// if is an assignment also assign the variable to $this->var['value']
if ( $is_init_variable )
$extra_var = " = \$ this->var[' { $var_name } '] { $variable_path } " . $extra_var ;
2012-04-22 03:59:45 +08:00
2012-04-30 01:15:06 +08:00
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//if there's a function
if ( $function_var ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// check if there's a function or a static method and separate, function by parameters
$function_var = str_replace ( " :: " , " @double_dot@ " , $function_var );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// get the position of the first :
if ( $dot_position = strpos ( $function_var , " : " ) ){
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
// get the function and the parameters
$function = substr ( $function_var , 0 , $dot_position );
$params = substr ( $function_var , $dot_position + 1 );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
else {
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
//get the function
$function = str_replace ( " @double_dot@ " , " :: " , $function_var );
$params = null ;
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
// replace back the @double_dot@ with ::
$function = str_replace ( " @double_dot@ " , " :: " , $function );
$params = str_replace ( " @double_dot@ " , " :: " , $params );
}
else
$function = $params = null ;
//if it is inside a loop
if ( $loop_level ){
//verify the variable name
if ( $var_name == 'key' )
$php_var = '$key' . $loop_level ;
elseif ( $var_name == 'value' )
$php_var = '$value' . $loop_level . $variable_path ;
elseif ( $var_name == 'counter' )
$php_var = '$counter' . $loop_level ;
else
$php_var = '$' . $var_name . $variable_path ;
} else
$php_var = '$' . $var_name . $variable_path ;
// compile the variable for php
if ( isset ( $function ) )
$php_var = $php_left_delimiter . ( ! $is_init_variable && $echo ? 'echo ' : null ) . ( $params ? " ( $function ( $php_var , $params ) ) " : " $function ( $php_var ) " ) . $php_right_delimiter ;
else
$php_var = $php_left_delimiter . ( ! $is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter ;
2012-04-30 01:15:06 +08:00
2015-09-05 07:55:19 +08:00
$html = str_replace ( $tag , $php_var , $html );
2012-04-22 03:59:45 +08:00
2015-09-05 07:55:19 +08:00
}
}
2012-04-22 03:59:45 +08:00
return $html ;
}
/**
* Check if function is in black list ( sandbox )
*
2015-08-16 21:55:31 +08:00
* @ access protected
* @ param string $code
* @ throws RainTpl_SyntaxException
* @ return void
2012-04-22 03:59:45 +08:00
*/
protected function function_check ( $code ){
$preg = '#(\W|\s)' . implode ( '(\W|\s)|(\W|\s)' , self :: $black_list ) . '(\W|\s)#' ;
// check if the function is in the black list (or not in white list)
if ( count ( self :: $black_list ) && preg_match ( $preg , $code , $match ) ){
// find the line of the error
$line = 0 ;
$rows = explode ( " \n " , $this -> tpl [ 'source' ]);
while ( ! strpos ( $rows [ $line ], $code ) )
$line ++ ;
// stop the execution of the script
$e = new RainTpl_SyntaxException ( 'Unallowed syntax in ' . $this -> tpl [ 'tpl_filename' ] . ' template' );
throw $e -> setTemplateFile ( $this -> tpl [ 'tpl_filename' ])
-> setTag ( $code )
-> setTemplateLine ( $line );
}
}
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
* Prints debug info about exception or passes it further if debug is disabled .
*
2015-08-16 21:55:31 +08:00
* @ access protected
* @ param RainTpl_Exception $e
2012-04-22 03:59:45 +08:00
* @ return string
*/
protected function printDebug ( RainTpl_Exception $e ){
if ( ! self :: $debug ) {
throw $e ;
}
$output = sprintf ( '<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>' ,
get_class ( $e ),
$e -> getMessage (),
$e -> getTemplateFile ()
);
if ( $e instanceof RainTpl_SyntaxException ) {
2016-07-11 20:15:20 +08:00
if ( null !== $e -> getTemplateLine ()) {
2012-04-22 03:59:45 +08:00
$output .= '<p>line: ' . $e -> getTemplateLine () . '</p>' ;
}
2016-07-11 20:15:20 +08:00
if ( null !== $e -> getTag ()) {
2012-04-22 03:59:45 +08:00
$output .= '<p>in tag: ' . htmlspecialchars ( $e -> getTag ()) . '</p>' ;
}
2016-07-11 20:15:20 +08:00
if ( null !== $e -> getTemplateLine () && null !== $e -> getTag ()) {
2012-04-22 03:59:45 +08:00
$rows = explode ( " \n " , htmlspecialchars ( $this -> tpl [ 'source' ]));
$rows [ $e -> getTemplateLine ()] = '<font color=red>' . $rows [ $e -> getTemplateLine ()] . '</font>' ;
$output .= '<h3>template code</h3>' . implode ( '<br />' , $rows ) . '</pre>' ;
}
}
$output .= sprintf ( '<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>' ,
$e -> getFile (), $e -> getLine (),
nl2br ( htmlspecialchars ( $e -> getTraceAsString ()))
);
return $output ;
}
}
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
* Basic Rain tpl exception .
*/
class RainTpl_Exception extends Exception {
/**
* Path of template file with error .
*/
protected $templateFile = '' ;
/**
* Returns path of template file with error .
*
* @ return string
*/
public function getTemplateFile ()
{
return $this -> templateFile ;
}
/**
* Sets path of template file with error .
*
* @ param string $templateFile
* @ return RainTpl_Exception
*/
public function setTemplateFile ( $templateFile )
{
$this -> templateFile = ( string ) $templateFile ;
return $this ;
}
}
2015-09-05 07:55:19 +08:00
2012-04-22 03:59:45 +08:00
/**
* Exception thrown when template file does not exists .
*/
class RainTpl_NotFoundException extends RainTpl_Exception {
}
/**
* Exception thrown when syntax error occurs .
*/
class RainTpl_SyntaxException extends RainTpl_Exception {
/**
* Line in template file where error has occured .
*
* @ var int | null
*/
protected $templateLine = null ;
/**
* Tag which caused an error .
*
* @ var string | null
*/
protected $tag = null ;
/**
* Returns line in template file where error has occured
* or null if line is not defined .
*
* @ return int | null
*/
public function getTemplateLine ()
{
return $this -> templateLine ;
}
/**
* Sets line in template file where error has occured .
*
* @ param int $templateLine
* @ return RainTpl_SyntaxException
*/
public function setTemplateLine ( $templateLine )
{
$this -> templateLine = ( int ) $templateLine ;
return $this ;
}
/**
* Returns tag which caused an error .
*
* @ return string
*/
public function getTag ()
{
return $this -> tag ;
}
/**
* Sets tag which caused an error .
*
* @ param string $tag
* @ return RainTpl_SyntaxException
*/
public function setTag ( $tag )
{
$this -> tag = ( string ) $tag ;
return $this ;
}
}
// -- end