2006-09-13 20:00:00 +04:00
< ? php
2006-09-27 20:00:00 +04:00
2006-09-13 20:00:00 +04:00
/***************************************************************************
* template . php
* -------------------
* begin : Saturday , Feb 13 , 2001
2007-09-25 20:00:00 +04:00
* change : Tuesday , Sep 25 , 2007
2006-09-27 20:00:00 +04:00
* copyright : ( C ) 2001 The phpBB Group
2007-09-25 20:00:00 +04:00
* copyright : ( C ) 2006 - 2007 VitaliF
2007-02-27 20:00:00 +03:00
* email : vitalif @ yourcmc . ru
2006-09-13 20:00:00 +04:00
*
***************************************************************************/
/***************************************************************************
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
***************************************************************************/
/**
2007-09-25 20:00:00 +04:00
* Original Template class by Nathan Codding of the phpBB group .
2006-09-13 20:00:00 +04:00
* The interface was originally inspired by PHPLib templates ,
* and the template file formats are quite similar .
*/
/**
* Documentation :
* <!-- BEGIN * --> and <!-- END * --> can be placed on one line
* { block_name . block_name . etc .... varname } = varref varname of current iteration of block block_name . block_name . etc ...
* { block_name . block_name . etc .... varname | varoption } = varref varname if it ' s set or block_name . block_name . etc .... varoption if not
* { block_name . #} = number of current iteration of block block_name
* <!-- BEGIN block_name --> = begin block block_name , blocks can be nested
* <!-- END block_name --> = end block
* <!-- INCLUDE filename --> = include file " filename " directly at this point ( path relative to current template file path )
* <!-- BEGIN block_name AT #start[ #count] --> = begin block_name at iteration #start and do #count iterations (or 1 by default)
* <!-- BEGIN block_name MOD #div #mod --> = do block_name iterations with numbers #no if (#no % #div) = #mod
2007-09-25 20:00:00 +04:00
* <!-- # Comment #-->
* <!-- IF block_name . block_name . etc . [ ... ] . varname --> [[ ... ]] <!-- END --> = parse [[ ... ]] only if varref { block_name . block_name . etc . [ ... ] . varname } is set and is true ( if PHP thinks that it ' s true :- ))
* <!-- IF ! block_name . block_name . etc . [ ... ] . varname --> [[ ... ]] <!-- END --> = parse [[ ... ]] only if varref { block_name . block_name . etc . [ ... ] . varname } is not set or is not true
2006-09-13 20:00:00 +04:00
*
* Документация :
2007-09-25 20:00:00 +04:00
* <!-- BEGIN * --> и <!-- END * --> могут быть даже на одной строке
2006-09-13 20:00:00 +04:00
* { VAR } - подстановка корневой переменной VAR
* <!-- INCLUDE filename --> = дословное включение файла " filename "
* <!-- BEGIN б ло к1 --> = обработать в цикле все итерации блока " б ло к1"
* <!-- BEGIN б ло к1 AT < начало > [ < количество > ] --> = обработать в цикле < количество > ( или все оставшиеся если < количество > не задано ) итераций блока " б ло к1"
* <!-- BEGIN б ло к1 MOD < делитель > < остаток > --> = обработать в цикле все итерации блока " б ло к1" , номера которых по модулю < делитель > равны < остаток >
* <!-- END б ло к1 --> = конец тела цикла
* Блоки могут быть вложенными , <!-- BEGIN ... --> и <!-- END ... --> могут быть расположены на одной строке .
* Подстановки внутриблоковых переменных работают внутри блока : )
* { б ло к1. б ло к2. и_та к_да ле е . VAR } = подстановка переменной VAR блока " б ло к1.б ло к2.и_та к_да ле е "
2007-09-25 20:00:00 +04:00
* { и_та к_да ле е . #} = номер текущей итерации блока "б ло к1.б ло к2.и_та к_да ле е ", если "и_та к_да ле е " - самый внутренний по вложенности блок
2006-09-13 20:00:00 +04:00
* { б ло к1. б ло к2. и_та к_да ле е . VAR | VAR2 } = подстановка переменной VAR , а если она не задана - то VAR2 блока " б ло к1.б ло к2.и_та к_да ле е "
* <!-- # Комментарий, не попадающий в выходной HTML файл #-->
2006-09-27 20:00:00 +04:00
* <!-- IF б ло к1. б ло к2. и_та к_да ле е . VAR --> [[ ... ]] <!-- END --> = показать [[ ... ]] только если переменная VAR текущей итерации блока " б ло к1.б ло к2.и_та к_да ле е " задана
* <!-- IF ! б ло к1. б ло к2. и_та к_да ле е . VAR --> [[ ... ]] <!-- END --> = показать [[ ... ]] только если переменная VAR текущей итерации блока " б ло к1.б ло к2.и_та к_да ле е " Н Е задана
2007-09-25 20:00:00 +04:00
*
* Code example / пример кода :
*
* $template = new Template ( " /directory/with/templates " );
* $template -> cachedir = " /directory/for/caching/compiled/code " ; # your script must have write access to this directory / выполняемый скрипт должен иметь доступ на запись в эту директорию
* $template -> no_pparse = true ; # highly recommended with previous versions, but unnecessary with this / важно с предыдущими версиями, но необязательно с этой
*
* $template -> set_filenames ( array ( 'body' => 'hello.tpl' ));
* $template -> assign_vars ( array (
* " HELLO " => " Hello, world! " ,
* " SHOW1 " => 1 ,
* " TEXT1 " => " I'll be visible (SHOW1 = true) " ,
* " TEXT2 " => " And I not (SHOW2) " ,
* ));
* $template -> assign_block_vars ( 'language' , array (
* 'NAME' => 'PHP' ,
* 'URL' => 'http://php.net/'
* ));
* $template -> assign_block_vars ( 'language' , array (
* 'NAME' => 'Perl' ,
* 'URL' => 'http://perl.org/'
* ));
*
* $template -> pparse ( 'body' ); # same as "echo $template->rparse('body');" if $template->no_pparse is true / то же, что и "echo $template->rparse('body');", если $template->no_pparse истинно
*
* Template example / пример шаблона :
*
* < html >
* < head >
* < title > Hello , world ! by template . php </ title >
* </ head >
* < body >
* < font size = " +3 " > { HELLO } </ font >< br />
* <!-- IF SHOW1 --> { TEXT1 } <!-- END -->< br />
* <!-- IF SHOW2 --> { TEXT2 } <!-- END --><!-- IF ! SHOW2 --> SHOW2 is false <!-- END -->< br />
* <!-- BEGIN language -->
* { language . NAME } --> < a href = " { language.URL} " > { language . URL } </ a >< br />
* <!-- END language -->
* </ body >
* </ html >
2006-09-13 20:00:00 +04:00
*/
class Template
{
2007-09-25 20:00:00 +04:00
var $classname = " Template " ;
// set this variable to cache directory and template compiler will use caching
var $cachedir = false ;
// variable that holds all the data we'll be substituting into
// the compiled templates.
// ...
// This will end up being a multi-dimensional array like this:
// $this->_tpldata[block.][iteration#][child.][iteration#][child2.][iteration#][variablename] == value
// if it's a root-level variable, it'll be like this:
// $this->_tpldata[.][0][varname] == value
var $_tpldata = array ();
var $_tpldata_stack = array ();
// Hash of filenames for each template handle.
var $files = array ();
// Root template directory.
var $root = " " ;
// this will hash handle names to the compiled code for that handle.
var $compiled_code = array ();
// This will hold the uncompiled code for that handle.
var $uncompiled_code = array ();
// This option causes pparse() to be equal to "echo rparse()"
var $no_pparse = true ;
// Set this to the name of "wrapper" function, which is called by
// rparse() after compiling and running.
var $wrapper = false ;
/**
*
* $conv defines available conversions . $conv [ 0 ] define conversions that take 0 parameters ,
* $conv [ 1 ] - 1.
*
* By default , the following conversions are available :
* T = strip_tags
* i = intval
* b = nl2br
* h = html_strip [ requires lib . php by VMX ]
* H = html_strip_pbr [ requires lib . php by VMX ]
* s = htmlspecialchars
* S = html_pbr [ requires lib . php by VMX ]
* l = strtolower
* u = strtoupper
* c ### = strlimit (###) where ### is number of any length
*
*/
var $conv = array (
0 => array (
'T' => 'strip_tags' ,
'i' => 'intval' ,
'b' => 'nl2br' ,
'h' => 'html_strip' ,
'H' => 'html_strip_pbr' ,
's' => 'htmlspecialchars' ,
'S' => 'html_pbr' ,
'l' => 'strtolower' ,
'u' => 'strtoupper' ,
'q' => 'addslashes' ,
'l' => 'urlencode' ,
),
1 => array (
'c' => 'strlimit'
)
);
/**
* Constructor . Simply sets the root dir .
*/
function Template ( $root = " . " )
{
$this -> set_rootdir ( $root );
}
/**
* Destroys this template object . Should be called when you ' re done with it , in order
* to clear out the template data so you can load / parse a new template set .
*/
function destroy ()
{
$this -> _tpldata = array ();
}
/**
* Saves current template data in template data stack and then destroys data .
*/
function datapush ()
{
array_push ( $this -> _tpldata_stack , $this -> _tpldata );
}
/**
* Restores last saved template data from template data stack .
*/
function datapop ()
{
if ( count ( $this -> _tpldata_stack ))
$this -> _tpldata = array_pop ( $this -> _tpldata_stack );
}
/**
* Sets the template root directory for this Template object .
*/
function set_rootdir ( $dir )
{
if ( ! is_dir ( $dir ))
return false ;
$this -> root = $dir ;
return true ;
}
/**
* Sets the template filenames for handles . $filename_array
* should be a hash of handle => filename pairs .
*/
function set_filenames ( $filename_array )
{
if ( ! is_array ( $filename_array ))
return false ;
reset ( $filename_array );
$a = $this -> wrapper ;
$this -> wrapper = false ;
while ( list ( $handle , $filename ) = each ( $filename_array ))
{
$this -> files [ $handle ] = $this -> make_filename ( $filename );
$this -> uncompiled_code [ $handle ] = '' ;
$this -> compiled_code [ $handle ] = '' ;
}
$this -> wrapper = $a ;
return true ;
}
/**
* Sets the template code for handle directly , without loading it from any files .
*/
function set_template_code ( $handle , $code )
{
$this -> files [ $handle ] = false ;
$this -> uncompiled_code [ $handle ] = $code ;
unset ( $this -> compiled_code [ $handle ]);
return true ;
}
/**
* Load the file for the handle , compile the file ,
* and run the compiled code . This will print out
* the results of executing the template .
*/
function pparse ( $handle )
{
if ( $this -> no_pparse )
{
echo $this -> rparse ( $handle );
return ;
}
if ( ! $this -> loadfile ( $handle ))
die ( " Template->pparse(): Couldn't load template file for handle $handle " );
// actually compile the template now.
if ( ! isset ( $this -> compiled_code [ $handle ]) || empty ( $this -> compiled_code [ $handle ]))
$this -> compiled_code [ $handle ] = $this -> compile ( $this -> uncompiled_code [ $handle ]);
//echo ($this->compiled_code[$handle]);
// Run the compiled code.
eval ( $this -> compiled_code [ $handle ]);
return true ;
}
/**
* Load the file for the handle , compile the file ,
* and run the compiled code . This will RETURN
* the results of executing the template .
*/
function rparse ( $handle )
{
if ( ! $this -> loadfile ( $handle ))
die ( " Template->rparse(): Couldn't load template file for handle $handle " );
// Compile it, with the "no echo statements" option on.
$_str = " " ;
$code = $this -> compile ( $this -> uncompiled_code [ $handle ], true , '_str' );
// evaluate the variable assignment.
eval ( $code );
// call wrapper if it's set
if ( $this -> wrapper )
{
$fn = $this -> wrapper ;
$_str = $fn ( $_str );
}
// return the value of the generated variable.
return $_str ;
}
/**
* Inserts the uncompiled code for $handle as the
* value of $varname in the root - level . This can be used
* to effectively include a template in the middle of another
* template .
* Note that all desired assignments to the variables in $handle should be done
* BEFORE calling this function .
*/
function assign_var_from_handle ( $varname , $handle )
{
if ( ! $this -> loadfile ( $handle ))
die ( " Template->assign_var_from_handle(): Couldn't load template file for handle $handle " );
// Compile it, with the "no echo statements" option on.
$_str = " " ;
$code = $this -> compile ( $this -> uncompiled_code [ $handle ], true , '_str' );
// evaluate the variable assignment.
eval ( $code );
// assign the value of the generated variable to the given varname.
$this -> assign_var ( $varname , $_str );
return true ;
}
/**
* Block - level variable assignment . Adds a new block iteration with the given
* variable assignments . Note that this should only be called once per block
* iteration .
*/
function assign_block_vars ( $blockname , $vararray )
{
if ( $blockname == '.' || ! $blockname )
return $this -> assign_vars ( $vararray );
if ( $blockname { 0 } == '.' )
$blockname = substr ( $blockname , 1 );
$vararray = $this -> array_do_conversions ( $vararray );
if ( strstr ( $blockname , '.' ))
{
// Nested block.
$blocks = explode ( '.' , $blockname );
$blockcount = sizeof ( $blocks ) - 1 ;
$str = '$this->_tpldata' ;
for ( $i = 0 ; $i < $blockcount ; $i ++ )
{
$str .= '[\'' . $blocks [ $i ] . '.\']' ;
eval ( '$lastiteration = sizeof(' . $str . ') - 1;' );
$str .= '[' . $lastiteration . ']' ;
}
// Now we add the block that we're actually assigning to.
// We're adding a new iteration to this block with the given
// variable assignments.
$str .= '[\'' . $blocks [ $blockcount ] . '.\'][] = $vararray;' ;
// Now we evaluate this assignment we've built up.
eval ( $str );
}
else
{
// Top-level block.
// Add a new iteration to this block with the variable assignments
// we were given.
$this -> _tpldata [ $blockname . '.' ][] = $vararray ;
}
return true ;
}
/**
* Block - level variable assignment . This function does not add any iterations
* to block , it only appends $vararray to last existing iteration . [ VMX 2006 ]
*/
function append_block_vars ( $blockname , $vararray )
{
if ( $blockname == '.' || ! $blockname )
$this -> assign_vars ( $vararray );
if ( $blockname { 0 } == '.' )
$blockname = substr ( $blockname , 1 );
$vararray = $this -> array_do_conversions ( $vararray );
if ( strstr ( $blockname , '.' ))
{
// Nested block.
$blocks = explode ( '.' , $blockname );
$blockcount = sizeof ( $blocks );
$str = '$this->_tpldata' ;
for ( $i = 0 ; $i < $blockcount ; $i ++ )
{
$str .= '[\'' . $blocks [ $i ] . '.\']' ;
eval ( '$lastiteration = sizeof(' . $str . ') - 1;' );
$str .= '[' . $lastiteration . ']' ;
}
eval ( $str .= ' = array_merge (' . $str . ', $vararray);' );
}
else
{
// Top-level block.
if (( $len = count ( $this -> _tpldata [ $blockname . '.' ])) > 0 )
$this -> _tpldata [ $blockname . '.' ][ $len - 1 ] = array_merge ( $this -> _tpldata [ $blockname . '.' ][ $len - 1 ], $vararray );
}
return true ;
}
/**
* Root - level variable assignment . Adds to current assignments , overriding
* any existing variable assignment with the same name .
*/
function assign_vars ( $vararray )
{
$vararray = $this -> array_do_conversions ( $vararray );
foreach ( $vararray as $key => $val )
$this -> _tpldata [ '.' ][ 0 ][ $key ] = $val ;
return true ;
}
/**
* Root - level variable assignment . Adds to current assignments , overriding
* any existing variable assignment with the same name .
*/
function assign_var ( $varname , $varval )
{
if (( $p = strpos ( $varname , '/' )) !== false )
{
eval ( '$varval = ' . $this -> generate_conversion_ref ( '$varval' , substr ( $varname , $p + 1 )) . ';' );
$varname = substr ( $varname , 0 , $p );
}
$this -> _tpldata [ '.' ][ 0 ][ $varname ] = $varval ;
return true ;
}
/**
* Generates a full path + filename for the given filename , which can either
* be an absolute name , or a name relative to the rootdir for this Template
* object .
*/
function make_filename ( $filename )
{
// Check if it's an absolute or relative path.
if ( substr ( $filename , 0 , 1 ) != '/' )
$filename = $this -> root . '/' . $filename ; //($rp_filename = phpbb_realpath($this->root . '/' . $filename)) ? $rp_filename : $filename;
if ( ! file_exists ( $filename ))
die ( " Template->make_filename(): Error - file $filename does not exist " );
return $filename ;
}
/**
* If not already done , load the file for the given handle and populate
* the uncompiled_code [] hash with its code . Do not compile .
*/
function loadfile ( $handle )
{
// If the file for this handle is already loaded and compiled, do nothing.
if ( isset ( $this -> uncompiled_code [ $handle ]) && ! empty ( $this -> uncompiled_code [ $handle ]))
return true ;
// If we don't have a file assigned to this handle, die.
if ( ! isset ( $this -> files [ $handle ]))
die ( " Template->loadfile(): No file specified for handle $handle " );
// << VMX >> if $this->files[$handle] is false - template contents are specified directly
if ( $this -> files [ $handle ] !== false )
{
$filename = $this -> files [ $handle ];
$filepath = substr ( $filename , 0 , strrpos ( $filename , '/' ) + 1 );
$str = @ file_get_contents ( $filename );
if ( empty ( $str ))
die ( " Template->loadfile(): File $filename for handle $handle is empty " );
// Handle <!-- INCLUDE --> instructions
while ( preg_match ( '#<!-- INCLUDE (.*?) -->#' , $str , $m ))
$str = str_replace ( $m [ 0 ], @ file_get_contents ( $filepath . $m [ 1 ]), $str );
$this -> uncompiled_code [ $handle ] = $str ;
}
return true ;
}
/**
* Compiles the given string of code , and returns
* the result in a string .
* If " do_not_echo " is true , the returned code will not be directly
* executable , but can be used as part of a variable assignment
* for use in assign_code_from_handle () or rparse () .
*/
function compile ( $code , $do_not_echo = false , $retvar = '' )
{
if ( $this -> cachedir )
{
$sfile = $this -> cachedir . ( $do_not_echo ? 'alt_' . $retvar : '' ) . md5 ( $code ) . '.tps' ;
if (( $cached = @ file_get_contents ( $sfile )) !== false )
return $cached ;
}
else
unset ( $sfile );
$default_addmode = $do_not_echo ? '$' . $retvar . ' .= ' : 'echo ' ;
// Сначала <!--# комментарии #-->
$code = preg_replace ( '/\s*<!--#.*?#-->/s' , '' , $code );
// форматирование кода для красоты
$code = preg_replace ( '/^\s*(<!-- (?:BEGIN|END|IF!?|ELSE|SET|ENDSET) .*?-->)\s*$/m' , " \x01 \\ 1 \x01 \n " , $code );
$ncode = $code ;
do {
$code = $ncode ;
$ncode = preg_replace ( " /([^ \x01 ])(<!-- (?:BEGIN|END|IF!?|ELSE|SET|ENDSET) .*?-->)/m " , " \\ 1 \x01 \\ 2 " , $code );
} while ( $ncode != $code );
$ncode = $code ;
do {
$code = $ncode ;
$ncode = preg_replace ( " /<!-- (?:BEGIN|END|IF!?|ELSE|SET|ENDSET) .*?-->(?=[^ \x01 ])/m " , " \\ 0 \x01 " , $code );
} while ( $ncode != $code );
// replace \ with \\ and then ' with \'.
$code = str_replace ( '\\' , '\\\\' , $code );
$code = str_replace ( '\'' , '\\\'' , $code );
// VMX :: handle iteration numbers
$code = preg_replace ( '/\{([a-z0-9\-_]+)\.#\}/' , '\'.(1+(isset($_\1_i)?$_\1_i:0)).\'' , $code );
// change template varrefs into PHP varrefs
// handle all varrefs (instead of only non-root)
$varrefs = array ();
preg_match_all ( '#\{(([a-z0-9\-_]+?\.)+)?([a-z0-9\-_/]+?)(\|([a-z0-9\-_/]+?))?\}#is' , $code , $varrefs );
$varcount = sizeof ( $varrefs [ 1 ]);
for ( $i = 0 ; $i < $varcount ; $i ++ )
{
$namespace = $varrefs [ 1 ][ $i ];
$varname = $varrefs [ 3 ][ $i ];
if ( isset ( $varrefs [ 5 ][ $i ]))
$varoption = $varrefs [ 5 ][ $i ];
else
$varoption = false ;
$new = $this -> generate_block_varref ( $namespace , $varname , $varoption );
$code = str_replace ( $varrefs [ 0 ][ $i ], $new , $code );
}
// // This will handle the remaining root-level varrefs
// $code = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $code);
// replace \n with \n\x1
$code = str_replace ( " \n " , " \n \x1 " , $code );
//$code = preg_replace ('#(?:\x1\s*)+\x1#', "\x1", $code);
// Break code up into lines
$code_lines = explode ( " \x1 " , $code );
$block_nesting_level = 0 ;
$block_names = array ();
$block_names [ 0 ] = " . " ;
$addmodes [ 0 ][ 0 ] = $default_addmode ;
$addmodes [ 0 ][ 1 ] = ';' ;
$addmodes [ 0 ][ 2 ] = 0 ;
$line_count = sizeof ( $code_lines );
$in_set = false ;
for ( $i = 0 ; $i < $line_count ; $i ++ )
{
if ( strlen ( $code_lines [ $i ]) == 0 )
continue ;
// Additional part will handle AT and MOD
if ( preg_match ( '#^<!-- BEGIN (!?)([A-Za-z0-9\-_]+?) ([A-Za-z \t\-_0-9]*)-->$#' , $code_lines [ $i ], $m )) // ((APPEND|PREPEND) (([A-Za-z0-9\-_]+?\.)*)([A-Za-z0-9\-_]+?) )?
{
$b_not = $m [ 1 ];
$b_bn = $m [ 2 ];
$b_mod = $m [ 3 ];
$n [ 0 ] = $m [ 0 ];
$n [ 1 ] = $b_bn ;
// We have the start of a block.
$block_nesting_level ++ ;
$block_names [ $block_nesting_level ] = $b_bn ;
$addmodes [ $block_nesting_level ][ 0 ] = $default_addmode ;
$addmodes [ $block_nesting_level ][ 1 ] = ';' ;
$addmodes [ $block_nesting_level ][ 2 ] = 0 ;
$cbstart = 0 ;
$cbcount = 0 ;
$cbplus = '++' ;
if ( preg_match ( '#^[ \t]*AT ([0-9]+)[ \t]*(?:([0-9]+)[ \t]*)?$#' , $b_mod , $nem ))
{
$cbstart = isset ( $nem [ 1 ]) ? ( 0 + $nem [ 1 ]) : 0 ;
$cbcount = ( isset ( $nem [ 2 ]) ? ( 0 + $nem [ 2 ]) + $cbstart : 0 );
}
if ( preg_match ( '#^[ \t]*MOD ([1-9][0-9]*) ([0-9]+)[ \t]*$#' , $b_mod , $nem ))
{
$cbstart = $nem [ 2 ];
$cbplus = '+=' . $nem [ 1 ];
}
if ( $block_nesting_level < 2 )
{
// Block is not nested.
if ( ! $cbcount )
$code_lines [ $i ] = '$_' . $b_bn . '_count = ( isset($this->_tpldata[\'' . $b_bn . '.\']) ) ? sizeof($this->_tpldata[\'' . $b_bn . '.\']) : ' . $addmodes [ $block_nesting_level ][ 2 ] . ';' ;
else $code_lines [ $i ] = '$_' . $b_bn . '_count = min (@sizeof($this->_tpldata[\'' . $b_bn . '.\']), ' . $cbcount . ');' ;
}
else
{
// This block is nested.
// Generate a namespace string for this block.
$namespace = implode ( '.' , $block_names );
// strip leading period from root level..
$namespace = substr ( $namespace , 2 );
// Get a reference to the data array for this block that depends on the
// current indices of all parent blocks.
$varref = $this -> generate_block_data_ref ( $namespace , false );
// $this->_tpldata['categories.'][$_categories_i]['modules.']
// Create the for loop code to iterate over this block.
if ( ! $cbcount )
$code_lines [ $i ] = '$_' . $b_bn . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : ' . $addmodes [ $block_nesting_level ][ 2 ] . ';' ;
else $code_lines [ $i ] = '$_' . $b_bn . '_count = min (@sizeof(' . $varref . '), ' . $cbcount . ');' ;
}
if ( ! $b_not )
$code_lines [ $i ] .= " \n " . 'for ($_' . $b_bn . '_i = ' . $cbstart . '; $_' . $b_bn . '_i < $_' . $b_bn . '_count; $_' . $b_bn . '_i' . $cbplus . ')' ;
else
$code_lines [ $i ] .= " \n " . 'for ($_' . $b_bn . '_i = ' . $cbcount . '; $_' . $b_bn . '_i < ' . max ( $cbcount , 1 ) . '; $_' . $b_bn . '_i' . $cbplus . ')' ;
$code_lines [ $i ] .= " \n " . '{' ;
}
else if ( preg_match ( '#<!-- END\s+(.*?)-->#' , $code_lines [ $i ], $m ))
{
// Make strict check - only <!-- END block --> corresponds to <!-- BEGIN block -->
if ( $block_nesting_level > 0 && trim ( $m [ 1 ]) == $block_names [ $block_nesting_level ])
{
unset ( $block_names [ $block_nesting_level ]);
$block_nesting_level -- ;
}
$code_lines [ $i ] = '} // END ' . $m [ 1 ];
}
else if ( preg_match ( '#<!-- IF(!?)\s+((?:[a-zA-Z0-9\-_]+\.)+)?([a-zA-Z0-9\-_/]+) -->#is' , $code_lines [ $i ], $m ))
{
$varref = $this -> generate_block_data_ref ( substr ( $m [ 2 ], 0 , strlen ( $m [ 2 ]) - 1 ), true ) . '[\'' . $m [ 3 ] . '\']' ;
$code_lines [ $i ] = 'if(' . ( empty ( $m [ 1 ]) ? '!' : '' ) . 'empty(' . $varref . ')) {' ;
}
else if ( preg_match ( '#<!-- ELSE -->#is' , $code_lines [ $i ], $m ))
$code_lines [ $i ] = '} else {' ;
else if ( ! $in_set && preg_match ( '#^\s*<!--\s*SET\s+((?:[a-zA-Z0-9\-_]+\.)+)?([a-zA-Z0-9\-_/]+)\s*-->\s*$#s' , $code_lines [ $i ], $m ))
{
$varref = $this -> generate_block_data_ref ( substr ( $m [ 1 ], 0 , strlen ( $m [ 1 ]) - 1 ), true ) . '[\'' . $m [ 2 ] . '\']' ;
$code_lines [ $i ] = " \$ ${ retvar } _tmp = \$ $retvar ; \n \$ $retvar = ''; \n $varref = eval(<<<ENDSET " ;
$in_set = true ;
}
else if ( $in_set && preg_match ( '/^\s*<!--\s*ENDSET\s*-->\s*$/s' , $code_lines [ $i ], $m ))
{
$code_lines [ $i ] = " return \\ \$ _str; \n ENDSET \n ); \n \$ $retvar = \$ ${ retvar } _tmp; " ;
$in_set = false ;
}
else if ( ! preg_match ( '/^\s*$/' , $code_lines [ $i ]))
{
// We have an ordinary line of code
$code_lines [ $i ] = $addmodes [ $block_nesting_level ][ 0 ] . '\'' . $code_lines [ $i ] . '\'' . $addmodes [ $block_nesting_level ][ 1 ];
}
if ( $in_set && ! preg_match ( '/<<<ENDSET$/' , $code_lines [ $i ]))
$code_lines [ $i ] = str_replace ( '$' , '\\$' , $code_lines [ $i ]);
}
// Bring it back into a single string of lines of code.
$code = implode ( " \n " , $code_lines );
// Cache compiled code if needed
if ( isset ( $sfile ) && $fd = @ fopen ( $sfile , 'w' ))
{
fwrite ( $fd , $code );
fclose ( $fd );
}
return $code ;
}
/**
* Generates a reference to the given variable inside the given ( possibly nested )
* block namespace . This is a string of the form :
* ' . $this->_tpldata[' parent '][$_parent_i][' $child1 '][$_child1_i][' $child2 '][$_child2_i]...[' varname '] . '
* It ' s ready to be inserted into an " echo " line in one of the templates .
* NOTE : expects a trailing " . " on the namespace .
*/
function generate_block_varref ( $namespace , $varname , $varoption = false )
{
$varconv = false ;
if (( $p = strpos ( $varname , '/' )) !== false )
{
$varconv = substr ( $varname , $p + 1 );
$varname = substr ( $varname , 0 , $p );
}
// Strip the trailing period.
$namespace = substr ( $namespace , 0 , strlen ( $namespace ) - 1 );
// Get a reference to the data block for this namespace.
$varref = $this -> generate_block_data_ref ( $namespace , true );
// Prepend the necessary code to stick this in an echo line.
if ( $varoption === false )
$varoption = " '' " ;
else
$varoption = '((isset(' . $varref . '[\'' . $varoption . '\']' . ')) ? ' . $varref . '[\'' . $varoption . '\'] : \'\')' ;
// Append the variable reference.
$varref .= '[\'' . $varname . '\']' ;
$varref = '((isset(' . $varref . ')) ? ' . $varref . ' : ' . $varoption . ')' ;
if ( $varconv )
$varref = $this -> generate_conversion_ref ( $varref , $varconv );
$varref = '\' . ' . $varref . ' . \'' ;
return $varref ;
}
/**
* Generates a reference to the array of data values for the given
* ( possibly nested ) block namespace . This is a string of the form :
* $this -> _tpldata [ 'parent' ][ $_parent_i ][ '$child1' ][ $_child1_i ][ '$child2' ][ $_child2_i ] ... [ '$childN' ]
*
* If $include_last_iterator is true , then [ $_childN_i ] will be appended to the form shown above .
* NOTE : does not expect a trailing " . " on the blockname .
*/
function generate_block_data_ref ( $blockname , $include_last_iterator )
{
// Added - VMX: function now can handle root-level
if ( $blockname == '' )
return '$this->_tpldata[\'.\']' . ( $include_last_iterator ? '[0]' : '' );
// Get an array of the blocks involved.
$blocks = explode ( " . " , $blockname );
$blockcount = sizeof ( $blocks ) - 1 ;
$varref = '$this->_tpldata' ;
// Build up the string with everything but the last child.
for ( $i = 0 ; $i < $blockcount ; $i ++ )
$varref .= '[\'' . $blocks [ $i ] . '.\'][$_' . $blocks [ $i ] . '_i]' ;
// Add the block reference for the last child.
$varref .= '[\'' . $blocks [ $blockcount ] . '.\']' ;
// Add the iterator for the last child if requried.
if ( $include_last_iterator )
$varref .= '[$_' . $blocks [ $blockcount ] . '_i]' ;
return $varref ;
}
/**
* Конвертирует те переменные массива $array , имена которых заданы как NAME / conv
*/
function array_do_conversions ( $array )
{
$ra = array ();
foreach ( $array as $key => $val )
{
if (( $p = strpos ( $key , '/' )) !== false )
{
eval ( '$val = ' . $this -> generate_conversion_ref ( '$val' , substr ( $key , $p + 1 )) . ';' );
$key = substr ( $key , 0 , $p );
}
$ra [ $key ] = $val ;
}
return $ra ;
}
/**
* Generates function call series needed to perform conversion ( s ) according to $conv
*/
function generate_conversion_ref ( $val , $conv )
{
$cv = array ();
$l = strlen ( $conv );
for ( $i = 0 ; $i < $l ; $i ++ )
{
if ( array_key_exists ( $conv { $i }, $this -> conv [ 0 ]))
$cv [ $conv { $i }] = true ;
else if ( array_key_exists ( $conv { $i }, $this -> conv [ 1 ]))
$cv [ $conv { $i }] = 0 + substr ( $conv , $i + 1 );
}
$cv = array_reverse ( $cv );
$r = $val ;
foreach ( $cv as $k => $v )
{
if ( $v === true )
$r = $this -> conv [ 0 ][ $k ] . '(' . $r . ')' ;
else if ( is_numeric ( $v ))
$r = $this -> conv [ 1 ][ $k ] . '(' . $r . ',' . $v . ')' ;
}
return $r ;
}
2006-09-13 20:00:00 +04:00
}
2006-09-27 20:00:00 +04:00
?>