aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/template/twig
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes/template/twig')
-rw-r--r--phpBB/includes/template/twig/definition.php50
-rw-r--r--phpBB/includes/template/twig/environment.php181
-rw-r--r--phpBB/includes/template/twig/extension.php143
-rw-r--r--phpBB/includes/template/twig/lexer.php229
-rw-r--r--phpBB/includes/template/twig/loader.php51
-rw-r--r--phpBB/includes/template/twig/node/define.php49
-rw-r--r--phpBB/includes/template/twig/node/event.php50
-rw-r--r--phpBB/includes/template/twig/node/expression/binary/equalequal.php16
-rw-r--r--phpBB/includes/template/twig/node/expression/binary/notequalequal.php16
-rw-r--r--phpBB/includes/template/twig/node/include.php47
-rw-r--r--phpBB/includes/template/twig/node/includejs.php39
-rw-r--r--phpBB/includes/template/twig/node/includephp.php77
-rw-r--r--phpBB/includes/template/twig/node/php.php46
-rw-r--r--phpBB/includes/template/twig/tokenparser/define.php57
-rw-r--r--phpBB/includes/template/twig/tokenparser/event.php38
-rw-r--r--phpBB/includes/template/twig/tokenparser/if.php78
-rw-r--r--phpBB/includes/template/twig/tokenparser/include.php37
-rw-r--r--phpBB/includes/template/twig/tokenparser/includejs.php38
-rw-r--r--phpBB/includes/template/twig/tokenparser/includephp.php47
-rw-r--r--phpBB/includes/template/twig/tokenparser/php.php46
-rw-r--r--phpBB/includes/template/twig/twig.php454
21 files changed, 1789 insertions, 0 deletions
diff --git a/phpBB/includes/template/twig/definition.php b/phpBB/includes/template/twig/definition.php
new file mode 100644
index 0000000000..110437eb32
--- /dev/null
+++ b/phpBB/includes/template/twig/definition.php
@@ -0,0 +1,50 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* This class holds all DEFINE variables from the current page load
+*/
+class phpbb_template_twig_definition
+{
+ /** @var array **/
+ protected $definitions = array();
+
+ /**
+ * Get a DEFINE'd variable
+ *
+ * @param string $name
+ * @return mixed Null if not found
+ */
+ public function __call($name, $arguments)
+ {
+ return (isset($this->definitions[$name])) ? $this->definitions[$name] : null;
+ }
+
+ /**
+ * DEFINE a variable
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return phpbb_template_twig_definition
+ */
+ public function set($name, $value)
+ {
+ $this->definitions[$name] = $value;
+
+ return $this;
+ }
+}
diff --git a/phpBB/includes/template/twig/environment.php b/phpBB/includes/template/twig/environment.php
new file mode 100644
index 0000000000..d20da965c3
--- /dev/null
+++ b/phpBB/includes/template/twig/environment.php
@@ -0,0 +1,181 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+class phpbb_template_twig_environment extends Twig_Environment
+{
+ /** @var array */
+ protected $phpbb_extensions;
+
+ /** @var phpbb_config */
+ protected $phpbb_config;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var array **/
+ protected $namespaceLookUpOrder = array('__main__');
+
+ public function __construct($phpbb_config, $phpbb_extensions, $phpbb_root_path, Twig_LoaderInterface $loader = null, $options = array())
+ {
+ $this->phpbb_config = $phpbb_config;
+ $this->phpbb_extensions = $phpbb_extensions;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ return parent::__construct($loader, $options);
+ }
+
+ /**
+ * Gets the cache filename for a given template.
+ *
+ * @param string $name The template name
+ *
+ * @return string The cache file name
+ */
+ public function ignoregetCacheFilename($name)
+ {
+ if (false === $this->cache) {
+ return false;
+ }
+// @todo
+ $file_path = $this->getLoader()->getCacheKey($name);
+ foreach ($this->getLoader()->getNamespaces() as $namespace)
+ {
+ foreach ($this->getLoader()->getPaths($namespace) as $path)
+ {
+ if (strpos($file_path, $path) === 0)
+ {
+ //return $this->getCache() . '/' . preg_replace('#[^a-zA-Z0-9_/]#', '_', $namespace . '/' . $name) . '.php';
+ }
+ }
+ }
+
+ // We probably should never get here under normal circumstances
+ return $this->getCache() . '/' . preg_replace('#[^a-zA-Z0-9_/]#', '_', $name) . '.php';
+ return $this->getCache() . '/' . preg_replace('#[^a-zA-Z0-9_/]#', '_', $name) . '_' . md5($this->getLoader()->getCacheKey($name)) . '.php';
+ }
+
+ /**
+ * Get the list of enabled phpBB extensions
+ *
+ * @return array
+ */
+ public function get_phpbb_extensions()
+ {
+ return $this->phpbb_extensions;
+ }
+
+ /**
+ * Get phpBB config
+ *
+ * @return phpbb_config
+ */
+ public function get_phpbb_config()
+ {
+ return $this->phpbb_config;
+ }
+
+ /**
+ * Get the phpBB root path
+ *
+ * @return string
+ */
+ public function get_phpbb_root_path()
+ {
+ return $this->phpbb_root_path;
+ }
+
+ /**
+ * Get the namespace look up order
+ *
+ * @return array
+ */
+ public function getNamespaceLookUpOrder()
+ {
+ return $this->namespaceLookUpOrder;
+ }
+
+ /**
+ * Set the namespace look up order to load templates from
+ *
+ * @param array $namespace
+ * @return Twig_Environment
+ */
+ public function setNamespaceLookUpOrder($namespace)
+ {
+ $this->namespaceLookUpOrder = $namespace;
+
+ return $this;
+ }
+
+ /**
+ * Loads a template by name.
+ *
+ * @param string $name The template name
+ * @param integer $index The index if it is an embedded template
+ *
+ * @return Twig_TemplateInterface A template instance representing the given template name
+ */
+ public function loadTemplate($name, $index = null)
+ {
+ if (strpos($name, '@') === false)
+ {
+ foreach ($this->namespaceLookUpOrder as $namespace)
+ {
+ try
+ {
+ if ($namespace === '__main__')
+ {
+ return parent::loadTemplate($name, $index);
+ }
+
+ return parent::loadTemplate('@' . $namespace . '/' . $name, $index);
+ }
+ catch (Twig_Error_Loader $e)
+ {
+ }
+ }
+
+ // We were unable to load any templates
+ throw $e;
+ }
+ else
+ {
+ return parent::loadTemplate($name, $index);
+ }
+ }
+
+ /**
+ * recursive helper to set variables into $context so that Twig can properly fetch them for display
+ *
+ * @param array $data Data to set at the end of the chain
+ * @param array $blocks Array of blocks to loop into still
+ * @param mixed $current_location Current location in $context (recursive!)
+ */
+ public function context_recursive_loop_builder($data, $blocks, &$current_location)
+ {
+ $block = array_shift($blocks);
+
+ if (empty($blocks))
+ {
+ $current_location[$block] = $data;
+ }
+ else
+ {
+ $this->context_recursive_loop_builder($data, $blocks, $current_location[$block]);
+ }
+ }
+}
diff --git a/phpBB/includes/template/twig/extension.php b/phpBB/includes/template/twig/extension.php
new file mode 100644
index 0000000000..1ea5f7b662
--- /dev/null
+++ b/phpBB/includes/template/twig/extension.php
@@ -0,0 +1,143 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+class phpbb_template_twig_extension extends Twig_Extension
+{
+ /** @var phpbb_user */
+ protected $user;
+
+ public function __construct($user)
+ {
+ $this->user = $user;
+ }
+
+ public function getName()
+ {
+ return 'phpbb';
+ }
+
+ /**
+ * Returns the token parser instance to add to the existing list.
+ *
+ * @return array An array of Twig_TokenParser instances
+ */
+ public function getTokenParsers()
+ {
+ return array(
+ new phpbb_template_twig_tokenparser_if,
+ new phpbb_template_twig_tokenparser_define,
+ new phpbb_template_twig_tokenparser_include,
+ new phpbb_template_twig_tokenparser_includejs,
+ new phpbb_template_twig_tokenparser_event,
+ new phpbb_template_twig_tokenparser_includephp,
+ new phpbb_template_twig_tokenparser_php,
+ );
+ }
+
+ /**
+ * Returns a list of filters to add to the existing list.
+ *
+ * @return array An array of filters
+ */
+ public function getFilters()
+ {
+ return array(
+ new Twig_SimpleFilter('subset', array($this, 'loop_subset'), array('needs_environment' => true)),
+ new Twig_SimpleFilter('addslashes', 'addslashes'),
+ );
+ }
+
+ /**
+ * Returns a list of global functions to add to the existing list.
+ *
+ * @return array An array of global functions
+ */
+ public function getFunctions()
+ {
+ return array(
+ new Twig_SimpleFunction('lang', array($this->user, 'lang')),
+ );
+ }
+
+ /**
+ * Returns a list of operators to add to the existing list.
+ *
+ * @return array An array of operators
+ */
+ public function getOperators()
+ {
+ return array(
+ array(
+ '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
+ ),
+ array(
+ // precedence settings are copied from similar operators in Twig core extension
+ '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+
+ 'eq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+
+ 'ne' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'neq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ '<>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+
+ '===' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_equalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ '!==' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_notequalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+
+ 'gt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'gte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'ge' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'lt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'lte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ 'le' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+
+ 'mod' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+ ),
+ );
+ }
+
+ /**
+ * Grabs a subset of a loop
+ *
+ * @param Twig_Environment $env A Twig_Environment instance
+ * @param mixed $item A variable
+ * @param integer $start Start of the subset
+ * @param integer $end End of the subset
+ * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array)
+ *
+ * @return mixed The sliced variable
+ */
+ function loop_subset(Twig_Environment $env, $item, $start, $end = null, $preserveKeys = false)
+ {
+ // We do almost the same thing as array_slice, except when $end is positive
+ if ($end >= 1)
+ {
+ // When end is > 1, subset will end on the last item in an array with the specified $end
+ // This is different from slice in that it is the number we end on rather than the number
+ // of items to grab (length)
+
+ // Start must always be the actual starting number for this calculation (not negative)
+ $start = ($start < 0) ? sizeof($item) + $start : $start;
+ $end = $end - $start;
+ }
+
+ // We always include the last element (this was the past design)
+ $end = ($end == -1 || $end === null) ? null : $end + 1;
+
+ return twig_slice($env, $item, $start, $end, $preserveKeys);
+ }
+}
diff --git a/phpBB/includes/template/twig/lexer.php b/phpBB/includes/template/twig/lexer.php
new file mode 100644
index 0000000000..131cf654ef
--- /dev/null
+++ b/phpBB/includes/template/twig/lexer.php
@@ -0,0 +1,229 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+class phpbb_template_twig_lexer extends Twig_Lexer
+{
+ public function tokenize($code, $filename = null)
+ {
+ $valid_starting_tokens = array(
+ // Commented out tokens are handled separately from the main replace
+ /*'BEGIN',
+ 'BEGINELSE',
+ 'END',*/
+ 'IF',
+ 'ELSE',
+ 'ELSEIF',
+ 'ENDIF',
+ /*'DEFINE',
+ 'UNDEFINE',*/
+ 'ENDDEFINE',
+ 'INCLUDE',
+ 'INCLUDEPHP',
+ 'INCLUDEJS',
+ 'PHP',
+ 'ENDPHP',
+ 'EVENT',
+ );
+
+ // Fix tokens that may have inline variables (e.g. <!-- DEFINE $TEST = '{FOO}')
+ $code = $this->fix_inline_variable_tokens(array(
+ 'DEFINE.+=',
+ 'INCLUDE',
+ 'INCLUDEPHP',
+ ), $code);
+
+ // Fix our BEGIN statements
+ $code = $this->fix_begin_tokens($code);
+
+ // Fix our IF tokens
+ $code = $this->fix_if_tokens($code);
+
+ // Fix our DEFINE tokens
+ $code = $this->fix_define_tokens($code);
+
+ // Replace all of our starting tokens, <!-- TOKEN --> with Twig style, {% TOKEN %}
+ // This also strips outer parenthesis, <!-- IF (blah) --> becomes <!-- IF blah -->
+ $code = preg_replace('#<!-- (' . implode('|', $valid_starting_tokens) . ')(?: (.*?) ?)?-->#', '{% $1 $2 %}', $code);
+
+ // Replace all of our language variables, {L_VARNAME}, with Twig style, {{ lang('NAME') }}
+ $code = preg_replace('#{L_([a-zA-Z0-9_\.]+)}#', '{{ lang(\'$1\') }}', $code);
+
+ // Replace all of our escaped language variables, {LA_VARNAME}, with Twig style, {{ lang('NAME')|addslashes }}
+ $code = preg_replace('#{LA_([a-zA-Z0-9_\.]+)}#', '{{ lang(\'$1\')|addslashes }}', $code);
+
+ // Replace all of our variables, {VARNAME}, with Twig style, {{ VARNAME }}
+ $code = preg_replace('#{([a-zA-Z0-9_\.]+)}#', '{{ $1 }}', $code);
+
+ return parent::tokenize($code, $filename);
+ }
+
+ /**
+ * Fix tokens that may have inline variables
+ *
+ * E.g. <!-- INCLUDE {TEST}.html
+ *
+ * @param array $tokens array of tokens to search for (imploded to a regular expression)
+ * @param string $code
+ * @return string
+ */
+ protected function fix_inline_variable_tokens($tokens, $code)
+ {
+ $callback = function($matches)
+ {
+ // Remove any quotes that may have been used in different implementations
+ // E.g. DEFINE $TEST = 'blah' vs INCLUDE foo
+ // Replace {} with start/end to parse variables (' ~ TEST ~ '.html)
+ $matches[2] = str_replace(array('"', "'", '{', '}'), array('', '', "' ~ ", " ~ '"), $matches[2]);
+
+ // Surround the matches in single quotes ('' ~ TEST ~ '.html')
+ return "<!-- {$matches[1]} '{$matches[2]}' -->";
+ };
+
+ return preg_replace_callback('#<!-- (' . implode('|', $tokens) . ') (.+?) -->#', $callback, $code);
+ }
+
+ /**
+ * Fix begin tokens (convert our BEGIN to Twig for)
+ *
+ * Not meant to be used outside of this context, public because the anonymous function calls this
+ *
+ * @param string $code
+ * @param array $parent_nodes (used in recursion)
+ * @return string
+ */
+ public function fix_begin_tokens($code, $parent_nodes = array())
+ {
+ // PHP 5.3 cannot use $this in an anonymous function, so use this as a work-around
+ $parent_class = $this;
+ $callback = function ($matches) use ($parent_class, $parent_nodes)
+ {
+ $name = $matches[1];
+ $subset = trim(substr($matches[2], 1, -1)); // Remove parenthesis
+ $body = $matches[3];
+
+ // Is the designer wanting to call another loop in a loop?
+ // <!-- BEGIN loop -->
+ // <!-- BEGIN !loop2 -->
+ // <!-- END !loop2 -->
+ // <!-- END 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($name, '!') === 0)
+ {
+ // Count the number if ! occurrences
+ $count = substr_count($name, '!');
+ for ($i = 0; $i < $count; $i++)
+ {
+ array_pop($parent_nodes);
+ $name = substr($name, 1);
+ }
+ }
+
+ // Remove all parent nodes, e.g. foo, bar from foo.bar.foobar
+ foreach ($parent_nodes as $node)
+ {
+ $body = preg_replace('#([^a-zA-Z0-9])' . $node . '\.#', '$1', $body);
+ }
+
+ // Add current node to list of parent nodes for child nodes
+ $parent_nodes[] = $name;
+
+ // Recursive...fix any child nodes
+ $body = $parent_class->fix_begin_tokens($body, $parent_nodes);
+
+ // Rename loopname vars (to prevent collisions, loop children are named (loop name)_loop_element)
+ $body = str_replace($name . '.', $name . '_loop_element.', $body);
+
+ // Need the parent variable name
+ array_pop($parent_nodes);
+ $parent = (!empty($parent_nodes)) ? end($parent_nodes) . '_loop_element.' : '';
+
+ if ($subset !== '')
+ {
+ $subset = '|subset(' . $subset . ')';
+ }
+
+ // Turn into a Twig for loop, using (loop name)_loop_element for each child
+ return "{% for {$name}_loop_element in {$parent}{$name}{$subset} %}{$body}{% endfor %}";
+ };
+
+ // Replace <!-- BEGINELSE --> correctly, only needs to be done once
+ $code = str_replace('<!-- BEGINELSE -->', '{% else %}', $code);
+
+ return preg_replace_callback('#<!-- BEGIN ([!a-zA-Z0-9_]+)(\([0-9,\-]+\))? -->(.+?)<!-- END \1 -->#s', $callback, $code);
+ }
+
+ /**
+ * Fix IF statements
+ *
+ * @param string $code
+ * @return string
+ */
+ protected function fix_if_tokens($code)
+ {
+ $callback = function($matches)
+ {
+ // Replace $TEST with definition.TEST
+ $matches[1] = preg_replace('#\s\$([a-zA-Z_0-9]+)#', ' definition.$1', $matches[1]);
+
+ // Replace .test with test|length
+ $matches[1] = preg_replace('#\s\.([a-zA-Z_0-9]+)#', ' $1|length', $matches[1]);
+
+ return '<!-- IF' . $matches[1] . '-->';
+ };
+
+ // Replace our "div by" with Twig's divisibleby (Twig does not like test names with spaces)
+ $code = preg_replace('# div by ([0-9]+)#', ' divisibleby($1)', $code);
+
+ return preg_replace_callback('#<!-- IF((.*)[\s][\$|\.|!]([^\s]+)(.*))-->#', $callback, $code);
+ }
+
+ /**
+ * Fix DEFINE statements and {$VARNAME} variables
+ *
+ * @param string $code
+ * @return string
+ */
+ protected function fix_define_tokens($code)
+ {
+ /**
+ * Changing $VARNAME to definition.varname because set is only local
+ * context (e.g. DEFINE $TEST will only make $TEST available in current
+ * template and any child templates, but not any parent templates).
+ *
+ * DEFINE handles setting it properly to definition in its node, but the
+ * variables reading FROM it need to be altered to definition.VARNAME
+ *
+ * Setting up definition as a class in the array passed to Twig
+ * ($context) makes set definition.TEST available in the global context
+ */
+
+ // Replace <!-- DEFINE $NAME with {% DEFINE definition.NAME
+ $code = preg_replace('#<!-- DEFINE \$(.*)-->#', '{% DEFINE $1 %}', $code);
+
+ // Changing UNDEFINE NAME to DEFINE NAME = null to save from creating an extra token parser/node
+ $code = preg_replace('#<!-- UNDEFINE \$(.*)-->#', '{% DEFINE $1= null %}', $code);
+
+ // Replace all of our variables, {$VARNAME}, with Twig style, {{ definition.VARNAME }}
+ $code = preg_replace('#{\$([a-zA-Z0-9_\.]+)}#', '{{ definition.$1 }}', $code);
+
+ // Replace all of our variables, ~ $VARNAME ~, with Twig style, ~ definition.VARNAME ~
+ $code = preg_replace('#~ \$([a-zA-Z0-9_\.]+) ~#', '~ definition.$1 ~', $code);
+
+ return $code;
+ }
+}
diff --git a/phpBB/includes/template/twig/loader.php b/phpBB/includes/template/twig/loader.php
new file mode 100644
index 0000000000..b153bd81ea
--- /dev/null
+++ b/phpBB/includes/template/twig/loader.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+// @todo remove if not needed
+class phpbb_template_twig_loader extends Twig_Loader_Filesystem
+{
+ protected $phpbb_locator;
+
+ /**
+ * Constructor.
+ *
+ * @param string|array $paths A path or an array of paths where to look for templates
+ * @param phpbb_template_locator
+ */
+ public function __construct($paths = array(), phpbb_template_locator $phpbb_locator)
+ {
+ if ($paths) {
+ $this->setPaths($paths);
+ }
+
+ $this->phpbb_locator = $phpbb_locator;
+ }
+
+ protected function findTemplate($name)
+ {
+ $name = (string) $name;
+
+ if (!$name)
+ {
+ throw new Twig_Error_Loader(sprintf('Unable to find template "%s".', $name));
+ }
+
+ $this->phpbb_locator->set_filenames(array(
+ 'temp' => $name,
+ ));
+ $location = $this->phpbb_locator->get_source_file_for_handle('temp');
+
+ if (!$location)
+ {
+ throw new Twig_Error_Loader(sprintf('Unable to find template "%s".', $name));
+ }
+
+ return $this->cache[$name] = $location;
+ }
+}
diff --git a/phpBB/includes/template/twig/node/define.php b/phpBB/includes/template/twig/node/define.php
new file mode 100644
index 0000000000..0c4d400767
--- /dev/null
+++ b/phpBB/includes/template/twig/node/define.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_define extends Twig_Node
+{
+ public function __construct($capture, Twig_NodeInterface $name, Twig_NodeInterface $value, $lineno, $tag = null)
+ {
+ parent::__construct(array('name' => $name, 'value' => $value), array('capture' => $capture, 'safe' => false), $lineno, $tag);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ if ($this->getAttribute('capture')) {
+ $compiler
+ ->write("ob_start();\n")
+ ->subcompile($this->getNode('value'))
+ ;
+
+ $compiler->write("\$value = ('' === \$value = ob_get_clean()) ? '' : new Twig_Markup(\$value, \$this->env->getCharset());\n");
+ }
+ else
+ {
+ $compiler
+ ->write("\$value = ")
+ ->subcompile($this->getNode('value'))
+ ->raw(";\n")
+ ;
+ }
+
+ $compiler
+ ->write("\$context['definition']->set('")
+ ->raw($this->getNode('name')->getAttribute('name'))
+ ->raw("', \$value);\n")
+ ;
+ }
+}
diff --git a/phpBB/includes/template/twig/node/event.php b/phpBB/includes/template/twig/node/event.php
new file mode 100644
index 0000000000..358c68dae5
--- /dev/null
+++ b/phpBB/includes/template/twig/node/event.php
@@ -0,0 +1,50 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_event extends Twig_Node
+{
+ /** @var Twig_Environment */
+ protected $environment;
+
+ public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null)
+ {
+ $this->environment = $environment;
+
+ parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $location = $this->getNode('expr')->getAttribute('name');
+
+ foreach ($this->environment->get_phpbb_extensions() as $ext_namespace => $ext_path)
+ {
+ $ext_namespace = str_replace('/', '_', $ext_namespace);
+
+ if ($this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html'))
+ {
+ $compiler
+ ->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
+
+ // We set the namespace lookup order to be this extension first, then the main path
+ ->write("\$this->env->setNamespaceLookUpOrder(array('" . $ext_namespace . "', '__main__'));\n")
+ ->write("\$this->env->loadTemplate('@" . $ext_namespace . "/" . $location . ".html')->display(\$context);\n")
+ ->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n")
+ ;
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/template/twig/node/expression/binary/equalequal.php b/phpBB/includes/template/twig/node/expression/binary/equalequal.php
new file mode 100644
index 0000000000..3a0c79c839
--- /dev/null
+++ b/phpBB/includes/template/twig/node/expression/binary/equalequal.php
@@ -0,0 +1,16 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_expression_binary_equalequal extends Twig_Node_Expression_Binary
+{
+ public function operator(Twig_Compiler $compiler)
+ {
+ return $compiler->raw('===');
+ }
+}
diff --git a/phpBB/includes/template/twig/node/expression/binary/notequalequal.php b/phpBB/includes/template/twig/node/expression/binary/notequalequal.php
new file mode 100644
index 0000000000..b53bc56b2d
--- /dev/null
+++ b/phpBB/includes/template/twig/node/expression/binary/notequalequal.php
@@ -0,0 +1,16 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_expression_binary_notequalequal extends Twig_Node_Expression_Binary
+{
+ public function operator(Twig_Compiler $compiler)
+ {
+ return $compiler->raw('!==');
+ }
+}
diff --git a/phpBB/includes/template/twig/node/include.php b/phpBB/includes/template/twig/node/include.php
new file mode 100644
index 0000000000..2a90dc19e4
--- /dev/null
+++ b/phpBB/includes/template/twig/node/include.php
@@ -0,0 +1,47 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_include extends Twig_Node_Include
+{
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $compiler
+ ->write("\$location = ")
+ ->subcompile($this->getNode('expr'))
+ ->raw(";\n")
+ ->write("\$namespace = false;\n")
+ ->write("if (strpos(\$location, '@') === 0) {\n")
+ ->indent()
+ ->write("\$namespace = substr(\$location, 1, strpos(\$location, '/') - 1);\n")
+ ->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
+
+ // We set the namespace lookup order to be this namespace first, then the main path
+ ->write("\$this->env->setNamespaceLookUpOrder(array(\$namespace, '__main__'));\n")
+ ->outdent()
+ ->write("}\n")
+ ;
+
+ parent::compile($compiler);
+
+ $compiler
+ ->write("if (\$namespace) {\n")
+ ->indent()
+ ->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n")
+ ->outdent()
+ ->write("}\n")
+ ;
+ }
+}
diff --git a/phpBB/includes/template/twig/node/includejs.php b/phpBB/includes/template/twig/node/includejs.php
new file mode 100644
index 0000000000..f4c26affa4
--- /dev/null
+++ b/phpBB/includes/template/twig/node/includejs.php
@@ -0,0 +1,39 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_includejs extends Twig_Node
+{
+ /** @var Twig_Environment */
+ protected $environment;
+
+ public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null)
+ {
+ $this->environment = $environment;
+
+ parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $config = $this->environment->get_phpbb_config();
+
+ $compiler
+ ->write("\$context['SCRIPTS'] .= '<script type=\"text/javascript\" src=\"' . ")
+ ->subcompile($this->getNode('expr'))
+ ->raw(" . '?assets_version=" . $config['assets_version'] . "\"></script>';\n\n")
+ ;
+ }
+}
diff --git a/phpBB/includes/template/twig/node/includephp.php b/phpBB/includes/template/twig/node/includephp.php
new file mode 100644
index 0000000000..5315d136d7
--- /dev/null
+++ b/phpBB/includes/template/twig/node/includephp.php
@@ -0,0 +1,77 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_includephp extends Twig_Node
+{
+ /** @var Twig_Environment */
+ protected $environment;
+
+ public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $ignoreMissing = false, $lineno, $tag = null)
+ {
+ $this->environment = $environment;
+
+ parent::__construct(array('expr' => $expr), array('ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $config = $this->environment->get_phpbb_config();
+
+ if (!$config['tpl_allow_php'])
+ {
+ $compiler
+ ->write("// INCLUDEPHP Disabled\n")
+ ;
+
+ return;
+ }
+
+ if ($this->getAttribute('ignore_missing')) {
+ $compiler
+ ->write("try {\n")
+ ->indent()
+ ;
+ }
+
+ // Replace variables in the expression
+ $expr = preg_replace('#{{ ([a-zA-Z0-9_]+) }}#', '\' . ((isset($context["$1"])) ? $context["$1"] : null) . \'', $this->getNode('expr')->getAttribute('value'));
+
+ $compiler
+ ->write("if (phpbb_is_absolute('$expr')) {\n")
+ ->indent()
+ ->write("require('$expr');\n")
+ ->outdent()
+ ->write("} else {\n")
+ ->indent()
+ ->write("require(\$this->getEnvironment()->get_phpbb_root_path() . '")
+ ->raw($expr)
+ ->raw("');\n")
+ ->outdent()
+ ->write("}\n")
+ ;
+
+ if ($this->getAttribute('ignore_missing')) {
+ $compiler
+ ->outdent()
+ ->write("} catch (Twig_Error_Loader \$e) {\n")
+ ->indent()
+ ->write("// ignore missing template\n")
+ ->outdent()
+ ->write("}\n\n")
+ ;
+ }
+ }
+}
diff --git a/phpBB/includes/template/twig/node/php.php b/phpBB/includes/template/twig/node/php.php
new file mode 100644
index 0000000000..953cd184a7
--- /dev/null
+++ b/phpBB/includes/template/twig/node/php.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_node_php extends Twig_Node
+{
+ /** @var Twig_Environment */
+ protected $environment;
+
+ public function __construct(Twig_Node_Text $text, phpbb_template_twig_environment $environment, $lineno, $tag = null)
+ {
+ $this->environment = $environment;
+
+ parent::__construct(array('text' => $text), array(), $lineno, $tag);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $config = $this->environment->get_phpbb_config();
+
+ if (!$config['tpl_allow_php'])
+ {
+ $compiler
+ ->write("// PHP Disabled\n")
+ ;
+
+ return;
+ }
+
+ $compiler
+ ->raw($this->getNode('text')->getAttribute('data'))
+ ;
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/define.php b/phpBB/includes/template/twig/tokenparser/define.php
new file mode 100644
index 0000000000..ebf7cb4c4a
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/define.php
@@ -0,0 +1,57 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_define extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $lineno = $token->getLine();
+ $stream = $this->parser->getStream();
+ $name = $this->parser->getExpressionParser()->parseExpression();
+
+ $capture = false;
+ if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
+ $stream->next();
+ $value = $this->parser->getExpressionParser()->parseExpression();
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+ } else {
+ $capture = true;
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ $value = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+ }
+
+ return new phpbb_template_twig_node_define($capture, $name, $value, $lineno, $this->getTag());
+ }
+
+ public function decideBlockEnd(Twig_Token $token)
+ {
+ return $token->test('ENDDEFINE');
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'DEFINE';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/event.php b/phpBB/includes/template/twig/tokenparser/event.php
new file mode 100644
index 0000000000..03810454ed
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/event.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_event extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+
+ $stream = $this->parser->getStream();
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new phpbb_template_twig_node_event($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'EVENT';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/if.php b/phpBB/includes/template/twig/tokenparser/if.php
new file mode 100644
index 0000000000..04ee048f94
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/if.php
@@ -0,0 +1,78 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_if extends Twig_TokenParser_If
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $lineno = $token->getLine();
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+ $stream = $this->parser->getStream();
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+ $body = $this->parser->subparse(array($this, 'decideIfFork'));
+ $tests = array($expr, $body);
+ $else = null;
+
+ $end = false;
+ while (!$end) {
+ switch ($stream->next()->getValue()) {
+ case 'ELSE':
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+ $else = $this->parser->subparse(array($this, 'decideIfEnd'));
+ break;
+
+ case 'ELSEIF':
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+ $body = $this->parser->subparse(array($this, 'decideIfFork'));
+ $tests[] = $expr;
+ $tests[] = $body;
+ break;
+
+ case 'ENDIF':
+ $end = true;
+ break;
+
+ default:
+ throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "ELSE", "ELSEIF", or "ENDIF" to close the "IF" block started at line %d)', $lineno), $stream->getCurrent()->getLine(), $stream->getFilename());
+ }
+ }
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag());
+ }
+
+ public function decideIfFork(Twig_Token $token)
+ {
+ return $token->test(array('ELSEIF', 'ELSE', 'ENDIF'));
+ }
+
+ public function decideIfEnd(Twig_Token $token)
+ {
+ return $token->test(array('ENDIF'));
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'IF';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/include.php b/phpBB/includes/template/twig/tokenparser/include.php
new file mode 100644
index 0000000000..0f7e4faf8f
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/include.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_include extends Twig_TokenParser_Include
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+
+ list($variables, $only, $ignoreMissing) = $this->parseArguments();
+
+ return new phpbb_template_twig_node_include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'INCLUDE';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/includejs.php b/phpBB/includes/template/twig/tokenparser/includejs.php
new file mode 100644
index 0000000000..0b46f315d2
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/includejs.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_includejs extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+
+ $stream = $this->parser->getStream();
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new phpbb_template_twig_node_includejs($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'INCLUDEJS';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/includephp.php b/phpBB/includes/template/twig/tokenparser/includephp.php
new file mode 100644
index 0000000000..a81d663c09
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/includephp.php
@@ -0,0 +1,47 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_includephp extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $expr = $this->parser->getExpressionParser()->parseExpression();
+
+ $stream = $this->parser->getStream();
+
+ $ignoreMissing = false;
+ if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) {
+ $stream->next();
+ $stream->expect(Twig_Token::NAME_TYPE, 'missing');
+
+ $ignoreMissing = true;
+ }
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new phpbb_template_twig_node_includephp($expr, $this->parser->getEnvironment(), $ignoreMissing, $token->getLine(), $this->getTag());
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'INCLUDEPHP';
+ }
+}
diff --git a/phpBB/includes/template/twig/tokenparser/php.php b/phpBB/includes/template/twig/tokenparser/php.php
new file mode 100644
index 0000000000..7db57081e2
--- /dev/null
+++ b/phpBB/includes/template/twig/tokenparser/php.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_template_twig_tokenparser_php extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $stream = $this->parser->getStream();
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ $body = $this->parser->subparse(array($this, 'decideEnd'), true);
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new phpbb_template_twig_node_php($body, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
+ }
+
+ public function decideEnd(Twig_Token $token)
+ {
+ return $token->test('ENDPHP');
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
+ public function getTag()
+ {
+ return 'PHP';
+ }
+}
diff --git a/phpBB/includes/template/twig/twig.php b/phpBB/includes/template/twig/twig.php
new file mode 100644
index 0000000000..5fe8ba97f6
--- /dev/null
+++ b/phpBB/includes/template/twig/twig.php
@@ -0,0 +1,454 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Twig Template class.
+* @package phpBB3
+*/
+class phpbb_template_twig implements phpbb_template
+{
+ /**
+ * Template context.
+ * Stores template data used during template rendering.
+ * @var phpbb_template_context
+ */
+ protected $context;
+
+ /**
+ * Path of the cache directory for the template
+ * @var string
+ */
+ public $cachepath = '';
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP file extension
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * phpBB config instance
+ * @var phpbb_config
+ */
+ protected $config;
+
+ /**
+ * Current user
+ * @var phpbb_user
+ */
+ protected $user;
+
+ /**
+ * Extension manager.
+ *
+ * @var phpbb_extension_manager
+ */
+ protected $extension_manager;
+
+ /**
+ * Name of the style that the template being compiled and/or rendered
+ * belongs to, and its parents, in inheritance tree order.
+ *
+ * Used to invoke style-specific template events.
+ *
+ * @var array
+ */
+ protected $style_names;
+
+ /**
+ * Twig Environment
+ *
+ * @var Twig_Environment
+ */
+ protected $twig;
+
+ /**
+ * Array of filenames assigned to set_filenames
+ *
+ * @var array
+ */
+ protected $filenames = array();
+
+ /**
+ * Constructor.
+ *
+ * @todo remove unnecessary dependencies
+ *
+ * @param string $phpbb_root_path phpBB root path
+ * @param user $user current user
+ * @param phpbb_template_locator $locator template locator
+ * @param phpbb_template_context $context template context
+ * @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked
+ */
+ public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_locator $locator, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->config = $config;
+ $this->user = $user;
+ $this->context = $context;
+ $this->extension_manager = $extension_manager;
+
+ $this->cachepath = $phpbb_root_path . 'cache/twig/';
+
+ // Initiate the loader, __main__ namespace paths will be setup later in set_style_names()
+ $loader = new Twig_Loader_Filesystem('');
+
+ $this->twig = new phpbb_template_twig_environment(
+ $this->config,
+ ($this->extension_manager) ? $this->extension_manager->all_enabled() : array(),
+ $this->phpbb_root_path,
+ $loader,
+ array(
+ 'cache' => $this->cachepath,
+ 'debug' => true, // @todo
+ 'auto_reload' => true, // @todo
+ 'autoescape' => false,
+ )
+ );
+
+ // Clear previous cache files (while WIP)
+ // @todo remove
+ $this->clear_cache();
+
+ $this->twig->addExtension(
+ new phpbb_template_twig_extension(
+ $this->user
+ )
+ );
+
+ $lexer = new phpbb_template_twig_lexer($this->twig);
+
+ $this->twig->setLexer($lexer);
+ }
+
+ /**
+ * Clear the cache
+ *
+ * @return phpbb_template
+ */
+ public function clear_cache()
+ {
+ if (is_dir($this->cachepath))
+ {
+ $this->twig->clearCacheFiles();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the template filenames for handles.
+ *
+ * @param array $filename_array Should be a hash of handle => filename pairs.
+ * @return phpbb_template $this
+ */
+ public function set_filenames(array $filename_array)
+ {
+ $this->filenames = array_merge($filename_array, $this->filenames);
+
+ return $this;
+ }
+
+ /**
+ * Sets the style names corresponding to style hierarchy being compiled
+ * and/or rendered.
+ *
+ * @param array $style_names List of style names in inheritance tree order
+ * @return phpbb_template $this
+ */
+ public function set_style_names(array $style_names, $style_paths = array())
+ {
+ $this->style_names = $style_names;
+
+ // Set as __main__ namespace
+ $this->twig->getLoader()->setPaths($style_paths);
+
+ // Core style namespace from phpbb_style::set_style()
+ if ($this->user && ($style_names === array($this->user->style['style_path']) || $style_names[0] == $this->user->style['style_path']))
+ {
+ $this->twig->getLoader()->setPaths($style_paths, 'core');
+ }
+
+ // Add admin namespace
+ // @todo use phpbb_admin path
+ if (is_dir($this->phpbb_root_path . 'adm/style/'))
+ {
+ $this->twig->getLoader()->setPaths($this->phpbb_root_path . 'adm/style/', 'admin');
+ }
+
+ // Add all namespaces for all extensions
+ if ($this->extension_manager instanceof phpbb_extension_manager)
+ {
+ $style_names[] = 'all';
+
+ foreach ($this->extension_manager->all_enabled() as $ext_namespace => $ext_path)
+ {
+ // namespaces cannot contain /
+ $namespace = str_replace('/', '_', $ext_namespace);
+ $paths = array();
+
+ foreach ($style_names as $style_name)
+ {
+ $ext_style_path = $ext_path . 'styles/' . $style_name . '/template';
+
+ if (is_dir($ext_style_path))
+ {
+ $paths[] = $ext_style_path;
+ }
+ }
+
+ $this->twig->getLoader()->setPaths($paths, $namespace);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clears all variables and blocks assigned to this template.
+ *
+ * @return phpbb_template $this
+ */
+ public function destroy()
+ {
+ $this->context = array();
+
+ return $this;
+ }
+
+ /**
+ * Reset/empty complete block
+ *
+ * @param string $blockname Name of block to destroy
+ */
+ public function destroy_block_vars($blockname)
+ {
+ return $this->context->destroy_block_vars($blockname);
+ }
+
+ /**
+ * 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
+ */
+ public function display($handle)
+ {
+ $result = $this->call_hook($handle, __FUNCTION__);
+ if ($result !== false)
+ {
+ return $result[0];
+ }
+
+ $context = &$this->get_template_vars();
+ $this->twig->display($this->get_filename_from_handle($handle), $context);
+
+ return true;
+ }
+
+ /**
+ * Calls hook if any is defined.
+ *
+ * @param string $handle Template handle being displayed.
+ * @param string $method Method name of the caller.
+ */
+ protected function call_hook($handle, $method)
+ {
+ global $phpbb_hook;
+
+ if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this))
+ {
+ if ($phpbb_hook->hook_return(array(__CLASS__, $method)))
+ {
+ $result = $phpbb_hook->hook_return_result(array(__CLASS__, $method));
+ 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)
+ {
+ if ($return_content)
+ {
+ return $this->twig->render($this->get_filename_from_handle($handle));
+ }
+
+ $this->assign_var($template_var, $this->twig->render($this->get_filename_from_handle($handle)));
+
+ return true;
+ }
+
+ /**
+ * 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 scalar value to a single key.
+ *
+ * Value can be a string, an integer or a boolean.
+ *
+ * @param string $varname Variable name
+ * @param string $varval Value to assign to variable
+ */
+ public function assign_var($varname, $varval)
+ {
+ return $this->context->assign_var($varname, $varval);
+ }
+
+ /**
+ * Append text to the string value stored in a key.
+ *
+ * Text is appended using the string concatenation operator (.).
+ *
+ * @param string $varname Variable name
+ * @param string $varval Value to append to variable
+ */
+ public function append_var($varname, $varval)
+ {
+ return $this->context->append_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);
+ }
+
+ /**
+ * Get template vars in a format Twig will use (from the context)
+ *
+ * @return array
+ */
+ public function get_template_vars()
+ {
+ $context_vars = $this->context->get_data_ref();
+
+ $vars = array_merge(
+ $context_vars['.'][0], // To get normal vars
+ $context_vars, // To get loops
+ array(
+ 'definition' => new phpbb_template_twig_definition(),
+ 'user' => $this->user,
+ )
+ );
+
+ // cleanup
+ unset($vars['.']);
+
+ return $vars;
+ }
+
+ /**
+ * Get a filename from the handle
+ *
+ * @param string $handle
+ * @return string
+ */
+ protected function get_filename_from_handle($handle)
+ {
+ return (isset($this->filenames[$handle])) ? $this->filenames[$handle] : $handle;
+ }
+}