execute() scope!) * "#" - constant, {#CON} is eq to CON * */ if (!defined('BB_ROOT')) { die(basename(__FILE__)); } define('XS_SEPARATOR', '.'); define('XS_USE_ISSET', '1'); // cache filenames prefix define('XS_TPL_PREFIX', 'tpl_'); define('XS_TPL_PREFIX2', 'tpl2_'); // internal xs mod definitions. do not edit. define('XS_TAG_NONE', 0); define('XS_TAG_PHP', 1); define('XS_TAG_BEGIN', 2); define('XS_TAG_END', 3); define('XS_TAG_INCLUDE', 4); define('XS_TAG_IF', 5); define('XS_TAG_ELSE', 6); define('XS_TAG_ELSEIF', 7); define('XS_TAG_ENDIF', 8); define('XS_TAG_BEGINELSE', 11); 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 // if it's a root-level variable, it'll be like this: // $this->vars[varname] == value or $this->_tpldata['.'][0][varname] == value // array "vars" is added for easier access to data var $_tpldata = array('.' => array(0 => array())); var $vars; // Hash of filenames for each template handle. var $files = array(); var $files_cache = array(); // array of cache files that exists var $files_cache2 = array(); // array of cache files (exists or not exists) // Root template directory. var $root = ''; // Cache directory var $cachedir = CACHE_DIR; // Template root directory var $tpldir = ''; // Default template directory. // If file for default template isn't found file from this template is used. var $tpldef = 'default'; // 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(); // Cache settings var $use_cache = 1; var $cache_writable = 1; // Auto-compile setting var $auto_compile = 1; // Current template name var $tpl = ''; var $cur_tpl = ''; // List of replacements. tpl files in this list will be replaced with other tpl files // according to configuration in xs.cfg var $replace = array(); // counter for include var $include_count = 0; // extension tpl-cache files var $cached_tpl_ext = 'php'; // eXtreme Styles variables var $xs_started = 0; var $xs_version = 8; // number version. internal. do not change. var $xs_versiontxt = '2.3.1'; // text version // These handles will be parsed if pparse() is executed. // Can be used to automatically include header/footer if there is any content. var $preparse = ''; var $postparse = ''; // subtemplates mod detection var $subtemplates = false; // style configuration var $style_config = array(); var $lang = array(); /** * Constructor. Installs XS mod on first run or updates it and sets the root dir. */ function Template($root = '.') { global $bb_cfg, $lang; // setting pointer "vars" $this->vars = &$this->_tpldata['.'][0]; // load configuration $this->tpldir = TEMPLATES_DIR; $this->root = $root; $this->tpl = basename($root); $this->lang =& $lang; $this->use_cache = $bb_cfg['xs_use_cache']; // Check template exists if (!file_exists($this->root) || !is_dir($this->root)) { die("Theme ({$this->tpl}) directory not found"); } } /** * 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('.' => array(0 => array())); $this->vars = &$this->_tpldata['.'][0]; $this->xs_started = 0; } /** * 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, $xs_include = false) { // Check replacements list if(!$xs_include && isset($this->replace[$filename])) { $filename = $this->replace[$filename]; } // Check if it's an absolute or relative path. if ((substr($filename, 0, 1) !== '/') && (substr($filename, 1, 1) !== ':')) { return $this->root . '/' . $filename; } else { return $filename; } } /** * Converts template filename to cache filename. * Returns empty string if non-cachable (for tpl files outside of root dir). * $filename should be absolute filename */ function make_filename_cache ($filename) { $filename = clean_filename(str_replace(TEMPLATES_DIR, '', $filename)); return $this->cachedir . XS_TPL_PREFIX . $filename .'.'. $this->cached_tpl_ext; } /** * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. */ function set_filenames ($filenames) { foreach ($filenames as $handle => $filename) { $this->set_filename($handle, $filename); } } /** * Assigns template filename for handle. */ function set_filename($handle, $filename, $xs_include = false, $quiet = false) { $can_cache = $this->use_cache; $this->files[$handle] = $this->make_filename($filename, $xs_include); $this->files_cache[$handle] = ''; $this->files_cache2[$handle] = ''; // checking if we have valid filename if(!$this->files[$handle]) { if($xs_include || $quiet) { return false; } else { die("Template->make_filename(): Error - invalid template $filename"); } } // creating cache filename if($can_cache) { $this->files_cache2[$handle] = $this->make_filename_cache($this->files[$handle]); if(@file_exists($this->files_cache2[$handle])) { $this->files_cache[$handle] = $this->files_cache2[$handle]; } } // checking if tpl and/or php file exists if(empty($this->files_cache[$handle]) && !@file_exists($this->files[$handle])) { if($quiet) { return false; } die('Template->make_filename(): Error - template file not found:

' . hide_bb_path($this->files[$handle])); } // checking if we should recompile cache if(!empty($this->files_cache[$handle])) { $cache_time = @filemtime($this->files_cache[$handle]); if(@filemtime($this->files[$handle]) > $cache_time) { // file was changed. don't use cache file (will be recompled if configuration allowes it) $this->files_cache[$handle] = ''; } } return true; } /** * includes file or executes code */ function execute($filename, $code, $handle) { $this->cur_tpl = $filename; global $lang, $source_lang, $bb_cfg, $user, $tr_cfg; $L =& $lang; $V =& $this->vars; $SL =& $source_lang; if ($filename) { include($filename); } else { eval($code); } } /** * 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) { // parsing header if there is one if($this->preparse || $this->postparse) { $preparse = $this->preparse; $postparse = $this->postparse; $this->preparse = ''; $this->postparse = ''; if($preparse) { $this->pparse($preparse); } if($postparse) { $str = $handle; $handle = $postparse; $this->pparse($str); } } // checking if handle exists if (empty($this->files[$handle]) && empty($this->files_cache[$handle])) { die("Template->loadfile(): No files found for handle $handle"); } $this->xs_startup(); $force_recompile = empty($this->uncompiled_code[$handle]) ? false : true; // checking if php file exists. if (!empty($this->files_cache[$handle]) && !$force_recompile) { // php file exists - running it instead of tpl $this->execute($this->files_cache[$handle], '', $handle); return true; } if (!$this->loadfile($handle)) { die("Template->pparse(): Could not load template file for handle $handle"); } // actually compile the template now. if (empty($this->compiled_code[$handle])) { // Actually compile the code now. if(!empty($this->files_cache2[$handle]) && empty($this->files_cache[$handle]) && !$force_recompile) { $this->compiled_code[$handle] = $this->compile2($this->uncompiled_code[$handle], $handle, $this->files_cache2[$handle]); } else { $this->compiled_code[$handle] = $this->compile2($this->uncompiled_code[$handle], '', ''); } } // Run the compiled code. if (empty($this->files_cache[$handle]) || $force_recompile) { $this->execute('', $this->compiled_code[$handle], $handle); } else { $this->execute($this->files_cache[$handle], '', $handle); } return true; } /** * Precompile file */ function precompile($template, $filename) { global $precompile_num; if(empty($precompile_num)) { $precompile_num = 0; } $precompile_num ++; $handle = 'precompile_' . $precompile_num; // save old configuration $root = $this->root; $tpl_name = $this->tpl; $old_config = $this->use_cache; $old_autosave = $this->auto_compile; // set temporary configuration $this->root = $this->tpldir . $template; $this->tpl = $template; $this->use_cache = 1; $this->auto_compile = 1; // set filename $res = $this->set_filename($handle, $filename, true, true); if(!$res || !$this->files_cache2[$handle]) { $this->root = $root; $this->tpl = $tpl_name; $this->use_cache = $old_config; $this->auto_compile = $old_autosave; return false; } $this->files_cache[$handle] = ''; // load template $res = $this->loadfile($handle); if(!$res || empty($this->uncompiled_code[$handle])) { $this->root = $root; $this->tpl = $tpl_name; $this->use_cache = $old_config; $this->auto_compile = $old_autosave; return false; } // compile the code $this->compile2($this->uncompiled_code[$handle], $handle, $this->files_cache2[$handle]); // restore confirugation $this->root = $root; $this->tpl = $tpl_name; $this->use_cache = $old_config; $this->auto_compile = $old_autosave; return true; } /** * 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) { ob_start(); $res = $this->pparse($handle); $this->vars[$varname] = ob_get_contents(); ob_end_clean(); return $res; } /** * 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 (strstr($blockname, '.')) { // Nested block. $blocks = explode('.', $blockname); $blockcount = sizeof($blocks) - 1; $str = &$this->_tpldata; for($i = 0; $i < $blockcount; $i++) { $str = &$str[$blocks[$i].'.']; $str = &$str[sizeof($str)-1]; } // 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; } else { // Top-level block. // Add a new iteration to this block with the variable assignments // we were given. $this->_tpldata[$blockname.'.'][] = $vararray; } return true; } /** * Root-level variable assignment. Adds to current assignments, overriding * any existing variable assignment with the same name. */ function assign_vars ($vararray) { foreach ($vararray as $key => $val) { $this->vars[$key] = $val; } } /** * Root-level variable assignment. Adds to current assignments, overriding * any existing variable assignment with the same name. */ function assign_var ($varname, $varval = true) { $this->vars[$varname] = $varval; } /** * TODO: Add type check [??] * Root-level. Adds to current assignments, appends * to any existing variable assignment with the same name. */ function append_vars ($vararray) { foreach ($vararray as $key => $val) { $this->vars[$key] = !isset($this->vars[$key]) ? $val : $this->vars[$key] . $val; } } /** * 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 cached file exists do nothing - it will be included via include() if(!empty($this->files_cache[$handle])) { return true; } // If the file for this handle is already loaded and compiled, do nothing. if (!empty($this->uncompiled_code[$handle])) { return true; } // If we don't have a file assigned to this handle, die. if (empty($this->files[$handle])) { die("Template->loadfile(): No file specified for handle $handle"); } $filename = $this->files[$handle]; if (($str = @file_get_contents($filename)) === false) { die("Template->loadfile(): File $filename for handle $handle is empty"); } $this->uncompiled_code[$handle] = $str; return true; } /** * 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) { // 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. // Append the variable reference. $varref .= "['$varname']"; $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) { // Get an array of the blocks involved. $blocks = explode('.', $blockname); $blockcount = sizeof($blocks) - 1; if($include_last_iterator) { return '$'. $blocks[$blockcount]. '_item'; } else { return '$'. $blocks[$blockcount-1]. '_item[\''. $blocks[$blockcount]. '.\']'; } } function compile_code($filename, $code) { // $filename - file to load code from. used if $code is empty // $code - tpl code // load code from file if (!$code && !empty($filename)) { $code = file_get_contents($filename); } // Replace tags $search = array('', ''); $replace = array('<'.'?php ', ' ?'.'>'); $code = str_replace($search, $replace, $code); // Break it up into lines and put " -->" back. $code_lines = explode(' -->', $code); $count = count($code_lines); for ($i = 0; $i < ($count - 1); $i++) { $code_lines[$i] .= ' -->'; } $block_nesting_level = 0; $block_names = array(); $block_names[0] = "."; $block_items = array(); $count_if = 0; // prepare array for compiled code $compiled = array(); // array of switches $sw = array(); // replace all short php tags $new_code = array(); $line_count = count($code_lines); for($i=0; $i<$line_count; $i++) { $line = $code_lines[$i]; $pos = strpos($line, ''; $code_lines[$i] = substr($line, $pos + 2); $i --; } $code_lines = $new_code; // main loop $line_count = count($code_lines); for($i=0; $i<$line_count; $i++) { $line = $code_lines[$i]; // reset keyword type $keyword_type = XS_TAG_NONE; // check if we have valid keyword in current line $pos1 = strpos($line, '', $pos1); if($pos2 !== false) { // find end of keyword in comment $pos3 = strpos($line, ' ', $pos1 + 5); if($pos3 !== false && $pos3 <= $pos2) { $keyword = substr($line, $pos1 + 5, $pos3 - $pos1 - 5); // check keyword against list of supported keywords. case-sensitive if($keyword === 'BEGIN') { $keyword_type = XS_TAG_BEGIN; } elseif($keyword === 'END') { $keyword_type = XS_TAG_END; } elseif($keyword === 'INCLUDE') { $keyword_type = XS_TAG_INCLUDE; } elseif($keyword === 'IF') { $keyword_type = XS_TAG_IF; } elseif($keyword === 'ELSE') { $keyword_type = XS_TAG_ELSE; } elseif($keyword === 'ELSEIF') { $keyword_type = XS_TAG_ELSEIF; } elseif($keyword === 'ENDIF') { $keyword_type = XS_TAG_ENDIF; } elseif($keyword === 'BEGINELSE') { $keyword_type = XS_TAG_BEGINELSE; } } } if(!$keyword_type) { // not valid keyword. process the rest of line $compiled[] = $this->_compile_text(substr($line, 0, $pos1 + 4)); $code_lines[$i] = substr($line, $pos1 + 4); $i --; continue; } // remove code before keyword if($pos1 > 0) { $compiled[] = $this->_compile_text(substr($line, 0, $pos1)); } // remove keyword $keyword_str = substr($line, $pos1, $pos2 - $pos1 + 4); $params_str = $pos2 == $pos3 ? '' : substr($line, $pos3 + 1, $pos2 - $pos3 - 1); $code_lines[$i] = substr($line, $pos2 + 4); $i--; // Check keywords /* * */ if($keyword_type == XS_TAG_BEGIN) { $params = explode(' ', $params_str); $num_params = count($params); // get variable name if($num_params == 1) { $var = $params[0]; } elseif($num_params == 2) { if($params[0] === '') { $var = $params[1]; } elseif($params[1] === '') { $var = $params[0]; } else { // invalid tag $compiled[] = $keyword_str; continue; } } else { // invalid tag $compiled[] = $keyword_str; continue; } // adding code $block_nesting_level++; $block_names[$block_nesting_level] = $var; if(isset($block_items[$var])) { $block_items[$var] ++; } else { $block_items[$var] = 1; } if ($block_nesting_level < 2) { // Block is not nested. $line = '<'."?php\n\n"; $line .= '$'. $var. '_count = ( isset($this->_tpldata[\''. $var. '.\']) ) ? sizeof($this->_tpldata[\''. $var. '.\']) : 0;'; $line .= "\n" . 'for ($'. $var. '_i = 0; $'. $var. '_i < $'. $var. '_count; $'. $var. '_i++)'; $line .= "\n". '{'. "\n"; $line .= ' $'. $var. '_item = &$this->_tpldata[\''. $var. '.\'][$'. $var. '_i];'."\n"; $line .= " \${$var}_item['S_ROW_COUNT'] = \${$var}_i;\n"; $line .= " \${$var}_item['S_NUM_ROWS'] = \${$var}_count;\n"; $line .= "\n?".">"; } else { // This block is nested. // Generate a namespace string for this block. $namespace = join('.', $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. $line = '<'."?php\n\n"; $line .= '$'. $var. '_count = ( isset('. $varref. ') ) ? sizeof('. $varref. ') : 0;'; $line .= "\n". 'for ($'. $var. '_i = 0; $'. $var. '_i < $'. $var. '_count; $'. $var. '_i++)'; $line .= "\n". '{'. "\n"; $line .= ' $'. $var. '_item = &'. $varref. '[$'. $var. '_i];'."\n"; $line .= " \${$var}_item['S_ROW_COUNT'] = \${$var}_i;\n"; $line .= " \${$var}_item['S_NUM_ROWS'] = \${$var}_count;\n"; $line .= "\n?".">"; } $compiled[] = $line; continue; } /* * */ if($keyword_type == XS_TAG_END) { $params = explode(' ', $params_str); $num_params = count($params); if($num_params == 1) { $var = $params[0]; } elseif($num_params == 2 && $params[0] === '') { $var = $params[1]; } elseif($num_params == 2 && $params[1] === '') { $var = $params[0]; } else { $compiled[] = $keyword_str; continue; } // We have the end of a block. $line = '<'."?php\n\n"; $line .= '} // END ' . $var . "\n\n"; $line .= 'if(isset($' . $var . '_item)) { unset($' . $var . '_item); } '; $line .= "\n\n?".">"; if(isset($block_items[$var])) { $block_items[$var] --; } else { $block_items[$var] = -1; } unset($block_names[$block_nesting_level]); $block_nesting_level--; $compiled[] = $line; continue; } /* * */ if($keyword_type == XS_TAG_BEGINELSE) { if($block_nesting_level) { $var = $block_names[$block_nesting_level]; $compiled[] = '<' . '?php } if(!$' . $var . '_count) { ?' . '>'; } else { $compiled[] = $keyword_str; continue; } } /* * */ if($keyword_type == XS_TAG_INCLUDE) { $params = explode(' ', $params_str); $num_params = count($params); if($num_params != 1) { $compiled[] = $keyword_str; continue; } $line = '<'.'?php '; $filehash = md5($params_str . $this->include_count . TIMENOW); $line .= ' $this->set_filename(\'xs_include_' . $filehash . '\', \'' . $params_str .'\', true); '; $line .= ' $this->pparse(\'xs_include_' . $filehash . '\'); '; $line .= ' ?'.'>'; $this->include_count ++; $compiled[] = $line; continue; } /* * */ if($keyword_type == XS_TAG_IF || $keyword_type == XS_TAG_ELSEIF) { if(!$count_if) { $keyword_type = XS_TAG_IF; } $str = $this->compile_tag_if($params_str, $keyword_type == XS_TAG_IF ? false : true); if($str) { $compiled[] = ''; if($keyword_type == XS_TAG_IF) { $count_if ++; } } else { $compiled[] = $keyword_str; } continue; } /* * */ if($keyword_type == XS_TAG_ELSE && $count_if > 0) { $compiled[] = ''; continue; } /* * */ if($keyword_type == XS_TAG_ENDIF && $count_if > 0) { $compiled[] = ''; $count_if --; continue; } } // bring it back into a single string. $code_header = ''; $code_footer = ''; return $code_header . join('', $compiled) . $code_footer; } /* * Compile code between tags */ function _compile_text($code) { if(strlen($code) < 3) { return $code; } // change template varrefs into PHP varrefs // 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]); $search = array(); $replace = array(); for ($i = 0; $i < $varcount; $i++) { $namespace = $varrefs[1][$i]; $varname = $varrefs[3][$i]; $new = $this->generate_block_varref($namespace, $varname); $search[] = $varrefs[0][$i]; $replace[] = $new; } if(count($search) > 0) { $code = str_replace($search, $replace, $code); } // This will handle the remaining root-level varrefs $code = preg_replace('#\{(L_([a-z0-9\-_]+?))\}#i', '', $code); $code = preg_replace('#\{(\$[a-z_][a-z0-9_$\->\'\"\.\[\]]*?)\}#i', '', $code); $code = preg_replace('#\{(\#([a-z_][a-z0-9_]*?))\}#i', '', $code); $code = preg_replace('#\{([a-z0-9\-_]+?)\}#i', '', $code); return $code; } // // Compile IF tags - much of this is from Smarty with // some adaptions for our block level methods // function compile_tag_if($tag_args, $elseif) { /* Tokenize args for 'if' tag. */ preg_match_all('/(?: "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | [(),] | [^\s(),]+)/x', $tag_args, $match); $tokens = $match[0]; $tokens_cnt = count($tokens); $is_arg_stack = array(); for ($i = 0; $i < $tokens_cnt; $i++) { $token = &$tokens[$i]; switch ($token) { case 'eq': $token = '=='; break; case 'ne': case 'neq': $token = '!='; break; case 'lt': $token = '<'; break; case 'le': case 'lte': $token = '<='; break; case 'gt': $token = '>'; break; case 'ge': case 'gte': $token = '>='; break; case 'and': $token = '&&'; break; case 'or': $token = '||'; break; case 'not': $token = '!'; break; case 'mod': $token = '%'; break; case '(': array_push($is_arg_stack, $i); break; case 'is': $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1; $is_arg = join(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); array_splice($tokens, $is_arg_start, count($tokens), $new_tokens); $i = $is_arg_start; break; default: $pattern = '@^ ( # 1 ([a-z0-9\-_]+?\.)+? # 2 block tpl vars (VAR1.VAR2.) but without last )? ( # 3 ([a-z_][a-z0-9\-_]*)? # 4 single tpl var or last block var (last in block) (\$[a-z_][a-z0-9_$\->\'\"\.\[\]]*)? # 5 php var (\#([a-z_][a-z0-9_]*))? # 7 php const ) $@ix'; if (preg_match($pattern, $token, $m)) { if (!empty($m[1])) { $token = $this->generate_block_data_ref(substr($m[1], 0, -1), true) . "['{$m[4]}']"; } else if (!empty($m[4])) { $token = ($tokens_cnt == 1) ? "!empty(\$V['{$m[4]}'])" : "\$V['{$m[4]}']"; } else if (!empty($m[5])) { $token = ($tokens_cnt == 1) ? "!empty({$m[5]})" : "{$m[5]}"; } else if (!empty($m[7])) { $token = ($tokens_cnt == 1) ? "defined('{$m[7]}') && {$m[7]}" : "{$m[7]}"; } } break; } } if ($elseif) { $code = '} elseif ('. join(' ', $tokens) .') {'; } else { $code = 'if ('. join(' ', $tokens) .') {'; } return $code; } // 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 { $expr = "!($is_arg % 2)"; } break; case 'odd': if (@$tokens[$expr_end] == 'by') { $expr_end++; $expr_arg = $tokens[$expr_end++]; $expr = "(($is_arg / $expr_arg) % $expr_arg)"; } else { $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; } if ($negate_expr) { $expr = "!($expr)"; } array_splice($tokens, 0, $expr_end, $expr); return $tokens; } /** * Compiles code and writes to cache if needed */ function compile2($code, $handle, $cache_file) { $code = $this->compile_code('', $code, XS_USE_ISSET); if ($cache_file && !empty($this->use_cache) && !empty($this->auto_compile)) { $res = $this->write_cache($cache_file, $code); if ($handle && $res) { $this->files_cache[$handle] = $cache_file; } } $code = '?'.'>'.$code.'<'."?php\n"; return $code; } /** * 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(). * This function isn't used and kept only for compatibility with original template.php */ function compile($code, $do_not_echo = false, $retvar = '') { $code = ' ?'.'>' . $this->compile_code('', $code, true) . '<'."?php \n"; if ($do_not_echo) { $code = "ob_start();\n". $code. "\n\${$retvar} = ob_get_contents();\nob_end_clean();\n"; } return $code; } /** * Write cache to disk */ function write_cache($filename, $code) { file_write($code, $filename, false, true, true); } function xs_startup() { global $bb_cfg; if (empty($this->xs_started)) { // adding predefined variables $this->xs_started = 1; // adding language variable (eg: "english" or "german") // can be used to make truly multi-lingual templates $this->vars['LANG'] = isset($this->vars['LANG']) ? $this->vars['LANG'] : $bb_cfg['default_lang']; // adding current template $tpl = $this->root . '/'; if (substr($tpl, 0, 2) === './') { $tpl = substr($tpl, 2, strlen($tpl)); } $this->vars['TEMPLATE'] = isset($this->vars['TEMPLATE']) ? $this->vars['TEMPLATE'] : $tpl; $this->vars['TEMPLATE_NAME'] = isset($this->vars['TEMPLATE_NAME']) ? $this->vars['TEMPLATE_NAME'] : $this->tpl; } } function lang_error($var) { trigger_error(basename($this->cur_tpl) ." : undefined language variable {L_{$var}}", E_USER_WARNING); return "Undefined: {L_{$var}}"; } }