changed the way globals behave to be more BC with 1.11 and to avoid speed problems...
authorFabien Potencier <fabien.potencier@gmail.com>
Thu, 27 Dec 2012 08:44:14 +0000 (09:44 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Thu, 27 Dec 2012 10:00:35 +0000 (11:00 +0100)
* Globals are now managed by themselves, independently of other elements
  contained in extensions

* A global variable value can now be changed after the runtime has been
  initialized (to be more BC with the way Twig 1.11 works)

* Extensions are not initialized anymore when rendering a template that
  is already in the cache (like in Twig 1.11)

lib/Twig/Environment.php
test/Twig/Tests/EnvironmentTest.php

index 9958573..d8e700e 100644 (file)
@@ -964,16 +964,24 @@ class Twig_Environment
     /**
      * Registers a Global.
      *
+     * New globals can be added before compiling or rendering a template;
+     * but after, you can only update existing globals.
+     *
      * @param string $name  The global name
      * @param mixed  $value The global value
      */
     public function addGlobal($name, $value)
     {
-        if ($this->extensionInitialized) {
-            throw new LogicException(sprintf('Unable to add global "%s" as extensions have already been initialized.', $name));
+        if (($this->extensionInitialized || $this->runtimeInitialized) && !array_key_exists($name, $this->globals)) {
+            throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
         }
 
-        $this->staging->addGlobal($name, $value);
+        if ($this->extensionInitialized || $this->runtimeInitialized) {
+            // update the value
+            $this->globals[$name] = $value;
+        } else {
+            $this->staging->addGlobal($name, $value);
+        }
     }
 
     /**
@@ -983,8 +991,8 @@ class Twig_Environment
      */
     public function getGlobals()
     {
-        if (!$this->extensionInitialized) {
-            $this->initExtensions();
+        if (null === $this->globals || !($this->runtimeInitialized || $this->extensionInitialized)) {
+            $this->initGlobals();
         }
 
         return $this->globals;
@@ -1052,6 +1060,15 @@ class Twig_Environment
         return array_keys($alternatives);
     }
 
+    protected function initGlobals()
+    {
+        $this->globals = array();
+        foreach ($this->extensions as $extension) {
+            $this->globals = array_merge($this->globals, $extension->getGlobals());
+        }
+        $this->globals = array_merge($this->globals, $this->staging->getGlobals());
+    }
+
     protected function initExtensions()
     {
         if ($this->extensionInitialized) {
@@ -1063,7 +1080,6 @@ class Twig_Environment
         $this->filters = array();
         $this->functions = array();
         $this->tests = array();
-        $this->globals = array();
         $this->visitors = array();
         $this->unaryOperators = array();
         $this->binaryOperators = array();
@@ -1112,9 +1128,6 @@ class Twig_Environment
             $this->tests[$name] = $test;
         }
 
-        // globals
-        $this->globals = array_merge($this->globals, $extension->getGlobals());
-
         // token parsers
         foreach ($extension->getTokenParsers() as $parser) {
             if ($parser instanceof Twig_TokenParserInterface) {
index 72a9025..76f787d 100644 (file)
@@ -33,6 +33,111 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
         return $filename;
     }
 
+    public function testGlobals()
+    {
+        // globals can be added after calling getGlobals
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->initRuntime();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after extensions init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after extensions and runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->initRuntime();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals cannot be added after runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->initRuntime();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+
+        // globals cannot be added after extensions init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+
+        // globals cannot be added after extensions and runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->initRuntime();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+    }
+
+    public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
+    {
+        $options = array('cache' => sys_get_temp_dir().'/twig', 'auto_reload' => false, 'debug' => false);
+
+        // force compilation
+        $twig = new Twig_Environment(new Twig_Loader_String(), $options);
+        $cache = $twig->getCacheFilename('{{ foo }}');
+        if (!is_dir(dirname($cache))) {
+            mkdir(dirname($cache), 0777, true);
+        }
+        file_put_contents($cache, $twig->compileSource('{{ foo }}', '{{ foo }}'));
+
+        // check that extensions won't be initialized when rendering a template that is already in the cache
+        $twig = $this
+            ->getMockBuilder('Twig_Environment')
+            ->setConstructorArgs(array(new Twig_Loader_String(), $options))
+            ->setMethods(array('initExtensions'))
+            ->getMock()
+        ;
+
+        $twig->expects($this->never())->method('initExtensions');
+
+        // render template
+        $output = $twig->render('{{ foo }}', array('foo' => 'bar'));
+        $this->assertEquals('bar', $output);
+
+        unlink($cache);
+    }
+
     public function testAddExtension()
     {
         $twig = new Twig_Environment(new Twig_Loader_String());