From 2d11e1c095b7cf6197b7ce21bde806ad2cc178d8 Mon Sep 17 00:00:00 2001 From: "Marek A. Ruszczynski" Date: Mon, 7 Mar 2011 16:59:41 +0100 Subject: [feature/template-engine] Improved template engine. PHPBB3-9726 --- phpBB/includes/functions_template.php | 812 ---------------------------- phpBB/includes/template.php | 287 +++++++--- phpBB/includes/template_compile.php | 982 ++++++++++++++++++++++++++++++++++ 3 files changed, 1204 insertions(+), 877 deletions(-) delete mode 100644 phpBB/includes/functions_template.php create mode 100644 phpBB/includes/template_compile.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/functions_template.php b/phpBB/includes/functions_template.php deleted file mode 100644 index 8c58c33b0d..0000000000 --- a/phpBB/includes/functions_template.php +++ /dev/null @@ -1,812 +0,0 @@ -template = &$template; - } - - /** - * Load template source from file - * @access private - */ - function _tpl_load_file($handle, $store_in_db = false) - { - // Try and open template for read - if (!file_exists($this->template->files[$handle])) - { - trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR); - } - - $this->template->compiled_code[$handle] = $this->compile(trim(@file_get_contents($this->template->files[$handle]))); - - // Actually compile the code now. - $this->compile_write($handle, $this->template->compiled_code[$handle]); - - // Store in database if required... - if ($store_in_db) - { - global $db, $user; - - $sql_ary = array( - 'template_id' => $this->template->files_template[$handle], - 'template_filename' => $this->template->filename[$handle], - 'template_included' => '', - 'template_mtime' => time(), - 'template_data' => trim(@file_get_contents($this->template->files[$handle])), - ); - - $sql = 'INSERT INTO ' . STYLES_TEMPLATE_DATA_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); - $db->sql_query($sql); - } - } - - /** - * Remove any PHP tags that do not belong, these regular expressions are derived from - * the ones that exist in zend_language_scanner.l - * @access private - */ - function remove_php_tags(&$code) - { - // This matches the information gathered from the internal PHP lexer - $match = array( - '#<([\?%])=?.*?\1>#s', - '#.*?#s', - '#<\?php(?:\r\n?|[ \n\t]).*?\?>#s' - ); - - $code = preg_replace($match, '', $code); - } - - /** - * The all seeing all doing compile method. Parts are inspired by or directly from Smarty - * @access private - */ - function compile($code, $no_echo = false, $echo_var = '') - { - global $config; - - if ($echo_var) - { - global $$echo_var; - } - - // Remove any "loose" php ... we want to give admins the ability - // to switch on/off PHP for a given template. Allowing unchecked - // php is a no-no. There is a potential issue here in that non-php - // content may be removed ... however designers should use entities - // if they wish to display < and > - $this->remove_php_tags($code); - - // Pull out all block/statement level elements and separate plain text - preg_match_all('#(.*?)#s', $code, $matches); - $php_blocks = $matches[1]; - $code = preg_replace('#.*?#s', '', $code); - - preg_match_all('##', $code, $matches); - $include_blocks = $matches[1]; - $code = preg_replace('##', '', $code); - - preg_match_all('##', $code, $matches); - $includephp_blocks = $matches[1]; - $code = preg_replace('##', '', $code); - - preg_match_all('##', $code, $blocks, PREG_SET_ORDER); - - $text_blocks = preg_split('##', $code); - - for ($i = 0, $j = sizeof($text_blocks); $i < $j; $i++) - { - $this->compile_var_tags($text_blocks[$i]); - } - $compile_blocks = array(); - - for ($curr_tb = 0, $tb_size = sizeof($blocks); $curr_tb < $tb_size; $curr_tb++) - { - $block_val = &$blocks[$curr_tb]; - - switch ($block_val[1]) - { - case 'BEGIN': - $this->block_else_level[] = false; - $compile_blocks[] = 'compile_tag_block($block_val[2]) . ' ?>'; - break; - - case 'BEGINELSE': - $this->block_else_level[sizeof($this->block_else_level) - 1] = true; - $compile_blocks[] = ''; - break; - - case 'END': - array_pop($this->block_names); - $compile_blocks[] = 'block_else_level)) ? '}' : '}}') . ' ?>'; - break; - - case 'IF': - $compile_blocks[] = 'compile_tag_if($block_val[2], false) . ' ?>'; - break; - - case 'ELSE': - $compile_blocks[] = ''; - break; - - case 'ELSEIF': - $compile_blocks[] = 'compile_tag_if($block_val[2], true) . ' ?>'; - break; - - case 'ENDIF': - $compile_blocks[] = ''; - break; - - case 'DEFINE': - $compile_blocks[] = 'compile_tag_define($block_val[2], true) . ' ?>'; - break; - - case 'UNDEFINE': - $compile_blocks[] = 'compile_tag_define($block_val[2], false) . ' ?>'; - break; - - case 'INCLUDE': - $temp = array_shift($include_blocks); - - // Dynamic includes - // Cheap match rather than a full blown regexp, we already know - // the format of the input so just use string manipulation. - if ($temp[0] == '{') - { - $file = false; - - if ($temp[1] == '$') - { - $var = substr($temp, 2, -1); - //$file = $this->template->_tpldata['DEFINE']['.'][$var]; - $temp = "\$this->_tpldata['DEFINE']['.']['$var']"; - } - else - { - $var = substr($temp, 1, -1); - //$file = $this->template->_rootref[$var]; - $temp = "\$this->_rootref['$var']"; - } - } - else - { - $file = $temp; - } - - $compile_blocks[] = 'compile_tag_include($temp) . ' ?>'; - - // No point in checking variable includes - if ($file) - { - $this->template->_tpl_include($file, false); - } - break; - - case 'INCLUDEPHP': - $compile_blocks[] = ($config['tpl_allow_php']) ? 'compile_tag_include_php(array_shift($includephp_blocks)) . ' ?>' : ''; - break; - - case 'PHP': - $compile_blocks[] = ($config['tpl_allow_php']) ? '' : ''; - break; - - default: - $this->compile_var_tags($block_val[0]); - $trim_check = trim($block_val[0]); - $compile_blocks[] = (!$no_echo) ? ((!empty($trim_check)) ? $block_val[0] : '') : ((!empty($trim_check)) ? $block_val[0] : ''); - break; - } - } - - $template_php = ''; - for ($i = 0, $size = sizeof($text_blocks); $i < $size; $i++) - { - $trim_check_text = trim($text_blocks[$i]); - $template_php .= (!$no_echo) ? (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '') : (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : ''); - } - - // Remove unused opening/closing tags - $template_php = str_replace(' ?>([\r\n])#', '?>\1\1', $template_php); - - // There will be a number of occasions where we switch into and out of - // PHP mode instantaneously. Rather than "burden" the parser with this - // we'll strip out such occurences, minimising such switching - if ($no_echo) - { - return "\$$echo_var .= '" . $template_php . "'"; - } - - return $template_php; - } - - /** - * Compile variables - * @access private - */ - function compile_var_tags(&$text_blocks) - { - // change template varrefs into PHP varrefs - $varrefs = array(); - - // This one will handle varrefs WITH namespaces - preg_match_all('#\{((?:[a-z0-9\-_]+\.)+)(\$)?([A-Z0-9\-_]+)\}#', $text_blocks, $varrefs, PREG_SET_ORDER); - - foreach ($varrefs as $var_val) - { - $namespace = $var_val[1]; - $varname = $var_val[3]; - $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); - - $text_blocks = str_replace($var_val[0], $new, $text_blocks); - } - - // This will handle the remaining root-level varrefs - // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array - if (strpos($text_blocks, '{L_') !== false) - { - $text_blocks = preg_replace('#\{L_([A-Z0-9\-_]+)\}#', "_rootref['L_\\1'])) ? \$this->_rootref['L_\\1'] : ((isset(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '{ \\1 }')); ?>", $text_blocks); - } - - // Handle addslashed language variables prefixed with LA_ - // If a template variable already exist, it will be used in favor of it... - if (strpos($text_blocks, '{LA_') !== false) - { - $text_blocks = preg_replace('#\{LA_([A-Z0-9\-_]+)\}#', "_rootref['LA_\\1'])) ? \$this->_rootref['LA_\\1'] : ((isset(\$this->_rootref['L_\\1'])) ? addslashes(\$this->_rootref['L_\\1']) : ((isset(\$user->lang['\\1'])) ? addslashes(\$user->lang['\\1']) : '{ \\1 }'))); ?>", $text_blocks); - } - - // Handle remaining varrefs - $text_blocks = preg_replace('#\{([A-Z0-9\-_]+)\}#', "_rootref['\\1'])) ? \$this->_rootref['\\1'] : ''; ?>", $text_blocks); - $text_blocks = preg_replace('#\{\$([A-Z0-9\-_]+)\}#', "_tpldata['DEFINE']['.']['\\1'])) ? \$this->_tpldata['DEFINE']['.']['\\1'] : ''; ?>", $text_blocks); - - return; - } - - /** - * Compile blocks - * @access private - */ - function compile_tag_block($tag_args) - { - $no_nesting = false; - - // Is the designer wanting to call another loop in a loop? - if (strpos($tag_args, '!') === 0) - { - // Count the number of ! occurrences (not allowed in vars) - $no_nesting = substr_count($tag_args, '!'); - $tag_args = substr($tag_args, $no_nesting); - } - - // Allow for control of looping (indexes start from zero): - // foo(2) : Will start the loop on the 3rd entry - // foo(-2) : Will start the loop two entries from the end - // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth - // foo(3,-4) : Will start the loop on the fourth entry and end it four from last - if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) - { - $tag_args = $match[1]; - - if ($match[2] < 0) - { - $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; - } - else - { - $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; - } - - if (strlen($match[3]) < 1 || $match[3] == -1) - { - $loop_end = '$_' . $tag_args . '_count'; - } - else if ($match[3] >= 0) - { - $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; - } - else //if ($match[3] < -1) - { - $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); - } - } - else - { - $loop_start = 0; - $loop_end = '$_' . $tag_args . '_count'; - } - - $tag_template_php = ''; - array_push($this->block_names, $tag_args); - - if ($no_nesting !== false) - { - // We need to implode $no_nesting times from the end... - $block = array_slice($this->block_names, -$no_nesting); - } - else - { - $block = $this->block_names; - } - - if (sizeof($block) < 2) - { - // Block is not nested. - $tag_template_php = '$_' . $tag_args . "_count = (isset(\$this->_tpldata['$tag_args'])) ? sizeof(\$this->_tpldata['$tag_args']) : 0;"; - $varref = "\$this->_tpldata['$tag_args']"; - } - else - { - // This block is nested. - // Generate a namespace string for this block. - $namespace = implode('.', $block); - - // 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. - $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; - } - - $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; - - /** - * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory - * - * if (!$offset) - * { - * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; - * } - * - */ - - $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; - $tag_template_php .= '$_'. $tag_args . '_val = &' . $varref . '[$_'. $tag_args. '_i];'; - - return $tag_template_php; - } - - /** - * Compile IF tags - much of this is from Smarty with - * some adaptions for our block level methods - * @access private - */ - function compile_tag_if($tag_args, $elseif) - { - // Tokenize args for 'if' tag. - preg_match_all('/(?: - "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | - \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | - [(),] | - [^\s(),]+)/x', $tag_args, $match); - - $tokens = $match[0]; - $is_arg_stack = array(); - - for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) - { - $token = &$tokens[$i]; - - switch ($token) - { - case '!==': - case '===': - case '<<': - case '>>': - case '|': - case '^': - case '&': - case '~': - case ')': - case ',': - case '+': - case '-': - case '*': - case '/': - case '@': - break; - - case '==': - case 'eq': - $token = '=='; - break; - - case '!=': - case '<>': - case 'ne': - case 'neq': - $token = '!='; - break; - - case '<': - case 'lt': - $token = '<'; - break; - - case '<=': - case 'le': - case 'lte': - $token = '<='; - break; - - case '>': - case 'gt': - $token = '>'; - break; - - case '>=': - case 'ge': - case 'gte': - $token = '>='; - break; - - case '&&': - case 'and': - $token = '&&'; - break; - - case '||': - case 'or': - $token = '||'; - break; - - case '!': - case 'not': - $token = '!'; - break; - - case '%': - 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 = implode(' ', 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, sizeof($tokens), $new_tokens); - - $i = $is_arg_start; - - // no break - - default: - if (preg_match('#^((?:[a-z0-9\-_]+\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) - { - $token = (!empty($varrefs[1])) ? $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']' : (($varrefs[2]) ? '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$this->_rootref[\'' . $varrefs[3] . '\']'); - } - else if (preg_match('#^\.((?:[a-z0-9\-_]+\.?)+)$#s', $token, $varrefs)) - { - // Allow checking if loops are set with .loopname - // It is also possible to check the loop count by doing for example - $blocks = explode('.', $varrefs[1]); - - // If the block is nested, we have a reference that we can grab. - // If the block is not nested, we just go and grab the block from _tpldata - if (sizeof($blocks) > 1) - { - $block = array_pop($blocks); - $namespace = implode('.', $blocks); - $varref = $this->generate_block_data_ref($namespace, true); - - // Add the block reference for the last child. - $varref .= "['" . $block . "']"; - } - else - { - $varref = '$this->_tpldata'; - - // Add the block reference for the last child. - $varref .= "['" . $blocks[0] . "']"; - } - $token = "sizeof($varref)"; - } - else if (!empty($token)) - { - $token = '(' . $token . ')'; - } - - break; - } - } - - // If there are no valid tokens left or only control/compare characters left, we do skip this statement - if (!sizeof($tokens) || str_replace(array(' ', '=', '!', '<', '>', '&', '|', '%', '(', ')'), '', implode('', $tokens)) == '') - { - $tokens = array('false'); - } - return (($elseif) ? '} else if (' : 'if (') . (implode(' ', $tokens) . ') { '); - } - - /** - * Compile DEFINE tags - * @access private - */ - function compile_tag_define($tag_args, $op) - { - preg_match('#^((?:[a-z0-9\-_]+\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (\'?)([^\']*)(\'?))?$#', $tag_args, $match); - - if (empty($match[2]) || (!isset($match[4]) && $op)) - { - return ''; - } - - if (!$op) - { - return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; - } - - // Are we a string? - if ($match[3] && $match[5]) - { - $match[4] = str_replace(array('\\\'', '\\\\', '\''), array('\'', '\\', '\\\''), $match[4]); - - // Compile reference, we allow template variables in defines... - $match[4] = $this->compile($match[4]); - - // Now replace the php code - $match[4] = "'" . str_replace(array(''), array("' . ", " . '"), $match[4]) . "'"; - } - else - { - preg_match('#true|false|\.#i', $match[4], $type); - - switch (strtolower($type[0])) - { - case 'true': - case 'false': - $match[4] = strtoupper($match[4]); - break; - - case '.': - $match[4] = doubleval($match[4]); - break; - - default: - $match[4] = intval($match[4]); - break; - } - } - - return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $match[4] . ';'; - } - - /** - * Compile INCLUDE tag - * @access private - */ - function compile_tag_include($tag_args) - { - // Process dynamic includes - if ($tag_args[0] == '$') - { - return "if (isset($tag_args)) { \$this->_tpl_include($tag_args); }"; - } - - return "\$this->_tpl_include('$tag_args');"; - } - - /** - * Compile INCLUDE_PHP tag - * @access private - */ - function compile_tag_include_php($tag_args) - { - return "\$this->_php_include('$tag_args');"; - } - - /** - * parse expression - * This is from Smarty - * @access private - */ - 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 & 1)"; - } - 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 & 1)"; - } - break; - - case 'div': - if (@$tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "!($is_arg % $expr_arg)"; - } - break; - } - - 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) - * 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. - * @access private - */ - function generate_block_varref($namespace, $varname, $echo = true, $defop = false) - { - // Strip the trailing period. - $namespace = substr($namespace, 0, -1); - - // Get a reference to the data block for this namespace. - $varref = $this->generate_block_data_ref($namespace, true, $defop); - // Prepend the necessary code to stick this in an echo line. - - // Append the variable reference. - $varref .= "['$varname']"; - $varref = ($echo) ? "" : ((isset($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. - * @access private - */ - function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) - { - // Get an array of the blocks involved. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - - // DEFINE is not an element of any referenced variable, we must use _tpldata to access it - if ($defop) - { - $varref = '$this->_tpldata[\'DEFINE\']'; - // 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; - } - else if ($include_last_iterator) - { - return '$_'. $blocks[$blockcount] . '_val'; - } - else - { - return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; - } - } - - /** - * Write compiled file to cache directory - * @access private - */ - function compile_write($handle, $data) - { - global $phpEx; - - $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . $phpEx; - - $data = "' . $data); - - if ($fp = @fopen($filename, 'wb')) - { - @flock($fp, LOCK_EX); - @fwrite ($fp, $data); - @flock($fp, LOCK_UN); - @fclose($fp); - - phpbb_chmod($filename, CHMOD_READ | CHMOD_WRITE); - } - - return; - } -} diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 643a86cde9..47262eaccf 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -16,40 +16,75 @@ if (!defined('IN_PHPBB')) exit; } +/** +* @todo +* IMG_ for image substitution? +* {IMG_[key]:[alt]:[type]} +* {IMG_ICON_CONTACT:CONTACT:full} -> $user->img('icon_contact', 'CONTACT', 'full'); +* +* More in-depth... +* yadayada +*/ + /** * Base Template class. * @package phpBB3 */ -class template +class phpbb_template { - /** variable that holds all the data we'll be substituting into + public $phpbb_required = array('user', 'config'); + public $phpbb_optional = array(); + + /** + * variable that holds all the data we'll be substituting into * 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 + * @var array + */ + private $_tpldata = array('.' => array(0 => array())); + + /** + * @var array Reference to template->_tpldata['.'][0] + */ + private $_rootref; + + /** + * @var string Root dir for template. */ - var $_tpldata = array('.' => array(0 => array())); - var $_rootref; - - // Root dir and hash of filenames for each template handle. - var $root = ''; - var $cachepath = ''; - var $files = array(); - var $filename = array(); - var $files_inherit = array(); - var $files_template = array(); - var $inherit_root = ''; - var $orig_tpl_storedb; - var $orig_tpl_inherits_id; + private $root = ''; + + /** + * @var string Path of the cache directory for the template + */ + public $cachepath = ''; + + /** + * @var array Hash of handle => file path pairs + */ + public $files = array(); + + /** + * @var array Hash of handle => filename pairs + */ + public $filename = array(); + + public $files_inherit = array(); + public $files_template = array(); + public $inherit_root = ''; + + public $orig_tpl_storedb; + public $orig_tpl_inherits_id; // this will hash handle names to the compiled/uncompiled code for that handle. - var $compiled_code = array(); + public $compiled_code = array(); /** * Set template location * @access public */ - function set_template() + public function set_template() { global $phpbb_root_path, $user; @@ -89,8 +124,11 @@ class template /** * Set custom template location (able to use directory outside of phpBB) * @access public + * @param string $template_path Path to template directory + * @param string $template_name Name of template + * @param string $fallback_template_path Path to fallback template */ - function set_custom_template($template_path, $template_name, $fallback_template_path = false) + public function set_custom_template($template_path, $template_name, $fallback_template_path = false) { global $phpbb_root_path, $user; @@ -131,13 +169,10 @@ class template * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. * @access public + * @param array $filname_array Should be a hash of handle => filename pairs. */ - function set_filenames($filename_array) + public function set_filenames(array $filename_array) { - if (!is_array($filename_array)) - { - return false; - } foreach ($filename_array as $handle => $filename) { if (empty($filename)) @@ -161,17 +196,26 @@ class template * Destroy template data set * @access public */ - function destroy() + public function destroy() { $this->_tpldata = array('.' => array(0 => array())); $this->_rootref = &$this->_tpldata['.'][0]; } + /** + * destroy method kept for compatibility. + */ + public function __destruct() + { + $this->destroy(); + } + /** * Reset/empty complete block * @access public + * @param string $blockname Name of block to destroy */ - function destroy_block_vars($blockname) + public function destroy_block_vars($blockname) { if (strpos($blockname, '.') !== false) { @@ -200,12 +244,15 @@ class template /** * Display handle * @access public + * @param string $handle Handle to display + * @param bool $include_once Allow multiple inclusions + * @return bool True on success, false on failure */ - function display($handle, $include_once = true) + public function display($handle, $include_once = true) { global $user, $phpbb_hook; - if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) + if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once)) { if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) { @@ -221,13 +268,23 @@ class template } } - if ($filename = $this->_tpl_load($handle)) + $_tpldata = &$this->_tpldata; + $_rootref = &$this->_rootref; + $_lang = &$user->lang; + + if (($filename = $this->_tpl_load($handle)) !== false) { ($include_once) ? include_once($filename) : include($filename); } + else if (($code = $this->_tpl_eval($handle)) !== false) + { + $code = ' ?> ' . $code . ' ' . $this->compiled_code[$handle] . 'display($handle, $include_once); @@ -256,8 +318,11 @@ class template /** * Load a compiled template if possible, if not, recompile it * @access private + * @param string $handle Handle of the template to load + * @return string|bool Return filename on success otherwise false + * @uses template_compile is used to compile uncached templates */ - function _tpl_load(&$handle) + private function _tpl_load($handle) { global $user, $phpEx, $config; @@ -276,7 +341,9 @@ class template $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; $recompile = false; - if (!file_exists($filename) || @filesize($filename) === 0) + $recompile = (!file_exists($filename) || @filesize($filename) === 0 || ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle]))) ? true : false; + + if (defined('DEBUG_EXTRA')) { $recompile = true; } @@ -288,7 +355,7 @@ class template $this->files[$handle] = $this->files_inherit[$handle]; $this->files_template[$handle] = $user->theme['template_inherits_id']; } - $recompile = (@filemtime($filename) < filemtime($this->files[$handle])) ? true : false; + $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; } // Recompile page if the original template is newer, otherwise load the compiled version @@ -296,33 +363,59 @@ class template { return $filename; } + + // Inheritance - we point to another template file for this one. Equality is also used for store_db + if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + { + $this->files[$handle] = $this->files_inherit[$handle]; + $this->files_template[$handle] = $user->theme['template_inherits_id']; + } - global $db, $phpbb_root_path; + // If we don't have a file assigned to this handle, die. + if (!isset($this->files[$handle])) + { + trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + } - if (!class_exists('template_compile')) + if (!class_exists('phpbb_template_compile')) { - include($phpbb_root_path . 'includes/functions_template.' . $phpEx); + include 'template_compile.php'; } + + $compile = new phpbb_template_compile($this); - // Inheritance - we point to another template file for this one. Equality is also used for store_db - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + if ($compile->_tpl_load_file($handle) === false) { - $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $user->theme['template_inherits_id']; + return false; } + + return $filename; + } - $compile = new template_compile($this); + /** + * This code should only run when some high level error prevents us from writing to the cache. + * @access private + * @param string $handle Template handle to compile + * @return string|bool Return compiled code on success otherwise false + * @uses template_compile is used to compile template + */ + private function _tpl_eval($handle) + { + if (!class_exists('phpbb_template_compile')) + { + include 'template_compile.php'; + } + + $compile = new phpbb_template_compile($this); // If we don't have a file assigned to this handle, die. if (!isset($this->files[$handle])) { - trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + trigger_error("template->_tpl_eval(): No file specified for handle $handle", E_USER_ERROR); } - // Just compile if no user object is present (happens within the installer) - if (!$user) + if (($code = $compile->_tpl_gen_src($handle)) === false) { - $compile->_tpl_load_file($handle); return false; } @@ -337,6 +430,8 @@ class template } $ids[] = $user->theme['template_id']; + global $db; + foreach ($ids as $id) { $sql = 'SELECT * @@ -381,7 +476,7 @@ class template $this->files_template[$row['template_filename']] = $user->theme['template_id']; } - if ($force_reload || $row['template_mtime'] < filemtime($file)) + if ($force_reload || $row['template_mtime'] < @filemtime($file)) { if ($row['template_filename'] == $this->filename[$handle]) { @@ -441,8 +536,9 @@ class template /** * Assign key variable pairs from an array * @access public + * @param array $vararray A hash of variable name => value pairs */ - function assign_vars($vararray) + public function assign_vars(array $vararray) { foreach ($vararray as $key => $val) { @@ -455,8 +551,10 @@ class template /** * Assign a single variable to a single key * @access public + * @param string $varname Variable name + * @param string $varval Value to assign to variable */ - function assign_var($varname, $varval) + public function assign_var($varname, $varval) { $this->_rootref[$varname] = $varval; @@ -466,8 +564,10 @@ class template /** * Assign key variable pairs from an array to a specified block * @access public + * @param string $blockname Name of block to assign $vararray to + * @param array $vararray A hash of variable name => value pairs */ - function assign_block_vars($blockname, $vararray) + public function assign_block_vars($blockname, array $vararray) { if (strpos($blockname, '.') !== false) { @@ -558,18 +658,51 @@ class template * @return bool false on error, true on success * @access public */ - function alter_block_array($blockname, $vararray, $key = false, $mode = 'insert') + public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { if (strpos($blockname, '.') !== false) { - // Nested blocks are not supported - return false; + // Nested block. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = &$this->_tpldata; + for ($i = 0; $i < $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) + { + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) + { + $index = sizeof($block[$name]) - 1; + } + else + { + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); + } + } + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + $block = &$block[$name]; + $block = &$block[$index]; + } + + $block = &$block[$blocks[$i]]; // Traverse the last block + } + else + { + // Top-level block. + $block = &$this->_tpldata[$blockname]; } // Change key to zero (change first position) if false and to last position if true if ($key === false || $key === true) { - $key = ($key === false) ? 0 : sizeof($this->_tpldata[$blockname]); + $key = ($key === false) ? 0 : sizeof($block); } // Get correct position if array given @@ -579,7 +712,7 @@ class template list($search_key, $search_value) = @each($key); $key = NULL; - foreach ($this->_tpldata[$blockname] as $i => $val_ary) + foreach ($block as $i => $val_ary) { if ($val_ary[$search_key] === $search_value) { @@ -612,15 +745,13 @@ class template } // Re-position template blocks - for ($i = sizeof($this->_tpldata[$blockname]); $i > $key; $i--) + for ($i = sizeof($block); $i > $key; $i--) { - $this->_tpldata[$blockname][$i] = $this->_tpldata[$blockname][$i-1]; - $this->_tpldata[$blockname][$i]['S_ROW_COUNT'] = $i; + $block[$i] = $block[$i-1]; } // Insert vararray at given position - $vararray['S_ROW_COUNT'] = $key; - $this->_tpldata[$blockname][$key] = $vararray; + $block[$key] = $vararray; return true; } @@ -628,12 +759,13 @@ class template // Which block to change? if ($mode == 'change') { - if ($key == sizeof($this->_tpldata[$blockname])) + if ($key == sizeof($block)) { $key--; } - $this->_tpldata[$blockname][$key] = array_merge($this->_tpldata[$blockname][$key], $vararray); + $block[$key] = array_merge($block[$key], $vararray); + return true; } @@ -643,8 +775,11 @@ class template /** * Include a separate template * @access private + * @param string $filename Template filename to include + * @param bool $include True to include the file, false to just load it + * @uses template_compile is used to compile uncached templates */ - function _tpl_include($filename, $include = true) + private function _tpl_include($filename, $include = true) { $handle = $filename; $this->filename[$handle] = $filename; @@ -654,18 +789,31 @@ class template $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; } - $filename = $this->_tpl_load($handle); + $filename = $this->_tpl_load($handle); if ($include) { global $user; + $_tpldata = &$this->_tpldata; + $_rootref = &$this->_rootref; + $_lang = &$user->lang; + if ($filename) { include($filename); return; } - eval(' ?>' . $this->compiled_code[$handle] . '_tpl_gen_src($handle)) !== false) + { + $code = ' ?> ' . $code . ' '; + + const REGEX_TOKENS = '~|{((?:[a-z][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; + + /** + * @var array + */ + private $block_names = array(); + + /** + * @var array + */ + private $block_else_level = array(); + + /** + * @var string + */ + private $chunk; + + /** + * @var bool + */ + private $in_php; + + public function filter($in, $out, &$consumed, $closing) + { + $written = false; + + while ($bucket = stream_bucket_make_writeable($in)) + { + $consumed += $bucket->datalen; + + $data = $this->chunk . $bucket->data; + $last_nl = strrpos($data, "\n"); + $this->chunk = substr($data, $last_nl); + $data = substr($data, 0, $last_nl); + + if (!strlen($data)) + { + continue; + } + + $written = true; + + $bucket->data = $this->compile($data); + $bucket->datalen = strlen($bucket->data); + stream_bucket_append($out, $bucket); + } + + if ($closing && strlen($this->chunk)) + { + $written = true; + $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); + stream_bucket_append($out, $bucket); + } + + return $written ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + public function onCreate() + { + $this->chunk = ''; + $this->in_php = false; + return true; + } + + private function compile($data) + { + $block_start_in_php = $this->in_php; + + $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); + $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); + + global $config; + + // Remove php + if (!$config['tpl_allow_php']) + { + if ($block_start_in_php + && $this->in_php + && strpos($data, '') === false + && strpos($data, '') === false) + { + // This is just php code + return ''; + } + $data = preg_replace('~^.*?~', '', $data); + $data = preg_replace('~.*?~', '', $data); + $data = preg_replace('~.*?$~', '', $data); + } + + "?>/**/"; + + /* + Preserve whitespace. + PHP removes a newline after the closing tag (if it's there). This is by design. + + + Consider the following template: + + + some content + + + If we were to simply preserve all whitespace, we could simply replace all "?>" tags + with "?>\n". + Doing that, would add additional newlines to the compiled tempalte in place of the + IF and ENDIF statements. These newlines are unwanted (and one is conditional). + The IF and ENDIF are usually on their own line for ease of reading. + + This replacement preserves newlines only for statements that aren't the only statement on a line. + It will NOT preserve newlines at the end of statements in the above examle. + It will preserve newlines in situations like: + + inline content + + + */ +//* + $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); + $data = str_replace('/**/?>', "?>\n", $data); + $data = str_replace('?>in_php && $matches[1] != 'ENDPHP') + { + return ''; + } + + if (isset($matches[3])) + { + return $this->compile_var_tags($matches[0]); + } + + global $config; + + switch ($matches[1]) + { + case 'BEGIN': + $this->block_else_level[] = false; + return 'compile_tag_block($matches[2]) . ' ?>'; + break; + + case 'BEGINELSE': + $this->block_else_level[sizeof($this->block_else_level) - 1] = true; + return ''; + break; + + case 'END': + array_pop($this->block_names); + return 'block_else_level)) ? '}' : '}}') . ' ?>'; + break; + + case 'IF': + return 'compile_tag_if($matches[2], false) . ' ?>'; + break; + + case 'ELSE': + return ''; + break; + + case 'ELSEIF': + return 'compile_tag_if($matches[2], true) . ' ?>'; + break; + + case 'ENDIF': + return ''; + break; + + case 'DEFINE': + return 'compile_tag_define($matches[2], true) . ' ?>'; + break; + + case 'UNDEFINE': + return 'compile_tag_define($matches[2], false) . ' ?>'; + break; + + case 'INCLUDE': + return 'compile_tag_include($matches[2]) . ' ?>'; + break; + + case 'INCLUDEPHP': + return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; + break; + + case 'PHP': + if ($config['tpl_allow_php']) + { + $this->in_php = true; + return ''; + break; + + case 'ENDPHP': + if ($config['tpl_allow_php']) + { + $this->in_php = false; + return ' ?>'; + } + return ''; + break; + + default: + return $matches[0]; + break; + + } + return ''; + } + + /** + * Compile variables + * @access private + */ + private function compile_var_tags(&$text_blocks) + { + // change template varrefs into PHP varrefs + $varrefs = array(); + + // This one will handle varrefs WITH namespaces + preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); + + foreach ($varrefs as $var_val) + { + $namespace = $var_val[1]; + $varname = $var_val[3]; + $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); + + $text_blocks = str_replace($var_val[0], $new, $text_blocks); + } + + // Handle special language tags L_ and LA_ + $this->compile_language_tags($text_blocks); + + // This will handle the remaining root-level varrefs + $text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + $text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + + return $text_blocks; + } + + /** + * Handles special language tags L_ and LA_ + */ + private function compile_language_tags(&$text_blocks) + { + // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array + if (strpos($text_blocks, '{L_') !== false) + { + $text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + } + + // Handle addslashed language variables prefixed with LA_ + // If a template variable already exist, it will be used in favor of it... + if (strpos($text_blocks, '{LA_') !== false) + { + $text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR . '+)\}#', "", $text_blocks); + } + } + + /** + * Compile blocks + * @access private + */ + private function compile_tag_block($tag_args) + { + $no_nesting = false; + + // Is the designer wanting to call another loop in a loop? + // + // + // + // + // 'loop2' is actually on the same nesting level as 'loop' you assign + // variables to it with template->assign_block_vars('loop2', array(...)) + if (strpos($tag_args, '!') === 0) + { + // Count the number if ! occurrences (not allowed in vars) + $no_nesting = substr_count($tag_args, '!'); + $tag_args = substr($tag_args, $no_nesting); + } + + // Allow for control of looping (indexes start from zero): + // foo(2) : Will start the loop on the 3rd entry + // foo(-2) : Will start the loop two entries from the end + // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth + // foo(3,-4) : Will start the loop on the fourth entry and end it four from last + $match = array(); + + if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) + { + $tag_args = $match[1]; + + if ($match[2] < 0) + { + $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; + } + else + { + $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; + } + + if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) + { + $loop_end = '$_' . $tag_args . '_count'; + } + else if ($match[3] >= 0) + { + $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; + } + else //if ($match[3] < -1) + { + $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); + } + } + else + { + $loop_start = 0; + $loop_end = '$_' . $tag_args . '_count'; + } + + $tag_template_php = ''; + array_push($this->block_names, $tag_args); + + if ($no_nesting !== false) + { + // We need to implode $no_nesting times from the end... + $block = array_slice($this->block_names, -$no_nesting); + } + else + { + $block = $this->block_names; + } + + if (sizeof($block) < 2) + { + // Block is not nested. + $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; + $varref = "\$_tpldata['$tag_args']"; + } + else + { + // This block is nested. + // Generate a namespace string for this block. + $namespace = implode('.', $block); + + // 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. + $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; + } + + $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; + + /** + * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory + * + * if (!$offset) + * { + * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; + * } + * + */ + + $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; + $tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; + + return $tag_template_php; + } + + /** + * Compile a general expression - much of this is from Smarty with + * some adaptions for our block level methods + * @access private + */ + private function compile_expression($tag_args, &$vars = array()) + { + $match = array(); + preg_match_all('/(?: + "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | + [(),] | + [^\s(),]+)/x', $tag_args, $match); + + $tokens = $match[0]; + $is_arg_stack = array(); + + for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) + { + $token = &$tokens[$i]; + + switch ($token) + { + case '!==': + case '===': + case '<<': + case '>>': + case '|': + case '^': + case '&': + case '~': + case ')': + case ',': + case '+': + case '-': + case '*': + case '/': + case '@': + break; + + case '==': + case 'eq': + $token = '=='; + break; + + case '!=': + case '<>': + case 'ne': + case 'neq': + $token = '!='; + break; + + case '<': + case 'lt': + $token = '<'; + break; + + case '<=': + case 'le': + case 'lte': + $token = '<='; + break; + + case '>': + case 'gt': + $token = '>'; + break; + + case '>=': + case 'ge': + case 'gte': + $token = '>='; + break; + + case '&&': + case 'and': + $token = '&&'; + break; + + case '||': + case 'or': + $token = '||'; + break; + + case '!': + case 'not': + $token = '!'; + break; + + case '%': + 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 = implode(' ', 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, sizeof($tokens), $new_tokens); + + $i = $is_arg_start; + + // no break + + default: + $varrefs = array(); + if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) + { + if (!empty($varrefs[1])) + { + $namespace = substr($varrefs[1], 0, -1); + $namespace = (strpos($namespace, '.') === false) ? $namespace : strrchr($namespace, '.'); + + // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varrefs[3]) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $token = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $token = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $token = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $token = "'$namespace'"; + break; + + default: + $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; + $vars[$token] = true; + break; + } + } + else + { + $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; + $vars[$token] = true; + } + + } + else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) + { + // Allow checking if loops are set with .loopname + // It is also possible to check the loop count by doing for example + $blocks = explode('.', $varrefs[1]); + + // If the block is nested, we have a reference that we can grab. + // If the block is not nested, we just go and grab the block from _tpldata + if (sizeof($blocks) > 1) + { + $block = array_pop($blocks); + $namespace = implode('.', $blocks); + $varref = $this->generate_block_data_ref($namespace, true); + + // Add the block reference for the last child. + $varref .= "['" . $block . "']"; + } + else + { + $varref = '$_tpldata'; + + // Add the block reference for the last child. + $varref .= "['" . $blocks[0] . "']"; + } + $token = "isset($varref) && sizeof($varref)"; + } + + break; + } + } + + return $tokens; + } + + + private function compile_tag_if($tag_args, $elseif) + { + $vars = array(); + + $tokens = $this->compile_expression($tag_args, $vars); + + $tpl = ($elseif) ? '} else if (' : 'if ('; + + if (!empty($vars)) + { + $tpl .= '(isset(' . implode(') && isset(', array_keys($vars)) . ')) && '; + } + + $tpl .= implode(' ', $tokens); + $tpl .= ') { '; + + return $tpl; + } + + /** + * Compile DEFINE tags + * @access private + */ + private function compile_tag_define($tag_args, $op) + { + $match = array(); + preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); + + if (empty($match[2]) || (!isset($match[3]) && $op)) + { + return ''; + } + + if (!$op) + { + return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; + } + + $parsed_statement = implode(' ', $this->compile_expression($match[3])); + + return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; + } + + /** + * Compile INCLUDE tag + * @access private + */ + private function compile_tag_include($tag_args) + { + return "\$this->_tpl_include('$tag_args');"; + } + + /** + * Compile INCLUDE_PHP tag + * @access private + */ + private function compile_tag_include_php($tag_args) + { + return "include('$tag_args');"; + } + + /** + * parse expression + * This is from Smarty + * @access private + */ + private 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "!($is_arg & 1)"; + } + break; + + case 'odd': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "($is_arg & 1)"; + } + break; + + case 'div': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!($is_arg % $expr_arg)"; + } + break; + } + + if ($negate_expr) + { + if ($expr[0] == '!') + { + // Negated expression, de-negate it. + $expr = substr($expr, 1); + } + else + { + $expr = "!($expr)"; + } + } + + array_splice($tokens, 0, $expr_end, $expr); + + return $tokens; + } + + /** + * Generates a reference to the given variable inside the given (possibly nested) + * block namespace. This is a string of the form: + * ' . $_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. + * + * @access private + * @param string $namespace Namespace to access (expects a trailing "." on the namespace) + * @param string $varname Variable name to use + * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable or echo it if $echo is true + */ + private function generate_block_varref($namespace, $varname, $echo = true, $defop = false) + { + // Strip the trailing period. + $namespace = substr($namespace, 0, -1); + + $expr = true; + $isset = false; + + // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varname) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $varref = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $varref = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $varref = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $varref = "'$namespace'"; + break; + + default: + // Get a reference to the data block for this namespace. + $varref = $this->generate_block_data_ref($namespace, true, $defop); + // Prepend the necessary code to stick this in an echo line. + + // Append the variable reference. + $varref .= "['$varname']"; + + $expr = false; + $isset = true; + break; + } + // @todo Test the !$expr more + $varref = ($echo) ? '' : (($expr || isset($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: + * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] + * + * @access private + * @param string $blockname Block to access (does not expect a trailing "." on the blockname) + * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable + */ + private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) + { + // Get an array of the blocks involved. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + // DEFINE is not an element of any referenced variable, we must use _tpldata to access it + if ($defop) + { + $varref = '$_tpldata[\'DEFINE\']'; + // 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; + } + else if ($include_last_iterator) + { + return '$_'. $blocks[$blockcount] . '_val'; + } + else + { + return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; + } + } +} + +stream_filter_register('phpbb_template', 'phpbb_template_filter'); + +/** +* Extension of template class - Functions needed for compiling templates only. +* +* psoTFX, phpBB Development Team - 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 +* +* DEFINE directive inspired by a request by Cyberalien +* +* @package phpBB3 +* @uses template_filter As a PHP stream filter to perform compilation of templates +*/ +class phpbb_template_compile +{ + /** + * @var phpbb_template Reference to the {@link phpbb_template template} object performing compilation + */ + private $template; + + /** + * Constructor + * @param phpbb_template $template {@link phpbb_template Template} object performing compilation + */ + public function __construct(phpbb_template $template) + { + $this->template = $template; + } + + /** + * Load template source from file + * @access public + * @param string $handle Template handle we wish to load + * @return bool Return true on success otherwise false + */ + public function _tpl_load_file($handle) + { + // Try and open template for read + if (!file_exists($this->template->files[$handle])) + { + trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR); + } + + // Actually compile the code now. + return $this->compile_write($handle, $this->template->files[$handle]); + } + + /** + * Load template source from file + * @access public + * @param string $handle Template handle we wish to compile + * @return string|bool Return compiled code on successful compilation otherwise false + */ + public function _tpl_gen_src($handle) + { + // Try and open template for read + if (!file_exists($this->template->files[$handle])) + { + trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR); + } + + // Actually compile the code now. + return $this->compile_gen($this->template->files[$handle]); + } + + /** + * Write compiled file to cache directory + * @access private + * @param string $handle Template handle to compile + * @param string $source_file Source template file + * @return bool Return true on success otherwise false + */ + private function compile_write($handle, $source_file) + { + global $system, $phpEx; + + $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . $phpEx; + + $source_handle = @fopen($source_file, 'rb'); + $destination_handle = @fopen($filename, 'wb'); + + if (!$source_handle || !$destination_handle) + { + return false; + } + + @flock($destination_handle, LOCK_EX); + + stream_filter_append($source_handle, 'phpbb_template'); + stream_copy_to_stream($source_handle, $destination_handle); + + @fclose($source_handle); + @flock($destination_handle, LOCK_UN); + @fclose($destination_handle); + + phpbb_chmod($filename, CHMOD_READ | CHMOD_WRITE); + + clearstatcache(); + + return true; + } + + /** + * Generate source for eval() + * @access private + * @param string $source_file Source template file + * @return string|bool Return compiled code on successful compilation otherwise false + */ + private function compile_gen($source_file) + { + $source_handle = @fopen($source_file, 'rb'); + $destination_handle = @fopen('php://temp' ,'r+b'); + + if (!$source_handle || !$destination_handle) + { + return false; + } + + stream_filter_append($source_handle, 'phpbb_template'); + stream_copy_to_stream($source_handle, $destination_handle); + + @fclose($source_handle); + + rewind($destination_handle); + return stream_get_contents($destination_handle); + } +} + +?> \ No newline at end of file -- cgit v1.2.1 From 4f3e966fdc4bc241b8941ee9598d8c397f03f6a3 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 23 Apr 2011 22:47:22 -0400 Subject: [feature/template-engine] Delete ?>, add newline at EOF. PHPBB3-9726 --- phpBB/includes/template.php | 2 +- phpBB/includes/template_compile.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 47262eaccf..5eebeeae34 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -844,4 +844,4 @@ class phpbb_template class template extends phpbb_template { // dirty hack -} \ No newline at end of file +} diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 64dd8117a0..d9eda1d8c2 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -978,5 +978,3 @@ class phpbb_template_compile return stream_get_contents($destination_handle); } } - -?> \ No newline at end of file -- cgit v1.2.1 From 203187a8410c411b0bdd90729e19c257ec3a7820 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 00:22:41 -0400 Subject: [feature/template-engine] Fix recompilation logic. Do not change $recompile from true to false - any recompilation condition alone is sufficient to force recompilation. Also uncomment the nonexistent file test which passes with this fix. PHPBB3-9726 --- phpBB/includes/template.php | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 5eebeeae34..5d0e8416a4 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -343,19 +343,22 @@ class phpbb_template $recompile = false; $recompile = (!file_exists($filename) || @filesize($filename) === 0 || ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle]))) ? true : false; - if (defined('DEBUG_EXTRA')) - { - $recompile = true; - } - else if ($config['load_tplcompile']) + if (!$recompile) { - // No way around it: we need to check inheritance here - if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + if (defined('DEBUG_EXTRA')) { - $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $user->theme['template_inherits_id']; + $recompile = true; + } + else if ($config['load_tplcompile']) + { + // No way around it: we need to check inheritance here + if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + { + $this->files[$handle] = $this->files_inherit[$handle]; + $this->files_template[$handle] = $user->theme['template_inherits_id']; + } + $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; } - $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; } // Recompile page if the original template is newer, otherwise load the compiled version -- cgit v1.2.1 From f29f32e0d67e88a271702264c37852406f4013d8 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 00:44:19 -0400 Subject: [feature/template-engine] Allow leading underscores in variable names. Subsilver uses ._file in overall_header. PHPBB3-9726 --- phpBB/includes/template_compile.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index d9eda1d8c2..92695a54fa 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -23,13 +23,13 @@ if (!defined('IN_PHPBB')) */ class phpbb_template_filter extends php_user_filter { - const REGEX_NS = '[a-z][a-z_0-9]+'; + const REGEX_NS = '[a-z_][a-z_0-9]+'; - const REGEX_VAR = '[A-Z][A-Z_0-9]+'; + const REGEX_VAR = '[A-Z_][A-Z_0-9]+'; const REGEX_TAG = ''; - const REGEX_TOKENS = '~|{((?:[a-z][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; + const REGEX_TOKENS = '~|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; /** * @var array -- cgit v1.2.1 From 321ecf427356f2d503818089c7b0e008e5a73c2c Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 01:18:09 -0400 Subject: [feature/template-engine] Delete class_exists checks, rely on autoloading. PHPBB3-9726 --- phpBB/includes/template.php | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 5d0e8416a4..30ede2f242 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -380,11 +380,6 @@ class phpbb_template trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); } - if (!class_exists('phpbb_template_compile')) - { - include 'template_compile.php'; - } - $compile = new phpbb_template_compile($this); if ($compile->_tpl_load_file($handle) === false) @@ -404,11 +399,6 @@ class phpbb_template */ private function _tpl_eval($handle) { - if (!class_exists('phpbb_template_compile')) - { - include 'template_compile.php'; - } - $compile = new phpbb_template_compile($this); // If we don't have a file assigned to this handle, die. -- cgit v1.2.1 From c8db531fcb11cdeeefcae88b05c2674b74e2d824 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 01:59:40 -0400 Subject: [feature/template-engine] Removed a useless space. PHPBB3-9726 --- phpBB/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 30ede2f242..24d7aeadd0 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -782,7 +782,7 @@ class phpbb_template $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; } - $filename = $this->_tpl_load($handle); + $filename = $this->_tpl_load($handle); if ($include) { -- cgit v1.2.1 From 81962d1d8ff083dd366c77e7d3858fd2c9ed7d43 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Tue, 23 Nov 2010 16:09:09 +0100 Subject: [ticket/9924] Pass template instance into $template->display hook This is a cherry-pick of 053cf790a93e9cfb521f484901d79c72783f868f which appears to have been partially reverted here. PHPBB3-9924 --- phpBB/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 24d7aeadd0..2b4c7a09f7 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -252,7 +252,7 @@ class phpbb_template { global $user, $phpbb_hook; - if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once)) + if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) { if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) { -- cgit v1.2.1 From a2c75f60537ccf6a424c7f066eafbffc13c0c38d Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 21:18:18 -0400 Subject: [feature/template-engine] Deleted $template from phpbb_template_compile class. phpbb_template_compile is now much simpler. It takes complete file paths as inputs, either source template path or source template path and output compiled template path. The number of methods also went down to two - compile template and returned compiled text or compile and write to file. phpbb_compile class is responsible for determining source and compiled paths. It already had all the data necessary for this, now the code is in the same place as the data it uses. PHPBB3-9726 --- phpBB/includes/template.php | 67 +++++++++++++++++------- phpBB/includes/template_compile.php | 100 +++++++++--------------------------- 2 files changed, 73 insertions(+), 94 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 2b4c7a09f7..e1e96791c9 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -366,7 +366,7 @@ class phpbb_template { return $filename; } - + // Inheritance - we point to another template file for this one. Equality is also used for store_db if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { @@ -374,20 +374,54 @@ class phpbb_template $this->files_template[$handle] = $user->theme['template_inherits_id']; } + $source_file = $this->_source_file_for_handle($handle); + + $compile = new phpbb_template_compile($this); + + if ($compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)) === false) + { + return false; + } + + return $filename; + } + + /** + * Resolves template handle $handle to source file path. + * @access private + * @param string $handle Template handle (i.e. "friendly" template name) + * @return string Source file path + */ + private function _source_file_for_handle($handle) + { // If we don't have a file assigned to this handle, die. if (!isset($this->files[$handle])) { - trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + trigger_error("_source_file_for_handle(): No file specified for handle $handle", E_USER_ERROR); } - $compile = new phpbb_template_compile($this); + $source_file = $this->files[$handle]; - if ($compile->_tpl_load_file($handle) === false) + // Try and open template for reading + if (!file_exists($source_file)) { - return false; + trigger_error("_source_file_for_handle(): File $source_file does not exist", E_USER_ERROR); } - - return $filename; + return $source_file; + } + + /** + * Determines compiled file path for handle $handle. + * @access private + * @param string $handle Template handle (i.e. "friendly" template name) + * @return string Compiled file path + */ + private function _compiled_file_for_handle($handle) + { + global $phpEx; + + $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; + return $compiled_file; } /** @@ -401,13 +435,9 @@ class phpbb_template { $compile = new phpbb_template_compile($this); - // If we don't have a file assigned to this handle, die. - if (!isset($this->files[$handle])) - { - trigger_error("template->_tpl_eval(): No file specified for handle $handle", E_USER_ERROR); - } + $source_file = $this->_source_file_for_handle($handle); - if (($code = $compile->_tpl_gen_src($handle)) === false) + if (($code = $compile->compile_gen($source_file)) === false) { return false; } @@ -473,13 +503,13 @@ class phpbb_template { if ($row['template_filename'] == $this->filename[$handle]) { - $compile->_tpl_load_file($handle, true); + $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); } else { $this->files[$row['template_filename']] = $file; $this->filename[$row['template_filename']] = $row['template_filename']; - $compile->_tpl_load_file($row['template_filename'], true); + $compile->compile_write($this->_source_file_for_handle($row['template_filename']), $this->_compiled_file_for_handle($row['template_filename'])); unset($this->compiled_code[$row['template_filename']]); unset($this->files[$row['template_filename']]); unset($this->filename[$row['template_filename']]); @@ -515,14 +545,14 @@ class phpbb_template $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id']; } // Try to load from filesystem and instruct to insert into the styles table... - $compile->_tpl_load_file($handle, true); + $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); return false; } return false; } - $compile->_tpl_load_file($handle); + $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); return false; } @@ -801,7 +831,8 @@ class phpbb_template { $compile = new phpbb_template_compile($this); - if (($code = $compile->_tpl_gen_src($handle)) !== false) + $source_file = $this->_source_file_for_handle($handle); + if (($code = $compile->compile_gen($source_file)) !== false) { $code = ' ?> ' . $code . ' in_php; - + $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); global $config; - + // Remove php if (!$config['tpl_allow_php']) { @@ -117,33 +117,33 @@ class phpbb_template_filter extends php_user_filter $data = preg_replace('~.*?~', '', $data); $data = preg_replace('~.*?$~', '', $data); } - + "?>/**/"; - + /* Preserve whitespace. PHP removes a newline after the closing tag (if it's there). This is by design. - - + + Consider the following template: - + some content - + If we were to simply preserve all whitespace, we could simply replace all "?>" tags with "?>\n". Doing that, would add additional newlines to the compiled tempalte in place of the IF and ENDIF statements. These newlines are unwanted (and one is conditional). The IF and ENDIF are usually on their own line for ease of reading. - + This replacement preserves newlines only for statements that aren't the only statement on a line. It will NOT preserve newlines at the end of statements in the above examle. It will preserve newlines in situations like: - + inline content - - + + */ //* $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); @@ -158,7 +158,7 @@ class phpbb_template_filter extends php_user_filter { return ''; } - + if (isset($matches[3])) { return $this->compile_var_tags($matches[0]); @@ -558,7 +558,7 @@ class phpbb_template_filter extends php_user_filter $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; $vars[$token] = true; } - + } else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) { @@ -867,70 +867,17 @@ stream_filter_register('phpbb_template', 'phpbb_template_filter'); class phpbb_template_compile { /** - * @var phpbb_template Reference to the {@link phpbb_template template} object performing compilation - */ - private $template; - - /** - * Constructor - * @param phpbb_template $template {@link phpbb_template Template} object performing compilation - */ - public function __construct(phpbb_template $template) - { - $this->template = $template; - } - - /** - * Load template source from file + * Compiles template in $source_file and writes compiled template to + * cache directory * @access public - * @param string $handle Template handle we wish to load - * @return bool Return true on success otherwise false - */ - public function _tpl_load_file($handle) - { - // Try and open template for read - if (!file_exists($this->template->files[$handle])) - { - trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR); - } - - // Actually compile the code now. - return $this->compile_write($handle, $this->template->files[$handle]); - } - - /** - * Load template source from file - * @access public - * @param string $handle Template handle we wish to compile - * @return string|bool Return compiled code on successful compilation otherwise false - */ - public function _tpl_gen_src($handle) - { - // Try and open template for read - if (!file_exists($this->template->files[$handle])) - { - trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR); - } - - // Actually compile the code now. - return $this->compile_gen($this->template->files[$handle]); - } - - /** - * Write compiled file to cache directory - * @access private * @param string $handle Template handle to compile * @param string $source_file Source template file * @return bool Return true on success otherwise false */ - private function compile_write($handle, $source_file) + public function compile_write($source_file, $compiled_file) { - global $system, $phpEx; - - $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . $phpEx; - $source_handle = @fopen($source_file, 'rb'); - $destination_handle = @fopen($filename, 'wb'); + $destination_handle = @fopen($compiled_file, 'wb'); if (!$source_handle || !$destination_handle) { @@ -946,7 +893,7 @@ class phpbb_template_compile @flock($destination_handle, LOCK_UN); @fclose($destination_handle); - phpbb_chmod($filename, CHMOD_READ | CHMOD_WRITE); + phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE); clearstatcache(); @@ -954,12 +901,13 @@ class phpbb_template_compile } /** - * Generate source for eval() - * @access private + * Compiles a template located at $source_file. + * Returns PHP source suitable for eval(). + * @access public * @param string $source_file Source template file * @return string|bool Return compiled code on successful compilation otherwise false */ - private function compile_gen($source_file) + public function compile_gen($source_file) { $source_handle = @fopen($source_file, 'rb'); $destination_handle = @fopen('php://temp' ,'r+b'); -- cgit v1.2.1 From 5c3ebb3465eee36f9147a517d4f50f4032cd04d5 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 21:22:43 -0400 Subject: [feature/template-engine] Deleted silencing of notices. The code is now supposed to be notice-free, therefore there is no need to have the notices silenced. PHPBB3-9726 --- phpBB/includes/template.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index e1e96791c9..109f7a29ee 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -260,6 +260,7 @@ class phpbb_template } } + /* if (defined('IN_ERROR_HANDLER')) { if ((E_NOTICE & error_reporting()) == E_NOTICE) @@ -267,6 +268,7 @@ class phpbb_template error_reporting(error_reporting() ^ E_NOTICE); } } + */ $_tpldata = &$this->_tpldata; $_rootref = &$this->_rootref; -- cgit v1.2.1 From 5afc0b9b905814718ed0292fa5cc7342786c1fca Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Apr 2011 22:46:34 -0400 Subject: [feature/template-engine] Corrected an off-by-one error in nested namespaces. This error resulted in a dot from the namespace being placed into variable reference in compiled template code, thus creating bogus compiled template code. PHPBB3-9726 --- phpBB/includes/template_compile.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 07e497eca4..cba402f83b 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -520,7 +520,11 @@ class phpbb_template_filter extends php_user_filter if (!empty($varrefs[1])) { $namespace = substr($varrefs[1], 0, -1); - $namespace = (strpos($namespace, '.') === false) ? $namespace : strrchr($namespace, '.'); + $dot_pos = strrchr($namespace, '.'); + if ($dot_pos !== false) + { + $namespace = substr($dot_pos, 1); + } // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM -- cgit v1.2.1 From f97411b91143a0c75ef0ecec3ff03fc36a879728 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Mon, 25 Apr 2011 13:03:55 -0400 Subject: [feature/template-engine] Corrected miscompilation of loop size constructs. PHPBB3-9726 --- phpBB/includes/template_compile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index cba402f83b..9a8bc05343 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -588,7 +588,7 @@ class phpbb_template_filter extends php_user_filter // Add the block reference for the last child. $varref .= "['" . $blocks[0] . "']"; } - $token = "isset($varref) && sizeof($varref)"; + $token = "(isset($varref) ? sizeof($varref) : 0)"; } break; -- cgit v1.2.1 From 427a5122d55f06c861277297411f7e39a03a703c Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Tue, 26 Apr 2011 02:09:51 +0200 Subject: [feature/template-engine] Fix negative variable expressions compile_tag_if had the flawed approach of adding an isset statement for all variables to the beginning of the if. This fails for negative expressions, and checking those takes a considerable effort. The easier solution is to make the variable expression itself conditional, defaulting to null if it is not set. Thanks to naderman for the solution. PHPBB3-9726 --- phpBB/includes/template_compile.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 9a8bc05343..3f8a028e25 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -408,7 +408,7 @@ class phpbb_template_filter extends php_user_filter * some adaptions for our block level methods * @access private */ - private function compile_expression($tag_args, &$vars = array()) + private function compile_expression($tag_args) { $match = array(); preg_match_all('/(?: @@ -553,14 +553,14 @@ class phpbb_template_filter extends php_user_filter default: $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; - $vars[$token] = true; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; break; } } else { $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; - $vars[$token] = true; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; } } @@ -601,17 +601,10 @@ class phpbb_template_filter extends php_user_filter private function compile_tag_if($tag_args, $elseif) { - $vars = array(); - - $tokens = $this->compile_expression($tag_args, $vars); + $tokens = $this->compile_expression($tag_args); $tpl = ($elseif) ? '} else if (' : 'if ('; - if (!empty($vars)) - { - $tpl .= '(isset(' . implode(') && isset(', array_keys($vars)) . ')) && '; - } - $tpl .= implode(' ', $tokens); $tpl .= ') { '; -- cgit v1.2.1 From 909ee59871402aa8dc7fa4e540d2f7e5da1628fc Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 26 Apr 2011 12:10:52 -0400 Subject: [feature/template-engine] Removed $this from new phpbb_template_compile calls. The compile class no longer takes template as a parameter. PHPBB3-9726 --- phpBB/includes/template.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 109f7a29ee..ae68f5fad3 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -378,7 +378,7 @@ class phpbb_template $source_file = $this->_source_file_for_handle($handle); - $compile = new phpbb_template_compile($this); + $compile = new phpbb_template_compile(); if ($compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)) === false) { @@ -435,7 +435,7 @@ class phpbb_template */ private function _tpl_eval($handle) { - $compile = new phpbb_template_compile($this); + $compile = new phpbb_template_compile(); $source_file = $this->_source_file_for_handle($handle); @@ -831,7 +831,7 @@ class phpbb_template } else { - $compile = new phpbb_template_compile($this); + $compile = new phpbb_template_compile(); $source_file = $this->_source_file_for_handle($handle); if (($code = $compile->compile_gen($source_file)) !== false) -- cgit v1.2.1 From c844a277b2a6e4ad6232925fa78868cd945fc8a1 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 1 May 2011 03:09:49 -0400 Subject: [feature/template-engine] Check return value of display() in assign_display(). If display() failed, propagate the failure out of assign_display(). PHPBB3-9726 --- phpBB/includes/template.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index ae68f5fad3..b07e77989c 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -304,7 +304,11 @@ class phpbb_template public function assign_display($handle, $template_var = '', $return_content = true, $include_once = false) { ob_start(); - $this->display($handle, $include_once); + $result = $this->display($handle, $include_once); + if ($result === false) + { + return false; + } $contents = ob_get_clean(); if ($return_content) -- cgit v1.2.1 From 234b891a4be11dc0f20d93dd272242ad20344aa0 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 1 May 2011 03:14:11 -0400 Subject: [feature/template-engine] Fixed description of assign_display(). This function returns false on failure, which can happen if display() failed. Document the failure return value. PHPBB3-9726 --- phpBB/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index b07e77989c..ac37dc3dd2 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -299,7 +299,7 @@ class phpbb_template * @param string $template_var Template variable to assign compiled handle to * @param bool $return_content If true return compiled handle, otherwise assign to $template_var * @param bool $include_once Allow multiple inclusions of the file - * @return bool|string If $return_content is true return string of the compiled handle, otherwise return true + * @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true */ public function assign_display($handle, $template_var = '', $return_content = true, $include_once = false) { -- cgit v1.2.1 From 63ca4c2104e7c61b6f2238b13bd5428b579be9e7 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 1 May 2011 03:25:22 -0400 Subject: [feature/template-engine] Close output stream in compile(). There is no need to leave the stream to the garbage collector, and the amount of data stuck in it may be substantial. PHPBB3-9726 --- phpBB/includes/template_compile.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 3f8a028e25..e2bb9fd869 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -920,6 +920,9 @@ class phpbb_template_compile @fclose($source_handle); rewind($destination_handle); - return stream_get_contents($destination_handle); + $contents = stream_get_contents($destination_handle); + @fclose($dest_handle); + + return $contents; } } -- cgit v1.2.1 From d840de560cc59cd9b6c7da7b794dc3b2756e7377 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 1 May 2011 03:28:53 -0400 Subject: [feature/template-engine] Extracted compile_stream_to_stream. PHPBB3-9726 --- phpBB/includes/template_compile.php | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index e2bb9fd869..dfbf578b58 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -883,8 +883,7 @@ class phpbb_template_compile @flock($destination_handle, LOCK_EX); - stream_filter_append($source_handle, 'phpbb_template'); - stream_copy_to_stream($source_handle, $destination_handle); + $this->compile_stream_to_stream($source_handle, $destination_handle); @fclose($source_handle); @flock($destination_handle, LOCK_UN); @@ -914,8 +913,7 @@ class phpbb_template_compile return false; } - stream_filter_append($source_handle, 'phpbb_template'); - stream_copy_to_stream($source_handle, $destination_handle); + $this->compile_stream_to_stream($source_handle, $destination_handle); @fclose($source_handle); @@ -925,4 +923,21 @@ class phpbb_template_compile return $contents; } + + /** + * Compiles contents of $source_stream into $dest_stream. + * + * A stream filter is appended to $source_stream as part of the + * process. + * + * @access private + * @param resource $source_stream Source stream + * @param resource $dest_stream Destination stream + * @return void + */ + private function compile_stream_to_stream($source_stream, $dest_stream) + { + stream_filter_append($source_stream, 'phpbb_template'); + stream_copy_to_stream($source_stream, $dest_stream); + } } -- cgit v1.2.1 From 48691b53a6b3397ede4f009e8a108b14b7f7f305 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 1 May 2011 04:00:24 -0400 Subject: [feature/template-engine] Rename template_compile methods for clarity. PHPBB3-9726 --- phpBB/includes/template.php | 18 +++++++++--------- phpBB/includes/template_compile.php | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index ac37dc3dd2..dc461fb60e 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -384,7 +384,7 @@ class phpbb_template $compile = new phpbb_template_compile(); - if ($compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)) === false) + if ($compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)) === false) { return false; } @@ -443,7 +443,7 @@ class phpbb_template $source_file = $this->_source_file_for_handle($handle); - if (($code = $compile->compile_gen($source_file)) === false) + if (($code = $compile->compile_file($source_file)) === false) { return false; } @@ -509,13 +509,13 @@ class phpbb_template { if ($row['template_filename'] == $this->filename[$handle]) { - $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); + $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); } else { $this->files[$row['template_filename']] = $file; $this->filename[$row['template_filename']] = $row['template_filename']; - $compile->compile_write($this->_source_file_for_handle($row['template_filename']), $this->_compiled_file_for_handle($row['template_filename'])); + $compile->compile_file_to_file($this->_source_file_for_handle($row['template_filename']), $this->_compiled_file_for_handle($row['template_filename'])); unset($this->compiled_code[$row['template_filename']]); unset($this->files[$row['template_filename']]); unset($this->filename[$row['template_filename']]); @@ -525,7 +525,7 @@ class phpbb_template if ($row['template_filename'] == $this->filename[$handle]) { $this->compiled_code[$handle] = $compile->compile(trim($row['template_data'])); - $compile->compile_write($handle, $this->compiled_code[$handle]); + $compile->compile_file_to_file($handle, $this->compiled_code[$handle]); } else { @@ -533,7 +533,7 @@ class phpbb_template if (!file_exists($this->cachepath . str_replace('/', '.', $row['template_filename']) . '.' . $phpEx)) { $this->filename[$row['template_filename']] = $row['template_filename']; - $compile->compile_write($row['template_filename'], $compile->compile(trim($row['template_data']))); + $compile->compile_file_to_file($row['template_filename'], $compile->compile(trim($row['template_data']))); unset($this->filename[$row['template_filename']]); } } @@ -551,14 +551,14 @@ class phpbb_template $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id']; } // Try to load from filesystem and instruct to insert into the styles table... - $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); + $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); return false; } return false; } - $compile->compile_write($source_file, $this->_compiled_file_for_handle($handle)); + $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); return false; } @@ -838,7 +838,7 @@ class phpbb_template $compile = new phpbb_template_compile(); $source_file = $this->_source_file_for_handle($handle); - if (($code = $compile->compile_gen($source_file)) !== false) + if (($code = $compile->compile_file($source_file)) !== false) { $code = ' ?> ' . $code . ' Date: Sun, 1 May 2011 04:03:06 -0400 Subject: [feature/template-engine] Delete template class, use phpbb_template instead. PHPBB3-9726 --- phpBB/includes/functions_messenger.php | 2 +- phpBB/includes/template.php | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index f5d102b1da..8255d4b148 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -193,7 +193,7 @@ class messenger // tpl_msg now holds a template object we can use to parse the template file if (!isset($this->tpl_msg[$template_lang . $template_file])) { - $this->tpl_msg[$template_lang . $template_file] = new template(); + $this->tpl_msg[$template_lang . $template_file] = new phpbb_template(); $tpl = &$this->tpl_msg[$template_lang . $template_file]; $fallback_template_path = false; diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index dc461fb60e..2a32af406c 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -866,12 +866,3 @@ class phpbb_template include($file); } } - -/** - * @todo remove this - * - */ -class template extends phpbb_template -{ - // dirty hack -} -- cgit v1.2.1 From 237deb12cea909822e0f2fa3c072d31adfec3fb1 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 3 May 2011 23:50:17 -0400 Subject: [feature/template-engine] Removed storedb-related logic. phpBB 3.1 will not provide the option to store templates in the database. This commit removes code that handles templates stored in database from the template engine. PHPBB3-9726 --- phpBB/includes/template.php | 126 +------------------------------------------- 1 file changed, 2 insertions(+), 124 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 2a32af406c..add52b5c30 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -74,7 +74,6 @@ class phpbb_template public $files_template = array(); public $inherit_root = ''; - public $orig_tpl_storedb; public $orig_tpl_inherits_id; // this will hash handle names to the compiled/uncompiled code for that handle. @@ -93,17 +92,11 @@ class phpbb_template $this->root = $phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template'; $this->cachepath = $phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $user->theme['template_path']) . '_'; - if ($this->orig_tpl_storedb === null) - { - $this->orig_tpl_storedb = $user->theme['template_storedb']; - } - if ($this->orig_tpl_inherits_id === null) { $this->orig_tpl_inherits_id = $user->theme['template_inherits_id']; } - $user->theme['template_storedb'] = $this->orig_tpl_storedb; $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; if ($user->theme['template_inherits_id']) @@ -156,10 +149,6 @@ class phpbb_template $this->orig_tpl_inherits_id = false; } - // the database does not store the path or name of a custom template - // so there is no way we can properly store custom templates there - $this->orig_tpl_storedb = false; - $this->_rootref = &$this->_tpldata['.'][0]; return true; @@ -337,10 +326,9 @@ class phpbb_template trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); } - // reload these settings to have the values they had when this object was initialised + // reload this setting to have the values they had when this object was initialised // using set_template or set_custom_template, they might otherwise have been overwritten // by other template class instances in between. - $user->theme['template_storedb'] = $this->orig_tpl_storedb; $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; @@ -373,7 +361,7 @@ class phpbb_template return $filename; } - // Inheritance - we point to another template file for this one. Equality is also used for store_db + // Inheritance - we point to another template file for this one. if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { $this->files[$handle] = $this->files_inherit[$handle]; @@ -448,116 +436,6 @@ class phpbb_template return false; } - if (isset($user->theme['template_storedb']) && $user->theme['template_storedb']) - { - $rows = array(); - $ids = array(); - // Inheritance - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id']) - { - $ids[] = $user->theme['template_inherits_id']; - } - $ids[] = $user->theme['template_id']; - - global $db; - - foreach ($ids as $id) - { - $sql = 'SELECT * - FROM ' . STYLES_TEMPLATE_DATA_TABLE . ' - WHERE template_id = ' . $id . " - AND (template_filename = '" . $db->sql_escape($this->filename[$handle]) . "' - OR template_included " . $db->sql_like_expression($db->any_char . $this->filename[$handle] . ':' . $db->any_char) . ')'; - - $result = $db->sql_query($sql); - while ($row = $db->sql_fetchrow($result)) - { - $rows[$row['template_filename']] = $row; - } - $db->sql_freeresult($result); - } - - if (sizeof($rows)) - { - foreach ($rows as $row) - { - $file = $this->root . '/' . $row['template_filename']; - $force_reload = false; - if ($row['template_id'] != $user->theme['template_id']) - { - // make sure that we are not overlooking a file not in the db yet - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file)) - { - $file = $this->inherit_root . '/' . $row['template_filename']; - $this->files[$row['template_filename']] = $file; - $this->files_inherit[$row['template_filename']] = $file; - $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id']; - } - else if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id']) - { - // Ok, we have a situation. There is a file in the subtemplate, but nothing in the DB. We have to fix that. - $force_reload = true; - $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id']; - } - } - else - { - $this->files_template[$row['template_filename']] = $user->theme['template_id']; - } - - if ($force_reload || $row['template_mtime'] < @filemtime($file)) - { - if ($row['template_filename'] == $this->filename[$handle]) - { - $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); - } - else - { - $this->files[$row['template_filename']] = $file; - $this->filename[$row['template_filename']] = $row['template_filename']; - $compile->compile_file_to_file($this->_source_file_for_handle($row['template_filename']), $this->_compiled_file_for_handle($row['template_filename'])); - unset($this->compiled_code[$row['template_filename']]); - unset($this->files[$row['template_filename']]); - unset($this->filename[$row['template_filename']]); - } - } - - if ($row['template_filename'] == $this->filename[$handle]) - { - $this->compiled_code[$handle] = $compile->compile(trim($row['template_data'])); - $compile->compile_file_to_file($handle, $this->compiled_code[$handle]); - } - else - { - // Only bother compiling if it doesn't already exist - if (!file_exists($this->cachepath . str_replace('/', '.', $row['template_filename']) . '.' . $phpEx)) - { - $this->filename[$row['template_filename']] = $row['template_filename']; - $compile->compile_file_to_file($row['template_filename'], $compile->compile(trim($row['template_data']))); - unset($this->filename[$row['template_filename']]); - } - } - } - } - else - { - $file = $this->root . '/' . $row['template_filename']; - - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file)) - { - $file = $this->inherit_root . '/' . $row['template_filename']; - $this->files[$row['template_filename']] = $file; - $this->files_inherit[$row['template_filename']] = $file; - $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id']; - } - // Try to load from filesystem and instruct to insert into the styles table... - $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); - return false; - } - - return false; - } - $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); return false; } -- cgit v1.2.1 From d06e59f63bc8213a4d679ff0c20a23dcf8cd524e Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 3 May 2011 23:53:22 -0400 Subject: [feature/template-engine] Split template execution logic into classes. Template executor interface defines a template executor object. It is an object which can execute (i.e. display/render) a template. Currently there are two implementations: * phpbb_template_executor_include includes php code from a file. * phpbb_template_executor_eval eval's php code. PHPBB3-9726 --- phpBB/includes/template.php | 107 +++++++++++---------------- phpBB/includes/template_executor.php | 15 ++++ phpBB/includes/template_executor_eval.php | 32 ++++++++ phpBB/includes/template_executor_include.php | 32 ++++++++ 4 files changed, 123 insertions(+), 63 deletions(-) create mode 100644 phpBB/includes/template_executor.php create mode 100644 phpBB/includes/template_executor_eval.php create mode 100644 phpBB/includes/template_executor_include.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index add52b5c30..8529b90719 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -263,22 +263,17 @@ class phpbb_template $_rootref = &$this->_rootref; $_lang = &$user->lang; - if (($filename = $this->_tpl_load($handle)) !== false) - { - ($include_once) ? include_once($filename) : include($filename); - } - else if (($code = $this->_tpl_eval($handle)) !== false) + $executor = $this->_tpl_load($handle); + + if ($executor) { - $code = ' ?> ' . $code . ' execute(); + return true; } else { - // if we could not eval AND the file exists, something horrific has occured return false; } - - return true; } /** @@ -311,11 +306,27 @@ class phpbb_template } /** - * Load a compiled template if possible, if not, recompile it + * Obtains a template executor for a template identified by specified + * handle. THe template executor can execute the template later. + * + * Template source will first be compiled into php code. + * If template cache is writable the compiled php code will be stored + * on filesystem and template will not be subsequently recompiled. + * If template cache is not writable template source will be recompiled + * every time it is needed. DEBUG_EXTRA define and load_tplcompile + * configuration setting may be used to force templates to be always + * recompiled. + * + * Returns an object implementing phpbb_template_executor, or null + * if template loading or compilation failed. Call execute() on the + * executor to execute the template. This will result in template + * contents sent to the output stream (unless, of course, output + * buffering is in effect). + * * @access private * @param string $handle Handle of the template to load - * @return string|bool Return filename on success otherwise false - * @uses template_compile is used to compile uncached templates + * @return phpbb_template_executor Template executor object, or null on failure + * @uses template_compile is used to compile template source */ private function _tpl_load($handle) { @@ -358,7 +369,7 @@ class phpbb_template // Recompile page if the original template is newer, otherwise load the compiled version if (!$recompile) { - return $filename; + return new phpbb_template_executor_include($filename); } // Inheritance - we point to another template file for this one. @@ -372,12 +383,21 @@ class phpbb_template $compile = new phpbb_template_compile(); - if ($compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)) === false) + $output_file = $this->_compiled_file_for_handle($handle); + if ($compile->compile_file_to_file($source_file, $output_file) !== false) { - return false; + $executor = new phpbb_template_executor_include($output_file); + } + else if (($code = $compile->compile_file($source_file)) !== false) + { + $executor = new phpbb_template_executor_eval($code); + } + else + { + $executor = null; } - return $filename; + return $executor; } /** @@ -418,28 +438,6 @@ class phpbb_template return $compiled_file; } - /** - * This code should only run when some high level error prevents us from writing to the cache. - * @access private - * @param string $handle Template handle to compile - * @return string|bool Return compiled code on success otherwise false - * @uses template_compile is used to compile template - */ - private function _tpl_eval($handle) - { - $compile = new phpbb_template_compile(); - - $source_file = $this->_source_file_for_handle($handle); - - if (($code = $compile->compile_file($source_file)) === false) - { - return false; - } - - $compile->compile_file_to_file($source_file, $this->_compiled_file_for_handle($handle)); - return false; - } - /** * Assign key variable pairs from an array * @access public @@ -696,32 +694,15 @@ class phpbb_template $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; } - $filename = $this->_tpl_load($handle); + $executor = $this->_tpl_load($handle); - if ($include) + if ($executor) { - global $user; - - $_tpldata = &$this->_tpldata; - $_rootref = &$this->_rootref; - $_lang = &$user->lang; - - if ($filename) - { - include($filename); - return; - } - else - { - $compile = new phpbb_template_compile(); - - $source_file = $this->_source_file_for_handle($handle); - if (($code = $compile->compile_file($source_file)) !== false) - { - $code = ' ?> ' . $code . ' execute(); + } + else + { + // What should we do here? } } diff --git a/phpBB/includes/template_executor.php b/phpBB/includes/template_executor.php new file mode 100644 index 0000000000..9bbb3dca0d --- /dev/null +++ b/phpBB/includes/template_executor.php @@ -0,0 +1,15 @@ +code = $code; + } + + /** + * Executes the template managed by this executor by eval'ing php code + * of the template. + */ + public function execute() + { + eval($this->code); + } +} diff --git a/phpBB/includes/template_executor_include.php b/phpBB/includes/template_executor_include.php new file mode 100644 index 0000000000..74f0593b13 --- /dev/null +++ b/phpBB/includes/template_executor_include.php @@ -0,0 +1,32 @@ +path = $path; + } + + /** + * Executes the template managed by this executor by including + * the php file containing the template. + */ + public function execute() + { + include($this->path); + } +} -- cgit v1.2.1 From b057d7439e9ed27ec0aa6cff021fd6a9df6e85eb Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 4 May 2011 01:00:40 -0400 Subject: [feature/template-engine] Removed some dead code. PHPBB3-9726 --- phpBB/includes/template_compile.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 553f99bd8c..1feab3b7c4 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -118,8 +118,6 @@ class phpbb_template_filter extends php_user_filter $data = preg_replace('~.*?$~', '', $data); } - "?>/**/"; - /* Preserve whitespace. PHP removes a newline after the closing tag (if it's there). This is by design. @@ -145,7 +143,7 @@ class phpbb_template_filter extends php_user_filter */ -//* + $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); $data = str_replace('/**/?>', "?>\n", $data); $data = str_replace('?> Date: Thu, 5 May 2011 19:03:46 -0400 Subject: [feature/template-engine] Always call ob_end_clean. We have to stop output buffering even when rendering fails. PHPBB3-9726 --- phpBB/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 8529b90719..8d7eb57dc4 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -289,11 +289,11 @@ class phpbb_template { ob_start(); $result = $this->display($handle, $include_once); + $contents = ob_get_clean(); if ($result === false) { return false; } - $contents = ob_get_clean(); if ($return_content) { -- cgit v1.2.1 From 0501640d5db158a010741e27803191ab469834c4 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 7 May 2011 15:10:50 -0400 Subject: [feature/template-engine] Added phpbb_template_context class. Objects of this class hold variables assigned to templates. PHPBB3-9726 --- phpBB/includes/template.php | 281 ++++--------------------- phpBB/includes/template_compile.php | 2 +- phpBB/includes/template_context.php | 299 +++++++++++++++++++++++++++ phpBB/includes/template_executor.php | 4 +- phpBB/includes/template_executor_eval.php | 14 +- phpBB/includes/template_executor_include.php | 13 +- 6 files changed, 367 insertions(+), 246 deletions(-) create mode 100644 phpBB/includes/template_context.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 8d7eb57dc4..755e6adba9 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -36,19 +36,11 @@ class phpbb_template public $phpbb_optional = array(); /** - * variable that holds all the data we'll be substituting into - * 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 - * @var array - */ - private $_tpldata = array('.' => array(0 => array())); - - /** - * @var array Reference to template->_tpldata['.'][0] + * @var phpbb_template_context Template context. + * Stores template data used during template rendering. + * @access private */ - private $_rootref; + private $_context; /** * @var string Root dir for template. @@ -109,7 +101,7 @@ class phpbb_template trigger_error('Template path could not be found: styles/' . $user->theme['template_path'] . '/template', E_USER_ERROR); } - $this->_rootref = &$this->_tpldata['.'][0]; + $this->_context = new phpbb_template_context(); return true; } @@ -149,7 +141,7 @@ class phpbb_template $this->orig_tpl_inherits_id = false; } - $this->_rootref = &$this->_tpldata['.'][0]; + $this->_context = new phpbb_template_context(); return true; } @@ -181,24 +173,6 @@ class phpbb_template return true; } - /** - * Destroy template data set - * @access public - */ - public function destroy() - { - $this->_tpldata = array('.' => array(0 => array())); - $this->_rootref = &$this->_tpldata['.'][0]; - } - - /** - * destroy method kept for compatibility. - */ - public function __destruct() - { - $this->destroy(); - } - /** * Reset/empty complete block * @access public @@ -206,28 +180,7 @@ class phpbb_template */ public function destroy_block_vars($blockname) { - if (strpos($blockname, '.') !== false) - { - // 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]; - } - - unset($str[$blocks[$blockcount]]); - } - else - { - // Top-level block. - unset($this->_tpldata[$blockname]); - } - - return true; + $this->_context->destroy_block_vars($blockname); } /** @@ -239,7 +192,7 @@ class phpbb_template */ public function display($handle, $include_once = true) { - global $user, $phpbb_hook; + global $phpbb_hook; if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) { @@ -259,15 +212,11 @@ class phpbb_template } */ - $_tpldata = &$this->_tpldata; - $_rootref = &$this->_rootref; - $_lang = &$user->lang; - $executor = $this->_tpl_load($handle); if ($executor) { - $executor->execute(); + $executor->execute($this->_context, $this->get_lang()); return true; } else @@ -276,6 +225,27 @@ class phpbb_template } } + /** + * Obtains language array. + * This is either lang property of global $user object, or if + * it is not set an empty array. + * @return array language entries + */ + public function get_lang() + { + global $user; + + if (isset($user->lang)) + { + $lang = $user->lang; + } + else + { + $lang = array(); + } + return $lang; + } + /** * Display the handle and assign the output to a template variable or return the compiled result. * @access public @@ -369,7 +339,7 @@ class phpbb_template // Recompile page if the original template is newer, otherwise load the compiled version if (!$recompile) { - return new phpbb_template_executor_include($filename); + return new phpbb_template_executor_include($filename, $this); } // Inheritance - we point to another template file for this one. @@ -386,11 +356,11 @@ class phpbb_template $output_file = $this->_compiled_file_for_handle($handle); if ($compile->compile_file_to_file($source_file, $output_file) !== false) { - $executor = new phpbb_template_executor_include($output_file); + $executor = new phpbb_template_executor_include($output_file, $this); } else if (($code = $compile->compile_file($source_file)) !== false) { - $executor = new phpbb_template_executor_eval($code); + $executor = new phpbb_template_executor_eval($code, $this); } else { @@ -447,10 +417,8 @@ class phpbb_template { foreach ($vararray as $key => $val) { - $this->_rootref[$key] = $val; + $this->assign_var($key, $val); } - - return true; } /** @@ -461,11 +429,10 @@ class phpbb_template */ public function assign_var($varname, $varval) { - $this->_rootref[$varname] = $varval; - - return true; + $this->_context->assign_var($varname, $varval); } + // Docstring is copied from phpbb_template_context method with the same name. /** * Assign key variable pairs from an array to a specified block * @access public @@ -474,67 +441,10 @@ class phpbb_template */ public function assign_block_vars($blockname, array $vararray) { - if (strpos($blockname, '.') !== false) - { - // 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]; - } - - $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; - $vararray['S_ROW_COUNT'] = $s_row_count; - - // Assign S_FIRST_ROW - if (!$s_row_count) - { - $vararray['S_FIRST_ROW'] = true; - } - - // Now the tricky part, we always assign S_LAST_ROW and remove the entry before - // This is much more clever than going through the complete template data on display (phew) - $vararray['S_LAST_ROW'] = true; - if ($s_row_count > 0) - { - unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']); - } - - // 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. - $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0; - $vararray['S_ROW_COUNT'] = $s_row_count; - - // Assign S_FIRST_ROW - if (!$s_row_count) - { - $vararray['S_FIRST_ROW'] = true; - } - - // We always assign S_LAST_ROW and remove the entry before - $vararray['S_LAST_ROW'] = true; - if ($s_row_count > 0) - { - unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); - } - - // Add a new iteration to this block with the variable assignments we were given. - $this->_tpldata[$blockname][] = $vararray; - } - - return true; + return $this->_context->assign_block_vars($blockname, $vararray); } + // Docstring is copied from phpbb_template_context method with the same name. /** * Change already assigned key variable pair (one-dimensional - single loop entry) * @@ -565,116 +475,7 @@ class phpbb_template */ public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { - if (strpos($blockname, '.') !== false) - { - // Nested block. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - - $block = &$this->_tpldata; - for ($i = 0; $i < $blockcount; $i++) - { - if (($pos = strpos($blocks[$i], '[')) !== false) - { - $name = substr($blocks[$i], 0, $pos); - - if (strpos($blocks[$i], '[]') === $pos) - { - $index = sizeof($block[$name]) - 1; - } - else - { - $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); - } - } - else - { - $name = $blocks[$i]; - $index = sizeof($block[$name]) - 1; - } - $block = &$block[$name]; - $block = &$block[$index]; - } - - $block = &$block[$blocks[$i]]; // Traverse the last block - } - else - { - // Top-level block. - $block = &$this->_tpldata[$blockname]; - } - - // Change key to zero (change first position) if false and to last position if true - if ($key === false || $key === true) - { - $key = ($key === false) ? 0 : sizeof($block); - } - - // Get correct position if array given - if (is_array($key)) - { - // Search array to get correct position - list($search_key, $search_value) = @each($key); - - $key = NULL; - foreach ($block as $i => $val_ary) - { - if ($val_ary[$search_key] === $search_value) - { - $key = $i; - break; - } - } - - // key/value pair not found - if ($key === NULL) - { - return false; - } - } - - // Insert Block - if ($mode == 'insert') - { - // Make sure we are not exceeding the last iteration - if ($key >= sizeof($this->_tpldata[$blockname])) - { - $key = sizeof($this->_tpldata[$blockname]); - unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']); - $vararray['S_LAST_ROW'] = true; - } - else if ($key === 0) - { - unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']); - $vararray['S_FIRST_ROW'] = true; - } - - // Re-position template blocks - for ($i = sizeof($block); $i > $key; $i--) - { - $block[$i] = $block[$i-1]; - } - - // Insert vararray at given position - $block[$key] = $vararray; - - return true; - } - - // Which block to change? - if ($mode == 'change') - { - if ($key == sizeof($block)) - { - $key--; - } - - $block[$key] = array_merge($block[$key], $vararray); - - return true; - } - - return false; + return $this->_context->alter_block_array($blockname, $vararray, $key, $mode); } /** @@ -684,7 +485,7 @@ class phpbb_template * @param bool $include True to include the file, false to just load it * @uses template_compile is used to compile uncached templates */ - private function _tpl_include($filename, $include = true) + public function _tpl_include($filename, $include = true) { $handle = $filename; $this->filename[$handle] = $filename; @@ -698,7 +499,7 @@ class phpbb_template if ($executor) { - $executor->execute(); + $executor->execute($this->_context, $this->get_lang()); } else { diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php index 1feab3b7c4..4132fb2e34 100644 --- a/phpBB/includes/template_compile.php +++ b/phpBB/includes/template_compile.php @@ -639,7 +639,7 @@ class phpbb_template_filter extends php_user_filter */ private function compile_tag_include($tag_args) { - return "\$this->_tpl_include('$tag_args');"; + return "\$_template->_tpl_include('$tag_args');"; } /** diff --git a/phpBB/includes/template_context.php b/phpBB/includes/template_context.php new file mode 100644 index 0000000000..ae898e9be1 --- /dev/null +++ b/phpBB/includes/template_context.php @@ -0,0 +1,299 @@ + $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 array + */ + private $_tpldata = array('.' => array(0 => array())); + + /** + * @var array Reference to template->_tpldata['.'][0] + */ + private $_rootref; + + public function __construct() + { + $this->clear(); + } + + /** + * Clears template data set. + * @access public + */ + public function clear() + { + $this->_tpldata = array('.' => array(0 => array())); + $this->_rootref = &$this->_tpldata['.'][0]; + } + + /** + * Assign a single variable to a single key + * @access public + * @param string $varname Variable name + * @param string $varval Value to assign to variable + */ + public function assign_var($varname, $varval) + { + $this->_rootref[$varname] = $varval; + + return true; + } + + public function get_data_ref() + { + return $this->_tpldata; + } + + public function get_root_ref() + { + return $this->_rootref; + } + + /** + * Assign key variable pairs from an array to a specified block + * @access public + * @param string $blockname Name of block to assign $vararray to + * @param array $vararray A hash of variable name => value pairs + */ + public function assign_block_vars($blockname, array $vararray) + { + if (strpos($blockname, '.') !== false) + { + // 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]; + } + + $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; + $vararray['S_ROW_COUNT'] = $s_row_count; + + // Assign S_FIRST_ROW + if (!$s_row_count) + { + $vararray['S_FIRST_ROW'] = true; + } + + // Now the tricky part, we always assign S_LAST_ROW and remove the entry before + // This is much more clever than going through the complete template data on display (phew) + $vararray['S_LAST_ROW'] = true; + if ($s_row_count > 0) + { + unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']); + } + + // 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. + $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0; + $vararray['S_ROW_COUNT'] = $s_row_count; + + // Assign S_FIRST_ROW + if (!$s_row_count) + { + $vararray['S_FIRST_ROW'] = true; + } + + // We always assign S_LAST_ROW and remove the entry before + $vararray['S_LAST_ROW'] = true; + if ($s_row_count > 0) + { + unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); + } + + // Add a new iteration to this block with the variable assignments we were given. + $this->_tpldata[$blockname][] = $vararray; + } + + return true; + } + + /** + * Change already assigned key variable pair (one-dimensional - single loop entry) + * + * An example of how to use this function: + * {@example alter_block_array.php} + * + * @param string $blockname the blockname, for example 'loop' + * @param array $vararray the var array to insert/add or merge + * @param mixed $key Key to search for + * + * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] + * + * int: Position [the position to change or insert at directly given] + * + * If key is false the position is set to 0 + * If key is true the position is set to the last entry + * + * @param string $mode Mode to execute (valid modes are 'insert' and 'change') + * + * If insert, the vararray is inserted at the given position (position counting from zero). + * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). + * + * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) + * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) + * + * @return bool false on error, true on success + * @access public + */ + public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') + { + if (strpos($blockname, '.') !== false) + { + // Nested block. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = &$this->_tpldata; + for ($i = 0; $i < $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) + { + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) + { + $index = sizeof($block[$name]) - 1; + } + else + { + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); + } + } + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + $block = &$block[$name]; + $block = &$block[$index]; + } + + $block = &$block[$blocks[$i]]; // Traverse the last block + } + else + { + // Top-level block. + $block = &$this->_tpldata[$blockname]; + } + + // Change key to zero (change first position) if false and to last position if true + if ($key === false || $key === true) + { + $key = ($key === false) ? 0 : sizeof($block); + } + + // Get correct position if array given + if (is_array($key)) + { + // Search array to get correct position + list($search_key, $search_value) = @each($key); + + $key = NULL; + foreach ($block as $i => $val_ary) + { + if ($val_ary[$search_key] === $search_value) + { + $key = $i; + break; + } + } + + // key/value pair not found + if ($key === NULL) + { + return false; + } + } + + // Insert Block + if ($mode == 'insert') + { + // Make sure we are not exceeding the last iteration + if ($key >= sizeof($this->_tpldata[$blockname])) + { + $key = sizeof($this->_tpldata[$blockname]); + unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']); + $vararray['S_LAST_ROW'] = true; + } + else if ($key === 0) + { + unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']); + $vararray['S_FIRST_ROW'] = true; + } + + // Re-position template blocks + for ($i = sizeof($block); $i > $key; $i--) + { + $block[$i] = $block[$i-1]; + } + + // Insert vararray at given position + $block[$key] = $vararray; + + return true; + } + + // Which block to change? + if ($mode == 'change') + { + if ($key == sizeof($block)) + { + $key--; + } + + $block[$key] = array_merge($block[$key], $vararray); + + return true; + } + + return false; + } + + /** + * Reset/empty complete block + * @access public + * @param string $blockname Name of block to destroy + */ + public function destroy_block_vars($blockname) + { + if (strpos($blockname, '.') !== false) + { + // 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]; + } + + unset($str[$blocks[$blockcount]]); + } + else + { + // Top-level block. + unset($this->_tpldata[$blockname]); + } + + return true; + } +} diff --git a/phpBB/includes/template_executor.php b/phpBB/includes/template_executor.php index 9bbb3dca0d..32e4e9a7b4 100644 --- a/phpBB/includes/template_executor.php +++ b/phpBB/includes/template_executor.php @@ -10,6 +10,8 @@ interface phpbb_template_executor { /** * Executes the template managed by this executor. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use */ - public function execute(); + public function execute($context, $lang); } diff --git a/phpBB/includes/template_executor_eval.php b/phpBB/includes/template_executor_eval.php index 27bdf95b52..af7d68fef3 100644 --- a/phpBB/includes/template_executor_eval.php +++ b/phpBB/includes/template_executor_eval.php @@ -13,20 +13,30 @@ class phpbb_template_executor_eval implements phpbb_template_executor /** * Constructor. Stores provided code for future evaluation. + * Template includes are delegated to template object $template. * * @param string $code php code of the template + * @param phpbb_template $template template object */ - public function __construct($code) + public function __construct($code, $template) { $this->code = $code; + $this->template = $template; } /** * Executes the template managed by this executor by eval'ing php code * of the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use */ - public function execute() + public function execute($context, $lang) { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + eval($this->code); } } diff --git a/phpBB/includes/template_executor_include.php b/phpBB/includes/template_executor_include.php index 74f0593b13..3671265cca 100644 --- a/phpBB/includes/template_executor_include.php +++ b/phpBB/includes/template_executor_include.php @@ -13,20 +13,29 @@ class phpbb_template_executor_include implements phpbb_template_executor /** * Constructor. Stores path to the template for future inclusion. + * Template includes are delegated to template object $template. * * @param string $path path to the template */ - public function __construct($path) + public function __construct($path, $template) { $this->path = $path; + $this->template = $template; } /** * Executes the template managed by this executor by including * the php file containing the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use */ - public function execute() + public function execute($context, $lang) { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + include($this->path); } } -- cgit v1.2.1 From b5444a7d7d1f6ac694468d8cc37fc0489e714547 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 7 May 2011 17:50:48 -0400 Subject: [feature/template-engine] Removed more dead code. PHPBB3-9726 --- phpBB/includes/template.php | 6 ------ 1 file changed, 6 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 755e6adba9..02815c4e15 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -32,9 +32,6 @@ if (!defined('IN_PHPBB')) */ class phpbb_template { - public $phpbb_required = array('user', 'config'); - public $phpbb_optional = array(); - /** * @var phpbb_template_context Template context. * Stores template data used during template rendering. @@ -68,9 +65,6 @@ class phpbb_template public $orig_tpl_inherits_id; - // this will hash handle names to the compiled/uncompiled code for that handle. - public $compiled_code = array(); - /** * Set template location * @access public -- cgit v1.2.1 From 1cba674b9a04390ff754bdb34691b3654f5916f9 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 7 May 2011 17:58:56 -0400 Subject: [feature/template-engine] Added docblocks and boilerplate to new files. PHPBB3-9726 --- phpBB/includes/template_context.php | 21 +++++++++++++++++++++ phpBB/includes/template_executor.php | 18 ++++++++++++++++++ phpBB/includes/template_executor_eval.php | 18 ++++++++++++++++++ phpBB/includes/template_executor_include.php | 19 +++++++++++++++++++ 4 files changed, 76 insertions(+) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template_context.php b/phpBB/includes/template_context.php index ae898e9be1..df5c4afb24 100644 --- a/phpBB/includes/template_context.php +++ b/phpBB/includes/template_context.php @@ -1,5 +1,26 @@ Date: Sun, 8 May 2011 04:03:41 -0400 Subject: [feature/template-engine] Renamed template executor and friends to renderer. PHPBB3-9726 --- phpBB/includes/template.php | 34 ++++++++-------- phpBB/includes/template_executor.php | 35 ---------------- phpBB/includes/template_executor_eval.php | 60 ---------------------------- phpBB/includes/template_executor_include.php | 60 ---------------------------- phpBB/includes/template_renderer.php | 35 ++++++++++++++++ phpBB/includes/template_renderer_eval.php | 60 ++++++++++++++++++++++++++++ phpBB/includes/template_renderer_include.php | 60 ++++++++++++++++++++++++++++ 7 files changed, 172 insertions(+), 172 deletions(-) delete mode 100644 phpBB/includes/template_executor.php delete mode 100644 phpBB/includes/template_executor_eval.php delete mode 100644 phpBB/includes/template_executor_include.php create mode 100644 phpBB/includes/template_renderer.php create mode 100644 phpBB/includes/template_renderer_eval.php create mode 100644 phpBB/includes/template_renderer_include.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 02815c4e15..f27d0c7560 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -206,11 +206,11 @@ class phpbb_template } */ - $executor = $this->_tpl_load($handle); + $renderer = $this->_tpl_load($handle); - if ($executor) + if ($renderer) { - $executor->execute($this->_context, $this->get_lang()); + $renderer->render($this->_context, $this->get_lang()); return true; } else @@ -270,8 +270,8 @@ class phpbb_template } /** - * Obtains a template executor for a template identified by specified - * handle. THe template executor can execute the template later. + * Obtains a template renderer for a template identified by specified + * handle. The template renderer can display the template later. * * Template source will first be compiled into php code. * If template cache is writable the compiled php code will be stored @@ -281,15 +281,15 @@ class phpbb_template * configuration setting may be used to force templates to be always * recompiled. * - * Returns an object implementing phpbb_template_executor, or null - * if template loading or compilation failed. Call execute() on the - * executor to execute the template. This will result in template + * Returns an object implementing phpbb_template_renderer, or null + * if template loading or compilation failed. Call render() on the + * renderer to display the template. This will result in template * contents sent to the output stream (unless, of course, output * buffering is in effect). * * @access private * @param string $handle Handle of the template to load - * @return phpbb_template_executor Template executor object, or null on failure + * @return phpbb_template_renderer Template renderer object, or null on failure * @uses template_compile is used to compile template source */ private function _tpl_load($handle) @@ -333,7 +333,7 @@ class phpbb_template // Recompile page if the original template is newer, otherwise load the compiled version if (!$recompile) { - return new phpbb_template_executor_include($filename, $this); + return new phpbb_template_renderer_include($filename, $this); } // Inheritance - we point to another template file for this one. @@ -350,18 +350,18 @@ class phpbb_template $output_file = $this->_compiled_file_for_handle($handle); if ($compile->compile_file_to_file($source_file, $output_file) !== false) { - $executor = new phpbb_template_executor_include($output_file, $this); + $renderer = new phpbb_template_renderer_include($output_file, $this); } else if (($code = $compile->compile_file($source_file)) !== false) { - $executor = new phpbb_template_executor_eval($code, $this); + $renderer = new phpbb_template_renderer_eval($code, $this); } else { - $executor = null; + $renderer = null; } - return $executor; + return $renderer; } /** @@ -489,11 +489,11 @@ class phpbb_template $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; } - $executor = $this->_tpl_load($handle); + $renderer = $this->_tpl_load($handle); - if ($executor) + if ($renderer) { - $executor->execute($this->_context, $this->get_lang()); + $renderer->render($this->_context, $this->get_lang()); } else { diff --git a/phpBB/includes/template_executor.php b/phpBB/includes/template_executor.php deleted file mode 100644 index 0d410679b9..0000000000 --- a/phpBB/includes/template_executor.php +++ /dev/null @@ -1,35 +0,0 @@ -code = $code; - $this->template = $template; - } - - /** - * Executes the template managed by this executor by eval'ing php code - * of the template. - * @param phpbb_template_context $context Template context to use - * @param array $lang Language entries to use - */ - public function execute($context, $lang) - { - $_template = &$this->template; - $_tpldata = &$context->get_data_ref(); - $_rootref = &$context->get_root_ref(); - $_lang = &$lang; - - eval($this->code); - } -} diff --git a/phpBB/includes/template_executor_include.php b/phpBB/includes/template_executor_include.php deleted file mode 100644 index aacabc61e4..0000000000 --- a/phpBB/includes/template_executor_include.php +++ /dev/null @@ -1,60 +0,0 @@ -path = $path; - $this->template = $template; - } - - /** - * Executes the template managed by this executor by including - * the php file containing the template. - * @param phpbb_template_context $context Template context to use - * @param array $lang Language entries to use - */ - public function execute($context, $lang) - { - $_template = &$this->template; - $_tpldata = &$context->get_data_ref(); - $_rootref = &$context->get_root_ref(); - $_lang = &$lang; - - include($this->path); - } -} diff --git a/phpBB/includes/template_renderer.php b/phpBB/includes/template_renderer.php new file mode 100644 index 0000000000..0af2d94bb6 --- /dev/null +++ b/phpBB/includes/template_renderer.php @@ -0,0 +1,35 @@ +code = $code; + $this->template = $template; + } + + /** + * Displays the template managed by this renderer by eval'ing php code + * of the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use + */ + public function render($context, $lang) + { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + + eval($this->code); + } +} diff --git a/phpBB/includes/template_renderer_include.php b/phpBB/includes/template_renderer_include.php new file mode 100644 index 0000000000..aca3a3634d --- /dev/null +++ b/phpBB/includes/template_renderer_include.php @@ -0,0 +1,60 @@ +path = $path; + $this->template = $template; + } + + /** + * Displays the template managed by this renderer by including + * the php file containing the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use + */ + public function render($context, $lang) + { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + + include($this->path); + } +} -- cgit v1.2.1 From 49cf28a9c43060d2a4c4d5fe882fc3c7dedd7d0b Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 8 May 2011 04:05:09 -0400 Subject: [feature/template-engine] Moved template classes to subdirectory. PHPBB3-9726 --- phpBB/includes/template/compile.php | 941 +++++++++++++++++++++++++++ phpBB/includes/template/context.php | 320 +++++++++ phpBB/includes/template/renderer.php | 35 + phpBB/includes/template/renderer_eval.php | 60 ++ phpBB/includes/template/renderer_include.php | 60 ++ phpBB/includes/template_compile.php | 941 --------------------------- phpBB/includes/template_context.php | 320 --------- phpBB/includes/template_renderer.php | 35 - phpBB/includes/template_renderer_eval.php | 60 -- phpBB/includes/template_renderer_include.php | 60 -- 10 files changed, 1416 insertions(+), 1416 deletions(-) create mode 100644 phpBB/includes/template/compile.php create mode 100644 phpBB/includes/template/context.php create mode 100644 phpBB/includes/template/renderer.php create mode 100644 phpBB/includes/template/renderer_eval.php create mode 100644 phpBB/includes/template/renderer_include.php delete mode 100644 phpBB/includes/template_compile.php delete mode 100644 phpBB/includes/template_context.php delete mode 100644 phpBB/includes/template_renderer.php delete mode 100644 phpBB/includes/template_renderer_eval.php delete mode 100644 phpBB/includes/template_renderer_include.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php new file mode 100644 index 0000000000..4132fb2e34 --- /dev/null +++ b/phpBB/includes/template/compile.php @@ -0,0 +1,941 @@ +'; + + const REGEX_TOKENS = '~|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; + + /** + * @var array + */ + private $block_names = array(); + + /** + * @var array + */ + private $block_else_level = array(); + + /** + * @var string + */ + private $chunk; + + /** + * @var bool + */ + private $in_php; + + public function filter($in, $out, &$consumed, $closing) + { + $written = false; + + while ($bucket = stream_bucket_make_writeable($in)) + { + $consumed += $bucket->datalen; + + $data = $this->chunk . $bucket->data; + $last_nl = strrpos($data, "\n"); + $this->chunk = substr($data, $last_nl); + $data = substr($data, 0, $last_nl); + + if (!strlen($data)) + { + continue; + } + + $written = true; + + $bucket->data = $this->compile($data); + $bucket->datalen = strlen($bucket->data); + stream_bucket_append($out, $bucket); + } + + if ($closing && strlen($this->chunk)) + { + $written = true; + $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); + stream_bucket_append($out, $bucket); + } + + return $written ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + public function onCreate() + { + $this->chunk = ''; + $this->in_php = false; + return true; + } + + private function compile($data) + { + $block_start_in_php = $this->in_php; + + $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); + $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); + + global $config; + + // Remove php + if (!$config['tpl_allow_php']) + { + if ($block_start_in_php + && $this->in_php + && strpos($data, '') === false + && strpos($data, '') === false) + { + // This is just php code + return ''; + } + $data = preg_replace('~^.*?~', '', $data); + $data = preg_replace('~.*?~', '', $data); + $data = preg_replace('~.*?$~', '', $data); + } + + /* + Preserve whitespace. + PHP removes a newline after the closing tag (if it's there). This is by design. + + + Consider the following template: + + + some content + + + If we were to simply preserve all whitespace, we could simply replace all "?>" tags + with "?>\n". + Doing that, would add additional newlines to the compiled tempalte in place of the + IF and ENDIF statements. These newlines are unwanted (and one is conditional). + The IF and ENDIF are usually on their own line for ease of reading. + + This replacement preserves newlines only for statements that aren't the only statement on a line. + It will NOT preserve newlines at the end of statements in the above examle. + It will preserve newlines in situations like: + + inline content + + + */ + + $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); + $data = str_replace('/**/?>', "?>\n", $data); + $data = str_replace('?>in_php && $matches[1] != 'ENDPHP') + { + return ''; + } + + if (isset($matches[3])) + { + return $this->compile_var_tags($matches[0]); + } + + global $config; + + switch ($matches[1]) + { + case 'BEGIN': + $this->block_else_level[] = false; + return 'compile_tag_block($matches[2]) . ' ?>'; + break; + + case 'BEGINELSE': + $this->block_else_level[sizeof($this->block_else_level) - 1] = true; + return ''; + break; + + case 'END': + array_pop($this->block_names); + return 'block_else_level)) ? '}' : '}}') . ' ?>'; + break; + + case 'IF': + return 'compile_tag_if($matches[2], false) . ' ?>'; + break; + + case 'ELSE': + return ''; + break; + + case 'ELSEIF': + return 'compile_tag_if($matches[2], true) . ' ?>'; + break; + + case 'ENDIF': + return ''; + break; + + case 'DEFINE': + return 'compile_tag_define($matches[2], true) . ' ?>'; + break; + + case 'UNDEFINE': + return 'compile_tag_define($matches[2], false) . ' ?>'; + break; + + case 'INCLUDE': + return 'compile_tag_include($matches[2]) . ' ?>'; + break; + + case 'INCLUDEPHP': + return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; + break; + + case 'PHP': + if ($config['tpl_allow_php']) + { + $this->in_php = true; + return ''; + break; + + case 'ENDPHP': + if ($config['tpl_allow_php']) + { + $this->in_php = false; + return ' ?>'; + } + return ''; + break; + + default: + return $matches[0]; + break; + + } + return ''; + } + + /** + * Compile variables + * @access private + */ + private function compile_var_tags(&$text_blocks) + { + // change template varrefs into PHP varrefs + $varrefs = array(); + + // This one will handle varrefs WITH namespaces + preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); + + foreach ($varrefs as $var_val) + { + $namespace = $var_val[1]; + $varname = $var_val[3]; + $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); + + $text_blocks = str_replace($var_val[0], $new, $text_blocks); + } + + // Handle special language tags L_ and LA_ + $this->compile_language_tags($text_blocks); + + // This will handle the remaining root-level varrefs + $text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + $text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + + return $text_blocks; + } + + /** + * Handles special language tags L_ and LA_ + */ + private function compile_language_tags(&$text_blocks) + { + // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array + if (strpos($text_blocks, '{L_') !== false) + { + $text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + } + + // Handle addslashed language variables prefixed with LA_ + // If a template variable already exist, it will be used in favor of it... + if (strpos($text_blocks, '{LA_') !== false) + { + $text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR . '+)\}#', "", $text_blocks); + } + } + + /** + * Compile blocks + * @access private + */ + private function compile_tag_block($tag_args) + { + $no_nesting = false; + + // Is the designer wanting to call another loop in a loop? + // + // + // + // + // 'loop2' is actually on the same nesting level as 'loop' you assign + // variables to it with template->assign_block_vars('loop2', array(...)) + if (strpos($tag_args, '!') === 0) + { + // Count the number if ! occurrences (not allowed in vars) + $no_nesting = substr_count($tag_args, '!'); + $tag_args = substr($tag_args, $no_nesting); + } + + // Allow for control of looping (indexes start from zero): + // foo(2) : Will start the loop on the 3rd entry + // foo(-2) : Will start the loop two entries from the end + // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth + // foo(3,-4) : Will start the loop on the fourth entry and end it four from last + $match = array(); + + if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) + { + $tag_args = $match[1]; + + if ($match[2] < 0) + { + $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; + } + else + { + $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; + } + + if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) + { + $loop_end = '$_' . $tag_args . '_count'; + } + else if ($match[3] >= 0) + { + $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; + } + else //if ($match[3] < -1) + { + $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); + } + } + else + { + $loop_start = 0; + $loop_end = '$_' . $tag_args . '_count'; + } + + $tag_template_php = ''; + array_push($this->block_names, $tag_args); + + if ($no_nesting !== false) + { + // We need to implode $no_nesting times from the end... + $block = array_slice($this->block_names, -$no_nesting); + } + else + { + $block = $this->block_names; + } + + if (sizeof($block) < 2) + { + // Block is not nested. + $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; + $varref = "\$_tpldata['$tag_args']"; + } + else + { + // This block is nested. + // Generate a namespace string for this block. + $namespace = implode('.', $block); + + // 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. + $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; + } + + $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; + + /** + * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory + * + * if (!$offset) + * { + * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; + * } + * + */ + + $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; + $tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; + + return $tag_template_php; + } + + /** + * Compile a general expression - much of this is from Smarty with + * some adaptions for our block level methods + * @access private + */ + private function compile_expression($tag_args) + { + $match = array(); + preg_match_all('/(?: + "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | + [(),] | + [^\s(),]+)/x', $tag_args, $match); + + $tokens = $match[0]; + $is_arg_stack = array(); + + for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) + { + $token = &$tokens[$i]; + + switch ($token) + { + case '!==': + case '===': + case '<<': + case '>>': + case '|': + case '^': + case '&': + case '~': + case ')': + case ',': + case '+': + case '-': + case '*': + case '/': + case '@': + break; + + case '==': + case 'eq': + $token = '=='; + break; + + case '!=': + case '<>': + case 'ne': + case 'neq': + $token = '!='; + break; + + case '<': + case 'lt': + $token = '<'; + break; + + case '<=': + case 'le': + case 'lte': + $token = '<='; + break; + + case '>': + case 'gt': + $token = '>'; + break; + + case '>=': + case 'ge': + case 'gte': + $token = '>='; + break; + + case '&&': + case 'and': + $token = '&&'; + break; + + case '||': + case 'or': + $token = '||'; + break; + + case '!': + case 'not': + $token = '!'; + break; + + case '%': + 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 = implode(' ', 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, sizeof($tokens), $new_tokens); + + $i = $is_arg_start; + + // no break + + default: + $varrefs = array(); + if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) + { + if (!empty($varrefs[1])) + { + $namespace = substr($varrefs[1], 0, -1); + $dot_pos = strrchr($namespace, '.'); + if ($dot_pos !== false) + { + $namespace = substr($dot_pos, 1); + } + + // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varrefs[3]) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $token = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $token = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $token = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $token = "'$namespace'"; + break; + + default: + $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; + break; + } + } + else + { + $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; + } + + } + else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) + { + // Allow checking if loops are set with .loopname + // It is also possible to check the loop count by doing for example + $blocks = explode('.', $varrefs[1]); + + // If the block is nested, we have a reference that we can grab. + // If the block is not nested, we just go and grab the block from _tpldata + if (sizeof($blocks) > 1) + { + $block = array_pop($blocks); + $namespace = implode('.', $blocks); + $varref = $this->generate_block_data_ref($namespace, true); + + // Add the block reference for the last child. + $varref .= "['" . $block . "']"; + } + else + { + $varref = '$_tpldata'; + + // Add the block reference for the last child. + $varref .= "['" . $blocks[0] . "']"; + } + $token = "(isset($varref) ? sizeof($varref) : 0)"; + } + + break; + } + } + + return $tokens; + } + + + private function compile_tag_if($tag_args, $elseif) + { + $tokens = $this->compile_expression($tag_args); + + $tpl = ($elseif) ? '} else if (' : 'if ('; + + $tpl .= implode(' ', $tokens); + $tpl .= ') { '; + + return $tpl; + } + + /** + * Compile DEFINE tags + * @access private + */ + private function compile_tag_define($tag_args, $op) + { + $match = array(); + preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); + + if (empty($match[2]) || (!isset($match[3]) && $op)) + { + return ''; + } + + if (!$op) + { + return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; + } + + $parsed_statement = implode(' ', $this->compile_expression($match[3])); + + return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; + } + + /** + * Compile INCLUDE tag + * @access private + */ + private function compile_tag_include($tag_args) + { + return "\$_template->_tpl_include('$tag_args');"; + } + + /** + * Compile INCLUDE_PHP tag + * @access private + */ + private function compile_tag_include_php($tag_args) + { + return "include('$tag_args');"; + } + + /** + * parse expression + * This is from Smarty + * @access private + */ + private 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "!($is_arg & 1)"; + } + break; + + case 'odd': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "($is_arg & 1)"; + } + break; + + case 'div': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!($is_arg % $expr_arg)"; + } + break; + } + + if ($negate_expr) + { + if ($expr[0] == '!') + { + // Negated expression, de-negate it. + $expr = substr($expr, 1); + } + else + { + $expr = "!($expr)"; + } + } + + array_splice($tokens, 0, $expr_end, $expr); + + return $tokens; + } + + /** + * Generates a reference to the given variable inside the given (possibly nested) + * block namespace. This is a string of the form: + * ' . $_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. + * + * @access private + * @param string $namespace Namespace to access (expects a trailing "." on the namespace) + * @param string $varname Variable name to use + * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable or echo it if $echo is true + */ + private function generate_block_varref($namespace, $varname, $echo = true, $defop = false) + { + // Strip the trailing period. + $namespace = substr($namespace, 0, -1); + + $expr = true; + $isset = false; + + // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varname) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $varref = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $varref = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $varref = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $varref = "'$namespace'"; + break; + + default: + // Get a reference to the data block for this namespace. + $varref = $this->generate_block_data_ref($namespace, true, $defop); + // Prepend the necessary code to stick this in an echo line. + + // Append the variable reference. + $varref .= "['$varname']"; + + $expr = false; + $isset = true; + break; + } + // @todo Test the !$expr more + $varref = ($echo) ? '' : (($expr || isset($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: + * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] + * + * @access private + * @param string $blockname Block to access (does not expect a trailing "." on the blockname) + * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable + */ + private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) + { + // Get an array of the blocks involved. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + // DEFINE is not an element of any referenced variable, we must use _tpldata to access it + if ($defop) + { + $varref = '$_tpldata[\'DEFINE\']'; + // 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; + } + else if ($include_last_iterator) + { + return '$_'. $blocks[$blockcount] . '_val'; + } + else + { + return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; + } + } +} + +stream_filter_register('phpbb_template', 'phpbb_template_filter'); + +/** +* Extension of template class - Functions needed for compiling templates only. +* +* psoTFX, phpBB Development Team - 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 +* +* DEFINE directive inspired by a request by Cyberalien +* +* @package phpBB3 +* @uses template_filter As a PHP stream filter to perform compilation of templates +*/ +class phpbb_template_compile +{ + /** + * Compiles template in $source_file and writes compiled template to + * cache directory + * @access public + * @param string $handle Template handle to compile + * @param string $source_file Source template file + * @return bool Return true on success otherwise false + */ + public function compile_file_to_file($source_file, $compiled_file) + { + $source_handle = @fopen($source_file, 'rb'); + $destination_handle = @fopen($compiled_file, 'wb'); + + if (!$source_handle || !$destination_handle) + { + return false; + } + + @flock($destination_handle, LOCK_EX); + + $this->compile_stream_to_stream($source_handle, $destination_handle); + + @fclose($source_handle); + @flock($destination_handle, LOCK_UN); + @fclose($destination_handle); + + phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE); + + clearstatcache(); + + return true; + } + + /** + * Compiles a template located at $source_file. + * Returns PHP source suitable for eval(). + * @access public + * @param string $source_file Source template file + * @return string|bool Return compiled code on successful compilation otherwise false + */ + public function compile_file($source_file) + { + $source_handle = @fopen($source_file, 'rb'); + $destination_handle = @fopen('php://temp' ,'r+b'); + + if (!$source_handle || !$destination_handle) + { + return false; + } + + $this->compile_stream_to_stream($source_handle, $destination_handle); + + @fclose($source_handle); + + rewind($destination_handle); + $contents = stream_get_contents($destination_handle); + @fclose($dest_handle); + + return $contents; + } + + /** + * Compiles contents of $source_stream into $dest_stream. + * + * A stream filter is appended to $source_stream as part of the + * process. + * + * @access private + * @param resource $source_stream Source stream + * @param resource $dest_stream Destination stream + * @return void + */ + private function compile_stream_to_stream($source_stream, $dest_stream) + { + stream_filter_append($source_stream, 'phpbb_template'); + stream_copy_to_stream($source_stream, $dest_stream); + } +} diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php new file mode 100644 index 0000000000..df5c4afb24 --- /dev/null +++ b/phpBB/includes/template/context.php @@ -0,0 +1,320 @@ + $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 array + */ + private $_tpldata = array('.' => array(0 => array())); + + /** + * @var array Reference to template->_tpldata['.'][0] + */ + private $_rootref; + + public function __construct() + { + $this->clear(); + } + + /** + * Clears template data set. + * @access public + */ + public function clear() + { + $this->_tpldata = array('.' => array(0 => array())); + $this->_rootref = &$this->_tpldata['.'][0]; + } + + /** + * Assign a single variable to a single key + * @access public + * @param string $varname Variable name + * @param string $varval Value to assign to variable + */ + public function assign_var($varname, $varval) + { + $this->_rootref[$varname] = $varval; + + return true; + } + + public function get_data_ref() + { + return $this->_tpldata; + } + + public function get_root_ref() + { + return $this->_rootref; + } + + /** + * Assign key variable pairs from an array to a specified block + * @access public + * @param string $blockname Name of block to assign $vararray to + * @param array $vararray A hash of variable name => value pairs + */ + public function assign_block_vars($blockname, array $vararray) + { + if (strpos($blockname, '.') !== false) + { + // 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]; + } + + $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; + $vararray['S_ROW_COUNT'] = $s_row_count; + + // Assign S_FIRST_ROW + if (!$s_row_count) + { + $vararray['S_FIRST_ROW'] = true; + } + + // Now the tricky part, we always assign S_LAST_ROW and remove the entry before + // This is much more clever than going through the complete template data on display (phew) + $vararray['S_LAST_ROW'] = true; + if ($s_row_count > 0) + { + unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']); + } + + // 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. + $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0; + $vararray['S_ROW_COUNT'] = $s_row_count; + + // Assign S_FIRST_ROW + if (!$s_row_count) + { + $vararray['S_FIRST_ROW'] = true; + } + + // We always assign S_LAST_ROW and remove the entry before + $vararray['S_LAST_ROW'] = true; + if ($s_row_count > 0) + { + unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); + } + + // Add a new iteration to this block with the variable assignments we were given. + $this->_tpldata[$blockname][] = $vararray; + } + + return true; + } + + /** + * Change already assigned key variable pair (one-dimensional - single loop entry) + * + * An example of how to use this function: + * {@example alter_block_array.php} + * + * @param string $blockname the blockname, for example 'loop' + * @param array $vararray the var array to insert/add or merge + * @param mixed $key Key to search for + * + * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] + * + * int: Position [the position to change or insert at directly given] + * + * If key is false the position is set to 0 + * If key is true the position is set to the last entry + * + * @param string $mode Mode to execute (valid modes are 'insert' and 'change') + * + * If insert, the vararray is inserted at the given position (position counting from zero). + * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). + * + * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) + * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) + * + * @return bool false on error, true on success + * @access public + */ + public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') + { + if (strpos($blockname, '.') !== false) + { + // Nested block. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = &$this->_tpldata; + for ($i = 0; $i < $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) + { + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) + { + $index = sizeof($block[$name]) - 1; + } + else + { + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); + } + } + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + $block = &$block[$name]; + $block = &$block[$index]; + } + + $block = &$block[$blocks[$i]]; // Traverse the last block + } + else + { + // Top-level block. + $block = &$this->_tpldata[$blockname]; + } + + // Change key to zero (change first position) if false and to last position if true + if ($key === false || $key === true) + { + $key = ($key === false) ? 0 : sizeof($block); + } + + // Get correct position if array given + if (is_array($key)) + { + // Search array to get correct position + list($search_key, $search_value) = @each($key); + + $key = NULL; + foreach ($block as $i => $val_ary) + { + if ($val_ary[$search_key] === $search_value) + { + $key = $i; + break; + } + } + + // key/value pair not found + if ($key === NULL) + { + return false; + } + } + + // Insert Block + if ($mode == 'insert') + { + // Make sure we are not exceeding the last iteration + if ($key >= sizeof($this->_tpldata[$blockname])) + { + $key = sizeof($this->_tpldata[$blockname]); + unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']); + $vararray['S_LAST_ROW'] = true; + } + else if ($key === 0) + { + unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']); + $vararray['S_FIRST_ROW'] = true; + } + + // Re-position template blocks + for ($i = sizeof($block); $i > $key; $i--) + { + $block[$i] = $block[$i-1]; + } + + // Insert vararray at given position + $block[$key] = $vararray; + + return true; + } + + // Which block to change? + if ($mode == 'change') + { + if ($key == sizeof($block)) + { + $key--; + } + + $block[$key] = array_merge($block[$key], $vararray); + + return true; + } + + return false; + } + + /** + * Reset/empty complete block + * @access public + * @param string $blockname Name of block to destroy + */ + public function destroy_block_vars($blockname) + { + if (strpos($blockname, '.') !== false) + { + // 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]; + } + + unset($str[$blocks[$blockcount]]); + } + else + { + // Top-level block. + unset($this->_tpldata[$blockname]); + } + + return true; + } +} diff --git a/phpBB/includes/template/renderer.php b/phpBB/includes/template/renderer.php new file mode 100644 index 0000000000..0af2d94bb6 --- /dev/null +++ b/phpBB/includes/template/renderer.php @@ -0,0 +1,35 @@ +code = $code; + $this->template = $template; + } + + /** + * Displays the template managed by this renderer by eval'ing php code + * of the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use + */ + public function render($context, $lang) + { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + + eval($this->code); + } +} diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php new file mode 100644 index 0000000000..aca3a3634d --- /dev/null +++ b/phpBB/includes/template/renderer_include.php @@ -0,0 +1,60 @@ +path = $path; + $this->template = $template; + } + + /** + * Displays the template managed by this renderer by including + * the php file containing the template. + * @param phpbb_template_context $context Template context to use + * @param array $lang Language entries to use + */ + public function render($context, $lang) + { + $_template = &$this->template; + $_tpldata = &$context->get_data_ref(); + $_rootref = &$context->get_root_ref(); + $_lang = &$lang; + + include($this->path); + } +} diff --git a/phpBB/includes/template_compile.php b/phpBB/includes/template_compile.php deleted file mode 100644 index 4132fb2e34..0000000000 --- a/phpBB/includes/template_compile.php +++ /dev/null @@ -1,941 +0,0 @@ -'; - - const REGEX_TOKENS = '~|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; - - /** - * @var array - */ - private $block_names = array(); - - /** - * @var array - */ - private $block_else_level = array(); - - /** - * @var string - */ - private $chunk; - - /** - * @var bool - */ - private $in_php; - - public function filter($in, $out, &$consumed, $closing) - { - $written = false; - - while ($bucket = stream_bucket_make_writeable($in)) - { - $consumed += $bucket->datalen; - - $data = $this->chunk . $bucket->data; - $last_nl = strrpos($data, "\n"); - $this->chunk = substr($data, $last_nl); - $data = substr($data, 0, $last_nl); - - if (!strlen($data)) - { - continue; - } - - $written = true; - - $bucket->data = $this->compile($data); - $bucket->datalen = strlen($bucket->data); - stream_bucket_append($out, $bucket); - } - - if ($closing && strlen($this->chunk)) - { - $written = true; - $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); - stream_bucket_append($out, $bucket); - } - - return $written ? PSFS_PASS_ON : PSFS_FEED_ME; - } - - public function onCreate() - { - $this->chunk = ''; - $this->in_php = false; - return true; - } - - private function compile($data) - { - $block_start_in_php = $this->in_php; - - $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); - $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); - - global $config; - - // Remove php - if (!$config['tpl_allow_php']) - { - if ($block_start_in_php - && $this->in_php - && strpos($data, '') === false - && strpos($data, '') === false) - { - // This is just php code - return ''; - } - $data = preg_replace('~^.*?~', '', $data); - $data = preg_replace('~.*?~', '', $data); - $data = preg_replace('~.*?$~', '', $data); - } - - /* - Preserve whitespace. - PHP removes a newline after the closing tag (if it's there). This is by design. - - - Consider the following template: - - - some content - - - If we were to simply preserve all whitespace, we could simply replace all "?>" tags - with "?>\n". - Doing that, would add additional newlines to the compiled tempalte in place of the - IF and ENDIF statements. These newlines are unwanted (and one is conditional). - The IF and ENDIF are usually on their own line for ease of reading. - - This replacement preserves newlines only for statements that aren't the only statement on a line. - It will NOT preserve newlines at the end of statements in the above examle. - It will preserve newlines in situations like: - - inline content - - - */ - - $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); - $data = str_replace('/**/?>', "?>\n", $data); - $data = str_replace('?>in_php && $matches[1] != 'ENDPHP') - { - return ''; - } - - if (isset($matches[3])) - { - return $this->compile_var_tags($matches[0]); - } - - global $config; - - switch ($matches[1]) - { - case 'BEGIN': - $this->block_else_level[] = false; - return 'compile_tag_block($matches[2]) . ' ?>'; - break; - - case 'BEGINELSE': - $this->block_else_level[sizeof($this->block_else_level) - 1] = true; - return ''; - break; - - case 'END': - array_pop($this->block_names); - return 'block_else_level)) ? '}' : '}}') . ' ?>'; - break; - - case 'IF': - return 'compile_tag_if($matches[2], false) . ' ?>'; - break; - - case 'ELSE': - return ''; - break; - - case 'ELSEIF': - return 'compile_tag_if($matches[2], true) . ' ?>'; - break; - - case 'ENDIF': - return ''; - break; - - case 'DEFINE': - return 'compile_tag_define($matches[2], true) . ' ?>'; - break; - - case 'UNDEFINE': - return 'compile_tag_define($matches[2], false) . ' ?>'; - break; - - case 'INCLUDE': - return 'compile_tag_include($matches[2]) . ' ?>'; - break; - - case 'INCLUDEPHP': - return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; - break; - - case 'PHP': - if ($config['tpl_allow_php']) - { - $this->in_php = true; - return ''; - break; - - case 'ENDPHP': - if ($config['tpl_allow_php']) - { - $this->in_php = false; - return ' ?>'; - } - return ''; - break; - - default: - return $matches[0]; - break; - - } - return ''; - } - - /** - * Compile variables - * @access private - */ - private function compile_var_tags(&$text_blocks) - { - // change template varrefs into PHP varrefs - $varrefs = array(); - - // This one will handle varrefs WITH namespaces - preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); - - foreach ($varrefs as $var_val) - { - $namespace = $var_val[1]; - $varname = $var_val[3]; - $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); - - $text_blocks = str_replace($var_val[0], $new, $text_blocks); - } - - // Handle special language tags L_ and LA_ - $this->compile_language_tags($text_blocks); - - // This will handle the remaining root-level varrefs - $text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - $text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - - return $text_blocks; - } - - /** - * Handles special language tags L_ and LA_ - */ - private function compile_language_tags(&$text_blocks) - { - // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array - if (strpos($text_blocks, '{L_') !== false) - { - $text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - } - - // Handle addslashed language variables prefixed with LA_ - // If a template variable already exist, it will be used in favor of it... - if (strpos($text_blocks, '{LA_') !== false) - { - $text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR . '+)\}#', "", $text_blocks); - } - } - - /** - * Compile blocks - * @access private - */ - private function compile_tag_block($tag_args) - { - $no_nesting = false; - - // Is the designer wanting to call another loop in a loop? - // - // - // - // - // 'loop2' is actually on the same nesting level as 'loop' you assign - // variables to it with template->assign_block_vars('loop2', array(...)) - if (strpos($tag_args, '!') === 0) - { - // Count the number if ! occurrences (not allowed in vars) - $no_nesting = substr_count($tag_args, '!'); - $tag_args = substr($tag_args, $no_nesting); - } - - // Allow for control of looping (indexes start from zero): - // foo(2) : Will start the loop on the 3rd entry - // foo(-2) : Will start the loop two entries from the end - // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth - // foo(3,-4) : Will start the loop on the fourth entry and end it four from last - $match = array(); - - if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) - { - $tag_args = $match[1]; - - if ($match[2] < 0) - { - $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; - } - else - { - $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; - } - - if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) - { - $loop_end = '$_' . $tag_args . '_count'; - } - else if ($match[3] >= 0) - { - $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; - } - else //if ($match[3] < -1) - { - $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); - } - } - else - { - $loop_start = 0; - $loop_end = '$_' . $tag_args . '_count'; - } - - $tag_template_php = ''; - array_push($this->block_names, $tag_args); - - if ($no_nesting !== false) - { - // We need to implode $no_nesting times from the end... - $block = array_slice($this->block_names, -$no_nesting); - } - else - { - $block = $this->block_names; - } - - if (sizeof($block) < 2) - { - // Block is not nested. - $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; - $varref = "\$_tpldata['$tag_args']"; - } - else - { - // This block is nested. - // Generate a namespace string for this block. - $namespace = implode('.', $block); - - // 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. - $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; - } - - $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; - - /** - * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory - * - * if (!$offset) - * { - * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; - * } - * - */ - - $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; - $tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; - - return $tag_template_php; - } - - /** - * Compile a general expression - much of this is from Smarty with - * some adaptions for our block level methods - * @access private - */ - private function compile_expression($tag_args) - { - $match = array(); - preg_match_all('/(?: - "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | - \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | - [(),] | - [^\s(),]+)/x', $tag_args, $match); - - $tokens = $match[0]; - $is_arg_stack = array(); - - for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) - { - $token = &$tokens[$i]; - - switch ($token) - { - case '!==': - case '===': - case '<<': - case '>>': - case '|': - case '^': - case '&': - case '~': - case ')': - case ',': - case '+': - case '-': - case '*': - case '/': - case '@': - break; - - case '==': - case 'eq': - $token = '=='; - break; - - case '!=': - case '<>': - case 'ne': - case 'neq': - $token = '!='; - break; - - case '<': - case 'lt': - $token = '<'; - break; - - case '<=': - case 'le': - case 'lte': - $token = '<='; - break; - - case '>': - case 'gt': - $token = '>'; - break; - - case '>=': - case 'ge': - case 'gte': - $token = '>='; - break; - - case '&&': - case 'and': - $token = '&&'; - break; - - case '||': - case 'or': - $token = '||'; - break; - - case '!': - case 'not': - $token = '!'; - break; - - case '%': - 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 = implode(' ', 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, sizeof($tokens), $new_tokens); - - $i = $is_arg_start; - - // no break - - default: - $varrefs = array(); - if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) - { - if (!empty($varrefs[1])) - { - $namespace = substr($varrefs[1], 0, -1); - $dot_pos = strrchr($namespace, '.'); - if ($dot_pos !== false) - { - $namespace = substr($dot_pos, 1); - } - - // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows - // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM - switch ($varrefs[3]) - { - case 'S_ROW_NUM': - case 'S_ROW_COUNT': - $token = "\$_${namespace}_i"; - break; - - case 'S_NUM_ROWS': - $token = "\$_${namespace}_count"; - break; - - case 'S_FIRST_ROW': - $token = "(\$_${namespace}_i == 0)"; - break; - - case 'S_LAST_ROW': - $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; - break; - - case 'S_BLOCK_NAME': - $token = "'$namespace'"; - break; - - default: - $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; - $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; - break; - } - } - else - { - $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; - $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; - } - - } - else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) - { - // Allow checking if loops are set with .loopname - // It is also possible to check the loop count by doing for example - $blocks = explode('.', $varrefs[1]); - - // If the block is nested, we have a reference that we can grab. - // If the block is not nested, we just go and grab the block from _tpldata - if (sizeof($blocks) > 1) - { - $block = array_pop($blocks); - $namespace = implode('.', $blocks); - $varref = $this->generate_block_data_ref($namespace, true); - - // Add the block reference for the last child. - $varref .= "['" . $block . "']"; - } - else - { - $varref = '$_tpldata'; - - // Add the block reference for the last child. - $varref .= "['" . $blocks[0] . "']"; - } - $token = "(isset($varref) ? sizeof($varref) : 0)"; - } - - break; - } - } - - return $tokens; - } - - - private function compile_tag_if($tag_args, $elseif) - { - $tokens = $this->compile_expression($tag_args); - - $tpl = ($elseif) ? '} else if (' : 'if ('; - - $tpl .= implode(' ', $tokens); - $tpl .= ') { '; - - return $tpl; - } - - /** - * Compile DEFINE tags - * @access private - */ - private function compile_tag_define($tag_args, $op) - { - $match = array(); - preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); - - if (empty($match[2]) || (!isset($match[3]) && $op)) - { - return ''; - } - - if (!$op) - { - return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; - } - - $parsed_statement = implode(' ', $this->compile_expression($match[3])); - - return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; - } - - /** - * Compile INCLUDE tag - * @access private - */ - private function compile_tag_include($tag_args) - { - return "\$_template->_tpl_include('$tag_args');"; - } - - /** - * Compile INCLUDE_PHP tag - * @access private - */ - private function compile_tag_include_php($tag_args) - { - return "include('$tag_args');"; - } - - /** - * parse expression - * This is from Smarty - * @access private - */ - private 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "!(($is_arg / $expr_arg) & 1)"; - } - else - { - $expr = "!($is_arg & 1)"; - } - break; - - case 'odd': - if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "(($is_arg / $expr_arg) & 1)"; - } - else - { - $expr = "($is_arg & 1)"; - } - break; - - case 'div': - if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "!($is_arg % $expr_arg)"; - } - break; - } - - if ($negate_expr) - { - if ($expr[0] == '!') - { - // Negated expression, de-negate it. - $expr = substr($expr, 1); - } - else - { - $expr = "!($expr)"; - } - } - - array_splice($tokens, 0, $expr_end, $expr); - - return $tokens; - } - - /** - * Generates a reference to the given variable inside the given (possibly nested) - * block namespace. This is a string of the form: - * ' . $_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. - * - * @access private - * @param string $namespace Namespace to access (expects a trailing "." on the namespace) - * @param string $varname Variable name to use - * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable - * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable - * @return string Code to access variable or echo it if $echo is true - */ - private function generate_block_varref($namespace, $varname, $echo = true, $defop = false) - { - // Strip the trailing period. - $namespace = substr($namespace, 0, -1); - - $expr = true; - $isset = false; - - // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows - // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM - switch ($varname) - { - case 'S_ROW_NUM': - case 'S_ROW_COUNT': - $varref = "\$_${namespace}_i"; - break; - - case 'S_NUM_ROWS': - $varref = "\$_${namespace}_count"; - break; - - case 'S_FIRST_ROW': - $varref = "(\$_${namespace}_i == 0)"; - break; - - case 'S_LAST_ROW': - $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; - break; - - case 'S_BLOCK_NAME': - $varref = "'$namespace'"; - break; - - default: - // Get a reference to the data block for this namespace. - $varref = $this->generate_block_data_ref($namespace, true, $defop); - // Prepend the necessary code to stick this in an echo line. - - // Append the variable reference. - $varref .= "['$varname']"; - - $expr = false; - $isset = true; - break; - } - // @todo Test the !$expr more - $varref = ($echo) ? '' : (($expr || isset($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: - * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] - * - * @access private - * @param string $blockname Block to access (does not expect a trailing "." on the blockname) - * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. - * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable - * @return string Code to access variable - */ - private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) - { - // Get an array of the blocks involved. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - - // DEFINE is not an element of any referenced variable, we must use _tpldata to access it - if ($defop) - { - $varref = '$_tpldata[\'DEFINE\']'; - // 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; - } - else if ($include_last_iterator) - { - return '$_'. $blocks[$blockcount] . '_val'; - } - else - { - return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; - } - } -} - -stream_filter_register('phpbb_template', 'phpbb_template_filter'); - -/** -* Extension of template class - Functions needed for compiling templates only. -* -* psoTFX, phpBB Development Team - 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 -* -* DEFINE directive inspired by a request by Cyberalien -* -* @package phpBB3 -* @uses template_filter As a PHP stream filter to perform compilation of templates -*/ -class phpbb_template_compile -{ - /** - * Compiles template in $source_file and writes compiled template to - * cache directory - * @access public - * @param string $handle Template handle to compile - * @param string $source_file Source template file - * @return bool Return true on success otherwise false - */ - public function compile_file_to_file($source_file, $compiled_file) - { - $source_handle = @fopen($source_file, 'rb'); - $destination_handle = @fopen($compiled_file, 'wb'); - - if (!$source_handle || !$destination_handle) - { - return false; - } - - @flock($destination_handle, LOCK_EX); - - $this->compile_stream_to_stream($source_handle, $destination_handle); - - @fclose($source_handle); - @flock($destination_handle, LOCK_UN); - @fclose($destination_handle); - - phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE); - - clearstatcache(); - - return true; - } - - /** - * Compiles a template located at $source_file. - * Returns PHP source suitable for eval(). - * @access public - * @param string $source_file Source template file - * @return string|bool Return compiled code on successful compilation otherwise false - */ - public function compile_file($source_file) - { - $source_handle = @fopen($source_file, 'rb'); - $destination_handle = @fopen('php://temp' ,'r+b'); - - if (!$source_handle || !$destination_handle) - { - return false; - } - - $this->compile_stream_to_stream($source_handle, $destination_handle); - - @fclose($source_handle); - - rewind($destination_handle); - $contents = stream_get_contents($destination_handle); - @fclose($dest_handle); - - return $contents; - } - - /** - * Compiles contents of $source_stream into $dest_stream. - * - * A stream filter is appended to $source_stream as part of the - * process. - * - * @access private - * @param resource $source_stream Source stream - * @param resource $dest_stream Destination stream - * @return void - */ - private function compile_stream_to_stream($source_stream, $dest_stream) - { - stream_filter_append($source_stream, 'phpbb_template'); - stream_copy_to_stream($source_stream, $dest_stream); - } -} diff --git a/phpBB/includes/template_context.php b/phpBB/includes/template_context.php deleted file mode 100644 index df5c4afb24..0000000000 --- a/phpBB/includes/template_context.php +++ /dev/null @@ -1,320 +0,0 @@ - $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 array - */ - private $_tpldata = array('.' => array(0 => array())); - - /** - * @var array Reference to template->_tpldata['.'][0] - */ - private $_rootref; - - public function __construct() - { - $this->clear(); - } - - /** - * Clears template data set. - * @access public - */ - public function clear() - { - $this->_tpldata = array('.' => array(0 => array())); - $this->_rootref = &$this->_tpldata['.'][0]; - } - - /** - * Assign a single variable to a single key - * @access public - * @param string $varname Variable name - * @param string $varval Value to assign to variable - */ - public function assign_var($varname, $varval) - { - $this->_rootref[$varname] = $varval; - - return true; - } - - public function get_data_ref() - { - return $this->_tpldata; - } - - public function get_root_ref() - { - return $this->_rootref; - } - - /** - * Assign key variable pairs from an array to a specified block - * @access public - * @param string $blockname Name of block to assign $vararray to - * @param array $vararray A hash of variable name => value pairs - */ - public function assign_block_vars($blockname, array $vararray) - { - if (strpos($blockname, '.') !== false) - { - // 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]; - } - - $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; - $vararray['S_ROW_COUNT'] = $s_row_count; - - // Assign S_FIRST_ROW - if (!$s_row_count) - { - $vararray['S_FIRST_ROW'] = true; - } - - // Now the tricky part, we always assign S_LAST_ROW and remove the entry before - // This is much more clever than going through the complete template data on display (phew) - $vararray['S_LAST_ROW'] = true; - if ($s_row_count > 0) - { - unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']); - } - - // 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. - $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0; - $vararray['S_ROW_COUNT'] = $s_row_count; - - // Assign S_FIRST_ROW - if (!$s_row_count) - { - $vararray['S_FIRST_ROW'] = true; - } - - // We always assign S_LAST_ROW and remove the entry before - $vararray['S_LAST_ROW'] = true; - if ($s_row_count > 0) - { - unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); - } - - // Add a new iteration to this block with the variable assignments we were given. - $this->_tpldata[$blockname][] = $vararray; - } - - return true; - } - - /** - * Change already assigned key variable pair (one-dimensional - single loop entry) - * - * An example of how to use this function: - * {@example alter_block_array.php} - * - * @param string $blockname the blockname, for example 'loop' - * @param array $vararray the var array to insert/add or merge - * @param mixed $key Key to search for - * - * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] - * - * int: Position [the position to change or insert at directly given] - * - * If key is false the position is set to 0 - * If key is true the position is set to the last entry - * - * @param string $mode Mode to execute (valid modes are 'insert' and 'change') - * - * If insert, the vararray is inserted at the given position (position counting from zero). - * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). - * - * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) - * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) - * - * @return bool false on error, true on success - * @access public - */ - public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') - { - if (strpos($blockname, '.') !== false) - { - // Nested block. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - - $block = &$this->_tpldata; - for ($i = 0; $i < $blockcount; $i++) - { - if (($pos = strpos($blocks[$i], '[')) !== false) - { - $name = substr($blocks[$i], 0, $pos); - - if (strpos($blocks[$i], '[]') === $pos) - { - $index = sizeof($block[$name]) - 1; - } - else - { - $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); - } - } - else - { - $name = $blocks[$i]; - $index = sizeof($block[$name]) - 1; - } - $block = &$block[$name]; - $block = &$block[$index]; - } - - $block = &$block[$blocks[$i]]; // Traverse the last block - } - else - { - // Top-level block. - $block = &$this->_tpldata[$blockname]; - } - - // Change key to zero (change first position) if false and to last position if true - if ($key === false || $key === true) - { - $key = ($key === false) ? 0 : sizeof($block); - } - - // Get correct position if array given - if (is_array($key)) - { - // Search array to get correct position - list($search_key, $search_value) = @each($key); - - $key = NULL; - foreach ($block as $i => $val_ary) - { - if ($val_ary[$search_key] === $search_value) - { - $key = $i; - break; - } - } - - // key/value pair not found - if ($key === NULL) - { - return false; - } - } - - // Insert Block - if ($mode == 'insert') - { - // Make sure we are not exceeding the last iteration - if ($key >= sizeof($this->_tpldata[$blockname])) - { - $key = sizeof($this->_tpldata[$blockname]); - unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']); - $vararray['S_LAST_ROW'] = true; - } - else if ($key === 0) - { - unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']); - $vararray['S_FIRST_ROW'] = true; - } - - // Re-position template blocks - for ($i = sizeof($block); $i > $key; $i--) - { - $block[$i] = $block[$i-1]; - } - - // Insert vararray at given position - $block[$key] = $vararray; - - return true; - } - - // Which block to change? - if ($mode == 'change') - { - if ($key == sizeof($block)) - { - $key--; - } - - $block[$key] = array_merge($block[$key], $vararray); - - return true; - } - - return false; - } - - /** - * Reset/empty complete block - * @access public - * @param string $blockname Name of block to destroy - */ - public function destroy_block_vars($blockname) - { - if (strpos($blockname, '.') !== false) - { - // 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]; - } - - unset($str[$blocks[$blockcount]]); - } - else - { - // Top-level block. - unset($this->_tpldata[$blockname]); - } - - return true; - } -} diff --git a/phpBB/includes/template_renderer.php b/phpBB/includes/template_renderer.php deleted file mode 100644 index 0af2d94bb6..0000000000 --- a/phpBB/includes/template_renderer.php +++ /dev/null @@ -1,35 +0,0 @@ -code = $code; - $this->template = $template; - } - - /** - * Displays the template managed by this renderer by eval'ing php code - * of the template. - * @param phpbb_template_context $context Template context to use - * @param array $lang Language entries to use - */ - public function render($context, $lang) - { - $_template = &$this->template; - $_tpldata = &$context->get_data_ref(); - $_rootref = &$context->get_root_ref(); - $_lang = &$lang; - - eval($this->code); - } -} diff --git a/phpBB/includes/template_renderer_include.php b/phpBB/includes/template_renderer_include.php deleted file mode 100644 index aca3a3634d..0000000000 --- a/phpBB/includes/template_renderer_include.php +++ /dev/null @@ -1,60 +0,0 @@ -path = $path; - $this->template = $template; - } - - /** - * Displays the template managed by this renderer by including - * the php file containing the template. - * @param phpbb_template_context $context Template context to use - * @param array $lang Language entries to use - */ - public function render($context, $lang) - { - $_template = &$this->template; - $_tpldata = &$context->get_data_ref(); - $_rootref = &$context->get_root_ref(); - $_lang = &$lang; - - include($this->path); - } -} -- cgit v1.2.1 From 0462ab3a4a5cea64699eaf4b2e9900e36d027e50 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Mon, 9 May 2011 22:00:38 -0400 Subject: [feature/template-engine] Add back IN_PHPBB preamble. PHPBB3-9726 --- phpBB/includes/template/compile.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 4132fb2e34..18a52e87a6 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -54,6 +54,7 @@ class phpbb_template_filter extends php_user_filter public function filter($in, $out, &$consumed, $closing) { $written = false; + $first = false; while ($bucket = stream_bucket_make_writeable($in)) { @@ -71,7 +72,13 @@ class phpbb_template_filter extends php_user_filter $written = true; - $bucket->data = $this->compile($data); + $data = $this->compile($data); + if (!$first) + { + $data = $this->prepend_preamble($data); + $first = false; + } + $bucket->data = $data; $bucket->datalen = strlen($bucket->data); stream_bucket_append($out, $bucket); } @@ -150,6 +157,18 @@ class phpbb_template_filter extends php_user_filter return $data; } + /** + * Prepends a preamble to compiled template. + * Currently preamble checks if IN_PHPBB is defined and calls exit() if it is not. + * @param string $data Compiled template chunk + * @return string Compiled template chunk with preamble prepended + */ + private function prepend_preamble($data) + { + $data = "' . $data); + return $data; + } + private function replace($matches) { if ($this->in_php && $matches[1] != 'ENDPHP') -- cgit v1.2.1 From df76885b11e56348f7fbe6274a6dbc727bb677ad Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Mon, 9 May 2011 22:01:07 -0400 Subject: [feature/template-engine] Reinstate phpbb_template#destroy function. PHPBB3-9726 --- phpBB/includes/template.php | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index f27d0c7560..c03b87b13c 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -167,6 +167,15 @@ class phpbb_template return true; } + /** + * Clears all variables and blocks assigned to this template. + * @access public + */ + public function destroy() + { + $this->_context->clear(); + } + /** * Reset/empty complete block * @access public -- cgit v1.2.1 From 0ca7ad66dc8d9173394e0c5fb7e8393977de22ed Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Mon, 9 May 2011 22:01:54 -0400 Subject: [feature/template-engine] Refactor hook logic into a separate function. PHPBB3-9726 --- phpBB/includes/template.php | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index c03b87b13c..9a030ffd35 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -195,14 +195,10 @@ class phpbb_template */ public function display($handle, $include_once = true) { - global $phpbb_hook; - - if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) + $result = $this->call_hook($handle, $include_once); + if ($result !== false) { - if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) - { - return $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__)); - } + return $result[0]; } /* @@ -228,6 +224,27 @@ class phpbb_template } } + /** + * Calls hook if any is defined. + * @param string $handle Template handle being displayed. + * @param bool $include_once Allow multiple inclusions + */ + private function call_hook($handle, $include_once) + { + global $phpbb_hook; + + if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) + { + if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) + { + $result = $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__)); + return array($result); + } + } + + return false; + } + /** * Obtains language array. * This is either lang property of global $user object, or if -- cgit v1.2.1 From 97d2a6527e75a5db9249a7c57360087cbaf424ac Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Mon, 9 May 2011 22:02:14 -0400 Subject: [feature/template-engine] Remove commented out error reporting logic. PHPBB3-9726 --- phpBB/includes/template.php | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 9a030ffd35..a30690615d 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -201,16 +201,6 @@ class phpbb_template return $result[0]; } - /* - if (defined('IN_ERROR_HANDLER')) - { - if ((E_NOTICE & error_reporting()) == E_NOTICE) - { - error_reporting(error_reporting() ^ E_NOTICE); - } - } - */ - $renderer = $this->_tpl_load($handle); if ($renderer) -- cgit v1.2.1 From 169c4377e98826432411db773ebcbfa19bcdb439 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 09:04:07 -0400 Subject: [feature/template-engine] Disposed of underscores in property names. PHPBB3-9726 --- phpBB/includes/template.php | 20 ++++++++--------- phpBB/includes/template/context.php | 44 ++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index a30690615d..4dadfac571 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -37,7 +37,7 @@ class phpbb_template * Stores template data used during template rendering. * @access private */ - private $_context; + private $context; /** * @var string Root dir for template. @@ -95,7 +95,7 @@ class phpbb_template trigger_error('Template path could not be found: styles/' . $user->theme['template_path'] . '/template', E_USER_ERROR); } - $this->_context = new phpbb_template_context(); + $this->context = new phpbb_template_context(); return true; } @@ -135,7 +135,7 @@ class phpbb_template $this->orig_tpl_inherits_id = false; } - $this->_context = new phpbb_template_context(); + $this->context = new phpbb_template_context(); return true; } @@ -173,7 +173,7 @@ class phpbb_template */ public function destroy() { - $this->_context->clear(); + $this->context->clear(); } /** @@ -183,7 +183,7 @@ class phpbb_template */ public function destroy_block_vars($blockname) { - $this->_context->destroy_block_vars($blockname); + $this->context->destroy_block_vars($blockname); } /** @@ -205,7 +205,7 @@ class phpbb_template if ($renderer) { - $renderer->render($this->_context, $this->get_lang()); + $renderer->render($this->context, $this->get_lang()); return true; } else @@ -439,7 +439,7 @@ class phpbb_template */ public function assign_var($varname, $varval) { - $this->_context->assign_var($varname, $varval); + $this->context->assign_var($varname, $varval); } // Docstring is copied from phpbb_template_context method with the same name. @@ -451,7 +451,7 @@ class phpbb_template */ public function assign_block_vars($blockname, array $vararray) { - return $this->_context->assign_block_vars($blockname, $vararray); + return $this->context->assign_block_vars($blockname, $vararray); } // Docstring is copied from phpbb_template_context method with the same name. @@ -485,7 +485,7 @@ class phpbb_template */ public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { - return $this->_context->alter_block_array($blockname, $vararray, $key, $mode); + return $this->context->alter_block_array($blockname, $vararray, $key, $mode); } /** @@ -509,7 +509,7 @@ class phpbb_template if ($renderer) { - $renderer->render($this->_context, $this->get_lang()); + $renderer->render($this->context, $this->get_lang()); } else { diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index df5c4afb24..c667282f7b 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -26,17 +26,17 @@ class phpbb_template_context /** * variable that holds all the data we'll be substituting into * the compiled templates. Takes form: - * --> $this->_tpldata[block][iteration#][child][iteration#][child2][iteration#][variablename] == value + * --> $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 array */ - private $_tpldata = array('.' => array(0 => array())); + private $tpldata = array('.' => array(0 => array())); /** - * @var array Reference to template->_tpldata['.'][0] + * @var array Reference to template->tpldata['.'][0] */ - private $_rootref; + private $rootref; public function __construct() { @@ -49,8 +49,8 @@ class phpbb_template_context */ public function clear() { - $this->_tpldata = array('.' => array(0 => array())); - $this->_rootref = &$this->_tpldata['.'][0]; + $this->tpldata = array('.' => array(0 => array())); + $this->rootref = &$this->tpldata['.'][0]; } /** @@ -61,19 +61,19 @@ class phpbb_template_context */ public function assign_var($varname, $varval) { - $this->_rootref[$varname] = $varval; + $this->rootref[$varname] = $varval; return true; } public function get_data_ref() { - return $this->_tpldata; + return $this->tpldata; } public function get_root_ref() { - return $this->_rootref; + return $this->rootref; } /** @@ -90,7 +90,7 @@ class phpbb_template_context $blocks = explode('.', $blockname); $blockcount = sizeof($blocks) - 1; - $str = &$this->_tpldata; + $str = &$this->tpldata; for ($i = 0; $i < $blockcount; $i++) { $str = &$str[$blocks[$i]]; @@ -122,7 +122,7 @@ class phpbb_template_context else { // Top-level block. - $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0; + $s_row_count = (isset($this->tpldata[$blockname])) ? sizeof($this->tpldata[$blockname]) : 0; $vararray['S_ROW_COUNT'] = $s_row_count; // Assign S_FIRST_ROW @@ -135,11 +135,11 @@ class phpbb_template_context $vararray['S_LAST_ROW'] = true; if ($s_row_count > 0) { - unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); + unset($this->tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']); } // Add a new iteration to this block with the variable assignments we were given. - $this->_tpldata[$blockname][] = $vararray; + $this->tpldata[$blockname][] = $vararray; } return true; @@ -181,7 +181,7 @@ class phpbb_template_context $blocks = explode('.', $blockname); $blockcount = sizeof($blocks) - 1; - $block = &$this->_tpldata; + $block = &$this->tpldata; for ($i = 0; $i < $blockcount; $i++) { if (($pos = strpos($blocks[$i], '[')) !== false) @@ -211,7 +211,7 @@ class phpbb_template_context else { // Top-level block. - $block = &$this->_tpldata[$blockname]; + $block = &$this->tpldata[$blockname]; } // Change key to zero (change first position) if false and to last position if true @@ -247,15 +247,15 @@ class phpbb_template_context if ($mode == 'insert') { // Make sure we are not exceeding the last iteration - if ($key >= sizeof($this->_tpldata[$blockname])) + if ($key >= sizeof($this->tpldata[$blockname])) { - $key = sizeof($this->_tpldata[$blockname]); - unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']); + $key = sizeof($this->tpldata[$blockname]); + unset($this->tpldata[$blockname][($key - 1)]['S_LAST_ROW']); $vararray['S_LAST_ROW'] = true; } else if ($key === 0) { - unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']); + unset($this->tpldata[$blockname][0]['S_FIRST_ROW']); $vararray['S_FIRST_ROW'] = true; } @@ -300,7 +300,7 @@ class phpbb_template_context $blocks = explode('.', $blockname); $blockcount = sizeof($blocks) - 1; - $str = &$this->_tpldata; + $str = &$this->tpldata; for ($i = 0; $i < $blockcount; $i++) { $str = &$str[$blocks[$i]]; @@ -312,7 +312,7 @@ class phpbb_template_context else { // Top-level block. - unset($this->_tpldata[$blockname]); + unset($this->tpldata[$blockname]); } return true; -- cgit v1.2.1 From 635460fa6dda70be8ae8b36c4c4be012c9c0a590 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 09:12:52 -0400 Subject: [feature/template-engine] Fixed reference usage. Hopefully this is right, I have not checked it against the manual (assuming the manual even covers these things). PHPBB3-9726 --- phpBB/includes/template/context.php | 6 +++++- phpBB/includes/template/renderer_eval.php | 2 +- phpBB/includes/template/renderer_include.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index c667282f7b..695f6e0ed3 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -68,11 +68,15 @@ class phpbb_template_context public function get_data_ref() { - return $this->tpldata; + // returning a reference directly is not + // something php is capable of doing + $ref = &$this->tpldata; + return $ref; } public function get_root_ref() { + // rootref is already a reference return $this->rootref; } diff --git a/phpBB/includes/template/renderer_eval.php b/phpBB/includes/template/renderer_eval.php index a3d7f88fa5..f3365ff8a7 100644 --- a/phpBB/includes/template/renderer_eval.php +++ b/phpBB/includes/template/renderer_eval.php @@ -50,7 +50,7 @@ class phpbb_template_renderer_eval implements phpbb_template_renderer */ public function render($context, $lang) { - $_template = &$this->template; + $_template = $this->template; $_tpldata = &$context->get_data_ref(); $_rootref = &$context->get_root_ref(); $_lang = &$lang; diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php index aca3a3634d..28a8027df3 100644 --- a/phpBB/includes/template/renderer_include.php +++ b/phpBB/includes/template/renderer_include.php @@ -50,7 +50,7 @@ class phpbb_template_renderer_include implements phpbb_template_renderer */ public function render($context, $lang) { - $_template = &$this->template; + $_template = $this->template; $_tpldata = &$context->get_data_ref(); $_rootref = &$context->get_root_ref(); $_lang = &$lang; -- cgit v1.2.1 From d6f75e97d60981efd3ca2792e1a9d379d922631d Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 09:17:20 -0400 Subject: [feature/template-engine] Added docblocks to get_*_ref in context. PHPBB3-9726 --- phpBB/includes/template/context.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index 695f6e0ed3..73f1373655 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -66,6 +66,17 @@ class phpbb_template_context return true; } + /** + * Returns a reference to template data array. + * + * This function is public so that template renderer may invoke it. + * Users should alter template variables via functions in phpbb_template. + * + * Note: modifying returned array will affect data stored in the context. + * + * @access public + * @return array template data + */ public function get_data_ref() { // returning a reference directly is not @@ -74,6 +85,17 @@ class phpbb_template_context return $ref; } + /** + * Returns a reference to template root scope. + * + * This function is public so that template renderer may invoke it. + * Users should not need to invoke this function. + * + * Note: modifying returned array will affect data stored in the context. + * + * @access public + * @return array template data + */ public function get_root_ref() { // rootref is already a reference -- cgit v1.2.1 From 504acaba6b898de13d1c27dde567f1a01c0b0bd6 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 09:23:16 -0400 Subject: [feature/template-engine] Deleted useless assignment. PHPBB3-9726 --- phpBB/includes/template.php | 1 - 1 file changed, 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 4dadfac571..85d378fa67 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -325,7 +325,6 @@ class phpbb_template $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; - $recompile = false; $recompile = (!file_exists($filename) || @filesize($filename) === 0 || ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle]))) ? true : false; if (!$recompile) -- cgit v1.2.1 From 77787718196c05b98efccec668c4a9762591398f Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 19:25:07 -0400 Subject: [feature/template-engine] Move DEBUG_EXTRA check for $recompile up. PHPBB3-9726 --- phpBB/includes/template.php | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 85d378fa67..865535cfd5 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -325,24 +325,20 @@ class phpbb_template $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; - $recompile = (!file_exists($filename) || @filesize($filename) === 0 || ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle]))) ? true : false; + $recompile = defined('DEBUG_EXTRA') || + !file_exists($filename) || + @filesize($filename) === 0 || + ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle])); - if (!$recompile) + if (!$recompile && $config['load_tplcompile']) { - if (defined('DEBUG_EXTRA')) - { - $recompile = true; - } - else if ($config['load_tplcompile']) + // No way around it: we need to check inheritance here + if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { - // No way around it: we need to check inheritance here - if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) - { - $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $user->theme['template_inherits_id']; - } - $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; + $this->files[$handle] = $this->files_inherit[$handle]; + $this->files_template[$handle] = $user->theme['template_inherits_id']; } + $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; } // Recompile page if the original template is newer, otherwise load the compiled version -- cgit v1.2.1 From 7638bcb56011ebde65005a0ce1abfe7f4a6be4b7 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 19:32:32 -0400 Subject: [feature/template-engine] Rename $filename to $compiled_path for clarity. PHPBB3-9726 --- phpBB/includes/template.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 865535cfd5..2ebbca3568 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -322,13 +322,13 @@ class phpbb_template // by other template class instances in between. $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; + $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; $recompile = defined('DEBUG_EXTRA') || - !file_exists($filename) || - @filesize($filename) === 0 || - ($config['load_tplcompile'] && @filemtime($filename) < @filemtime($this->files[$handle])); + !file_exists($compiled_path) || + @filesize($compiled_path) === 0 || + ($config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); if (!$recompile && $config['load_tplcompile']) { @@ -338,13 +338,13 @@ class phpbb_template $this->files[$handle] = $this->files_inherit[$handle]; $this->files_template[$handle] = $user->theme['template_inherits_id']; } - $recompile = (@filemtime($filename) < @filemtime($this->files[$handle])) ? true : false; + $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; } // Recompile page if the original template is newer, otherwise load the compiled version if (!$recompile) { - return new phpbb_template_renderer_include($filename, $this); + return new phpbb_template_renderer_include($compiled_path, $this); } // Inheritance - we point to another template file for this one. -- cgit v1.2.1 From d2daaf0317ac6bb224b8f93c1ac9578c9d570dc9 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 19:38:04 -0400 Subject: [feature/template-engine] Try to handle failed template includes. PHPBB3-9726 --- phpBB/includes/template.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 2ebbca3568..90b8bb2a22 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -508,7 +508,8 @@ class phpbb_template } else { - // What should we do here? + // trigger_error cannot be used here, as the output already started + echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; } } @@ -525,7 +526,7 @@ class phpbb_template if (!file_exists($file)) { // trigger_error cannot be used here, as the output already started - echo 'template->_php_include(): File ' . htmlspecialchars($file) . ' does not exist or is empty'; + echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n"; return; } include($file); -- cgit v1.2.1 From 345852d240fe224fa825f90b0ef8a34398c91291 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 11 May 2011 19:43:08 -0400 Subject: [feature/template-engine] Clarify cache directory path for set_custom_template Even if the template may be outside of phpBB, phpBB's cache directory is still going to be used for storing compiled template code. PHPBB3-9726 --- phpBB/includes/template.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 90b8bb2a22..daff785dc0 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -101,7 +101,10 @@ class phpbb_template } /** - * Set custom template location (able to use directory outside of phpBB) + * Set custom template location (able to use directory outside of phpBB). + * + * Note: Templates are still compiled to phpBB's cache directory. + * * @access public * @param string $template_path Path to template directory * @param string $template_name Name of template -- cgit v1.2.1 From d2ac05aa74d3a1c9723be41a4c0ffe86542f223f Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Thu, 12 May 2011 18:08:36 -0400 Subject: [feature/template-engine] Replaced globals with dependency injection. PHPBB3-9726 --- phpBB/includes/functions_messenger.php | 4 +- phpBB/includes/template.php | 93 +++++++++++++++++++++------------- 2 files changed, 60 insertions(+), 37 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 8255d4b148..da7e7cb9e4 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -175,7 +175,7 @@ class messenger */ function template($template_file, $template_lang = '', $template_path = '') { - global $config, $phpbb_root_path, $user; + global $config, $phpbb_root_path, $phpEx, $user; if (!trim($template_file)) { @@ -193,7 +193,7 @@ class messenger // tpl_msg now holds a template object we can use to parse the template file if (!isset($this->tpl_msg[$template_lang . $template_file])) { - $this->tpl_msg[$template_lang . $template_file] = new phpbb_template(); + $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user); $tpl = &$this->tpl_msg[$template_lang . $template_file]; $fallback_template_path = false; diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index daff785dc0..64a3b80f07 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -65,34 +65,67 @@ class phpbb_template public $orig_tpl_inherits_id; + /** + * @var string phpBB root path + */ + private $phpbb_root_path; + + /** + * @var phpEx PHP file extension + */ + private $phpEx; + + /** + * @var phpbb_config phpBB config instance + */ + private $config; + + /** + * @var user current user + */ + private $user; + + /** + * Constructor. + * + * @param string $phpbb_root_path phpBB root path + * @param user $user current user + */ + public function __construct($phpbb_root_path, $phpEx, $config, $user) + { + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = $phpEx; + $this->config = $config; + $this->user = $user; + } + /** * Set template location * @access public */ public function set_template() { - global $phpbb_root_path, $user; - - if (file_exists($phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template')) + $template_path = $this->user->theme['template_path']; + if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template')) { - $this->root = $phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template'; - $this->cachepath = $phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $user->theme['template_path']) . '_'; + $this->root = $this->phpbb_root_path . 'styles/' . $template_path . '/template'; + $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_'; if ($this->orig_tpl_inherits_id === null) { - $this->orig_tpl_inherits_id = $user->theme['template_inherits_id']; + $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; } - $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - if ($user->theme['template_inherits_id']) + if ($this->user->theme['template_inherits_id']) { - $this->inherit_root = $phpbb_root_path . 'styles/' . $user->theme['template_inherit_path'] . '/template'; + $this->inherit_root = $this->phpbb_root_path . 'styles/' . $this->user->theme['template_inherit_path'] . '/template'; } } else { - trigger_error('Template path could not be found: styles/' . $user->theme['template_path'] . '/template', E_USER_ERROR); + trigger_error('Template path could not be found: styles/' . $template_path . '/template', E_USER_ERROR); } $this->context = new phpbb_template_context(); @@ -112,8 +145,6 @@ class phpbb_template */ public function set_custom_template($template_path, $template_name, $fallback_template_path = false) { - global $phpbb_root_path, $user; - // Make sure $template_path has no ending slash if (substr($template_path, -1) == '/') { @@ -121,7 +152,7 @@ class phpbb_template } $this->root = $template_path; - $this->cachepath = $phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; + $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; if ($fallback_template_path !== false) { @@ -240,17 +271,15 @@ class phpbb_template /** * Obtains language array. - * This is either lang property of global $user object, or if + * This is either lang property of $user property, or if * it is not set an empty array. * @return array language entries */ public function get_lang() { - global $user; - - if (isset($user->lang)) + if (isset($this->user->lang)) { - $lang = $user->lang; + $lang = $this->user->lang; } else { @@ -313,8 +342,6 @@ class phpbb_template */ private function _tpl_load($handle) { - global $user, $phpEx, $config; - if (!isset($this->filename[$handle])) { trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); @@ -323,23 +350,23 @@ class phpbb_template // reload this setting to have the values they had when this object was initialised // using set_template or set_custom_template, they might otherwise have been overwritten // by other template class instances in between. - $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; - $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; + $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; + $this->files_template[$handle] = (isset($this->user->theme['template_id'])) ? $this->user->theme['template_id'] : 0; $recompile = defined('DEBUG_EXTRA') || !file_exists($compiled_path) || @filesize($compiled_path) === 0 || - ($config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); + ($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); - if (!$recompile && $config['load_tplcompile']) + if (!$recompile && $this->config['load_tplcompile']) { // No way around it: we need to check inheritance here - if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + if ($this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $user->theme['template_inherits_id']; + $this->files_template[$handle] = $this->user->theme['template_inherits_id']; } $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; } @@ -351,10 +378,10 @@ class phpbb_template } // Inheritance - we point to another template file for this one. - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + if (isset($this->user->theme['template_inherits_id']) && $this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $user->theme['template_inherits_id']; + $this->files_template[$handle] = $this->user->theme['template_inherits_id']; } $source_file = $this->_source_file_for_handle($handle); @@ -410,9 +437,7 @@ class phpbb_template */ private function _compiled_file_for_handle($handle) { - global $phpEx; - - $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; + $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; return $compiled_file; } @@ -522,9 +547,7 @@ class phpbb_template */ private function _php_include($filename) { - global $phpbb_root_path; - - $file = $phpbb_root_path . $filename; + $file = $this->phpbb_root_path . $filename; if (!file_exists($file)) { -- cgit v1.2.1 From 94560d708649df876f8486d4ba5361475037f1ff Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Thu, 12 May 2011 20:09:41 -0400 Subject: [feature/template-engine] Make INCLUDEPHP relative to board root. PHPBB3-9726 --- phpBB/includes/template.php | 6 +++--- phpBB/includes/template/compile.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 64a3b80f07..f2b1cccd2c 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -513,7 +513,7 @@ class phpbb_template /** * Include a separate template - * @access private + * @access public * @param string $filename Template filename to include * @param bool $include True to include the file, false to just load it * @uses template_compile is used to compile uncached templates @@ -543,9 +543,9 @@ class phpbb_template /** * Include a php-file - * @access private + * @access public */ - private function _php_include($filename) + public function _php_include($filename) { $file = $this->phpbb_root_path . $filename; diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 18a52e87a6..46a30d26d3 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -667,7 +667,7 @@ class phpbb_template_filter extends php_user_filter */ private function compile_tag_include_php($tag_args) { - return "include('$tag_args');"; + return "\$_template->_php_include('$tag_args');"; } /** -- cgit v1.2.1 From b04f0a5f70ed79d45e254d7b6db61f6fc1f484f5 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Fri, 13 May 2011 10:49:53 -0400 Subject: [feature/template-engine] Delete @access everywhere. Access specification in php 5 is done directly on functions/properties. PHPBB3-9726 --- phpBB/includes/template.php | 17 ----------------- phpBB/includes/template/compile.php | 12 ------------ phpBB/includes/template/context.php | 7 ------- 3 files changed, 36 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index f2b1cccd2c..5fb0f41311 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -35,7 +35,6 @@ class phpbb_template /** * @var phpbb_template_context Template context. * Stores template data used during template rendering. - * @access private */ private $context; @@ -101,7 +100,6 @@ class phpbb_template /** * Set template location - * @access public */ public function set_template() { @@ -138,7 +136,6 @@ class phpbb_template * * Note: Templates are still compiled to phpBB's cache directory. * - * @access public * @param string $template_path Path to template directory * @param string $template_name Name of template * @param string $fallback_template_path Path to fallback template @@ -177,7 +174,6 @@ class phpbb_template /** * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. - * @access public * @param array $filname_array Should be a hash of handle => filename pairs. */ public function set_filenames(array $filename_array) @@ -203,7 +199,6 @@ class phpbb_template /** * Clears all variables and blocks assigned to this template. - * @access public */ public function destroy() { @@ -212,7 +207,6 @@ class phpbb_template /** * Reset/empty complete block - * @access public * @param string $blockname Name of block to destroy */ public function destroy_block_vars($blockname) @@ -222,7 +216,6 @@ class phpbb_template /** * Display handle - * @access public * @param string $handle Handle to display * @param bool $include_once Allow multiple inclusions * @return bool True on success, false on failure @@ -290,7 +283,6 @@ class phpbb_template /** * Display the handle and assign the output to a template variable or return the compiled result. - * @access public * @param string $handle Handle to operate on * @param string $template_var Template variable to assign compiled handle to * @param bool $return_content If true return compiled handle, otherwise assign to $template_var @@ -335,7 +327,6 @@ class phpbb_template * contents sent to the output stream (unless, of course, output * buffering is in effect). * - * @access private * @param string $handle Handle of the template to load * @return phpbb_template_renderer Template renderer object, or null on failure * @uses template_compile is used to compile template source @@ -407,7 +398,6 @@ class phpbb_template /** * Resolves template handle $handle to source file path. - * @access private * @param string $handle Template handle (i.e. "friendly" template name) * @return string Source file path */ @@ -431,7 +421,6 @@ class phpbb_template /** * Determines compiled file path for handle $handle. - * @access private * @param string $handle Template handle (i.e. "friendly" template name) * @return string Compiled file path */ @@ -443,7 +432,6 @@ class phpbb_template /** * Assign key variable pairs from an array - * @access public * @param array $vararray A hash of variable name => value pairs */ public function assign_vars(array $vararray) @@ -456,7 +444,6 @@ class phpbb_template /** * Assign a single variable to a single key - * @access public * @param string $varname Variable name * @param string $varval Value to assign to variable */ @@ -468,7 +455,6 @@ class phpbb_template // Docstring is copied from phpbb_template_context method with the same name. /** * Assign key variable pairs from an array to a specified block - * @access public * @param string $blockname Name of block to assign $vararray to * @param array $vararray A hash of variable name => value pairs */ @@ -504,7 +490,6 @@ class phpbb_template * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) * * @return bool false on error, true on success - * @access public */ public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { @@ -513,7 +498,6 @@ class phpbb_template /** * Include a separate template - * @access public * @param string $filename Template filename to include * @param bool $include True to include the file, false to just load it * @uses template_compile is used to compile uncached templates @@ -543,7 +527,6 @@ class phpbb_template /** * Include a php-file - * @access public */ public function _php_include($filename) { diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 46a30d26d3..79a7d784cd 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -260,7 +260,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile variables - * @access private */ private function compile_var_tags(&$text_blocks) { @@ -310,7 +309,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile blocks - * @access private */ private function compile_tag_block($tag_args) { @@ -423,7 +421,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile a general expression - much of this is from Smarty with * some adaptions for our block level methods - * @access private */ private function compile_expression($tag_args) { @@ -630,7 +627,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile DEFINE tags - * @access private */ private function compile_tag_define($tag_args, $op) { @@ -654,7 +650,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile INCLUDE tag - * @access private */ private function compile_tag_include($tag_args) { @@ -663,7 +658,6 @@ class phpbb_template_filter extends php_user_filter /** * Compile INCLUDE_PHP tag - * @access private */ private function compile_tag_include_php($tag_args) { @@ -673,7 +667,6 @@ class phpbb_template_filter extends php_user_filter /** * parse expression * This is from Smarty - * @access private */ private function _parse_is_expr($is_arg, $tokens) { @@ -752,7 +745,6 @@ class phpbb_template_filter extends php_user_filter * ' . $_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. * - * @access private * @param string $namespace Namespace to access (expects a trailing "." on the namespace) * @param string $varname Variable name to use * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable @@ -815,7 +807,6 @@ class phpbb_template_filter extends php_user_filter * (possibly nested) block namespace. This is a string of the form: * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] * - * @access private * @param string $blockname Block to access (does not expect a trailing "." on the blockname) * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable @@ -883,7 +874,6 @@ class phpbb_template_compile /** * Compiles template in $source_file and writes compiled template to * cache directory - * @access public * @param string $handle Template handle to compile * @param string $source_file Source template file * @return bool Return true on success otherwise false @@ -916,7 +906,6 @@ class phpbb_template_compile /** * Compiles a template located at $source_file. * Returns PHP source suitable for eval(). - * @access public * @param string $source_file Source template file * @return string|bool Return compiled code on successful compilation otherwise false */ @@ -947,7 +936,6 @@ class phpbb_template_compile * A stream filter is appended to $source_stream as part of the * process. * - * @access private * @param resource $source_stream Source stream * @param resource $dest_stream Destination stream * @return void diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index 73f1373655..3c0dceb981 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -45,7 +45,6 @@ class phpbb_template_context /** * Clears template data set. - * @access public */ public function clear() { @@ -55,7 +54,6 @@ class phpbb_template_context /** * Assign a single variable to a single key - * @access public * @param string $varname Variable name * @param string $varval Value to assign to variable */ @@ -74,7 +72,6 @@ class phpbb_template_context * * Note: modifying returned array will affect data stored in the context. * - * @access public * @return array template data */ public function get_data_ref() @@ -93,7 +90,6 @@ class phpbb_template_context * * Note: modifying returned array will affect data stored in the context. * - * @access public * @return array template data */ public function get_root_ref() @@ -104,7 +100,6 @@ class phpbb_template_context /** * Assign key variable pairs from an array to a specified block - * @access public * @param string $blockname Name of block to assign $vararray to * @param array $vararray A hash of variable name => value pairs */ @@ -197,7 +192,6 @@ class phpbb_template_context * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) * * @return bool false on error, true on success - * @access public */ public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { @@ -315,7 +309,6 @@ class phpbb_template_context /** * Reset/empty complete block - * @access public * @param string $blockname Name of block to destroy */ public function destroy_block_vars($blockname) -- cgit v1.2.1 From 70ccf04e068c3de1611049d094b8295fb6f07b45 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Fri, 13 May 2011 10:52:33 -0400 Subject: [feature/template-engine] Moved phpbb_template_filter into own file. PHPBB3-9726 --- phpBB/includes/template/compile.php | 831 ----------------------------------- phpBB/includes/template/filter.php | 848 ++++++++++++++++++++++++++++++++++++ 2 files changed, 848 insertions(+), 831 deletions(-) create mode 100644 phpBB/includes/template/filter.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 79a7d784cd..246c83a010 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -16,837 +16,6 @@ if (!defined('IN_PHPBB')) exit; } -/** -* The template filter that does the actual compilation -* @see template_compile -* @package phpBB3 -*/ -class phpbb_template_filter extends php_user_filter -{ - const REGEX_NS = '[a-z_][a-z_0-9]+'; - - const REGEX_VAR = '[A-Z_][A-Z_0-9]+'; - - const REGEX_TAG = ''; - - const REGEX_TOKENS = '~|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; - - /** - * @var array - */ - private $block_names = array(); - - /** - * @var array - */ - private $block_else_level = array(); - - /** - * @var string - */ - private $chunk; - - /** - * @var bool - */ - private $in_php; - - public function filter($in, $out, &$consumed, $closing) - { - $written = false; - $first = false; - - while ($bucket = stream_bucket_make_writeable($in)) - { - $consumed += $bucket->datalen; - - $data = $this->chunk . $bucket->data; - $last_nl = strrpos($data, "\n"); - $this->chunk = substr($data, $last_nl); - $data = substr($data, 0, $last_nl); - - if (!strlen($data)) - { - continue; - } - - $written = true; - - $data = $this->compile($data); - if (!$first) - { - $data = $this->prepend_preamble($data); - $first = false; - } - $bucket->data = $data; - $bucket->datalen = strlen($bucket->data); - stream_bucket_append($out, $bucket); - } - - if ($closing && strlen($this->chunk)) - { - $written = true; - $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); - stream_bucket_append($out, $bucket); - } - - return $written ? PSFS_PASS_ON : PSFS_FEED_ME; - } - - public function onCreate() - { - $this->chunk = ''; - $this->in_php = false; - return true; - } - - private function compile($data) - { - $block_start_in_php = $this->in_php; - - $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); - $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); - - global $config; - - // Remove php - if (!$config['tpl_allow_php']) - { - if ($block_start_in_php - && $this->in_php - && strpos($data, '') === false - && strpos($data, '') === false) - { - // This is just php code - return ''; - } - $data = preg_replace('~^.*?~', '', $data); - $data = preg_replace('~.*?~', '', $data); - $data = preg_replace('~.*?$~', '', $data); - } - - /* - Preserve whitespace. - PHP removes a newline after the closing tag (if it's there). This is by design. - - - Consider the following template: - - - some content - - - If we were to simply preserve all whitespace, we could simply replace all "?>" tags - with "?>\n". - Doing that, would add additional newlines to the compiled tempalte in place of the - IF and ENDIF statements. These newlines are unwanted (and one is conditional). - The IF and ENDIF are usually on their own line for ease of reading. - - This replacement preserves newlines only for statements that aren't the only statement on a line. - It will NOT preserve newlines at the end of statements in the above examle. - It will preserve newlines in situations like: - - inline content - - - */ - - $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); - $data = str_replace('/**/?>', "?>\n", $data); - $data = str_replace('?>' . $data); - return $data; - } - - private function replace($matches) - { - if ($this->in_php && $matches[1] != 'ENDPHP') - { - return ''; - } - - if (isset($matches[3])) - { - return $this->compile_var_tags($matches[0]); - } - - global $config; - - switch ($matches[1]) - { - case 'BEGIN': - $this->block_else_level[] = false; - return 'compile_tag_block($matches[2]) . ' ?>'; - break; - - case 'BEGINELSE': - $this->block_else_level[sizeof($this->block_else_level) - 1] = true; - return ''; - break; - - case 'END': - array_pop($this->block_names); - return 'block_else_level)) ? '}' : '}}') . ' ?>'; - break; - - case 'IF': - return 'compile_tag_if($matches[2], false) . ' ?>'; - break; - - case 'ELSE': - return ''; - break; - - case 'ELSEIF': - return 'compile_tag_if($matches[2], true) . ' ?>'; - break; - - case 'ENDIF': - return ''; - break; - - case 'DEFINE': - return 'compile_tag_define($matches[2], true) . ' ?>'; - break; - - case 'UNDEFINE': - return 'compile_tag_define($matches[2], false) . ' ?>'; - break; - - case 'INCLUDE': - return 'compile_tag_include($matches[2]) . ' ?>'; - break; - - case 'INCLUDEPHP': - return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; - break; - - case 'PHP': - if ($config['tpl_allow_php']) - { - $this->in_php = true; - return ''; - break; - - case 'ENDPHP': - if ($config['tpl_allow_php']) - { - $this->in_php = false; - return ' ?>'; - } - return ''; - break; - - default: - return $matches[0]; - break; - - } - return ''; - } - - /** - * Compile variables - */ - private function compile_var_tags(&$text_blocks) - { - // change template varrefs into PHP varrefs - $varrefs = array(); - - // This one will handle varrefs WITH namespaces - preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); - - foreach ($varrefs as $var_val) - { - $namespace = $var_val[1]; - $varname = $var_val[3]; - $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); - - $text_blocks = str_replace($var_val[0], $new, $text_blocks); - } - - // Handle special language tags L_ and LA_ - $this->compile_language_tags($text_blocks); - - // This will handle the remaining root-level varrefs - $text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - $text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - - return $text_blocks; - } - - /** - * Handles special language tags L_ and LA_ - */ - private function compile_language_tags(&$text_blocks) - { - // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array - if (strpos($text_blocks, '{L_') !== false) - { - $text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR . ')\}#', "", $text_blocks); - } - - // Handle addslashed language variables prefixed with LA_ - // If a template variable already exist, it will be used in favor of it... - if (strpos($text_blocks, '{LA_') !== false) - { - $text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR . '+)\}#', "", $text_blocks); - } - } - - /** - * Compile blocks - */ - private function compile_tag_block($tag_args) - { - $no_nesting = false; - - // Is the designer wanting to call another loop in a loop? - // - // - // - // - // 'loop2' is actually on the same nesting level as 'loop' you assign - // variables to it with template->assign_block_vars('loop2', array(...)) - if (strpos($tag_args, '!') === 0) - { - // Count the number if ! occurrences (not allowed in vars) - $no_nesting = substr_count($tag_args, '!'); - $tag_args = substr($tag_args, $no_nesting); - } - - // Allow for control of looping (indexes start from zero): - // foo(2) : Will start the loop on the 3rd entry - // foo(-2) : Will start the loop two entries from the end - // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth - // foo(3,-4) : Will start the loop on the fourth entry and end it four from last - $match = array(); - - if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) - { - $tag_args = $match[1]; - - if ($match[2] < 0) - { - $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; - } - else - { - $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; - } - - if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) - { - $loop_end = '$_' . $tag_args . '_count'; - } - else if ($match[3] >= 0) - { - $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; - } - else //if ($match[3] < -1) - { - $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); - } - } - else - { - $loop_start = 0; - $loop_end = '$_' . $tag_args . '_count'; - } - - $tag_template_php = ''; - array_push($this->block_names, $tag_args); - - if ($no_nesting !== false) - { - // We need to implode $no_nesting times from the end... - $block = array_slice($this->block_names, -$no_nesting); - } - else - { - $block = $this->block_names; - } - - if (sizeof($block) < 2) - { - // Block is not nested. - $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; - $varref = "\$_tpldata['$tag_args']"; - } - else - { - // This block is nested. - // Generate a namespace string for this block. - $namespace = implode('.', $block); - - // 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. - $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; - } - - $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; - - /** - * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory - * - * if (!$offset) - * { - * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; - * } - * - */ - - $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; - $tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; - - return $tag_template_php; - } - - /** - * Compile a general expression - much of this is from Smarty with - * some adaptions for our block level methods - */ - private function compile_expression($tag_args) - { - $match = array(); - preg_match_all('/(?: - "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | - \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | - [(),] | - [^\s(),]+)/x', $tag_args, $match); - - $tokens = $match[0]; - $is_arg_stack = array(); - - for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) - { - $token = &$tokens[$i]; - - switch ($token) - { - case '!==': - case '===': - case '<<': - case '>>': - case '|': - case '^': - case '&': - case '~': - case ')': - case ',': - case '+': - case '-': - case '*': - case '/': - case '@': - break; - - case '==': - case 'eq': - $token = '=='; - break; - - case '!=': - case '<>': - case 'ne': - case 'neq': - $token = '!='; - break; - - case '<': - case 'lt': - $token = '<'; - break; - - case '<=': - case 'le': - case 'lte': - $token = '<='; - break; - - case '>': - case 'gt': - $token = '>'; - break; - - case '>=': - case 'ge': - case 'gte': - $token = '>='; - break; - - case '&&': - case 'and': - $token = '&&'; - break; - - case '||': - case 'or': - $token = '||'; - break; - - case '!': - case 'not': - $token = '!'; - break; - - case '%': - 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 = implode(' ', 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, sizeof($tokens), $new_tokens); - - $i = $is_arg_start; - - // no break - - default: - $varrefs = array(); - if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) - { - if (!empty($varrefs[1])) - { - $namespace = substr($varrefs[1], 0, -1); - $dot_pos = strrchr($namespace, '.'); - if ($dot_pos !== false) - { - $namespace = substr($dot_pos, 1); - } - - // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows - // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM - switch ($varrefs[3]) - { - case 'S_ROW_NUM': - case 'S_ROW_COUNT': - $token = "\$_${namespace}_i"; - break; - - case 'S_NUM_ROWS': - $token = "\$_${namespace}_count"; - break; - - case 'S_FIRST_ROW': - $token = "(\$_${namespace}_i == 0)"; - break; - - case 'S_LAST_ROW': - $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; - break; - - case 'S_BLOCK_NAME': - $token = "'$namespace'"; - break; - - default: - $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; - $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; - break; - } - } - else - { - $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; - $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; - } - - } - else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) - { - // Allow checking if loops are set with .loopname - // It is also possible to check the loop count by doing for example - $blocks = explode('.', $varrefs[1]); - - // If the block is nested, we have a reference that we can grab. - // If the block is not nested, we just go and grab the block from _tpldata - if (sizeof($blocks) > 1) - { - $block = array_pop($blocks); - $namespace = implode('.', $blocks); - $varref = $this->generate_block_data_ref($namespace, true); - - // Add the block reference for the last child. - $varref .= "['" . $block . "']"; - } - else - { - $varref = '$_tpldata'; - - // Add the block reference for the last child. - $varref .= "['" . $blocks[0] . "']"; - } - $token = "(isset($varref) ? sizeof($varref) : 0)"; - } - - break; - } - } - - return $tokens; - } - - - private function compile_tag_if($tag_args, $elseif) - { - $tokens = $this->compile_expression($tag_args); - - $tpl = ($elseif) ? '} else if (' : 'if ('; - - $tpl .= implode(' ', $tokens); - $tpl .= ') { '; - - return $tpl; - } - - /** - * Compile DEFINE tags - */ - private function compile_tag_define($tag_args, $op) - { - $match = array(); - preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); - - if (empty($match[2]) || (!isset($match[3]) && $op)) - { - return ''; - } - - if (!$op) - { - return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; - } - - $parsed_statement = implode(' ', $this->compile_expression($match[3])); - - return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; - } - - /** - * Compile INCLUDE tag - */ - private function compile_tag_include($tag_args) - { - return "\$_template->_tpl_include('$tag_args');"; - } - - /** - * Compile INCLUDE_PHP tag - */ - private function compile_tag_include_php($tag_args) - { - return "\$_template->_php_include('$tag_args');"; - } - - /** - * parse expression - * This is from Smarty - */ - private 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "!(($is_arg / $expr_arg) & 1)"; - } - else - { - $expr = "!($is_arg & 1)"; - } - break; - - case 'odd': - if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "(($is_arg / $expr_arg) & 1)"; - } - else - { - $expr = "($is_arg & 1)"; - } - break; - - case 'div': - if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') - { - $expr_end++; - $expr_arg = $tokens[$expr_end++]; - $expr = "!($is_arg % $expr_arg)"; - } - break; - } - - if ($negate_expr) - { - if ($expr[0] == '!') - { - // Negated expression, de-negate it. - $expr = substr($expr, 1); - } - else - { - $expr = "!($expr)"; - } - } - - array_splice($tokens, 0, $expr_end, $expr); - - return $tokens; - } - - /** - * Generates a reference to the given variable inside the given (possibly nested) - * block namespace. This is a string of the form: - * ' . $_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. - * - * @param string $namespace Namespace to access (expects a trailing "." on the namespace) - * @param string $varname Variable name to use - * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable - * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable - * @return string Code to access variable or echo it if $echo is true - */ - private function generate_block_varref($namespace, $varname, $echo = true, $defop = false) - { - // Strip the trailing period. - $namespace = substr($namespace, 0, -1); - - $expr = true; - $isset = false; - - // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows - // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM - switch ($varname) - { - case 'S_ROW_NUM': - case 'S_ROW_COUNT': - $varref = "\$_${namespace}_i"; - break; - - case 'S_NUM_ROWS': - $varref = "\$_${namespace}_count"; - break; - - case 'S_FIRST_ROW': - $varref = "(\$_${namespace}_i == 0)"; - break; - - case 'S_LAST_ROW': - $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; - break; - - case 'S_BLOCK_NAME': - $varref = "'$namespace'"; - break; - - default: - // Get a reference to the data block for this namespace. - $varref = $this->generate_block_data_ref($namespace, true, $defop); - // Prepend the necessary code to stick this in an echo line. - - // Append the variable reference. - $varref .= "['$varname']"; - - $expr = false; - $isset = true; - break; - } - // @todo Test the !$expr more - $varref = ($echo) ? '' : (($expr || isset($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: - * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] - * - * @param string $blockname Block to access (does not expect a trailing "." on the blockname) - * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. - * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable - * @return string Code to access variable - */ - private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) - { - // Get an array of the blocks involved. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - - // DEFINE is not an element of any referenced variable, we must use _tpldata to access it - if ($defop) - { - $varref = '$_tpldata[\'DEFINE\']'; - // 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; - } - else if ($include_last_iterator) - { - return '$_'. $blocks[$blockcount] . '_val'; - } - else - { - return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; - } - } -} - stream_filter_register('phpbb_template', 'phpbb_template_filter'); /** diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php new file mode 100644 index 0000000000..1c64cdb8da --- /dev/null +++ b/phpBB/includes/template/filter.php @@ -0,0 +1,848 @@ +'; + + const REGEX_TOKENS = '~|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; + + /** + * @var array + */ + private $block_names = array(); + + /** + * @var array + */ + private $block_else_level = array(); + + /** + * @var string + */ + private $chunk; + + /** + * @var bool + */ + private $in_php; + + public function filter($in, $out, &$consumed, $closing) + { + $written = false; + $first = false; + + while ($bucket = stream_bucket_make_writeable($in)) + { + $consumed += $bucket->datalen; + + $data = $this->chunk . $bucket->data; + $last_nl = strrpos($data, "\n"); + $this->chunk = substr($data, $last_nl); + $data = substr($data, 0, $last_nl); + + if (!strlen($data)) + { + continue; + } + + $written = true; + + $data = $this->compile($data); + if (!$first) + { + $data = $this->prepend_preamble($data); + $first = false; + } + $bucket->data = $data; + $bucket->datalen = strlen($bucket->data); + stream_bucket_append($out, $bucket); + } + + if ($closing && strlen($this->chunk)) + { + $written = true; + $bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); + stream_bucket_append($out, $bucket); + } + + return $written ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + public function onCreate() + { + $this->chunk = ''; + $this->in_php = false; + return true; + } + + private function compile($data) + { + $block_start_in_php = $this->in_php; + + $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); + $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); + + global $config; + + // Remove php + if (!$config['tpl_allow_php']) + { + if ($block_start_in_php + && $this->in_php + && strpos($data, '') === false + && strpos($data, '') === false) + { + // This is just php code + return ''; + } + $data = preg_replace('~^.*?~', '', $data); + $data = preg_replace('~.*?~', '', $data); + $data = preg_replace('~.*?$~', '', $data); + } + + /* + Preserve whitespace. + PHP removes a newline after the closing tag (if it's there). This is by design. + + + Consider the following template: + + + some content + + + If we were to simply preserve all whitespace, we could simply replace all "?>" tags + with "?>\n". + Doing that, would add additional newlines to the compiled tempalte in place of the + IF and ENDIF statements. These newlines are unwanted (and one is conditional). + The IF and ENDIF are usually on their own line for ease of reading. + + This replacement preserves newlines only for statements that aren't the only statement on a line. + It will NOT preserve newlines at the end of statements in the above examle. + It will preserve newlines in situations like: + + inline content + + + */ + + $data = preg_replace('~(?).)+(?)$~m', "$1\n", $data); + $data = str_replace('/**/?>', "?>\n", $data); + $data = str_replace('?>' . $data); + return $data; + } + + private function replace($matches) + { + if ($this->in_php && $matches[1] != 'ENDPHP') + { + return ''; + } + + if (isset($matches[3])) + { + return $this->compile_var_tags($matches[0]); + } + + global $config; + + switch ($matches[1]) + { + case 'BEGIN': + $this->block_else_level[] = false; + return 'compile_tag_block($matches[2]) . ' ?>'; + break; + + case 'BEGINELSE': + $this->block_else_level[sizeof($this->block_else_level) - 1] = true; + return ''; + break; + + case 'END': + array_pop($this->block_names); + return 'block_else_level)) ? '}' : '}}') . ' ?>'; + break; + + case 'IF': + return 'compile_tag_if($matches[2], false) . ' ?>'; + break; + + case 'ELSE': + return ''; + break; + + case 'ELSEIF': + return 'compile_tag_if($matches[2], true) . ' ?>'; + break; + + case 'ENDIF': + return ''; + break; + + case 'DEFINE': + return 'compile_tag_define($matches[2], true) . ' ?>'; + break; + + case 'UNDEFINE': + return 'compile_tag_define($matches[2], false) . ' ?>'; + break; + + case 'INCLUDE': + return 'compile_tag_include($matches[2]) . ' ?>'; + break; + + case 'INCLUDEPHP': + return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; + break; + + case 'PHP': + if ($config['tpl_allow_php']) + { + $this->in_php = true; + return ''; + break; + + case 'ENDPHP': + if ($config['tpl_allow_php']) + { + $this->in_php = false; + return ' ?>'; + } + return ''; + break; + + default: + return $matches[0]; + break; + + } + return ''; + } + + /** + * Compile variables + */ + private function compile_var_tags(&$text_blocks) + { + // change template varrefs into PHP varrefs + $varrefs = array(); + + // This one will handle varrefs WITH namespaces + preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); + + foreach ($varrefs as $var_val) + { + $namespace = $var_val[1]; + $varname = $var_val[3]; + $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]); + + $text_blocks = str_replace($var_val[0], $new, $text_blocks); + } + + // Handle special language tags L_ and LA_ + $this->compile_language_tags($text_blocks); + + // This will handle the remaining root-level varrefs + $text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + $text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + + return $text_blocks; + } + + /** + * Handles special language tags L_ and LA_ + */ + private function compile_language_tags(&$text_blocks) + { + // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array + if (strpos($text_blocks, '{L_') !== false) + { + $text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR . ')\}#', "", $text_blocks); + } + + // Handle addslashed language variables prefixed with LA_ + // If a template variable already exist, it will be used in favor of it... + if (strpos($text_blocks, '{LA_') !== false) + { + $text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR . '+)\}#', "", $text_blocks); + } + } + + /** + * Compile blocks + */ + private function compile_tag_block($tag_args) + { + $no_nesting = false; + + // Is the designer wanting to call another loop in a loop? + // + // + // + // + // 'loop2' is actually on the same nesting level as 'loop' you assign + // variables to it with template->assign_block_vars('loop2', array(...)) + if (strpos($tag_args, '!') === 0) + { + // Count the number if ! occurrences (not allowed in vars) + $no_nesting = substr_count($tag_args, '!'); + $tag_args = substr($tag_args, $no_nesting); + } + + // Allow for control of looping (indexes start from zero): + // foo(2) : Will start the loop on the 3rd entry + // foo(-2) : Will start the loop two entries from the end + // foo(3,4) : Will start the loop on the fourth entry and end it on the fifth + // foo(3,-4) : Will start the loop on the fourth entry and end it four from last + $match = array(); + + if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) + { + $tag_args = $match[1]; + + if ($match[2] < 0) + { + $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; + } + else + { + $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; + } + + if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) + { + $loop_end = '$_' . $tag_args . '_count'; + } + else if ($match[3] >= 0) + { + $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; + } + else //if ($match[3] < -1) + { + $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); + } + } + else + { + $loop_start = 0; + $loop_end = '$_' . $tag_args . '_count'; + } + + $tag_template_php = ''; + array_push($this->block_names, $tag_args); + + if ($no_nesting !== false) + { + // We need to implode $no_nesting times from the end... + $block = array_slice($this->block_names, -$no_nesting); + } + else + { + $block = $this->block_names; + } + + if (sizeof($block) < 2) + { + // Block is not nested. + $tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; + $varref = "\$_tpldata['$tag_args']"; + } + else + { + // This block is nested. + // Generate a namespace string for this block. + $namespace = implode('.', $block); + + // 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. + $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; + } + + $tag_template_php .= 'if ($_' . $tag_args . '_count) {'; + + /** + * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory + * + * if (!$offset) + * { + * $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; + * } + * + */ + + $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; + $tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; + + return $tag_template_php; + } + + /** + * Compile a general expression - much of this is from Smarty with + * some adaptions for our block level methods + */ + private function compile_expression($tag_args) + { + $match = array(); + preg_match_all('/(?: + "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | + [(),] | + [^\s(),]+)/x', $tag_args, $match); + + $tokens = $match[0]; + $is_arg_stack = array(); + + for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) + { + $token = &$tokens[$i]; + + switch ($token) + { + case '!==': + case '===': + case '<<': + case '>>': + case '|': + case '^': + case '&': + case '~': + case ')': + case ',': + case '+': + case '-': + case '*': + case '/': + case '@': + break; + + case '==': + case 'eq': + $token = '=='; + break; + + case '!=': + case '<>': + case 'ne': + case 'neq': + $token = '!='; + break; + + case '<': + case 'lt': + $token = '<'; + break; + + case '<=': + case 'le': + case 'lte': + $token = '<='; + break; + + case '>': + case 'gt': + $token = '>'; + break; + + case '>=': + case 'ge': + case 'gte': + $token = '>='; + break; + + case '&&': + case 'and': + $token = '&&'; + break; + + case '||': + case 'or': + $token = '||'; + break; + + case '!': + case 'not': + $token = '!'; + break; + + case '%': + 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 = implode(' ', 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, sizeof($tokens), $new_tokens); + + $i = $is_arg_start; + + // no break + + default: + $varrefs = array(); + if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) + { + if (!empty($varrefs[1])) + { + $namespace = substr($varrefs[1], 0, -1); + $dot_pos = strrchr($namespace, '.'); + if ($dot_pos !== false) + { + $namespace = substr($dot_pos, 1); + } + + // S_ROW_COUNT is deceptive, it returns the current row number not the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varrefs[3]) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $token = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $token = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $token = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $token = "'$namespace'"; + break; + + default: + $token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; + break; + } + } + else + { + $token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; + $token = '(isset(' . $token . ') ? ' . $token . ' : null)'; + } + + } + else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) + { + // Allow checking if loops are set with .loopname + // It is also possible to check the loop count by doing for example + $blocks = explode('.', $varrefs[1]); + + // If the block is nested, we have a reference that we can grab. + // If the block is not nested, we just go and grab the block from _tpldata + if (sizeof($blocks) > 1) + { + $block = array_pop($blocks); + $namespace = implode('.', $blocks); + $varref = $this->generate_block_data_ref($namespace, true); + + // Add the block reference for the last child. + $varref .= "['" . $block . "']"; + } + else + { + $varref = '$_tpldata'; + + // Add the block reference for the last child. + $varref .= "['" . $blocks[0] . "']"; + } + $token = "(isset($varref) ? sizeof($varref) : 0)"; + } + + break; + } + } + + return $tokens; + } + + + private function compile_tag_if($tag_args, $elseif) + { + $tokens = $this->compile_expression($tag_args); + + $tpl = ($elseif) ? '} else if (' : 'if ('; + + $tpl .= implode(' ', $tokens); + $tpl .= ') { '; + + return $tpl; + } + + /** + * Compile DEFINE tags + */ + private function compile_tag_define($tag_args, $op) + { + $match = array(); + preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); + + if (empty($match[2]) || (!isset($match[3]) && $op)) + { + return ''; + } + + if (!$op) + { + return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; + } + + $parsed_statement = implode(' ', $this->compile_expression($match[3])); + + return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; + } + + /** + * Compile INCLUDE tag + */ + private function compile_tag_include($tag_args) + { + return "\$_template->_tpl_include('$tag_args');"; + } + + /** + * Compile INCLUDE_PHP tag + */ + private function compile_tag_include_php($tag_args) + { + return "\$_template->_php_include('$tag_args');"; + } + + /** + * parse expression + * This is from Smarty + */ + private 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 (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "!($is_arg & 1)"; + } + break; + + case 'odd': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "(($is_arg / $expr_arg) & 1)"; + } + else + { + $expr = "($is_arg & 1)"; + } + break; + + case 'div': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') + { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!($is_arg % $expr_arg)"; + } + break; + } + + if ($negate_expr) + { + if ($expr[0] == '!') + { + // Negated expression, de-negate it. + $expr = substr($expr, 1); + } + else + { + $expr = "!($expr)"; + } + } + + array_splice($tokens, 0, $expr_end, $expr); + + return $tokens; + } + + /** + * Generates a reference to the given variable inside the given (possibly nested) + * block namespace. This is a string of the form: + * ' . $_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. + * + * @param string $namespace Namespace to access (expects a trailing "." on the namespace) + * @param string $varname Variable name to use + * @param bool $echo If true return an echo statement, otherwise a reference to the internal variable + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable or echo it if $echo is true + */ + private function generate_block_varref($namespace, $varname, $echo = true, $defop = false) + { + // Strip the trailing period. + $namespace = substr($namespace, 0, -1); + + $expr = true; + $isset = false; + + // S_ROW_COUNT is deceptive, it returns the current row number now the number of rows + // hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM + switch ($varname) + { + case 'S_ROW_NUM': + case 'S_ROW_COUNT': + $varref = "\$_${namespace}_i"; + break; + + case 'S_NUM_ROWS': + $varref = "\$_${namespace}_count"; + break; + + case 'S_FIRST_ROW': + $varref = "(\$_${namespace}_i == 0)"; + break; + + case 'S_LAST_ROW': + $varref = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; + break; + + case 'S_BLOCK_NAME': + $varref = "'$namespace'"; + break; + + default: + // Get a reference to the data block for this namespace. + $varref = $this->generate_block_data_ref($namespace, true, $defop); + // Prepend the necessary code to stick this in an echo line. + + // Append the variable reference. + $varref .= "['$varname']"; + + $expr = false; + $isset = true; + break; + } + // @todo Test the !$expr more + $varref = ($echo) ? '' : (($expr || isset($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: + * $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] + * + * @param string $blockname Block to access (does not expect a trailing "." on the blockname) + * @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. + * @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable + * @return string Code to access variable + */ + private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) + { + // Get an array of the blocks involved. + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + // DEFINE is not an element of any referenced variable, we must use _tpldata to access it + if ($defop) + { + $varref = '$_tpldata[\'DEFINE\']'; + // 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; + } + else if ($include_last_iterator) + { + return '$_'. $blocks[$blockcount] . '_val'; + } + else + { + return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; + } + } +} -- cgit v1.2.1 From 7d911e0bc1475ee769bfd3849d1eca6db7cc95d2 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Fri, 13 May 2011 10:54:23 -0400 Subject: [feature/template-engine] Delete @version everywhere. This is not used since the switch to git. PHPBB3-9726 --- phpBB/includes/template.php | 1 - phpBB/includes/template/compile.php | 1 - phpBB/includes/template/context.php | 1 - phpBB/includes/template/filter.php | 1 - phpBB/includes/template/renderer.php | 1 - phpBB/includes/template/renderer_eval.php | 1 - phpBB/includes/template/renderer_include.php | 1 - 7 files changed, 7 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index 5fb0f41311..e99b9a8c6c 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 246c83a010..837af1df05 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index 3c0dceb981..169add036e 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2011 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index 1c64cdb8da..2332a8281e 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2011 phpBB Group, sections (c) 2001 ispi of Lincoln Inc * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/renderer.php b/phpBB/includes/template/renderer.php index 0af2d94bb6..c15252fd83 100644 --- a/phpBB/includes/template/renderer.php +++ b/phpBB/includes/template/renderer.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2011 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/renderer_eval.php b/phpBB/includes/template/renderer_eval.php index f3365ff8a7..6cbcf5ce40 100644 --- a/phpBB/includes/template/renderer_eval.php +++ b/phpBB/includes/template/renderer_eval.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2011 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License * diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php index 28a8027df3..0e05630d55 100644 --- a/phpBB/includes/template/renderer_include.php +++ b/phpBB/includes/template/renderer_include.php @@ -2,7 +2,6 @@ /** * * @package phpBB3 -* @version $Id$ * @copyright (c) 2011 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License * -- cgit v1.2.1 From 6ae5a64f6c12937367ad56cac5e38104b445c780 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Thu, 19 May 2011 12:18:16 -0400 Subject: [feature/template-engine] Fixed absolute path PHP includes, added test. PHPBB3-9726 --- phpBB/includes/template.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index e99b9a8c6c..935605b12a 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -529,7 +529,14 @@ class phpbb_template */ public function _php_include($filename) { - $file = $this->phpbb_root_path . $filename; + if (is_absolute($filename)) + { + $file = $filename; + } + else + { + $file = $this->phpbb_root_path . $filename; + } if (!file_exists($file)) { -- cgit v1.2.1 From f7b06ca12db9da519d49ce334eaf96573d003c0f Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 9 Jul 2011 23:33:44 +0200 Subject: [feature/template-engine] Move template.php to includes/template This allows making use of autoloading. PHPBB3-9726 --- phpBB/includes/template.php | 549 ----------------------------------- phpBB/includes/template/template.php | 549 +++++++++++++++++++++++++++++++++++ 2 files changed, 549 insertions(+), 549 deletions(-) delete mode 100644 phpBB/includes/template.php create mode 100644 phpBB/includes/template/template.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php deleted file mode 100644 index 935605b12a..0000000000 --- a/phpBB/includes/template.php +++ /dev/null @@ -1,549 +0,0 @@ - $user->img('icon_contact', 'CONTACT', 'full'); -* -* More in-depth... -* yadayada -*/ - -/** -* Base Template class. -* @package phpBB3 -*/ -class phpbb_template -{ - /** - * @var phpbb_template_context Template context. - * Stores template data used during template rendering. - */ - private $context; - - /** - * @var string Root dir for template. - */ - private $root = ''; - - /** - * @var string Path of the cache directory for the template - */ - public $cachepath = ''; - - /** - * @var array Hash of handle => file path pairs - */ - public $files = array(); - - /** - * @var array Hash of handle => filename pairs - */ - public $filename = array(); - - public $files_inherit = array(); - public $files_template = array(); - public $inherit_root = ''; - - public $orig_tpl_inherits_id; - - /** - * @var string phpBB root path - */ - private $phpbb_root_path; - - /** - * @var phpEx PHP file extension - */ - private $phpEx; - - /** - * @var phpbb_config phpBB config instance - */ - private $config; - - /** - * @var user current user - */ - private $user; - - /** - * Constructor. - * - * @param string $phpbb_root_path phpBB root path - * @param user $user current user - */ - public function __construct($phpbb_root_path, $phpEx, $config, $user) - { - $this->phpbb_root_path = $phpbb_root_path; - $this->phpEx = $phpEx; - $this->config = $config; - $this->user = $user; - } - - /** - * Set template location - */ - public function set_template() - { - $template_path = $this->user->theme['template_path']; - if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template')) - { - $this->root = $this->phpbb_root_path . 'styles/' . $template_path . '/template'; - $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_'; - - if ($this->orig_tpl_inherits_id === null) - { - $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; - } - - $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - - if ($this->user->theme['template_inherits_id']) - { - $this->inherit_root = $this->phpbb_root_path . 'styles/' . $this->user->theme['template_inherit_path'] . '/template'; - } - } - else - { - trigger_error('Template path could not be found: styles/' . $template_path . '/template', E_USER_ERROR); - } - - $this->context = new phpbb_template_context(); - - return true; - } - - /** - * Set custom template location (able to use directory outside of phpBB). - * - * Note: Templates are still compiled to phpBB's cache directory. - * - * @param string $template_path Path to template directory - * @param string $template_name Name of template - * @param string $fallback_template_path Path to fallback template - */ - public function set_custom_template($template_path, $template_name, $fallback_template_path = false) - { - // Make sure $template_path has no ending slash - if (substr($template_path, -1) == '/') - { - $template_path = substr($template_path, 0, -1); - } - - $this->root = $template_path; - $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; - - if ($fallback_template_path !== false) - { - if (substr($fallback_template_path, -1) == '/') - { - $fallback_template_path = substr($fallback_template_path, 0, -1); - } - - $this->inherit_root = $fallback_template_path; - $this->orig_tpl_inherits_id = true; - } - else - { - $this->orig_tpl_inherits_id = false; - } - - $this->context = new phpbb_template_context(); - - return true; - } - - /** - * Sets the template filenames for handles. $filename_array - * should be a hash of handle => filename pairs. - * @param array $filname_array Should be a hash of handle => filename pairs. - */ - public function set_filenames(array $filename_array) - { - foreach ($filename_array as $handle => $filename) - { - if (empty($filename)) - { - trigger_error("template->set_filenames: Empty filename specified for $handle", E_USER_ERROR); - } - - $this->filename[$handle] = $filename; - $this->files[$handle] = $this->root . '/' . $filename; - - if ($this->inherit_root) - { - $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; - } - } - - return true; - } - - /** - * Clears all variables and blocks assigned to this template. - */ - public function destroy() - { - $this->context->clear(); - } - - /** - * Reset/empty complete block - * @param string $blockname Name of block to destroy - */ - public function destroy_block_vars($blockname) - { - $this->context->destroy_block_vars($blockname); - } - - /** - * Display handle - * @param string $handle Handle to display - * @param bool $include_once Allow multiple inclusions - * @return bool True on success, false on failure - */ - public function display($handle, $include_once = true) - { - $result = $this->call_hook($handle, $include_once); - if ($result !== false) - { - return $result[0]; - } - - $renderer = $this->_tpl_load($handle); - - if ($renderer) - { - $renderer->render($this->context, $this->get_lang()); - return true; - } - else - { - return false; - } - } - - /** - * Calls hook if any is defined. - * @param string $handle Template handle being displayed. - * @param bool $include_once Allow multiple inclusions - */ - private function call_hook($handle, $include_once) - { - global $phpbb_hook; - - if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) - { - if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) - { - $result = $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__)); - return array($result); - } - } - - return false; - } - - /** - * Obtains language array. - * This is either lang property of $user property, or if - * it is not set an empty array. - * @return array language entries - */ - public function get_lang() - { - if (isset($this->user->lang)) - { - $lang = $this->user->lang; - } - else - { - $lang = array(); - } - return $lang; - } - - /** - * Display the handle and assign the output to a template variable or return the compiled result. - * @param string $handle Handle to operate on - * @param string $template_var Template variable to assign compiled handle to - * @param bool $return_content If true return compiled handle, otherwise assign to $template_var - * @param bool $include_once Allow multiple inclusions of the file - * @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true - */ - public function assign_display($handle, $template_var = '', $return_content = true, $include_once = false) - { - ob_start(); - $result = $this->display($handle, $include_once); - $contents = ob_get_clean(); - if ($result === false) - { - return false; - } - - if ($return_content) - { - return $contents; - } - - $this->assign_var($template_var, $contents); - - return true; - } - - /** - * Obtains a template renderer for a template identified by specified - * handle. The template renderer can display the template later. - * - * Template source will first be compiled into php code. - * If template cache is writable the compiled php code will be stored - * on filesystem and template will not be subsequently recompiled. - * If template cache is not writable template source will be recompiled - * every time it is needed. DEBUG_EXTRA define and load_tplcompile - * configuration setting may be used to force templates to be always - * recompiled. - * - * Returns an object implementing phpbb_template_renderer, or null - * if template loading or compilation failed. Call render() on the - * renderer to display the template. This will result in template - * contents sent to the output stream (unless, of course, output - * buffering is in effect). - * - * @param string $handle Handle of the template to load - * @return phpbb_template_renderer Template renderer object, or null on failure - * @uses template_compile is used to compile template source - */ - private function _tpl_load($handle) - { - if (!isset($this->filename[$handle])) - { - trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); - } - - // reload this setting to have the values they had when this object was initialised - // using set_template or set_custom_template, they might otherwise have been overwritten - // by other template class instances in between. - $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - - $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; - $this->files_template[$handle] = (isset($this->user->theme['template_id'])) ? $this->user->theme['template_id'] : 0; - - $recompile = defined('DEBUG_EXTRA') || - !file_exists($compiled_path) || - @filesize($compiled_path) === 0 || - ($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); - - if (!$recompile && $this->config['load_tplcompile']) - { - // No way around it: we need to check inheritance here - if ($this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) - { - $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $this->user->theme['template_inherits_id']; - } - $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; - } - - // Recompile page if the original template is newer, otherwise load the compiled version - if (!$recompile) - { - return new phpbb_template_renderer_include($compiled_path, $this); - } - - // Inheritance - we point to another template file for this one. - if (isset($this->user->theme['template_inherits_id']) && $this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) - { - $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $this->user->theme['template_inherits_id']; - } - - $source_file = $this->_source_file_for_handle($handle); - - $compile = new phpbb_template_compile(); - - $output_file = $this->_compiled_file_for_handle($handle); - if ($compile->compile_file_to_file($source_file, $output_file) !== false) - { - $renderer = new phpbb_template_renderer_include($output_file, $this); - } - else if (($code = $compile->compile_file($source_file)) !== false) - { - $renderer = new phpbb_template_renderer_eval($code, $this); - } - else - { - $renderer = null; - } - - return $renderer; - } - - /** - * Resolves template handle $handle to source file path. - * @param string $handle Template handle (i.e. "friendly" template name) - * @return string Source file path - */ - private function _source_file_for_handle($handle) - { - // If we don't have a file assigned to this handle, die. - if (!isset($this->files[$handle])) - { - trigger_error("_source_file_for_handle(): No file specified for handle $handle", E_USER_ERROR); - } - - $source_file = $this->files[$handle]; - - // Try and open template for reading - if (!file_exists($source_file)) - { - trigger_error("_source_file_for_handle(): File $source_file does not exist", E_USER_ERROR); - } - return $source_file; - } - - /** - * Determines compiled file path for handle $handle. - * @param string $handle Template handle (i.e. "friendly" template name) - * @return string Compiled file path - */ - private function _compiled_file_for_handle($handle) - { - $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; - return $compiled_file; - } - - /** - * Assign key variable pairs from an array - * @param array $vararray A hash of variable name => value pairs - */ - public function assign_vars(array $vararray) - { - foreach ($vararray as $key => $val) - { - $this->assign_var($key, $val); - } - } - - /** - * Assign a single variable to a single key - * @param string $varname Variable name - * @param string $varval Value to assign to variable - */ - public function assign_var($varname, $varval) - { - $this->context->assign_var($varname, $varval); - } - - // Docstring is copied from phpbb_template_context method with the same name. - /** - * Assign key variable pairs from an array to a specified block - * @param string $blockname Name of block to assign $vararray to - * @param array $vararray A hash of variable name => value pairs - */ - public function assign_block_vars($blockname, array $vararray) - { - return $this->context->assign_block_vars($blockname, $vararray); - } - - // Docstring is copied from phpbb_template_context method with the same name. - /** - * Change already assigned key variable pair (one-dimensional - single loop entry) - * - * An example of how to use this function: - * {@example alter_block_array.php} - * - * @param string $blockname the blockname, for example 'loop' - * @param array $vararray the var array to insert/add or merge - * @param mixed $key Key to search for - * - * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] - * - * int: Position [the position to change or insert at directly given] - * - * If key is false the position is set to 0 - * If key is true the position is set to the last entry - * - * @param string $mode Mode to execute (valid modes are 'insert' and 'change') - * - * If insert, the vararray is inserted at the given position (position counting from zero). - * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). - * - * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) - * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) - * - * @return bool false on error, true on success - */ - public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') - { - return $this->context->alter_block_array($blockname, $vararray, $key, $mode); - } - - /** - * Include a separate template - * @param string $filename Template filename to include - * @param bool $include True to include the file, false to just load it - * @uses template_compile is used to compile uncached templates - */ - public function _tpl_include($filename, $include = true) - { - $handle = $filename; - $this->filename[$handle] = $filename; - $this->files[$handle] = $this->root . '/' . $filename; - if ($this->inherit_root) - { - $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; - } - - $renderer = $this->_tpl_load($handle); - - if ($renderer) - { - $renderer->render($this->context, $this->get_lang()); - } - else - { - // trigger_error cannot be used here, as the output already started - echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; - } - } - - /** - * Include a php-file - */ - public function _php_include($filename) - { - if (is_absolute($filename)) - { - $file = $filename; - } - else - { - $file = $this->phpbb_root_path . $filename; - } - - if (!file_exists($file)) - { - // trigger_error cannot be used here, as the output already started - echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n"; - return; - } - include($file); - } -} diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php new file mode 100644 index 0000000000..935605b12a --- /dev/null +++ b/phpBB/includes/template/template.php @@ -0,0 +1,549 @@ + $user->img('icon_contact', 'CONTACT', 'full'); +* +* More in-depth... +* yadayada +*/ + +/** +* Base Template class. +* @package phpBB3 +*/ +class phpbb_template +{ + /** + * @var phpbb_template_context Template context. + * Stores template data used during template rendering. + */ + private $context; + + /** + * @var string Root dir for template. + */ + private $root = ''; + + /** + * @var string Path of the cache directory for the template + */ + public $cachepath = ''; + + /** + * @var array Hash of handle => file path pairs + */ + public $files = array(); + + /** + * @var array Hash of handle => filename pairs + */ + public $filename = array(); + + public $files_inherit = array(); + public $files_template = array(); + public $inherit_root = ''; + + public $orig_tpl_inherits_id; + + /** + * @var string phpBB root path + */ + private $phpbb_root_path; + + /** + * @var phpEx PHP file extension + */ + private $phpEx; + + /** + * @var phpbb_config phpBB config instance + */ + private $config; + + /** + * @var user current user + */ + private $user; + + /** + * Constructor. + * + * @param string $phpbb_root_path phpBB root path + * @param user $user current user + */ + public function __construct($phpbb_root_path, $phpEx, $config, $user) + { + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = $phpEx; + $this->config = $config; + $this->user = $user; + } + + /** + * Set template location + */ + public function set_template() + { + $template_path = $this->user->theme['template_path']; + if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template')) + { + $this->root = $this->phpbb_root_path . 'styles/' . $template_path . '/template'; + $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_'; + + if ($this->orig_tpl_inherits_id === null) + { + $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; + } + + $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + + if ($this->user->theme['template_inherits_id']) + { + $this->inherit_root = $this->phpbb_root_path . 'styles/' . $this->user->theme['template_inherit_path'] . '/template'; + } + } + else + { + trigger_error('Template path could not be found: styles/' . $template_path . '/template', E_USER_ERROR); + } + + $this->context = new phpbb_template_context(); + + return true; + } + + /** + * Set custom template location (able to use directory outside of phpBB). + * + * Note: Templates are still compiled to phpBB's cache directory. + * + * @param string $template_path Path to template directory + * @param string $template_name Name of template + * @param string $fallback_template_path Path to fallback template + */ + public function set_custom_template($template_path, $template_name, $fallback_template_path = false) + { + // Make sure $template_path has no ending slash + if (substr($template_path, -1) == '/') + { + $template_path = substr($template_path, 0, -1); + } + + $this->root = $template_path; + $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; + + if ($fallback_template_path !== false) + { + if (substr($fallback_template_path, -1) == '/') + { + $fallback_template_path = substr($fallback_template_path, 0, -1); + } + + $this->inherit_root = $fallback_template_path; + $this->orig_tpl_inherits_id = true; + } + else + { + $this->orig_tpl_inherits_id = false; + } + + $this->context = new phpbb_template_context(); + + return true; + } + + /** + * Sets the template filenames for handles. $filename_array + * should be a hash of handle => filename pairs. + * @param array $filname_array Should be a hash of handle => filename pairs. + */ + public function set_filenames(array $filename_array) + { + foreach ($filename_array as $handle => $filename) + { + if (empty($filename)) + { + trigger_error("template->set_filenames: Empty filename specified for $handle", E_USER_ERROR); + } + + $this->filename[$handle] = $filename; + $this->files[$handle] = $this->root . '/' . $filename; + + if ($this->inherit_root) + { + $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; + } + } + + return true; + } + + /** + * Clears all variables and blocks assigned to this template. + */ + public function destroy() + { + $this->context->clear(); + } + + /** + * Reset/empty complete block + * @param string $blockname Name of block to destroy + */ + public function destroy_block_vars($blockname) + { + $this->context->destroy_block_vars($blockname); + } + + /** + * Display handle + * @param string $handle Handle to display + * @param bool $include_once Allow multiple inclusions + * @return bool True on success, false on failure + */ + public function display($handle, $include_once = true) + { + $result = $this->call_hook($handle, $include_once); + if ($result !== false) + { + return $result[0]; + } + + $renderer = $this->_tpl_load($handle); + + if ($renderer) + { + $renderer->render($this->context, $this->get_lang()); + return true; + } + else + { + return false; + } + } + + /** + * Calls hook if any is defined. + * @param string $handle Template handle being displayed. + * @param bool $include_once Allow multiple inclusions + */ + private function call_hook($handle, $include_once) + { + global $phpbb_hook; + + if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) + { + if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) + { + $result = $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__)); + return array($result); + } + } + + return false; + } + + /** + * Obtains language array. + * This is either lang property of $user property, or if + * it is not set an empty array. + * @return array language entries + */ + public function get_lang() + { + if (isset($this->user->lang)) + { + $lang = $this->user->lang; + } + else + { + $lang = array(); + } + return $lang; + } + + /** + * Display the handle and assign the output to a template variable or return the compiled result. + * @param string $handle Handle to operate on + * @param string $template_var Template variable to assign compiled handle to + * @param bool $return_content If true return compiled handle, otherwise assign to $template_var + * @param bool $include_once Allow multiple inclusions of the file + * @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true + */ + public function assign_display($handle, $template_var = '', $return_content = true, $include_once = false) + { + ob_start(); + $result = $this->display($handle, $include_once); + $contents = ob_get_clean(); + if ($result === false) + { + return false; + } + + if ($return_content) + { + return $contents; + } + + $this->assign_var($template_var, $contents); + + return true; + } + + /** + * Obtains a template renderer for a template identified by specified + * handle. The template renderer can display the template later. + * + * Template source will first be compiled into php code. + * If template cache is writable the compiled php code will be stored + * on filesystem and template will not be subsequently recompiled. + * If template cache is not writable template source will be recompiled + * every time it is needed. DEBUG_EXTRA define and load_tplcompile + * configuration setting may be used to force templates to be always + * recompiled. + * + * Returns an object implementing phpbb_template_renderer, or null + * if template loading or compilation failed. Call render() on the + * renderer to display the template. This will result in template + * contents sent to the output stream (unless, of course, output + * buffering is in effect). + * + * @param string $handle Handle of the template to load + * @return phpbb_template_renderer Template renderer object, or null on failure + * @uses template_compile is used to compile template source + */ + private function _tpl_load($handle) + { + if (!isset($this->filename[$handle])) + { + trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + } + + // reload this setting to have the values they had when this object was initialised + // using set_template or set_custom_template, they might otherwise have been overwritten + // by other template class instances in between. + $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + + $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; + $this->files_template[$handle] = (isset($this->user->theme['template_id'])) ? $this->user->theme['template_id'] : 0; + + $recompile = defined('DEBUG_EXTRA') || + !file_exists($compiled_path) || + @filesize($compiled_path) === 0 || + ($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); + + if (!$recompile && $this->config['load_tplcompile']) + { + // No way around it: we need to check inheritance here + if ($this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + { + $this->files[$handle] = $this->files_inherit[$handle]; + $this->files_template[$handle] = $this->user->theme['template_inherits_id']; + } + $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; + } + + // Recompile page if the original template is newer, otherwise load the compiled version + if (!$recompile) + { + return new phpbb_template_renderer_include($compiled_path, $this); + } + + // Inheritance - we point to another template file for this one. + if (isset($this->user->theme['template_inherits_id']) && $this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + { + $this->files[$handle] = $this->files_inherit[$handle]; + $this->files_template[$handle] = $this->user->theme['template_inherits_id']; + } + + $source_file = $this->_source_file_for_handle($handle); + + $compile = new phpbb_template_compile(); + + $output_file = $this->_compiled_file_for_handle($handle); + if ($compile->compile_file_to_file($source_file, $output_file) !== false) + { + $renderer = new phpbb_template_renderer_include($output_file, $this); + } + else if (($code = $compile->compile_file($source_file)) !== false) + { + $renderer = new phpbb_template_renderer_eval($code, $this); + } + else + { + $renderer = null; + } + + return $renderer; + } + + /** + * Resolves template handle $handle to source file path. + * @param string $handle Template handle (i.e. "friendly" template name) + * @return string Source file path + */ + private function _source_file_for_handle($handle) + { + // If we don't have a file assigned to this handle, die. + if (!isset($this->files[$handle])) + { + trigger_error("_source_file_for_handle(): No file specified for handle $handle", E_USER_ERROR); + } + + $source_file = $this->files[$handle]; + + // Try and open template for reading + if (!file_exists($source_file)) + { + trigger_error("_source_file_for_handle(): File $source_file does not exist", E_USER_ERROR); + } + return $source_file; + } + + /** + * Determines compiled file path for handle $handle. + * @param string $handle Template handle (i.e. "friendly" template name) + * @return string Compiled file path + */ + private function _compiled_file_for_handle($handle) + { + $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; + return $compiled_file; + } + + /** + * Assign key variable pairs from an array + * @param array $vararray A hash of variable name => value pairs + */ + public function assign_vars(array $vararray) + { + foreach ($vararray as $key => $val) + { + $this->assign_var($key, $val); + } + } + + /** + * Assign a single variable to a single key + * @param string $varname Variable name + * @param string $varval Value to assign to variable + */ + public function assign_var($varname, $varval) + { + $this->context->assign_var($varname, $varval); + } + + // Docstring is copied from phpbb_template_context method with the same name. + /** + * Assign key variable pairs from an array to a specified block + * @param string $blockname Name of block to assign $vararray to + * @param array $vararray A hash of variable name => value pairs + */ + public function assign_block_vars($blockname, array $vararray) + { + return $this->context->assign_block_vars($blockname, $vararray); + } + + // Docstring is copied from phpbb_template_context method with the same name. + /** + * Change already assigned key variable pair (one-dimensional - single loop entry) + * + * An example of how to use this function: + * {@example alter_block_array.php} + * + * @param string $blockname the blockname, for example 'loop' + * @param array $vararray the var array to insert/add or merge + * @param mixed $key Key to search for + * + * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] + * + * int: Position [the position to change or insert at directly given] + * + * If key is false the position is set to 0 + * If key is true the position is set to the last entry + * + * @param string $mode Mode to execute (valid modes are 'insert' and 'change') + * + * If insert, the vararray is inserted at the given position (position counting from zero). + * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). + * + * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) + * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) + * + * @return bool false on error, true on success + */ + public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') + { + return $this->context->alter_block_array($blockname, $vararray, $key, $mode); + } + + /** + * Include a separate template + * @param string $filename Template filename to include + * @param bool $include True to include the file, false to just load it + * @uses template_compile is used to compile uncached templates + */ + public function _tpl_include($filename, $include = true) + { + $handle = $filename; + $this->filename[$handle] = $filename; + $this->files[$handle] = $this->root . '/' . $filename; + if ($this->inherit_root) + { + $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; + } + + $renderer = $this->_tpl_load($handle); + + if ($renderer) + { + $renderer->render($this->context, $this->get_lang()); + } + else + { + // trigger_error cannot be used here, as the output already started + echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; + } + } + + /** + * Include a php-file + */ + public function _php_include($filename) + { + if (is_absolute($filename)) + { + $file = $filename; + } + else + { + $file = $this->phpbb_root_path . $filename; + } + + if (!file_exists($file)) + { + // trigger_error cannot be used here, as the output already started + echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n"; + return; + } + include($file); + } +} -- cgit v1.2.1 From ee0bba3ab65f68a24942a650b9414b8f2ad700b4 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 10 Jul 2011 00:33:25 +0200 Subject: [feature/template-engine] Fix some comments PHPBB3-9726 --- phpBB/includes/template/compile.php | 16 +--------------- phpBB/includes/template/filter.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 837af1df05..59ab9e654e 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -2,7 +2,7 @@ /** * * @package phpBB3 -* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc +* @copyright (c) 2005 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License * */ @@ -20,20 +20,6 @@ stream_filter_register('phpbb_template', 'phpbb_template_filter'); /** * Extension of template class - Functions needed for compiling templates only. * -* psoTFX, phpBB Development Team - 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 -* -* DEFINE directive inspired by a request by Cyberalien -* * @package phpBB3 * @uses template_filter As a PHP stream filter to perform compilation of templates */ diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index 2332a8281e..c0943ae8c0 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -17,6 +17,21 @@ if (!defined('IN_PHPBB')) /** * The template filter that does the actual compilation +* +* psoTFX, phpBB Development Team - 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 +* +* DEFINE directive inspired by a request by Cyberalien +* * @see template_compile * @package phpBB3 */ -- cgit v1.2.1 From ae53623230a45eeedf50cc3f6220164d8cd256c3 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 10 Jul 2011 00:35:07 +0200 Subject: [feature/template-engine] Refactor $config dependency out of the filter The template stream filter no longer depends on the $config global. Instead it uses a 'allow_php' param that is passed via stream_bucket_append's last argument. Tests also adjusted. PHPBB3-9726 --- phpBB/includes/template/compile.php | 14 +++++++++++++- phpBB/includes/template/filter.php | 25 +++++++++++++++++-------- phpBB/includes/template/template.php | 2 +- 3 files changed, 31 insertions(+), 10 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 59ab9e654e..647e8ccf8a 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -25,6 +25,18 @@ stream_filter_register('phpbb_template', 'phpbb_template_filter'); */ class phpbb_template_compile { + /** + * Whether tags are allowed + * + * @var bool + */ + private $allow_php; + + public function __construct($allow_php) + { + $this->allow_php = $allow_php; + } + /** * Compiles template in $source_file and writes compiled template to * cache directory @@ -96,7 +108,7 @@ class phpbb_template_compile */ private function compile_stream_to_stream($source_stream, $dest_stream) { - stream_filter_append($source_stream, 'phpbb_template'); + stream_filter_append($source_stream, 'phpbb_template', null, array('allow_php' => $this->allow_php)); stream_copy_to_stream($source_stream, $dest_stream); } } diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index c0943ae8c0..42700500a7 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -65,6 +65,18 @@ class phpbb_template_filter extends php_user_filter */ private $in_php; + /** + * Whether tags are allowed + * + * @var bool + */ + private $allow_php; + + public function __construct($allow_php) + { + $this->allow_php = $allow_php; + } + public function filter($in, $out, &$consumed, $closing) { $written = false; @@ -111,6 +123,7 @@ class phpbb_template_filter extends php_user_filter { $this->chunk = ''; $this->in_php = false; + $this->allow_php = $this->params['allow_php']; return true; } @@ -121,10 +134,8 @@ class phpbb_template_filter extends php_user_filter $data = preg_replace('#<(?:[\\?%]|script)#s', '', $data); $data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); - global $config; - // Remove php - if (!$config['tpl_allow_php']) + if (!$this->allow_php) { if ($block_start_in_php && $this->in_php @@ -195,8 +206,6 @@ class phpbb_template_filter extends php_user_filter return $this->compile_var_tags($matches[0]); } - global $config; - switch ($matches[1]) { case 'BEGIN': @@ -243,11 +252,11 @@ class phpbb_template_filter extends php_user_filter break; case 'INCLUDEPHP': - return ($config['tpl_allow_php']) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; + return ($this->allow_php) ? 'compile_tag_include_php($matches[2]) . ' ?>' : ''; break; case 'PHP': - if ($config['tpl_allow_php']) + if ($this->allow_php) { $this->in_php = true; return 'allow_php) { $this->in_php = false; return ' ?>'; diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 935605b12a..2c121d7247 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -376,7 +376,7 @@ class phpbb_template $source_file = $this->_source_file_for_handle($handle); - $compile = new phpbb_template_compile(); + $compile = new phpbb_template_compile($this->config['tpl_allow_php']); $output_file = $this->_compiled_file_for_handle($handle); if ($compile->compile_file_to_file($source_file, $output_file) !== false) -- cgit v1.2.1 From c58b09e65d428b53d2e69abf00c5847829f18fc7 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 10 Jul 2011 01:05:54 +0200 Subject: [feature/template-engine] Remove $include_once argument of display() PHPBB3-9726 --- phpBB/includes/template/template.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 2c121d7247..c16eddc426 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -216,12 +216,11 @@ class phpbb_template /** * Display handle * @param string $handle Handle to display - * @param bool $include_once Allow multiple inclusions * @return bool True on success, false on failure */ - public function display($handle, $include_once = true) + public function display($handle) { - $result = $this->call_hook($handle, $include_once); + $result = $this->call_hook($handle); if ($result !== false) { return $result[0]; @@ -243,13 +242,12 @@ class phpbb_template /** * Calls hook if any is defined. * @param string $handle Template handle being displayed. - * @param bool $include_once Allow multiple inclusions */ - private function call_hook($handle, $include_once) + private function call_hook($handle) { global $phpbb_hook; - if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once, $this)) + if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $this)) { if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__))) { @@ -285,13 +283,12 @@ class phpbb_template * @param string $handle Handle to operate on * @param string $template_var Template variable to assign compiled handle to * @param bool $return_content If true return compiled handle, otherwise assign to $template_var - * @param bool $include_once Allow multiple inclusions of the file * @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true */ - public function assign_display($handle, $template_var = '', $return_content = true, $include_once = false) + public function assign_display($handle, $template_var = '', $return_content = true) { ob_start(); - $result = $this->display($handle, $include_once); + $result = $this->display($handle); $contents = ob_get_clean(); if ($result === false) { -- cgit v1.2.1 From 66cef00589970ab0c16283a2de19f0ca37835839 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 10 Jul 2011 03:41:09 -0400 Subject: [feature/template-engine] Delete useless assignment by reference for $lang. PHPBB3-9726 --- phpBB/includes/template/renderer_eval.php | 2 +- phpBB/includes/template/renderer_include.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/renderer_eval.php b/phpBB/includes/template/renderer_eval.php index 6cbcf5ce40..07c50356c9 100644 --- a/phpBB/includes/template/renderer_eval.php +++ b/phpBB/includes/template/renderer_eval.php @@ -52,7 +52,7 @@ class phpbb_template_renderer_eval implements phpbb_template_renderer $_template = $this->template; $_tpldata = &$context->get_data_ref(); $_rootref = &$context->get_root_ref(); - $_lang = &$lang; + $_lang = $lang; eval($this->code); } diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php index 0e05630d55..b495105877 100644 --- a/phpBB/includes/template/renderer_include.php +++ b/phpBB/includes/template/renderer_include.php @@ -52,7 +52,7 @@ class phpbb_template_renderer_include implements phpbb_template_renderer $_template = $this->template; $_tpldata = &$context->get_data_ref(); $_rootref = &$context->get_root_ref(); - $_lang = &$lang; + $_lang = $lang; include($this->path); } -- cgit v1.2.1 From e43d8732d40eecc7f709cdda048ffb6e388710db Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 10 Jul 2011 03:45:57 -0400 Subject: [feature/template-engine] Add ampersands to return refs by reference. PHPBB3-9726 --- phpBB/includes/template/context.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index 169add036e..71d82880fe 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -73,7 +73,7 @@ class phpbb_template_context * * @return array template data */ - public function get_data_ref() + public function &get_data_ref() { // returning a reference directly is not // something php is capable of doing @@ -91,7 +91,7 @@ class phpbb_template_context * * @return array template data */ - public function get_root_ref() + public function &get_root_ref() { // rootref is already a reference return $this->rootref; -- cgit v1.2.1 From bf34264f4f6c0fe6bbe49c6537c0f384f9fc495b Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 16 Jul 2011 15:23:25 +0200 Subject: [feature/template-engine] Docblocks, no more constructor for filter Add docblocks for phpbb_template_filter, remove the useless constructor. PHPBB3-9726 --- phpBB/includes/template/filter.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index 42700500a7..499a2a0d6b 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -72,11 +72,12 @@ class phpbb_template_filter extends php_user_filter */ private $allow_php; - public function __construct($allow_php) - { - $this->allow_php = $allow_php; - } - + /** + * Stream filter + * + * Is invoked for evey chunk of the stream, allowing us + * to work on a chunk at a time, which saves memory. + */ public function filter($in, $out, &$consumed, $closing) { $written = false; @@ -119,6 +120,12 @@ class phpbb_template_filter extends php_user_filter return $written ? PSFS_PASS_ON : PSFS_FEED_ME; } + /** + * Initializer, called on creation. + * + * Get the allow_php option from params, which is passed + * to stream_filter_append. + */ public function onCreate() { $this->chunk = ''; @@ -194,6 +201,9 @@ class phpbb_template_filter extends php_user_filter return $data; } + /** + * Callback for replacing matched tokens with PHP code + */ private function replace($matches) { if ($this->in_php && $matches[1] != 'ENDPHP') @@ -635,7 +645,9 @@ class phpbb_template_filter extends php_user_filter return $tokens; } - + /** + * Compile IF tags + */ private function compile_tag_if($tag_args, $elseif) { $tokens = $this->compile_expression($tag_args); -- cgit v1.2.1 From e116561348b7bd3400bfe6acccf5eebaaaa7f562 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 24 Jul 2011 13:37:46 -0400 Subject: [feature/template-engine] Rename is_absolute to phpbb_is_absolute. PHPBB3-9726 --- phpBB/includes/functions.php | 4 ++-- phpBB/includes/template/template.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 86c6bc2505..e4727b9705 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -752,7 +752,7 @@ function phpbb_is_writable($file) * @param string $path Path to check absoluteness of * @return boolean */ -function is_absolute($path) +function phpbb_is_absolute($path) { return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:[/\\\]#i', $path))) ? true : false; } @@ -772,7 +772,7 @@ function phpbb_own_realpath($path) $path_prefix = ''; // Determine what sort of path we have - if (is_absolute($path)) + if (phpbb_is_absolute($path)) { $absolute = true; diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index c16eddc426..010284edf3 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -526,7 +526,7 @@ class phpbb_template */ public function _php_include($filename) { - if (is_absolute($filename)) + if (phpbb_is_absolute($filename)) { $file = $filename; } -- cgit v1.2.1 From 4126a571aceca6266b565a0ef1f85cef5230a521 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 30 Jul 2011 15:20:25 -0400 Subject: [feature/template-engine] Delete $files_template property. This seems to have been used for db storage of templates. We no longer offer db storage of templates, and thus currenty $files_template is only written to but not read anywhere. PHPBB3-9726 --- phpBB/includes/template/template.php | 4 ---- 1 file changed, 4 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 010284edf3..d2d035b3b5 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -58,7 +58,6 @@ class phpbb_template public $filename = array(); public $files_inherit = array(); - public $files_template = array(); public $inherit_root = ''; public $orig_tpl_inherits_id; @@ -340,7 +339,6 @@ class phpbb_template $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; - $this->files_template[$handle] = (isset($this->user->theme['template_id'])) ? $this->user->theme['template_id'] : 0; $recompile = defined('DEBUG_EXTRA') || !file_exists($compiled_path) || @@ -353,7 +351,6 @@ class phpbb_template if ($this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $this->user->theme['template_inherits_id']; } $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; } @@ -368,7 +365,6 @@ class phpbb_template if (isset($this->user->theme['template_inherits_id']) && $this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) { $this->files[$handle] = $this->files_inherit[$handle]; - $this->files_template[$handle] = $this->user->theme['template_inherits_id']; } $source_file = $this->_source_file_for_handle($handle); -- cgit v1.2.1 From 05b71ca04e8584428d335512a199e8c71c53a74f Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 30 Jul 2011 17:06:22 -0400 Subject: [feature/template-engine] Factor template locator out of template class. Template locator is responsible for maintaining mapping from template handles to filenames and paths, and provides resolution services using these mappings. Template locator is aware of template inheritance and is capable of checking template file existence on the filesystem. PHPBB3-9726 --- phpBB/includes/template/locator.php | 258 +++++++++++++++++++++++++++++++++++ phpBB/includes/template/template.php | 143 ++++--------------- 2 files changed, 283 insertions(+), 118 deletions(-) create mode 100644 phpBB/includes/template/locator.php (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php new file mode 100644 index 0000000000..961783e635 --- /dev/null +++ b/phpBB/includes/template/locator.php @@ -0,0 +1,258 @@ +relative_template_root_for_style($style_name); + $template_root = $this->phpbb_root_path . $relative_template_root; + if (!file_exists($template_root)) + { + trigger_error('template locator: Template path could not be found: ' . $relative_template_root, E_USER_ERROR); + } + + $this->root = $template_root; + + if ($this->orig_tpl_inherits_id === null) + { + $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; + } + + $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + + if ($this->user->theme['template_inherits_id']) + { + $this->inherit_root = $this->phpbb_root_path . $this->relative_template_root_for_style($this->user->theme['template_inherit_path']); + } + } + + /** + * Converts a style name to relative (to board root) path to + * the style's template files. + * + * @param $style_name string Style name + * @return string Path to style template files + */ + private function relative_template_root_for_style($style_name) + { + return 'styles/' . $style_name . '/template'; + } + + /** + * Set custom template location (able to use directory outside of phpBB). + * + * Note: Templates are still compiled to phpBB's cache directory. + * + * @param string $template_path Path to template directory + * @param string $template_name Name of template + * @param string|bool $fallback_template_path Path to fallback template, or false to disable fallback + */ + public function set_custom_template($template_path, $template_name, $fallback_template_path = false) + { + // Make sure $template_path has no ending slash + if (substr($template_path, -1) == '/') + { + $template_path = substr($template_path, 0, -1); + } + + $this->root = $template_path; + + if ($fallback_template_path !== false) + { + if (substr($fallback_template_path, -1) == '/') + { + $fallback_template_path = substr($fallback_template_path, 0, -1); + } + + $this->inherit_root = $fallback_template_path; + $this->orig_tpl_inherits_id = true; + } + else + { + $this->orig_tpl_inherits_id = false; + } + } + + /** + * Sets the template filenames for handles. $filename_array + * should be a hash of handle => filename pairs. + * @param array $filname_array Should be a hash of handle => filename pairs. + */ + public function set_filenames(array $filename_array) + { + foreach ($filename_array as $handle => $filename) + { + if (empty($filename)) + { + trigger_error("template locator: set_filenames: Empty filename specified for $handle", E_USER_ERROR); + } + + $this->filename[$handle] = $filename; + $this->files[$handle] = $this->root . '/' . $filename; + + if ($this->inherit_root) + { + $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; + } + } + } + + /** + * Determines the filename for a template handle. + * + * The filename comes from array used in a set_filenames call, + * which should have been performed prior to invoking this function. + * Return value is a file basename (without path). + * + * @param $handle string Template handle + * @return string Filename corresponding to the template handle + */ + public function get_filename_for_handle($handle) + { + if (!isset($this->filename[$handle])) + { + trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + } + return $this->filename[$handle]; + } + + /** + * Determines the source file path for a template handle without + * regard for template inheritance. + * + * This function returns the path in "primary" template directory + * corresponding to the given template handle. That path may or + * may not actually exist on the filesystem. Because this function + * does not perform stat calls to determine whether the path it + * returns actually exists, it is faster than get_source_file_for_handle. + * + * Use get_source_file_for_handle to obtain the actual path that is + * guaranteed to exist (which might come from the parent/fallback + * template directory if template inheritance is used). + * + * This function will trigger an error if the handle was never + * associated with a template file via set_filenames. + * + * @param $handle string Template handle + * @return string Path to source file path in primary template directory + */ + public function get_virtual_source_file_for_handle($handle) + { + // If we don't have a file assigned to this handle, die. + if (!isset($this->files[$handle])) + { + trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR); + } + + $source_file = $this->files[$handle]; + return $source_file; + } + + /** + * Determines the source file path for a template handle, accounting + * for template inheritance and verifying that the path exists. + * + * This function returns the actual path that may be compiled for + * the specified template handle. It will trigger an error if + * the template handle was never associated with a template path + * via set_filenames or if the template file does not exist on the + * filesystem. + * + * Use get_virtual_source_file_for_handle to just resolve a template + * handle to a path without any filesystem or inheritance checks. + * + * @param string $handle Template handle (i.e. "friendly" template name) + * @return string Source file path + */ + public function get_source_file_for_handle($handle) + { + // If we don't have a file assigned to this handle, die. + if (!isset($this->files[$handle])) + { + trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR); + } + + $source_file = $this->files[$handle]; + + // Try and open template for reading + if (!file_exists($source_file)) + { + if (isset($this->files_inherit[$handle]) && $this->files_inherit[$handle]) + { + $parent_source_file = $this->files_inherit[$handle]; + if (!file_exists($parent_source_file)) + { + trigger_error("template locator: Neither $source_file nor $parent_source_file exist", E_USER_ERROR); + } + $source_file = $parent_source_file; + } + else + { + trigger_error("template locator: File $source_file does not exist", E_USER_ERROR); + } + } + return $source_file; + } +} diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index d2d035b3b5..4ac15ae500 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -37,29 +37,11 @@ class phpbb_template */ private $context; - /** - * @var string Root dir for template. - */ - private $root = ''; - /** * @var string Path of the cache directory for the template */ public $cachepath = ''; - /** - * @var array Hash of handle => file path pairs - */ - public $files = array(); - - /** - * @var array Hash of handle => filename pairs - */ - public $filename = array(); - - public $files_inherit = array(); - public $inherit_root = ''; - public $orig_tpl_inherits_id; /** @@ -82,6 +64,11 @@ class phpbb_template */ private $user; + /** + * @var locator template locator + */ + private $locator; + /** * Constructor. * @@ -94,6 +81,7 @@ class phpbb_template $this->phpEx = $phpEx; $this->config = $config; $this->user = $user; + $this->locator = new phpbb_template_locator(); } /** @@ -101,23 +89,12 @@ class phpbb_template */ public function set_template() { - $template_path = $this->user->theme['template_path']; + $template_path = $style_name = $this->user->theme['template_path']; + $this->locator->set_template_path($style_name); + if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template')) { - $this->root = $this->phpbb_root_path . 'styles/' . $template_path . '/template'; $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_'; - - if ($this->orig_tpl_inherits_id === null) - { - $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; - } - - $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - - if ($this->user->theme['template_inherits_id']) - { - $this->inherit_root = $this->phpbb_root_path . 'styles/' . $this->user->theme['template_inherit_path'] . '/template'; - } } else { @@ -138,32 +115,13 @@ class phpbb_template * @param string $template_name Name of template * @param string $fallback_template_path Path to fallback template */ - public function set_custom_template($template_path, $template_name, $fallback_template_path = false) + public function set_custom_template($template_path, $style_name, $fallback_template_path = false) { - // Make sure $template_path has no ending slash - if (substr($template_path, -1) == '/') - { - $template_path = substr($template_path, 0, -1); - } + $this->locator->set_custom_template($template_path, $style_name, $fallback_template_path); + $template_name = $style_name; - $this->root = $template_path; $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; - if ($fallback_template_path !== false) - { - if (substr($fallback_template_path, -1) == '/') - { - $fallback_template_path = substr($fallback_template_path, 0, -1); - } - - $this->inherit_root = $fallback_template_path; - $this->orig_tpl_inherits_id = true; - } - else - { - $this->orig_tpl_inherits_id = false; - } - $this->context = new phpbb_template_context(); return true; @@ -176,21 +134,7 @@ class phpbb_template */ public function set_filenames(array $filename_array) { - foreach ($filename_array as $handle => $filename) - { - if (empty($filename)) - { - trigger_error("template->set_filenames: Empty filename specified for $handle", E_USER_ERROR); - } - - $this->filename[$handle] = $filename; - $this->files[$handle] = $this->root . '/' . $filename; - - if ($this->inherit_root) - { - $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; - } - } + $this->locator->set_filenames($filename_array); return true; } @@ -328,31 +272,25 @@ class phpbb_template */ private function _tpl_load($handle) { - if (!isset($this->filename[$handle])) - { - trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); - } + $virtual_source_file = $this->locator->get_virtual_source_file_for_handle($handle); + $source_file = null; // reload this setting to have the values they had when this object was initialised // using set_template or set_custom_template, they might otherwise have been overwritten // by other template class instances in between. $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - $compiled_path = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; + $compiled_path = $this->cachepath . str_replace('/', '.', $virtual_source_file) . '.' . $this->phpEx; $recompile = defined('DEBUG_EXTRA') || !file_exists($compiled_path) || @filesize($compiled_path) === 0 || - ($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($this->files[$handle])); + ($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($source_file)); if (!$recompile && $this->config['load_tplcompile']) { - // No way around it: we need to check inheritance here - if ($this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) - { - $this->files[$handle] = $this->files_inherit[$handle]; - } - $recompile = (@filemtime($compiled_path) < @filemtime($this->files[$handle])) ? true : false; + $source_file = $this->locator->get_source_file_for_handle($handle); + $recompile = (@filemtime($compiled_path) < @filemtime($source_file)) ? true : false; } // Recompile page if the original template is newer, otherwise load the compiled version @@ -361,14 +299,11 @@ class phpbb_template return new phpbb_template_renderer_include($compiled_path, $this); } - // Inheritance - we point to another template file for this one. - if (isset($this->user->theme['template_inherits_id']) && $this->user->theme['template_inherits_id'] && !file_exists($this->files[$handle])) + if ($source_file === null) { - $this->files[$handle] = $this->files_inherit[$handle]; + $source_file = $this->locator->get_source_file_for_handle($handle); } - $source_file = $this->_source_file_for_handle($handle); - $compile = new phpbb_template_compile($this->config['tpl_allow_php']); $output_file = $this->_compiled_file_for_handle($handle); @@ -388,29 +323,6 @@ class phpbb_template return $renderer; } - /** - * Resolves template handle $handle to source file path. - * @param string $handle Template handle (i.e. "friendly" template name) - * @return string Source file path - */ - private function _source_file_for_handle($handle) - { - // If we don't have a file assigned to this handle, die. - if (!isset($this->files[$handle])) - { - trigger_error("_source_file_for_handle(): No file specified for handle $handle", E_USER_ERROR); - } - - $source_file = $this->files[$handle]; - - // Try and open template for reading - if (!file_exists($source_file)) - { - trigger_error("_source_file_for_handle(): File $source_file does not exist", E_USER_ERROR); - } - return $source_file; - } - /** * Determines compiled file path for handle $handle. * @param string $handle Template handle (i.e. "friendly" template name) @@ -418,7 +330,8 @@ class phpbb_template */ private function _compiled_file_for_handle($handle) { - $compiled_file = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $this->phpEx; + $source_file = $this->locator->get_filename_for_handle($handle); + $compiled_file = $this->cachepath . str_replace('/', '.', $source_file) . '.' . $this->phpEx; return $compiled_file; } @@ -496,15 +409,9 @@ class phpbb_template */ public function _tpl_include($filename, $include = true) { - $handle = $filename; - $this->filename[$handle] = $filename; - $this->files[$handle] = $this->root . '/' . $filename; - if ($this->inherit_root) - { - $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename; - } + $this->locator->set_filenames(array($filename => $filename)); - $renderer = $this->_tpl_load($handle); + $renderer = $this->_tpl_load($filename); if ($renderer) { -- cgit v1.2.1 From 13536f2be5e9c26752a6c9c5db19892b3c8e5490 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 3 Aug 2011 01:09:37 -0400 Subject: [feature/template-engine] Add constructor to template locator. PHPBB3-9726 --- phpBB/includes/template/locator.php | 8 ++++++++ phpBB/includes/template/template.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index 961783e635..7d0b618be3 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -60,6 +60,14 @@ class phpbb_template_locator private $orig_tpl_inherits_id; + private $user; + + public function __construct($phpbb_root_path, $user) + { + $this->phpbb_root_path = $phpbb_root_path; + $this->user = $user; + } + /** * Set template location. * @param string $style_name Name of style from which templates are to be taken diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 4ac15ae500..2f360e4df4 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -81,7 +81,7 @@ class phpbb_template $this->phpEx = $phpEx; $this->config = $config; $this->user = $user; - $this->locator = new phpbb_template_locator(); + $this->locator = new phpbb_template_locator($phpbb_root_path, $user); } /** -- cgit v1.2.1 From 1a6250d8b6b61b09c1713e3d8683a0c1dd8e0857 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Thu, 4 Aug 2011 21:24:40 -0400 Subject: [feature/template-engine] Delete $style_name param from locator's set_custom_template. This parameter was unused, it was only used by template's set_custom_template to determine cache file prefix. PHPBB3-9726 --- phpBB/includes/template/locator.php | 3 +-- phpBB/includes/template/template.php | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index 7d0b618be3..d9c95ec4ff 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -114,10 +114,9 @@ class phpbb_template_locator * Note: Templates are still compiled to phpBB's cache directory. * * @param string $template_path Path to template directory - * @param string $template_name Name of template * @param string|bool $fallback_template_path Path to fallback template, or false to disable fallback */ - public function set_custom_template($template_path, $template_name, $fallback_template_path = false) + public function set_custom_template($template_path, $fallback_template_path = false) { // Make sure $template_path has no ending slash if (substr($template_path, -1) == '/') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 2f360e4df4..6a60ff34d6 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -117,10 +117,9 @@ class phpbb_template */ public function set_custom_template($template_path, $style_name, $fallback_template_path = false) { - $this->locator->set_custom_template($template_path, $style_name, $fallback_template_path); - $template_name = $style_name; + $this->locator->set_custom_template($template_path, $fallback_template_path); - $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; + $this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $style_name) . '_'; $this->context = new phpbb_template_context(); -- cgit v1.2.1 From 52f208900fb6baa470d0238829147b60e208060d Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Thu, 4 Aug 2011 21:31:58 -0400 Subject: [feature/template-engine] Get rid of orig_tpl_* in template engine. The origins of orig_tpl_* are not pretty. Please see the following commits and associated tickets: r9823, r9839, r9847, r10150, r10460. In short, multiple hacks were required due to template engine reading inheritance/storedb flags from $user (global) even when the template that was being looked up or rendered was not the "active style of the current user". We no longer store templates in the database, removing half of the problem. This commit fixes the second half of the problem by deleting set_template_path function from template locator, and moving that logic back into the template class' set_template. set_template now calls set_custom_template, the latter only taking the template path and the fallback paths as parameters. With this change template locator no longer uses $user and does not use phpbb root path either. All logic involving setting the user's "active" template is now encapsulated in a single template class's function, set_template. Setting other templates is done via set_custom_template and the caller is responsible for determining and passing in fallback/inheritance path, if any. PHPBB3-9726 --- phpBB/includes/template/locator.php | 55 ------------------------------------ phpBB/includes/template/template.php | 41 ++++++++++++++++++--------- 2 files changed, 28 insertions(+), 68 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index d9c95ec4ff..804fd07537 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -58,56 +58,6 @@ class phpbb_template_locator */ private $files_inherit = array(); - private $orig_tpl_inherits_id; - - private $user; - - public function __construct($phpbb_root_path, $user) - { - $this->phpbb_root_path = $phpbb_root_path; - $this->user = $user; - } - - /** - * Set template location. - * @param string $style_name Name of style from which templates are to be taken - */ - public function set_template_path($style_name) - { - $relative_template_root = $this->relative_template_root_for_style($style_name); - $template_root = $this->phpbb_root_path . $relative_template_root; - if (!file_exists($template_root)) - { - trigger_error('template locator: Template path could not be found: ' . $relative_template_root, E_USER_ERROR); - } - - $this->root = $template_root; - - if ($this->orig_tpl_inherits_id === null) - { - $this->orig_tpl_inherits_id = $this->user->theme['template_inherits_id']; - } - - $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - - if ($this->user->theme['template_inherits_id']) - { - $this->inherit_root = $this->phpbb_root_path . $this->relative_template_root_for_style($this->user->theme['template_inherit_path']); - } - } - - /** - * Converts a style name to relative (to board root) path to - * the style's template files. - * - * @param $style_name string Style name - * @return string Path to style template files - */ - private function relative_template_root_for_style($style_name) - { - return 'styles/' . $style_name . '/template'; - } - /** * Set custom template location (able to use directory outside of phpBB). * @@ -134,11 +84,6 @@ class phpbb_template_locator } $this->inherit_root = $fallback_template_path; - $this->orig_tpl_inherits_id = true; - } - else - { - $this->orig_tpl_inherits_id = false; } } diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 6a60ff34d6..ad5581499d 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -42,8 +42,6 @@ class phpbb_template */ public $cachepath = ''; - public $orig_tpl_inherits_id; - /** * @var string phpBB root path */ @@ -81,7 +79,7 @@ class phpbb_template $this->phpEx = $phpEx; $this->config = $config; $this->user = $user; - $this->locator = new phpbb_template_locator($phpbb_root_path, $user); + $this->locator = new phpbb_template_locator(); } /** @@ -89,18 +87,28 @@ class phpbb_template */ public function set_template() { - $template_path = $style_name = $this->user->theme['template_path']; - $this->locator->set_template_path($style_name); + $style_name = $this->user->theme['template_path']; + + $relative_template_root = $this->relative_template_root_for_style($style_name); + $template_root = $this->phpbb_root_path . $relative_template_root; + if (!file_exists($template_root)) + { + trigger_error('template locator: Template path could not be found: ' . $relative_template_root, E_USER_ERROR); + } - if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template')) + if ($this->user->theme['template_inherits_id']) { - $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_'; + $fallback_template_path = $this->phpbb_root_path . $this->relative_template_root_for_style($this->user->theme['template_inherit_path']); } else { - trigger_error('Template path could not be found: styles/' . $template_path . '/template', E_USER_ERROR); + $fallback_template_path = null; } + $this->locator->set_custom_template($template_root, $fallback_template_path); + + $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $style_name) . '_'; + $this->context = new phpbb_template_context(); return true; @@ -126,6 +134,18 @@ class phpbb_template return true; } + /** + * Converts a style name to relative (to board root) path to + * the style's template files. + * + * @param $style_name string Style name + * @return string Path to style template files + */ + private function relative_template_root_for_style($style_name) + { + return 'styles/' . $style_name . '/template'; + } + /** * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. @@ -274,11 +294,6 @@ class phpbb_template $virtual_source_file = $this->locator->get_virtual_source_file_for_handle($handle); $source_file = null; - // reload this setting to have the values they had when this object was initialised - // using set_template or set_custom_template, they might otherwise have been overwritten - // by other template class instances in between. - $this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; - $compiled_path = $this->cachepath . str_replace('/', '.', $virtual_source_file) . '.' . $this->phpEx; $recompile = defined('DEBUG_EXTRA') || -- cgit v1.2.1 From 0b381516a0e7f3559fb697d98aa28e480c60b823 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 7 Aug 2011 15:33:45 -0400 Subject: [feature/template-engine] Create load_and_render to reduce code duplication. PHPBB3-9726 --- phpBB/includes/template/template.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index ad5581499d..c368fd2621 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -188,6 +188,18 @@ class phpbb_template return $result[0]; } + return $this->load_and_render($handle); + } + + /** + * Loads a template for $handle, compiling it if necessary, and + * renders the template. + * + * @param string $handle Template handle to render + * @return bool True on success, false on failure + */ + private function load_and_render($handle) + { $renderer = $this->_tpl_load($handle); if ($renderer) @@ -425,13 +437,7 @@ class phpbb_template { $this->locator->set_filenames(array($filename => $filename)); - $renderer = $this->_tpl_load($filename); - - if ($renderer) - { - $renderer->render($this->context, $this->get_lang()); - } - else + if (!$this->load_and_render($handle)) { // trigger_error cannot be used here, as the output already started echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; -- cgit v1.2.1 From 02fc5330664120705a7812103046f88eab723d3e Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 7 Aug 2011 15:42:04 -0400 Subject: [feature/template-engine] More documentation for template class. PHPBB3-9726 --- phpBB/includes/template/template.php | 43 +++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index c368fd2621..5ac9a32724 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -83,7 +83,7 @@ class phpbb_template } /** - * Set template location + * Set template location based on (current) user's chosen style. */ public function set_template() { @@ -147,8 +147,8 @@ class phpbb_template } /** - * Sets the template filenames for handles. $filename_array - * should be a hash of handle => filename pairs. + * Sets the template filenames for handles. + * * @param array $filname_array Should be a hash of handle => filename pairs. */ public function set_filenames(array $filename_array) @@ -168,6 +168,7 @@ class phpbb_template /** * Reset/empty complete block + * * @param string $blockname Name of block to destroy */ public function destroy_block_vars($blockname) @@ -176,7 +177,12 @@ class phpbb_template } /** - * Display handle + * Display a template for provided handle. + * + * The template will be loaded and compiled, if necessary, first. + * + * This function calls hooks. + * * @param string $handle Handle to display * @return bool True on success, false on failure */ @@ -215,6 +221,7 @@ class phpbb_template /** * Calls hook if any is defined. + * * @param string $handle Template handle being displayed. */ private function call_hook($handle) @@ -253,7 +260,9 @@ class phpbb_template } /** - * Display the handle and assign the output to a template variable or return the compiled result. + * Display the handle and assign the output to a template variable + * or return the compiled result. + * * @param string $handle Handle to operate on * @param string $template_var Template variable to assign compiled handle to * @param bool $return_content If true return compiled handle, otherwise assign to $template_var @@ -351,6 +360,7 @@ class phpbb_template /** * Determines compiled file path for handle $handle. + * * @param string $handle Template handle (i.e. "friendly" template name) * @return string Compiled file path */ @@ -363,6 +373,7 @@ class phpbb_template /** * Assign key variable pairs from an array + * * @param array $vararray A hash of variable name => value pairs */ public function assign_vars(array $vararray) @@ -375,6 +386,7 @@ class phpbb_template /** * Assign a single variable to a single key + * * @param string $varname Variable name * @param string $varval Value to assign to variable */ @@ -428,7 +440,12 @@ class phpbb_template } /** - * Include a separate template + * Include a separate template. + * + * This function is marked public due to the way the template + * implementation uses it. It is actually an implementation function + * and should not be considered part of template class's public API. + * * @param string $filename Template filename to include * @param bool $include True to include the file, false to just load it * @uses template_compile is used to compile uncached templates @@ -437,7 +454,7 @@ class phpbb_template { $this->locator->set_filenames(array($filename => $filename)); - if (!$this->load_and_render($handle)) + if (!$this->load_and_render($filename)) { // trigger_error cannot be used here, as the output already started echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; @@ -445,7 +462,17 @@ class phpbb_template } /** - * Include a php-file + * Include a PHP file. + * + * If a relative path is passed in $filename, it is considered to be + * relative to board root ($phpbb_root_path). Absolute paths are + * also allowed. + * + * This function is marked public due to the way the template + * implementation uses it. It is actually an implementation function + * and should not be considered part of template class's public API. + * + * @param string $filename Path to PHP file to include */ public function _php_include($filename) { -- cgit v1.2.1 From f3befa4b29b52680d5a32e197ea0776b8478d71d Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 7 Aug 2011 19:07:27 -0400 Subject: [feature/template-engine] Remaining documentation. PHPBB3-9726 --- phpBB/includes/template/compile.php | 8 ++++++ phpBB/includes/template/context.php | 4 +++ phpBB/includes/template/filter.php | 42 +++++++++++++++++++++++++++- phpBB/includes/template/locator.php | 1 + phpBB/includes/template/renderer.php | 1 + phpBB/includes/template/renderer_eval.php | 1 + phpBB/includes/template/renderer_include.php | 1 + 7 files changed, 57 insertions(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index 647e8ccf8a..8d8560ded5 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -32,6 +32,11 @@ class phpbb_template_compile */ private $allow_php; + /** + * Constructor. + * + * @param bool @allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag) + */ public function __construct($allow_php) { $this->allow_php = $allow_php; @@ -40,6 +45,7 @@ class phpbb_template_compile /** * Compiles template in $source_file and writes compiled template to * cache directory + * * @param string $handle Template handle to compile * @param string $source_file Source template file * @return bool Return true on success otherwise false @@ -71,7 +77,9 @@ class phpbb_template_compile /** * Compiles a template located at $source_file. + * * Returns PHP source suitable for eval(). + * * @param string $source_file Source template file * @return string|bool Return compiled code on successful compilation otherwise false */ diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index 71d82880fe..f85f86a0ec 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -28,6 +28,7 @@ class phpbb_template_context * --> $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 array */ private $tpldata = array('.' => array(0 => array())); @@ -53,6 +54,7 @@ class phpbb_template_context /** * Assign a single variable to a single key + * * @param string $varname Variable name * @param string $varval Value to assign to variable */ @@ -99,6 +101,7 @@ class phpbb_template_context /** * Assign key variable pairs from an array to a specified block + * * @param string $blockname Name of block to assign $vararray to * @param array $vararray A hash of variable name => value pairs */ @@ -308,6 +311,7 @@ class phpbb_template_context /** * Reset/empty complete block + * * @param string $blockname Name of block to destroy */ public function destroy_block_vars($blockname) diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index 499a2a0d6b..1d1d92378e 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -66,7 +66,9 @@ class phpbb_template_filter extends php_user_filter private $in_php; /** - * Whether tags are allowed + * Whether inline PHP code, and tags + * are allowed. If this is false all PHP code will be silently + * removed from the template during compilation. * * @var bool */ @@ -134,6 +136,15 @@ class phpbb_template_filter extends php_user_filter return true; } + /** + * Compiles a chunk of template. + * + * The chunk must comprise of one or more complete lines from the source + * template. + * + * @param string $data Chunk of source template to compile + * @return string Compiled PHP/HTML code + */ private function compile($data) { $block_start_in_php = $this->in_php; @@ -192,6 +203,7 @@ class phpbb_template_filter extends php_user_filter /** * Prepends a preamble to compiled template. * Currently preamble checks if IN_PHPBB is defined and calls exit() if it is not. + * * @param string $data Compiled template chunk * @return string Compiled template chunk with preamble prepended */ @@ -203,6 +215,9 @@ class phpbb_template_filter extends php_user_filter /** * Callback for replacing matched tokens with PHP code + * + * @param array $matches Regular expression matches + * @return string compiled template code */ private function replace($matches) { @@ -293,6 +308,9 @@ class phpbb_template_filter extends php_user_filter /** * Compile variables + * + * @param string $text_blocks Variable reference in source template + * @return string compiled template code */ private function compile_var_tags(&$text_blocks) { @@ -323,6 +341,8 @@ class phpbb_template_filter extends php_user_filter /** * Handles special language tags L_ and LA_ + * + * @param string $text_blocks Variable reference in source template */ private function compile_language_tags(&$text_blocks) { @@ -342,6 +362,9 @@ class phpbb_template_filter extends php_user_filter /** * Compile blocks + * + * @param string $tag_args Block contents in source template + * @return string compiled template code */ private function compile_tag_block($tag_args) { @@ -454,6 +477,9 @@ class phpbb_template_filter extends php_user_filter /** * Compile a general expression - much of this is from Smarty with * some adaptions for our block level methods + * + * @param string $tag_args Expression (tag arguments) in source template + * @return string compiled template code */ private function compile_expression($tag_args) { @@ -647,6 +673,10 @@ class phpbb_template_filter extends php_user_filter /** * Compile IF tags + * + * @param string $tag_args Expression given with IF in source template + * @param bool $elseif True if compiling an IF tag, false if compiling an ELSEIF tag + * @return string compiled template code */ private function compile_tag_if($tag_args, $elseif) { @@ -662,6 +692,10 @@ class phpbb_template_filter extends php_user_filter /** * Compile DEFINE tags + * + * @param string $tag_args Expression given with DEFINE in source template + * @param bool $op True if compiling a DEFINE tag, false if compiling an UNDEFINE tag + * @return string compiled template code */ private function compile_tag_define($tag_args, $op) { @@ -685,6 +719,9 @@ class phpbb_template_filter extends php_user_filter /** * Compile INCLUDE tag + * + * @param string $tag_args Expression given with INCLUDE in source template + * @return string compiled template code */ private function compile_tag_include($tag_args) { @@ -693,6 +730,9 @@ class phpbb_template_filter extends php_user_filter /** * Compile INCLUDE_PHP tag + * + * @param string $tag_args Expression given with INCLUDEPHP in source template + * @return string compiled template code */ private function compile_tag_include_php($tag_args) { diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index 804fd07537..e47548be23 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -90,6 +90,7 @@ class phpbb_template_locator /** * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. + * * @param array $filname_array Should be a hash of handle => filename pairs. */ public function set_filenames(array $filename_array) diff --git a/phpBB/includes/template/renderer.php b/phpBB/includes/template/renderer.php index c15252fd83..59c85df443 100644 --- a/phpBB/includes/template/renderer.php +++ b/phpBB/includes/template/renderer.php @@ -27,6 +27,7 @@ interface phpbb_template_renderer { /** * Displays the template managed by this renderer. + * * @param phpbb_template_context $context Template context to use * @param array $lang Language entries to use */ diff --git a/phpBB/includes/template/renderer_eval.php b/phpBB/includes/template/renderer_eval.php index 07c50356c9..11e2a30f06 100644 --- a/phpBB/includes/template/renderer_eval.php +++ b/phpBB/includes/template/renderer_eval.php @@ -44,6 +44,7 @@ class phpbb_template_renderer_eval implements phpbb_template_renderer /** * Displays the template managed by this renderer by eval'ing php code * of the template. + * * @param phpbb_template_context $context Template context to use * @param array $lang Language entries to use */ diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php index b495105877..40e7a3376c 100644 --- a/phpBB/includes/template/renderer_include.php +++ b/phpBB/includes/template/renderer_include.php @@ -44,6 +44,7 @@ class phpbb_template_renderer_include implements phpbb_template_renderer /** * Displays the template managed by this renderer by including * the php file containing the template. + * * @param phpbb_template_context $context Template context to use * @param array $lang Language entries to use */ -- cgit v1.2.1 From db257956aa02a2533577a4e4081c2502dbd3ee00 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 7 Aug 2011 19:17:22 -0400 Subject: [feature/template-engine] Corrected an error message in template locator. PHPBB3-9726 --- phpBB/includes/template/locator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index e47548be23..35e33bb9e6 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -126,7 +126,7 @@ class phpbb_template_locator { if (!isset($this->filename[$handle])) { - trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); + trigger_error("template locator: get_filename_for_handle: No file specified for handle $handle", E_USER_ERROR); } return $this->filename[$handle]; } -- cgit v1.2.1 From df46a576e952881df963dcc03daede58dfb98927 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sun, 7 Aug 2011 19:26:28 -0400 Subject: [feature/template-engine] Use template engine class in bbcode class. PHPBB3-9726 --- phpBB/includes/bbcode.php | 23 ++++++----------------- phpBB/includes/template/template.php | 13 +++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index a360bcd5d1..c29875dcbf 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -128,28 +128,17 @@ class bbcode */ function bbcode_cache_init() { - global $phpbb_root_path, $template, $user; + global $phpbb_root_path, $phpEx, $template, $config, $user; if (empty($this->template_filename)) { $this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']); - $this->template_filename = $phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template/bbcode.html'; - if (!@file_exists($this->template_filename)) - { - if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id']) - { - $this->template_filename = $phpbb_root_path . 'styles/' . $user->theme['template_inherit_path'] . '/template/bbcode.html'; - if (!@file_exists($this->template_filename)) - { - trigger_error('The file ' . $this->template_filename . ' is missing.', E_USER_ERROR); - } - } - else - { - trigger_error('The file ' . $this->template_filename . ' is missing.', E_USER_ERROR); - } - } + $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user); + $template->set_template(); + $locator = $template->_get_locator(); + $locator->set_filenames(array('bbcode.html' => 'bbcode.html')); + $this->template_filename = $locator->get_source_file_for_handle('bbcode.html'); } $bbcode_ids = $rowset = $sql = array(); diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 5ac9a32724..71fecc7d26 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -493,4 +493,17 @@ class phpbb_template } include($file); } + + /** + * Retrieves the template locator object. + * + * This function is public for the benefit of bbcode implementation. + * It should not be considered part of template class's public API. + * + * @return phpbb_template_locator Template locator for this template + */ + public function _get_locator() + { + return $this->locator; + } } -- cgit v1.2.1 From 035a8d7154b25414532bfb2de066cde1065feded Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 9 Aug 2011 23:18:37 -0400 Subject: [feature/template-engine] Delete useless $template globalization. PHPBB3-9726 --- phpBB/includes/bbcode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index c29875dcbf..93c4bba009 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -128,7 +128,7 @@ class bbcode */ function bbcode_cache_init() { - global $phpbb_root_path, $phpEx, $template, $config, $user; + global $phpbb_root_path, $phpEx, $config, $user; if (empty($this->template_filename)) { -- cgit v1.2.1 From 4bb56cddb3a0ab8012e9324f94138630799449de Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 9 Aug 2011 23:18:58 -0400 Subject: [feature/template-engine] Delete no longer used $template_filename property. PHPBB3-9726 --- phpBB/includes/bbcode.php | 1 - 1 file changed, 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index 93c4bba009..85d7459163 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -30,7 +30,6 @@ class bbcode var $bbcodes = array(); var $template_bitfield; - var $template_filename = ''; /** * Constructor -- cgit v1.2.1 From 66232035aa1e0d81d5c8f141b45521998cd7207e Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 9 Aug 2011 23:20:37 -0400 Subject: [feature/template-engine] Delete useless code from set_template. set_custom_template performs these calls, repeating them in set_template is not needed. PHPBB3-9726 --- phpBB/includes/template/template.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 71fecc7d26..80052ba59a 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -105,13 +105,7 @@ class phpbb_template $fallback_template_path = null; } - $this->locator->set_custom_template($template_root, $fallback_template_path); - - $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $style_name) . '_'; - - $this->context = new phpbb_template_context(); - - return true; + return $this->locator->set_custom_template($template_root, $fallback_template_path); } /** -- cgit v1.2.1 From acb767f14d6300ebff793f87b1a62ebbb4bde82a Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 9 Aug 2011 23:28:40 -0400 Subject: [feature/template-engine] Dependency inject locator into template. PHPBB3-9726 --- phpBB/includes/bbcode.php | 3 ++- phpBB/includes/functions_messenger.php | 3 ++- phpBB/includes/template/template.php | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index 85d7459163..482858446c 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -133,7 +133,8 @@ class bbcode { $this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']); - $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user); + $template_locator = new phpbb_template_locator(); + $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator); $template->set_template(); $locator = $template->_get_locator(); $locator->set_filenames(array('bbcode.html' => 'bbcode.html')); diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index da7e7cb9e4..507f523866 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -193,7 +193,8 @@ class messenger // tpl_msg now holds a template object we can use to parse the template file if (!isset($this->tpl_msg[$template_lang . $template_file])) { - $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user); + $template_locator = new phpbb_template_locator(); + $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator); $tpl = &$this->tpl_msg[$template_lang . $template_file]; $fallback_template_path = false; diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 80052ba59a..0409b66da6 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -72,14 +72,15 @@ class phpbb_template * * @param string $phpbb_root_path phpBB root path * @param user $user current user + * @param phpbb_template_locator $locator template locator */ - public function __construct($phpbb_root_path, $phpEx, $config, $user) + public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_template_locator $locator) { $this->phpbb_root_path = $phpbb_root_path; $this->phpEx = $phpEx; $this->config = $config; $this->user = $user; - $this->locator = new phpbb_template_locator(); + $this->locator = $locator; } /** -- cgit v1.2.1 From fb8a1d999f95eddbd22c5d1e89acdb923caa4bef Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Tue, 9 Aug 2011 23:33:47 -0400 Subject: [feature/template-engine] Need to call set_template on template. PHPBB3-9726 --- phpBB/includes/template/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 0409b66da6..4bca813971 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -106,7 +106,7 @@ class phpbb_template $fallback_template_path = null; } - return $this->locator->set_custom_template($template_root, $fallback_template_path); + return $this->set_custom_template($template_root, $style_name, $fallback_template_path); } /** -- cgit v1.2.1 From 7cfd4052c5a9f7f6caabd9a1fc734d7b9e838dcd Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 13 Aug 2011 23:48:39 -0400 Subject: [feature/template-engine] Clean up template locator usage in bbcode. PHPBB3-9726 --- phpBB/includes/bbcode.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index 482858446c..eeac98d3f3 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -136,9 +136,8 @@ class bbcode $template_locator = new phpbb_template_locator(); $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator); $template->set_template(); - $locator = $template->_get_locator(); - $locator->set_filenames(array('bbcode.html' => 'bbcode.html')); - $this->template_filename = $locator->get_source_file_for_handle('bbcode.html'); + $template_locator->set_filenames(array('bbcode.html' => 'bbcode.html')); + $this->template_filename = $template_locator->get_source_file_for_handle('bbcode.html'); } $bbcode_ids = $rowset = $sql = array(); -- cgit v1.2.1 From 41de09e408bc314c3020084fb7c5ec4a4c7262b5 Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Sat, 13 Aug 2011 23:47:24 -0400 Subject: [feature/template-engine] Delete _get_locator function. It is no longer needed as locator is injected into template. PHPBB3-9726 --- phpBB/includes/template/template.php | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'phpBB/includes') diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index 4bca813971..4007b77db8 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -488,17 +488,4 @@ class phpbb_template } include($file); } - - /** - * Retrieves the template locator object. - * - * This function is public for the benefit of bbcode implementation. - * It should not be considered part of template class's public API. - * - * @return phpbb_template_locator Template locator for this template - */ - public function _get_locator() - { - return $this->locator; - } } -- cgit v1.2.1