+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+class ServicesResetter
+{
+ private $resettableServices;
+ private $resetMethods;
+
+ public function __construct(\Traversable $resettableServices, array $resetMethods)
+ {
+ $this->resettableServices = $resettableServices;
+ $this->resetMethods = $resetMethods;
+ }
+
+ public function reset()
+ {
+ foreach ($this->resettableServices as $id => $service) {
+ $service->{$this->resetMethods[$id]}();
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ServiceResetListener.php b/src/Symfony/Component/HttpKernel/EventListener/ServiceResetListener.php
deleted file mode 100644
index cf6d15930315f..0000000000000
--- a/src/Symfony/Component/HttpKernel/EventListener/ServiceResetListener.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\EventListener;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\KernelEvents;
-
-/**
- * Clean up services between requests.
- *
- * @author Alexander M. Turek
- */
-class ServiceResetListener implements EventSubscriberInterface
-{
- private $services;
- private $resetMethods;
-
- public function __construct(\Traversable $services, array $resetMethods)
- {
- $this->services = $services;
- $this->resetMethods = $resetMethods;
- }
-
- public function onKernelTerminate()
- {
- foreach ($this->services as $id => $service) {
- $method = $this->resetMethods[$id];
- $service->$method();
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public static function getSubscribedEvents()
- {
- return array(
- KernelEvents::TERMINATE => array('onKernelTerminate', -2048),
- );
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index cb143d327cb07..f4e4fb7a5171b 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -64,6 +64,8 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $projectDir;
private $warmupDir;
+ private $requestStackSize = 0;
+ private $resetServices = false;
const VERSION = '3.4.0-DEV';
const VERSION_ID = 30400;
@@ -99,6 +101,8 @@ public function __clone()
$this->booted = false;
$this->container = null;
+ $this->requestStackSize = 0;
+ $this->resetServices = false;
}
/**
@@ -107,8 +111,20 @@ public function __clone()
public function boot()
{
if (true === $this->booted) {
+ if (!$this->requestStackSize && $this->resetServices) {
+ if ($this->container->has('services_resetter')) {
+ $this->container->get('services_resetter')->reset();
+ }
+ $this->resetServices = false;
+ }
+
return;
}
+ if ($this->debug && !isset($_SERVER['SHELL_VERBOSITY'])) {
+ putenv('SHELL_VERBOSITY=3');
+ $_ENV['SHELL_VERBOSITY'] = 3;
+ $_SERVER['SHELL_VERBOSITY'] = 3;
+ }
if ($this->loadClassCache) {
$this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]);
@@ -169,6 +185,8 @@ public function shutdown()
}
$this->container = null;
+ $this->requestStackSize = 0;
+ $this->resetServices = false;
}
/**
@@ -176,17 +194,15 @@ public function shutdown()
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
- if (false === $this->booted) {
- if ($this->debug && !isset($_SERVER['SHELL_VERBOSITY'])) {
- putenv('SHELL_VERBOSITY=3');
- $_ENV['SHELL_VERBOSITY'] = 3;
- $_SERVER['SHELL_VERBOSITY'] = 3;
- }
+ $this->boot();
+ ++$this->requestStackSize;
+ $this->resetServices = true;
- $this->boot();
+ try {
+ return $this->getHttpKernel()->handle($request, $type, $catch);
+ } finally {
+ --$this->requestStackSize;
}
-
- return $this->getHttpKernel()->handle($request, $type, $catch);
}
/**
diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php
index c998ef2eaf086..f7ea16dbfb036 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php
@@ -8,7 +8,7 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass;
-use Symfony\Component\HttpKernel\EventListener\ServiceResetListener;
+use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
use Symfony\Component\HttpKernel\Tests\Fixtures\ClearableService;
use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService;
@@ -24,14 +24,14 @@ public function testCompilerPass()
->setPublic(true)
->addTag('kernel.reset', array('method' => 'clear'));
- $container->register(ServiceResetListener::class)
+ $container->register('services_resetter', ServicesResetter::class)
->setPublic(true)
->setArguments(array(null, array()));
- $container->addCompilerPass(new ResettableServicePass('kernel.reset'));
+ $container->addCompilerPass(new ResettableServicePass());
$container->compile();
- $definition = $container->getDefinition(ServiceResetListener::class);
+ $definition = $container->getDefinition('services_resetter');
$this->assertEquals(
array(
@@ -57,9 +57,9 @@ public function testMissingMethod()
$container = new ContainerBuilder();
$container->register(ResettableService::class)
->addTag('kernel.reset');
- $container->register(ServiceResetListener::class)
+ $container->register('services_resetter', ServicesResetter::class)
->setArguments(array(null, array()));
- $container->addCompilerPass(new ResettableServicePass('kernel.reset'));
+ $container->addCompilerPass(new ResettableServicePass());
$container->compile();
}
@@ -67,22 +67,12 @@ public function testMissingMethod()
public function testCompilerPassWithoutResetters()
{
$container = new ContainerBuilder();
- $container->register(ServiceResetListener::class)
+ $container->register('services_resetter', ServicesResetter::class)
->setArguments(array(null, array()));
$container->addCompilerPass(new ResettableServicePass());
$container->compile();
- $this->assertFalse($container->has(ServiceResetListener::class));
- }
-
- public function testCompilerPassWithoutListener()
- {
- $container = new ContainerBuilder();
- $container->addCompilerPass(new ResettableServicePass());
-
- $container->compile();
-
- $this->assertFalse($container->has(ServiceResetListener::class));
+ $this->assertFalse($container->has('services_resetter'));
}
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php
new file mode 100644
index 0000000000000..47c62abd0d998
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
+use Symfony\Component\HttpKernel\Tests\Fixtures\ClearableService;
+use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService;
+
+class ServicesResetterTest extends TestCase
+{
+ protected function setUp()
+ {
+ ResettableService::$counter = 0;
+ ClearableService::$counter = 0;
+ }
+
+ public function testResetServices()
+ {
+ $resetter = new ServicesResetter(new \ArrayIterator(array(
+ 'id1' => new ResettableService(),
+ 'id2' => new ClearableService(),
+ )), array(
+ 'id1' => 'reset',
+ 'id2' => 'clear',
+ ));
+
+ $resetter->reset();
+
+ $this->assertEquals(1, ResettableService::$counter);
+ $this->assertEquals(1, ClearableService::$counter);
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ServiceResetListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ServiceResetListenerTest.php
deleted file mode 100644
index 603d11b2bf412..0000000000000
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ServiceResetListenerTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-buildContainer();
- $container->get('reset_subscriber')->onKernelTerminate();
-
- $this->assertEquals(0, ResettableService::$counter);
- $this->assertEquals(0, ClearableService::$counter);
- }
-
- public function testResetServicesPartially()
- {
- $container = $this->buildContainer();
- $container->get('one');
- $container->get('reset_subscriber')->onKernelTerminate();
-
- $this->assertEquals(1, ResettableService::$counter);
- $this->assertEquals(0, ClearableService::$counter);
- }
-
- public function testResetServicesTwice()
- {
- $container = $this->buildContainer();
- $container->get('one');
- $container->get('reset_subscriber')->onKernelTerminate();
- $container->get('two');
- $container->get('reset_subscriber')->onKernelTerminate();
-
- $this->assertEquals(2, ResettableService::$counter);
- $this->assertEquals(1, ClearableService::$counter);
- }
-
- /**
- * @return ContainerBuilder
- */
- private function buildContainer()
- {
- $container = new ContainerBuilder();
- $container->register('one', ResettableService::class)->setPublic(true);
- $container->register('two', ClearableService::class)->setPublic(true);
-
- $container->register('reset_subscriber', ServiceResetListener::class)
- ->setPublic(true)
- ->addArgument(new IteratorArgument(array(
- 'one' => new Reference('one', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE),
- 'two' => new Reference('two', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE),
- )))
- ->addArgument(array(
- 'one' => 'reset',
- 'two' => 'clear',
- ));
-
- $container->compile();
-
- return $container;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
index fd7f6f09da13d..21342f026e4d7 100644
--- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
@@ -18,6 +18,8 @@
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\Config\EnvParametersResource;
+use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass;
+use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -25,6 +27,7 @@
use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest;
use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForOverrideName;
use Symfony\Component\HttpKernel\Tests\Fixtures\KernelWithoutBundles;
+use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService;
class KernelTest extends TestCase
{
@@ -840,6 +843,38 @@ public function testKernelPass()
$this->assertTrue($kernel->getContainer()->getParameter('test.processed'));
}
+ public function testServicesResetter()
+ {
+ $httpKernelMock = $this->getMockBuilder(HttpKernelInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $httpKernelMock
+ ->expects($this->exactly(2))
+ ->method('handle');
+
+ $kernel = new CustomProjectDirKernel(function ($container) {
+ $container->addCompilerPass(new ResettableServicePass());
+ $container->register('one', ResettableService::class)
+ ->setPublic(true)
+ ->addTag('kernel.reset', array('method' => 'reset'));
+ $container->register('services_resetter', ServicesResetter::class)->setPublic(true);
+ }, $httpKernelMock, 'resetting');
+
+ ResettableService::$counter = 0;
+
+ $request = new Request();
+
+ $kernel->handle($request);
+ $kernel->getContainer()->get('one');
+
+ $this->assertEquals(0, ResettableService::$counter);
+ $this->assertFalse($kernel->getContainer()->initialized('services_resetter'));
+
+ $kernel->handle($request);
+
+ $this->assertEquals(1, ResettableService::$counter);
+ }
+
/**
* Returns a mock for the BundleInterface.
*
@@ -941,13 +976,15 @@ class CustomProjectDirKernel extends Kernel
{
private $baseDir;
private $buildContainer;
+ private $httpKernel;
- public function __construct(\Closure $buildContainer = null)
+ public function __construct(\Closure $buildContainer = null, HttpKernelInterface $httpKernel = null, $name = 'custom')
{
- parent::__construct('custom', true);
+ parent::__construct($name, true);
$this->baseDir = 'foo';
$this->buildContainer = $buildContainer;
+ $this->httpKernel = $httpKernel;
}
public function registerBundles()
@@ -975,6 +1012,11 @@ protected function build(ContainerBuilder $container)
$build($container);
}
}
+
+ protected function getHttpKernel()
+ {
+ return $this->httpKernel;
+ }
}
class PassKernel extends CustomProjectDirKernel implements CompilerPassInterface