[feature/twig] Fixing DEFINE statements

PHPBB3-11598
This commit is contained in:
Nathaniel Guse 2013-06-29 11:07:10 -05:00
parent abb7901edb
commit 64963b5962
5 changed files with 196 additions and 33 deletions

View file

@ -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;
}
}

View file

@ -20,6 +20,7 @@ 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',*/
@ -27,9 +28,8 @@ class phpbb_template_twig_lexer extends Twig_Lexer
'ELSE',
'ELSEIF',
'ENDIF',
'DEFINE',
'DEFINE',
'UNDEFINE',
/*'DEFINE',
'UNDEFINE',*/
'ENDDEFINE',
/*'INCLUDE',
'INCLUDEPHP',*/
@ -42,22 +42,21 @@ class phpbb_template_twig_lexer extends Twig_Lexer
// 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 <!-- INCLUDE blah.html --> with {% include 'blah.html' %}
$code = preg_replace('#<!-- INCLUDE(PHP)? (.*?) -->#', "{% INCLUDE$1 '$2' %}", $code);
// This strips the $ inside of a tag directly after the token, which was used in <!-- DEFINE $NAME
$code = preg_replace('#<!-- DEFINE \$(.*)-->#', '<!-- DEFINE $1-->', $code);
// This strips the . or $ inside of a tag directly before a variable name, which was used in <!-- IF .blah and <!-- IF $BLAH
// In case of .varname, it replaces it with varname|length (as this is how it was treated before)
$code = preg_replace_callback('#<!-- IF((.*)[\s][\$|\.]([^\s]+)(.*))-->#', array($this, 'tag_if_cleanup'), $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 variables, {VARNAME} or {$VARNAME}, with Twig style, {{ VARNAME }}
$code = preg_replace('#{\$?([a-zA-Z0-9_\.]+)}#', '{{ $1 }}', $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);
}
@ -68,7 +67,7 @@ class phpbb_template_twig_lexer extends Twig_Lexer
* Not meant to be used outside of this context, public because the anonymous function calls this
*
* @param string $code
* @param array $parent_nodes
* @param array $parent_nodes (used in recursion)
* @return string
*/
public function fix_begin_tokens($code, $parent_nodes = array())
@ -134,19 +133,56 @@ class phpbb_template_twig_lexer extends Twig_Lexer
}
/**
* preg_replace_callback to clean up IF statements
* Fix IF statements
*
* This strips the . or $ inside of a tag directly before a variable name.
* Was used in <!-- IF .blah or <!-- IF $BLAH
*
* @param mixed $matches
* @param string $code
* @return string
*/
protected function tag_if_cleanup($matches)
protected function fix_if_tokens($code)
{
// Replace $TEST with TEST
$matches[1] = preg_replace('#\s\$([a-zA-Z_0-9]+)#', ' $1', $matches[1]);
$matches[1] = preg_replace('#\s\.([a-zA-Z_0-9]+)#', ' $1|length', $matches[1]);
$callback = function($matches)
{
// Replace $TEST with definition.TEST
$matches[1] = preg_replace('#\s\$([a-zA-Z_0-9]+)#', ' definition.$1', $matches[1]);
return '<!-- IF ' . $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] . ' -->';
};
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);
return $code;
}
}

View file

@ -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->raw(" = ('' === \$value = ob_get_clean()) ? '' : new Twig_Markup(\$value, \$this->env->getCharset())");
}
else
{
$compiler
->write("\$value = '")
->raw($this->getNode('value')->getAttribute('value'))
->raw("';\n")
;
}
$compiler
->raw("\$context['definition']->set('")
->raw($this->getNode('name')->getAttribute('name'))
->raw("', \$value);\n")
;
}
}

View file

@ -7,8 +7,39 @@
*
*/
class phpbb_template_twig_tokenparser_define extends Twig_TokenParser_Set
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');

View file

@ -261,14 +261,8 @@ class phpbb_template_twig implements phpbb_template
return $result[0];
}
try
{
$this->twig->display($this->filenames[$handle], $this->get_template_vars());
}
catch (Twig_Error $e)
{
throw $e;
}
$context = &$this->get_template_vars();
$this->twig->display($this->filenames[$handle], $context);
return true;
}
@ -454,7 +448,10 @@ class phpbb_template_twig implements phpbb_template
$vars = array_merge(
$vars,
$this->context->get_rootref(),
$this->context->get_tpldata()
$this->context->get_tpldata(),
array(
'definition' => new phpbb_template_twig_definition(),
)
);
// cleanup