diff --git a/CHANGELOG b/CHANGELOG index c0f26c20d..3bc9a51d5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # 3.13.1 (2024-XX-XX) + * Add the possibility to reset globals via `Environment::resetGlobals()` * Deprecate `Environment::mergeGlobals()` # 3.13.0 (2024-09-07) diff --git a/doc/advanced.rst b/doc/advanced.rst index b3eaa1a2e..6b3f1118e 100644 --- a/doc/advanced.rst +++ b/doc/advanced.rst @@ -104,8 +104,8 @@ What? Implementation difficulty? How often? When? Globals ------- -A global variable is like any other template variable, except that it's -available in all templates and macros:: +Global variables are available in all templates and macros. Use ``addGlobal()`` +to add a global variable to a Twig environment:: $twig = new \Twig\Environment($loader); $twig->addGlobal('text', new Text()); @@ -680,6 +680,16 @@ method:: // ... } +.. caution:: + + Globals are fetched once from extensions and then cached for the lifetime + of the Twig environment. It means that globals should not be used to store + values that can change during the lifetime of the Twig environment. For + instance, if you're using an application server like RoadRunner or + FrakenPHP, you should not store values related to the current context (like + the HTTP request). If you do so, don't forget to reset the cache between + requests by calling ``Environment::resetGlobals()``. + Functions ~~~~~~~~~ diff --git a/src/Environment.php b/src/Environment.php index 297efa9cc..41bb518eb 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -830,6 +830,12 @@ class Environment return array_merge($this->extensionSet->getGlobals(), $this->globals); } + public function resetGlobals(): void + { + $this->resolvedGlobals = null; + $this->extensionSet->resetGlobals(); + } + /** * @deprecated since Twig 3.13 */ diff --git a/src/Extension/GlobalsInterface.php b/src/Extension/GlobalsInterface.php index 6f1dfe8a7..d52cd107e 100644 --- a/src/Extension/GlobalsInterface.php +++ b/src/Extension/GlobalsInterface.php @@ -12,10 +12,7 @@ namespace Twig\Extension; /** - * Enables usage of the deprecated Twig\Extension\AbstractExtension::getGlobals() method. - * - * Explicitly implement this interface if you really need to implement the - * deprecated getGlobals() method in your extensions. + * Allows Twig extensions to add globals to the context. * * @author Fabien Potencier */ diff --git a/src/ExtensionSet.php b/src/ExtensionSet.php index d2848b59c..28d57a41c 100644 --- a/src/ExtensionSet.php +++ b/src/ExtensionSet.php @@ -339,6 +339,11 @@ final class ExtensionSet return $globals; } + public function resetGlobals(): void + { + $this->globals = null; + } + public function addTest(TwigTest $test): void { if ($this->initialized) { diff --git a/tests/EnvironmentTest.php b/tests/EnvironmentTest.php index 192b8474f..b5100a0aa 100644 --- a/tests/EnvironmentTest.php +++ b/tests/EnvironmentTest.php @@ -470,6 +470,33 @@ EOF return $loader; } + + public function testResettingGlobals() + { + $twig = new Environment(new ArrayLoader(['index' => ''])); + $twig->addExtension(new class() extends AbstractExtension implements GlobalsInterface { + public function getGlobals(): array + { + return [ + 'global_ext' => bin2hex(random_bytes(16)), + ]; + } + }); + + // Force extensions initialization + $twig->load('index'); + + // Simulate request + $g1 = $twig->getGlobals(); + // Simulate another call from request 1 (the globals are cached) + $g2 = $twig->getGlobals(); + $this->assertSame($g1['global_ext'], $g2['global_ext']); + + // Simulate request 2 + $twig->resetGlobals(); + $g3 = $twig->getGlobals(); + $this->assertNotSame($g3['global_ext'], $g2['global_ext']); + } } class EnvironmentTest_Extension_WithGlobals extends AbstractExtension