1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-02-26 04:52:36 +01:00
Oleg Pudeyev 05b71ca04e [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
2011-07-30 17:06:22 -04:00

450 lines
12 KiB
PHP

<?php
/**
*
* @package phpBB3
* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* @todo
* IMG_ for image substitution?
* {IMG_[key]:[alt]:[type]}
* {IMG_ICON_CONTACT:CONTACT:full} -> $user->img('icon_contact', 'CONTACT', 'full');
*
* More in-depth...
* yadayada
*/
/**
* Base Template class.
* @package phpBB3
*/
class phpbb_template
{
/**
* @var phpbb_template_context Template context.
* Stores template data used during template rendering.
*/
private $context;
/**
* @var string Path of the cache directory for the template
*/
public $cachepath = '';
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;
/**
* @var locator template locator
*/
private $locator;
/**
* 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;
$this->locator = new phpbb_template_locator();
}
/**
* Set template location
*/
public function set_template()
{
$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->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_';
}
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, $style_name, $fallback_template_path = false)
{
$this->locator->set_custom_template($template_path, $style_name, $fallback_template_path);
$template_name = $style_name;
$this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_';
$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)
{
$this->locator->set_filenames($filename_array);
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
* @return bool True on success, false on failure
*/
public function display($handle)
{
$result = $this->call_hook($handle);
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.
*/
private function call_hook($handle)
{
global $phpbb_hook;
if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $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
* @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)
{
ob_start();
$result = $this->display($handle);
$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)
{
$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') ||
!file_exists($compiled_path) ||
@filesize($compiled_path) === 0 ||
($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($source_file));
if (!$recompile && $this->config['load_tplcompile'])
{
$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
if (!$recompile)
{
return new phpbb_template_renderer_include($compiled_path, $this);
}
if ($source_file === null)
{
$source_file = $this->locator->get_source_file_for_handle($handle);
}
$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)
{
$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;
}
/**
* 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)
{
$source_file = $this->locator->get_filename_for_handle($handle);
$compiled_file = $this->cachepath . str_replace('/', '.', $source_file) . '.' . $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)
{
$this->locator->set_filenames(array($filename => $filename));
$renderer = $this->_tpl_load($filename);
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 (phpbb_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);
}
}