diff options
author | Paul S. Owen <psotfx@users.sourceforge.net> | 2002-07-14 14:33:30 +0000 |
---|---|---|
committer | Paul S. Owen <psotfx@users.sourceforge.net> | 2002-07-14 14:33:30 +0000 |
commit | a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae (patch) | |
tree | 3b768551bf873541330137b07de5b7e72789fecc /phpBB/includes | |
parent | 05b6c0348e0b4deaad756c9c1acac90c44bc748b (diff) | |
download | forums-a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae.tar forums-a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae.tar.gz forums-a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae.tar.bz2 forums-a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae.tar.xz forums-a0ee4ce6ae2bca41e99b3631c83dbc567ad3bfae.zip |
This should work fine with existing templates ... note that you will need to create a directory called cache/ in your templates directory ... give it a+w access (chmod 700)
git-svn-id: file:///svn/phpbb/trunk@2662 89ea8834-ac86-4346-8a33-228a782c2dd0
Diffstat (limited to 'phpBB/includes')
-rw-r--r-- | phpBB/includes/template.php | 842 |
1 files changed, 578 insertions, 264 deletions
diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 2803277214..2b8b800b9f 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -1,6 +1,6 @@ <?php /*************************************************************************** - * template.inc + * template.php * ------------------- * begin : Saturday, Feb 13, 2001 * copyright : (C) 2001 The phpBB Group @@ -8,7 +8,6 @@ * * $Id$ * - * ***************************************************************************/ /*************************************************************************** @@ -20,70 +19,65 @@ * ***************************************************************************/ -/** - * Template class. By Nathan Codding of the phpBB group. - * The interface was originally inspired by PHPLib templates, - * and the template file formats are quite similar. - * - */ +/* + Template class. + + Nathan Codding - Original version design and implementation + Crimsonbane - Initial caching proposal and work + psoTFX - Completion of file caching, decompilation routines and implementation of + conditionals/keywords and associated changes + + The interface was inspired by PHPLib templates, and the template file (formats are + quite similar) + + The keyword/conditional implementation is currently based on sections of code from + the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released + (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code + derived from an LGPL application may be relicenced under the GPL, this applies + to this source +*/ class Template { + var $classname = 'Template'; // 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 + // the compiled templates. Takes form: + // --> $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 + // --> $this->_tpldata[.][0][varname] == value var $_tpldata = array(); - // Hash of filenames for each template handle. - var $files = array(); - - // Root template directory. + // Root dir and hash of filenames for each template handle. var $root = ''; + var $files = array(); - // this will hash handle names to the compiled code for that handle. + // this will hash handle names to the compiled/uncompiled code for that handle. var $compiled_code = array(); - - // This will hold the uncompiled code for that handle. var $uncompiled_code = array(); + // Various counters and storage arrays + var $block_names = array(); + var $block_else_level = array(); + var $include_counter = 1; + var $block_nesting_level = 0; + /** * Constructor. Simply sets the root dir. * */ - function Template($root = '.') + function Template($template = '') { - global $board_config, $db; + global $phpbb_root_path; - $this->set_rootdir($root); - $this->db = $db; - } - - /** - * 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(); - } + $this->root = $phpbb_root_path . 'templates/' . $template; + $this->cachedir = $phpbb_root_path . 'templates/cache/' . $template . '/'; - /** - * Sets the template root directory for this Template object. - */ - function set_rootdir($dir) - { - if (!is_dir($dir)) + if ( !file_exists($this->cachedir) ) { - return false; + mkdir($this->cachedir, 0644); } - $this->root = $dir; - $this->cachedir = $dir . '/cache/'; return true; } @@ -102,6 +96,11 @@ class Template { @reset($filename_array); while ( list($handle, $filename) = @each($filename_array) ) { + if ( empty($filename) ) + { + message_die(ERROR, "Template error - Empty filename specified for $handle"); + } + $this->filename[$handle] = $filename; $this->files[$handle] = $this->make_filename($filename); } @@ -109,48 +108,92 @@ class Template { 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. + return ( substr($filename, 0, 1) != '/' ) ? $this->root . '/' . $filename : $filename; + } /** - * Load the file for the handle, compile the file, - * and run the compiled code. This will print out - * the results of executing the template. + * If not already done, load the file for the given handle and populate + * the uncompiled_code[] hash with its code. Do not compile. */ - function pparse($handle) + function loadfile($handle) { - $cache_file = $this->cachedir . $this->filename[$handle] . '.php'; + // If the file for this handle is already loaded and compiled, do nothing. + if ( !empty($this->uncompiled_code[$handle]) ) + { + return true; + } - if( @filemtime($cache_file) == @filemtime($this->files[$handle]) ) + // If we don't have a file assigned to this handle, die. + if ( !isset($this->files[$handle]) ) { - $_str = ''; - include($cache_file); + message_die("Template->loadfile(): No file specified for handle $handle"); + } - if ( $_str != '' ) - { - echo $_str; - } + if ( !($fp = @fopen($this->files[$handle], 'r')) ) + { + message_die("Template->loadfile(): Error - file $filename does not exist or is empty"); } - else + + $str = ''; + while ( !feof($fp) ) + { + $str .= fread($fp, 4096); + } + + @fclose($fp); + + $this->uncompiled_code[$handle] = trim($str); + + return true; + } + + /** + * 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(); + } + + function security() + { + 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 display($handle) + { + $_str = ''; + + if ( !($this->compile_load($_str, $handle, true)) ) { if ( !$this->loadfile($handle) ) { - die("Template->pparse(): Couldn't load template file for handle $handle"); + message_die("Template->pparse(): Couldn't load template file for handle $handle"); } // // Actually compile the code now. // $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]); - - $fp = fopen($cache_file, 'w+'); - fwrite ($fp, '<?php' . "\n" . $this->compiled_code[$handle] . "\n?" . '>'); - fclose($fp); - - touch($cache_file, filemtime($this->files[$handle])); - @chmod($cache_file, 0777); + $this->compile_write($handle, $this->compiled_code[$handle]); eval($this->compiled_code[$handle]); } - + return true; } @@ -164,34 +207,20 @@ class Template { */ function assign_var_from_handle($varname, $handle) { - $cache_file = $this->cachedir . $this->filename[$handle] . '.php'; + $_str = ''; - if( @filemtime($cache_file) == @filemtime($this->files[$handle]) ) - { - $_str = ''; - include($cache_file); - } - else + if ( !($this->compile_load($_str, $handle, false)) ) { if ( !$this->loadfile($handle) ) { - die("Template->pparse(): Couldn't load template file for handle $handle"); + message_die("Template->pparse(): Couldn't load template file for handle $handle"); } $code = $this->compile($this->uncompiled_code[$handle], true, '_str'); + $this->compile_write($handle, $code); - $fp = fopen($cache_file, 'w+'); - fwrite ($fp, '<?php' . "\n" . $code . "\n?" . '>'); - fclose($fp); - - touch($cache_file, filemtime($this->files[$handle])); - @chmod($cache_file, 0777); - - // Compile It, With The "no Echo Statements" Option On. - $_str = ''; // evaluate the variable assignment. eval($code); - } // assign the value of the generated variable to the given varname. @@ -200,6 +229,54 @@ class Template { return true; } + function assign_from_include($filename) + { + $handle = 'include_' . $this->include_counter++; + + $this->filename[$handle] = $filename; + $this->files[$handle] = $this->make_filename($filename); + $_str = ''; + + if ( !($this->compile_load($_str, $handle, false)) ) + { + if ( !$this->loadfile($handle) ) + { + message_die("Template->pparse(): Couldn't load template file for handle $handle"); + } + + $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]); + $this->compile_write($handle, $this->compiled_code[$handle]); + + eval($this->compiled_code[$handle]); + } + } + + /** + * Root-level variable assignment. Adds to current assignments, overriding + * any existing variable assignment with the same name. + */ + function assign_vars($vararray) + { + reset($vararray); + while ( list($key, $val) = each($vararray) ) + { + $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) + { + $this->_tpldata['.'][0][$varname] = $varval; + + 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 @@ -207,18 +284,20 @@ class Template { */ function assign_block_vars($blockname, $vararray) { - if (strstr($blockname, '.')) + 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. @@ -239,236 +318,317 @@ class Template { } /** - * Root-level variable assignment. Adds to current assignments, overriding - * any existing variable assignment with the same name. + * 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(). + * + * Parts of this where inspired by Smarty */ - function assign_vars($vararray) + function compile($code, $do_not_echo = false, $retvar = '') { - reset ($vararray); - while (list($key, $val) = each($vararray)) + // Pull out all block/statement level elements and seperate + // plain text + preg_match_all('#<!-- PHP -->(.*?)<!-- ENDPHP -->#s', $code, $matches); + $php_blocks = $matches[1]; + $code = preg_replace('#<!-- PHP -->(.*?)<!-- ENDPHP -->#s', '<!-- PHP -->', $code); + + preg_match_all('#<!-- (.*?) (.*?)?[ ]?-->#s', $code, $blocks); + $text_blocks = preg_split('#<!-- (.*?) (.*?)?[ ]?-->#s', $code); + for($i = 0; $i < count($text_blocks); $i++) { - $this->_tpldata['.'][0][$key] = $val; + $this->compile_var_tags($text_blocks[$i]); } - return true; + $compile_blocks = array(); + + for ($curr_tb = 0; $curr_tb < count($text_blocks); $curr_tb++) + { + switch ( $blocks[1][$curr_tb] ) + { + case 'BEGIN': + $this->block_else_level[] = false; + $compile_blocks[] = '// BEGIN ' . $blocks[2][$curr_tb] . "\n" . $this->compile_tag_block($blocks[2][$curr_tb]); + break; + case 'BEGINELSE': + $this->block_else_level[sizeof($this->block_else_level) - 1] = true; + $compile_blocks[] = "// BEGINELSE\n}} else {\n"; + break; + case 'END': + $compile_blocks[] = ( ( array_pop($this->block_else_level) ) ? "}\n" : "}}\n" ) . '// END ' . array_pop($this->block_names) . "\n"; + break; + case 'IF': + $compile_blocks[] = '// IF ' . $blocks[2][$curr_tb] . "\n" . $this->compile_tag_if($blocks[2][$curr_tb], false); + break; + case 'ELSE': + $compile_blocks[] = "// ELSE\n} else {\n"; + break; + case 'ELSEIF': + $compile_blocks[] = '// ELSEIF ' . $blocks[2][$curr_tb] . "\n" . $this->compile_tag_if($blocks[2][$curr_tb], true); + break; + case 'ENDIF': + $compile_blocks[] = "// ENDIF\n}\n"; + break; + case 'INCLUDE': + $compile_blocks[] = '// INCLUDE ' . $blocks[2][$curr_tb] . "\n" . $this->compile_tag_include($blocks[2][$curr_tb]); + break; + case 'INCLUDEPHP': + $compile_blocks[] = '// INCLUDEPHP ' . $blocks[2][$curr_tb] . "\n" . $this->compile_tag_include_php($blocks[2][$curr_tb]); + break; + case 'PHP': + $temp = ''; + list(, $temp) = each($php_blocks); + $compile_blocks[] = "// PHP START\n" . $temp . "\n// PHP END\n"; + break; + default: + $this->compile_var_tags($blocks[0][$curr_tb]); + $trim_check = trim($blocks[0][$curr_tb]); + $compile_blocks[] = ( !$do_not_echo ) ? ( ( !empty($trim_check) ) ? 'echo \'' . $blocks[0][$curr_tb] . '\';' : '' ) : ( ( !empty($trim_check) ) ? $blocks[0][$curr_tb] : '' ); + break; + } + } + + $template_php = ''; + for ($i = 0; $i < count($text_blocks); $i++) + { + $trim_check_text = trim($text_blocks[$i]); + $trim_check_block = trim($compile_blocks[$i]); + $template_php .= ( !$do_not_echo ) ? ( ( !empty($trim_check_text) ) ? 'echo \'' . $text_blocks[$i] . '\';' : '' ) . ( ( !empty($compile_blocks[$i]) ) ? $compile_blocks[$i] : '' ) : ( ( !empty($trim_check_text) ) ? $text_blocks[$i] . "\n" : '' ) . ( ( !empty($compile_blocks[$i]) ) ? $compile_blocks[$i] . "\n" : '' ); + } + + return ( !$do_not_echo ) ? $template_php : '$' . $retvar . '.= \'' . $template_php . '\''; } - /** - * Root-level variable assignment. Adds to current assignments, overriding - * any existing variable assignment with the same name. - */ - function assign_var($varname, $varval) + function compile_var_tags(&$text_blocks) { - $this->_tpldata['.'][0][$varname] = $varval; + // change template varrefs into PHP varrefs + $varrefs = array(); - return true; - } + $text_blocks = str_replace('\\', '\\\\', $text_blocks); + $text_blocks = str_replace('\'', '\\\'', $text_blocks); + // This one will handle varrefs WITH namespaces + preg_match_all('#\{(([a-z0-9\-_]+?\.)+?)([a-z0-9\-_]+?)\}#is', $text_blocks, $varrefs); - /** - * 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) != '/') + for ($j = 0; $j < sizeof($varrefs[1]); $j++) { - $filename = $this->root . '/' . $filename; + $namespace = $varrefs[1][$j]; + $varname = $varrefs[3][$j]; + $new = $this->generate_block_varref($namespace, $varname); + + $text_blocks = str_replace($varrefs[0][$j], $new, $text_blocks); } - if (!file_exists($filename)) + // This will handle the remaining root-level varrefs + $text_blocks = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $text_blocks); + + return; + } + + function compile_tag_block($tag_args) + { + $tag_template_php = ''; + array_push($this->block_names, $tag_args); + + if ( sizeof($this->block_names) < 2 ) { - die("Template->make_filename(): Error - file $filename does not exist"); + // Block is not nested. + $tag_template_php = '$_' . $tag_args . '_count = ( isset($this->_tpldata[\'' . $tag_args . '.\']) ) ? sizeof($this->_tpldata[\'' . $tag_args . '.\']) : 0;' . "\n"; + $tag_template_php .= 'if ( $_' . $tag_args . '_count ) {' . "\n"; + $tag_template_php .= 'for ($_' . $tag_args . '_i = 0; $_' . $tag_args . '_i < $_' . $tag_args . '_count; $_' . $tag_args . '_i++)'; } + else + { + // This block is nested. - return $filename; - } + // Generate a namespace string for this block. + $namespace = implode('.', $this->block_names); + // 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); - /** - * 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) + // Create the for loop code to iterate over this block. + $tag_template_php = '$_' . $tag_args . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;' . "\n"; + $tag_template_php .= 'if ( $_' . $tag_args . '_count ) {' . "\n"; + $tag_template_php .= 'for ($_' . $tag_args . '_i = 0; $_' . $tag_args . '_i < $_' . $tag_args . '_count; $_' . $tag_args . '_i++)'; + } + + $tag_template_php .= "\n{\n"; + + return $tag_template_php; + } + + // + // Compile IF tags - much of this is from Smarty with + // some adaptions for our block level methods + // + function compile_tag_if($tag_args, $elseif) { - switch ( $board_config['template_cache'] ) + /* Tokenize args for 'if' tag. */ + preg_match_all('/(?: + "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | + [(),] | + [^\s(),]+)/x', $tag_args, $match); + + $tokens = $match[0]; + $is_arg_stack = array(); + + for ($i = 0; $i < count($tokens); $i++) { - case 1: - break; - case 2: - break; - default: - // If the file for this handle is already loaded and compiled, do nothing. - if ( !empty($this->uncompiled_code[$handle]) ) - { - return true; - } + $token = &$tokens[$i]; - // 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"); - } + switch ( $token ) + { + case 'eq': + $token = '=='; + break; - $filename = $this->files[$handle]; + case 'ne': + case 'neq': + $token = '!='; + break; - $str = implode('', @file($filename)); - if (empty($str)) - { - die("Template->loadfile(): File $filename for handle $handle is empty"); - } + case 'lt': + $token = '<'; + break; - $this->uncompiled_code[$handle] = $str; - break; - } + case 'le': + case 'lte': + $token = '<='; + break; - return true; - } + case 'gt': + $token = '>'; + break; + case 'ge': + case 'gte': + $token = '>='; + break; + case 'and': + $token = '&&'; + break; - /** - * 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(). - */ - function compile($code, $do_not_echo = false, $retvar = '') - { - // replace \ with \\ and then ' with \'. - $code = str_replace('\\', '\\\\', $code); - $code = str_replace('\'', '\\\'', $code); + case 'or': + $token = '||'; + break; - // change template varrefs into PHP varrefs + case 'not': + $token = '!'; + break; - // This one will handle varrefs WITH namespaces - $varrefs = array(); - preg_match_all('#\{(([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]; - $new = $this->generate_block_varref($namespace, $varname); + case 'mod': + $token = '%'; + break; - $code = str_replace($varrefs[0][$i], $new, $code); - } + case '(': + array_push($is_arg_stack, $i); + break; - // 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); + case 'is': + $is_arg_start = ( $tokens[$i-1] == ')' ) ? array_pop($is_arg_stack) : $i-1; + $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); - // Break it up into lines. - $code_lines = explode("\n", $code); + $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); - $block_nesting_level = 0; - $block_names = array(); - $block_names[0] = '.'; + array_splice($tokens, $is_arg_start, count($tokens), $new_tokens); - // Second: prepend echo ', append ' . "\n"; to each line. - $line_count = sizeof($code_lines); - for ($i = 0; $i < $line_count; $i++) - { - $code_lines[$i] = chop($code_lines[$i]); - if (preg_match('#<!-- BEGIN (.*?) -->#', $code_lines[$i], $m)) - { - $n[0] = $m[0]; - $n[1] = $m[1]; + $i = $is_arg_start; - // Added: dougk_ff7-Keeps templates from bombing if begin is on the same line as end.. I think. :) - if ( preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $n) ) - { - $block_nesting_level++; - $block_names[$block_nesting_level] = $m[1]; - if ($block_nesting_level < 2) + default: + if ( preg_match('#^(([a-z0-9\-_]+?\.)+?)?([A-Z]+[A-Z0-9\-_]+?)$#s', $token, $varrefs) ) { - // Block is not nested. - $code_lines[$i] = '$_' . $a[1] . '_count = ( isset($this->_tpldata[\'' . $n[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $n[1] . '.\']) : 0;'; - $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; - $code_lines[$i] .= "\n" . '{'; - } - 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); - // Create the for loop code to iterate over this block. - $code_lines[$i] = '$_' . $a[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; - $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; - $code_lines[$i] .= "\n" . '{'; + $token = ( !empty($varrefs[1]) ) ? $this->generate_block_data_ref(substr($varrefs[1], 0, strlen($varrefs[1]) - 1), true) . '[\'' . $varrefs[3] . '\']' : '$this->_tpldata[\'.\'][0][\'' . $varrefs[3] . '\']'; } + break; + } + } + + return ( ( $elseif ) ? '} elseif (' : 'if (' ) . ( implode(' ', $tokens) . ') { ' . "\n" ); + } + + function compile_tag_include($tag_args) + { + return "\$this->assign_from_include('$tag_args');\n"; + } + + function compile_tag_include_php($tag_args) + { + return "include('" . $this->root . '/' . $tag_args . "');\n"; + } - // We have the end of a block. - unset($block_names[$block_nesting_level]); - $block_nesting_level--; - $code_lines[$i] .= '} // END ' . $n[1]; - $m[0] = $n[0]; - $m[1] = $n[1]; + // + // This is from Smarty + // + function _parse_is_expr($is_arg, $tokens) + { + $expr_end = 0; + $negate_expr = false; + + if ( ($first_token = array_shift($tokens)) == 'not' ) + { + $negate_expr = true; + $expr_type = array_shift($tokens); + } + else + { + $expr_type = $first_token; + } + + switch ( $expr_type ) + { + case 'even': + if ( @$tokens[$expr_end] == 'by' ) + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!(($is_arg / $expr_arg) % $expr_arg)"; } else { - // We have the start of a block. - $block_nesting_level++; - $block_names[$block_nesting_level] = $m[1]; - if ($block_nesting_level < 2) - { - // Block is not nested. - $code_lines[$i] = '$_' . $m[1] . '_count = ( isset($this->_tpldata[\'' . $m[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $m[1] . '.\']) : 0;'; - $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; - $code_lines[$i] .= "\n" . '{'; - } - 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); - // Create the for loop code to iterate over this block. - $code_lines[$i] = '$_' . $m[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; - $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; - $code_lines[$i] .= "\n" . '{'; - } + $expr = "!($is_arg % 2)"; } - } - else if (preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $m)) - { - // We have the end of a block. - unset($block_names[$block_nesting_level]); - $block_nesting_level--; - $code_lines[$i] = '} // END ' . $m[1]; - } - else - { - // We have an ordinary line of code. - if (!$do_not_echo) + break; + + case 'odd': + if ( @$tokens[$expr_end] == 'by' ) { - $code_lines[$i] = 'echo \'' . $code_lines[$i] . '\' . "\\n";'; + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "(($is_arg / $expr_arg) % $expr_arg)"; } else { - $code_lines[$i] = '$' . $retvar . '.= \'' . $code_lines[$i] . '\' . "\\n";'; + $expr = "($is_arg % 2)"; } - } + break; + + case 'div': + if ( @$tokens[$expr_end] == 'by' ) + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!($is_arg % $expr_arg)"; + } + break; + + default: + break; } - // Bring it back into a single string of lines of code. - $code = implode("\n", $code_lines); - return $code; - } + if ( $negate_expr ) + { + $expr = "!($expr)"; + } + + array_splice($tokens, 0, $expr_end, $expr); + return $tokens; + } /** * Generates a reference to the given variable inside the given (possibly nested) @@ -495,7 +655,6 @@ class Template { } - /** * Generates a reference to the array of data values for the given * (possibly nested) block namespace. This is a string of the form: @@ -510,15 +669,18 @@ class Template { $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) + if ( $include_last_iterator ) { $varref .= '[$_' . $blocks[$blockcount] . '_i]'; } @@ -526,6 +688,158 @@ class Template { return $varref; } + // + // Compilation stuff + // + function compile_load(&$_str, &$handle, $do_echo) + { + global $phpEx; + + $filename = $this->cachedir . $this->filename[$handle] . '.' . $phpEx; + + // + // Recompile page if the original template is newer, otherwise load the compiled version + // + if ( file_exists($filename) && @filemtime($filename) >= @filemtime($this->files[$handle]) ) + { + $_str = ''; + include($filename); + + if ( $do_echo && $_str != '' ) + { + echo $_str; + } + + return true; + } + return false; + } + + function compile_write(&$handle, $data) + { + global $phpEx; + + $filename = $this->cachedir . $this->filename[$handle] . '.' . $phpEx; + + $data = '<?php' . "\nif ( \$this->security() ) {\n" . $data . "\n}\n?".">"; + + $fp = fopen($filename, 'w+'); + fwrite ($fp, $data); + fclose($fp); + + touch($filename, filemtime($this->files[$handle])); + @chmod($filename, 0644); + + return; + } + + function compile_cache_clear($mode, &$dir, &$template) + { + $template_list = array(); + if ( $mode == 'all' ) + { + $dp = opendir($dir . 'cache'); + while ( $file = readdir($dp) ) array_push($template_list, $file); + closedir($dp); + } + else + { + array_push($template_list, $template); + } + + foreach ( $template_list as $template ) + { + $dp = opendir($dir . 'cache/' . $template); + while ( $file = readdir($dp) ) + { + unlink($dir . 'cache/' . $template . '/' . $file); + } + closedir($dp); + } + + return; + } + + function compile_cache_show(&$template) + { + global $phpbb_root_path; + + $template_cache = array(); + + $dp = opendir($phpbb_root_path . 'templates/cache/' . $template . '/'); + while ( $file = readdir($dp) ) + { + if ( strstr($file, '.html') && is_file($phpbb_root_path . 'templates/cache/' . $template . '/' . $file) ) + { + array_push($template_cache, $file); + } + } + closedir($dp); + + return; + } + + function decompile(&$_str, $savefile = false) + { + + $match_tags = array( + "#(<\?php\nif \( .?this\->security\(\) \) \{\n)|(\n\}\n\?".">)#", + "#echo '(.*?)';#s", + "#\/\/ (INCLUDEPHP .*?)\n.?this\->assign_from_include_php\('.*?'\);\n#s", + "#\/\/ (INCLUDE .*?)\n.?this\->assign_from_include\('.*?'\);\n#s", + "#\/\/ (IF .*?)\nif \(.*?\) \{[ ]?\n#", + "#\/\/ (ELSEIF .*?)\n\} elseif \(.*?\) \{[ ]?\n#", + "#\/\/ (ELSE)\n\} else \{\n#", + "#\/\/ (ENDIF)\n}\n#", + "#\/\/ (BEGIN .*?)\n.?_.*? = \(.*?\) : 0;\nif \(.*?\) \{\nfor \(.*?\)\n\{\n#", + "#\}\}?\n\/\/ (END .*?)\n#", + "#\/\/ (BEGINELSE)\n\}\} else \{\n#", + "#' \. \( \( isset\(.?this\->_tpldata\['\.'\]\[0\]\['([A-Z0-9_]+?)'\]\) \) \? .?this\->_tpldata\['\.'\]\[0\]\['([A-Z0-9_]+?)'\] : '' \) \. '#s", + "#' \. \( \( isset\(.?this\->_tpldata\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([A-Z0-9_]+)'\]\) \) \? .?this\->_tpldata\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[A-Z0-9_]+'\] : '' \) \. '#s", + "#' \. \( \( isset\(.?this\->_tpldata\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([A-Z0-9_]+)'\]\) \) \? .?this\->_tpldata\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[A-Z0-9_]+'\] : '' \) \. '#s", + "#' \. \( \( isset\(.?this\->_tpldata\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([a-z0-9_]+?\.)'\]\[.?_[a-z0-9_]+?\]\['([A-Z0-9_]+)'\]\) \) \? .?this\->_tpldata\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[a-z0-9_]+?\.'\]\[.?_[a-z0-9_]+?\]\['[A-Z0-9_]+'\] : '' \) \. '#s" + ); + + $replace_tags = array( + '', + "\\1", + "\\1", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "<!-- \\1 -->", + "{\\1}", + "{\\1\\2}", + "{\\1\\2\\3}", + "{\\1\\2\\3\\4}" + ); + + preg_match_all('#\/\/ PHP START\n(.*?)\n\/\/ PHP END\n#s', $_str, $matches); + $php_blocks = $matches[1]; + $_str = preg_replace('#\/\/ PHP START\n(.*?)\/\/ PHP END\n#s', '<!-- PHP -->', $_str); + + $_str = preg_replace($match_tags, $replace_tags, $_str); + $text_blocks = preg_split('#<!-- PHP -->#s', $_str); + + $_str = ''; + for ($i = 0; $i < count($text_blocks); $i++) + { + $_str .= $text_blocks[$i] . ( ( !empty($php_blocks[$i]) ) ? '<!-- PHP -->' . $php_blocks[$i] . '<!-- ENDPHP -->' : '' ); + } + + $tmpfile = ''; + if ( $savefile ) + { + $tmpfile = tmpfile(); + fwrite($tmpfile, $_str); + } + + return $tmpfile; + } } ?>
\ No newline at end of file |