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 ++ 5 files changed, 1416 insertions(+) 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 (limited to 'phpBB/includes/template') 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); + } +} -- 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/template') 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 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/context.php | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'phpBB/includes/template') 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/template') 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/template') 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 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/compile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes/template') 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/compile.php | 12 ------------ phpBB/includes/template/context.php | 7 ------- 2 files changed, 19 deletions(-) (limited to 'phpBB/includes/template') 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/template') 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/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 - 6 files changed, 6 deletions(-) (limited to 'phpBB/includes/template') 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 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/template.php | 549 +++++++++++++++++++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 phpBB/includes/template/template.php (limited to 'phpBB/includes/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'phpBB/includes/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template') 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/template/template.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'phpBB/includes/template') 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 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/template') 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/template/template.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'phpBB/includes/template') 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/template') 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 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/template') 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