$this->charset = $options['charset'];
$this->baseTemplateClass = $options['base_template_class'];
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
- $this->extensions = array(
- 'core' => new Twig_Extension_Core(),
- 'escaper' => new Twig_Extension_Escaper($options['autoescape']),
- 'optimizer' => new Twig_Extension_Optimizer($options['optimizations']),
- );
$this->strictVariables = (bool) $options['strict_variables'];
$this->runtimeInitialized = false;
$this->setCache($options['cache']);
$this->functionCallbacks = array();
$this->filterCallbacks = array();
- $this->staging = array(
- 'functions' => array(),
- 'filters' => array(),
- 'tests' => array(),
- 'token_parsers' => array(),
- 'visitors' => array(),
- 'globals' => array(),
- );
+ $this->parsers = new Twig_TokenParserBroker();
+ $this->filters = array();
+ $this->functions = array();
+ $this->tests = array();
+ $this->globals = array();
+ $this->visitors = array();
+ $this->unaryOperators = array();
+ $this->binaryOperators = array();
+
+ $this->addExtension(new Twig_Extension_Core());
+ $this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
+ $this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
}
/**
*/
public function addExtension(Twig_ExtensionInterface $extension)
{
- $this->extensions[$extension->getName()] = $extension;
- $this->parsers = null;
- $this->visitors = null;
- $this->filters = null;
- $this->tests = null;
- $this->functions = null;
- $this->globals = null;
+ $key = $extension->getName();
+ $this->extensions[$key] = $extension;
+ $this->staging[$key] = array(
+ 'filters' => (array) $extension->getFilters(),
+ 'functions' => (array) $extension->getFunctions(),
+ 'tests' => (array) $extension->getTests(),
+ 'globals' => (array) $extension->getGlobals(),
+ 'token_parsers' => (array) $extension->getTokenParsers(),
+ 'node_visitors' => (array) $extension->getNodeVisitors(),
+ 'operators' => (array) $extension->getOperators(),
+ );
+
+ // filters
+ foreach ($this->staging[$key]['filters'] as $name => $filter) {
+ $this->addFilter($name, $filter);
+ }
+
+ // functions
+ foreach ($this->staging[$key]['functions'] as $name => $function) {
+ $this->addFunction($name, $function);
+ }
+
+ // tests
+ foreach ($this->staging[$key]['tests'] as $name => $test) {
+ $this->addTest($name, $test);
+ }
+
+ // globals
+ $this->globals = array_merge($this->globals, $this->staging[$key]['globals']);
+
+ // token parsers
+ foreach ($this->staging[$key]['token_parsers'] as $parser) {
+ if ($parser instanceof Twig_TokenParserInterface) {
+ $this->parsers->addTokenParser($parser);
+ } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
+ $this->parsers->addTokenParserBroker($parser);
+ } else {
+ throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
+ }
+ }
+
+ // node visitors
+ foreach ($this->staging[$key]['node_visitors'] as $visitor) {
+ $this->addNodeVisitor($visitor);
+ }
+
+ // operators
+ if ($operators = $this->staging[$key]['operators']) {
+ if (2 !== count($operators)) {
+ throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
+ }
+
+ $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
+ $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
+ }
}
/**
* Removes an extension by name.
*
+ * This method is deprecated and you should not use it.
+ *
+ * This method won't work properly when extension B is removed and
+ * had overriden something registered by extension A.
+ *
* @param string $name The extension name
+ *
+ * @deprecated
*/
public function removeExtension($name)
{
+ if (!isset($this->extensions[$name])) {
+ return;
+ }
+
+ $extension = $this->staging[$name];
+
+ // filters
+ foreach ($extension['filters'] as $name => $filter) {
+ if (isset($this->filters[$name]) && $filter === $this->filters[$name]) {
+ unset($this->filters[$name]);
+ }
+ }
+
+ // functions
+ foreach ($extension['functions'] as $name => $function) {
+ if (isset($this->functions[$name]) && $function === $this->functions[$name]) {
+ unset($this->functions[$name]);
+ }
+ }
+
+ // tests
+ foreach ($extension['tests'] as $name => $test) {
+ if (isset($this->tests[$name]) && $test === $this->tests[$name]) {
+ unset($this->tests[$name]);
+ }
+ }
+
+ // globals
+ foreach ($extension['globals'] as $key => $value) {
+ if (isset($this->globals[$key]) && $value === $this->globals[$key]) {
+ unset($this->globals[$key]);
+ }
+ }
+
+ // token parsers
+ foreach ($extension['token_parsers'] as $parser) {
+ if ($parser instanceof Twig_TokenParserInterface) {
+ $this->parsers->removeTokenParser($parser);
+ } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
+ $this->parsers->removeTokenParserBroker($parser);
+ }
+ }
+
+ // node visitors
+ foreach ($extension['node_visitors'] as $visitor) {
+ if (false !== $pos = array_search($visitor, $this->visitors)) {
+ unset($this->visitors[$pos]);
+ }
+ }
+
+ // operators
+ if ($extension['operators']) {
+ foreach (array_keys($extension['operators'][0]) as $key) {
+ unset($this->unaryOperators[$key]);
+ }
+
+ foreach (array_keys($extension['operators'][1]) as $key) {
+ unset($this->binaryOperators[$key]);
+ }
+ }
+
unset($this->extensions[$name]);
- $this->parsers = null;
- $this->visitors = null;
- $this->filters = null;
- $this->tests = null;
- $this->functions = null;
- $this->globals = null;
+ unset($this->staging[$name]);
}
/**
*/
public function addTokenParser(Twig_TokenParserInterface $parser)
{
- $this->staging['token_parsers'][] = $parser;
- $this->parsers = null;
+ $this->parsers->addTokenParser($parser);
}
/**
*/
public function getTokenParsers()
{
- if (null === $this->parsers) {
- $this->parsers = new Twig_TokenParserBroker();
-
- if (isset($this->staging['token_parsers'])) {
- foreach ($this->staging['token_parsers'] as $parser) {
- $this->parsers->addTokenParser($parser);
- }
- }
-
- foreach ($this->getExtensions() as $extension) {
- $parsers = $extension->getTokenParsers();
- foreach ($parsers as $parser) {
- if ($parser instanceof Twig_TokenParserInterface) {
- $this->parsers->addTokenParser($parser);
- } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
- $this->parsers->addTokenParserBroker($parser);
- } else {
- throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
- }
- }
- }
- }
-
return $this->parsers;
}
*/
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
- $this->staging['visitors'][] = $visitor;
- $this->visitors = null;
+ $this->visitors[] = $visitor;
}
/**
*/
public function getNodeVisitors()
{
- if (null === $this->visitors) {
- foreach ($this->getExtensions() as $extension) {
- foreach ($extension->getNodeVisitors() as $visitor) {
- $this->addNodeVisitor($visitor);
- }
- }
-
- $this->visitors = $this->staging['visitors'];
- }
-
return $this->visitors;
}
*/
public function addFilter($name, Twig_FilterInterface $filter)
{
- $this->staging['filters'][$name] = $filter;
- $this->filters = null;
+ $this->filters[$name] = $filter;
}
/**
*/
public function getFilter($name)
{
- if (null === $this->filters) {
- $this->getFilters();
- }
-
if (isset($this->filters[$name])) {
return $this->filters[$name];
}
*/
public function getFilters()
{
- if (null === $this->filters) {
- foreach ($this->getExtensions() as $extension) {
- foreach ($extension->getFilters() as $name => $filter) {
- $this->addFilter($name, $filter);
- }
- }
-
- $this->filters = $this->staging['filters'];
- }
-
return $this->filters;
}
*/
public function addTest($name, Twig_TestInterface $test)
{
- $this->staging['tests'][$name] = $test;
- $this->tests = null;
+ $this->tests[$name] = $test;
}
/**
*/
public function getTests()
{
- if (null === $this->tests) {
- foreach ($this->getExtensions() as $extension) {
- foreach ($extension->getTests() as $name => $test) {
- $this->addTest($name, $test);
- }
- }
-
- $this->tests = $this->staging['tests'];
- }
-
return $this->tests;
}
*/
public function addFunction($name, Twig_FunctionInterface $function)
{
- $this->staging['functions'][$name] = $function;
- $this->functions = null;
+ $this->functions[$name] = $function;
}
/**
*/
public function getFunction($name)
{
- if (null === $this->functions) {
- $this->getFunctions();
- }
-
if (isset($this->functions[$name])) {
return $this->functions[$name];
}
*/
public function getFunctions()
{
- if (null === $this->functions) {
- foreach ($this->getExtensions() as $extension) {
- foreach ($extension->getFunctions() as $name => $function) {
- $this->addFunction($name, $function);
- }
- }
-
- $this->functions = $this->staging['functions'];
- }
-
return $this->functions;
}
*/
public function addGlobal($name, $value)
{
- $this->staging['globals'][$name] = $value;
- $this->globals = null;
+ $this->globals[$name] = $value;
}
/**
*/
public function getGlobals()
{
- if (null === $this->globals) {
- $this->globals = isset($this->staging['globals']) ? $this->staging['globals'] : array();
- foreach ($this->getExtensions() as $extension) {
- $this->globals = array_merge($this->globals, $extension->getGlobals());
- }
- }
-
return $this->globals;
}
*/
public function getUnaryOperators()
{
- if (null === $this->unaryOperators) {
- $this->initOperators();
- }
-
return $this->unaryOperators;
}
*/
public function getBinaryOperators()
{
- if (null === $this->binaryOperators) {
- $this->initOperators();
- }
-
return $this->binaryOperators;
}
return array_keys($alternatives);
}
- protected function initOperators()
- {
- $this->unaryOperators = array();
- $this->binaryOperators = array();
- foreach ($this->getExtensions() as $extension) {
- $operators = $extension->getOperators();
-
- if (!$operators) {
- continue;
- }
-
- if (2 !== count($operators)) {
- throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
- }
-
- $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
- $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
- }
- }
-
protected function writeCacheFile($file, $content)
{
$dir = dirname($file);
{
return $filename;
}
+
+ public function testGlobalsAreSetEvenIfGetGlobalsIsCalledFirst()
+ {
+ $twig = new Twig_Environment(new Twig_Loader_String());
+
+ $globals = $twig->getGlobals();
+ $twig->addGlobal('foo', 'foo');
+ $globals = $twig->getGlobals();
+
+ $this->assertArrayHasKey('foo', $globals);
+ }
+
+ public function testAddExtension()
+ {
+ $twig = new Twig_Environment(new Twig_Loader_String());
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
+
+ $this->assertArrayHasKey('test', $twig->getTags());
+ $this->assertArrayHasKey('foo_filter', $twig->getFilters());
+ $this->assertArrayHasKey('foo_function', $twig->getFunctions());
+ $this->assertArrayHasKey('foo_test', $twig->getTests());
+ $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators());
+ $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators());
+ $this->assertArrayHasKey('foo_global', $twig->getGlobals());
+ $visitors = $twig->getNodeVisitors();
+ $this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2]));
+ }
+
+ public function testRemoveExtension()
+ {
+ $twig = new Twig_Environment(new Twig_Loader_String());
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
+ $twig->removeExtension('test');
+
+ $this->assertFalse(array_key_exists('test', $twig->getTags()));
+ $this->assertFalse(array_key_exists('foo_filter', $twig->getFilters()));
+ $this->assertFalse(array_key_exists('foo_function', $twig->getFunctions()));
+ $this->assertFalse(array_key_exists('foo_test', $twig->getTests()));
+ $this->assertFalse(array_key_exists('foo_unary', $twig->getUnaryOperators()));
+ $this->assertFalse(array_key_exists('foo_binary', $twig->getBinaryOperators()));
+ $this->assertFalse(array_key_exists('foo_global', $twig->getGlobals()));
+ $this->assertCount(2, $twig->getNodeVisitors());
+ }
+}
+
+class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension
+{
+ public function getTokenParsers()
+ {
+ return array(
+ new Twig_Tests_EnvironmentTest_TokenParser(),
+ );
+ }
+
+ public function getNodeVisitors()
+ {
+ return array(
+ new Twig_Tests_EnvironmentTest_NodeVisitor(),
+ );
+ }
+
+ public function getFilters()
+ {
+ return array(
+ 'foo_filter' => new Twig_Filter_Function('foo_filter'),
+ );
+ }
+
+ public function getTests()
+ {
+ return array(
+ 'foo_test' => new Twig_Test_Function('foo_test'),
+ );
+ }
+
+ public function getFunctions()
+ {
+ return array(
+ 'foo_function' => new Twig_Function_Function('foo_function'),
+ );
+ }
+
+ public function getOperators()
+ {
+ return array(
+ array('foo_unary' => array()),
+ array('foo_binary' => array()),
+ );
+ }
+
+ public function getGlobals()
+ {
+ return array(
+ 'foo_global' => 'foo_global',
+ );
+ }
+
+ public function getName()
+ {
+ return 'test';
+ }
+}
+
+class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
+{
+ public function parse(Twig_Token $token)
+ {
+ }
+
+ public function getTag()
+ {
+ return 'test';
+ }
+}
+
+class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterface
+{
+ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
+ {
+ return $node;
+ }
+
+ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
+ {
+ return $node;
+ }
+
+ public function getPriority()
+ {
+ return 0;
+ }
}