diff --git a/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php index ed7d5f9670cd..6e363b552cc6 100644 --- a/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php @@ -23,7 +23,7 @@ class PostResponseEvent extends Event { /** * The kernel in which this event was thrown - * @var Symfony\Component\HttpKernel\HttpKernelInterface + * @var HttpKernelInterface */ private $kernel; @@ -35,10 +35,10 @@ public function __construct(HttpKernelInterface $kernel) /** * Returns the kernel in which this event was thrown * - * @return Symfony\Component\HttpKernel\HttpKernelInterface + * @return HttpKernelInterface */ public function getKernel() { return $this->kernel; } -} \ No newline at end of file +} diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 9bf5d21ad12b..364883150a68 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -16,6 +16,7 @@ namespace Symfony\Component\HttpKernel\HttpCache; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\TerminableInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -26,7 +27,7 @@ * * @api */ -class HttpCache implements HttpKernelInterface +class HttpCache implements HttpKernelInterface, TerminableInterface { private $kernel; private $store; @@ -216,15 +217,15 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ } /** - * Terminates a request/response cycle - * - * Should be called before shutdown, but after sending the response + * {@inheritdoc} * * @api */ public function terminate() { - $this->kernel->terminate(); + if ($this->getKernel() instanceof TerminableInterface) { + $this->getKernel()->terminate(); + } } /** diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index ac04ac6a9a63..fe9e52701da2 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -30,7 +30,7 @@ * * @api */ -class HttpKernel implements HttpKernelInterface +class HttpKernel implements HttpKernelInterface, TerminableInterface { private $dispatcher; private $resolver; @@ -80,9 +80,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ } /** - * Terminates a request/response cycle - * - * Should be called before shutdown, but after sending the response + * {@inheritdoc} * * @api */ diff --git a/src/Symfony/Component/HttpKernel/HttpKernelInterface.php b/src/Symfony/Component/HttpKernel/HttpKernelInterface.php index 086e7dc461fb..a2a0f1c5fd59 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernelInterface.php +++ b/src/Symfony/Component/HttpKernel/HttpKernelInterface.php @@ -44,11 +44,4 @@ interface HttpKernelInterface * @api */ function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); - - /** - * Terminates a request/response cycle - * - * Should be called after sending the response - */ - function terminate(); } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 098f830323cf..c044826807db 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -44,7 +44,7 @@ * * @api */ -abstract class Kernel implements KernelInterface +abstract class Kernel implements KernelInterface, TerminableInterface { protected $bundles; protected $bundleMap; @@ -134,6 +134,22 @@ public function boot() $this->booted = true; } + /** + * {@inheritdoc} + * + * @api + */ + public function terminate() + { + if (false === $this->booted) { + return; + } + + if ($this->getHttpKernel() instanceof TerminableInterface) { + $this->getHttpKernel()->terminate(); + } + } + /** * Shutdowns the kernel. * @@ -171,22 +187,6 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ return $this->getHttpKernel()->handle($request, $type, $catch); } - /** - * Terminates a request/response cycle - * - * Should be called before shutdown, but after sending the response - * - * @api - */ - public function terminate() - { - if (false === $this->booted) { - throw new \LogicException('The kernel has been shutdown already'); - } - - $this->getHttpKernel()->terminate(); - } - /** * Gets a http kernel from the container * diff --git a/src/Symfony/Component/HttpKernel/TerminableInterface.php b/src/Symfony/Component/HttpKernel/TerminableInterface.php new file mode 100644 index 000000000000..4f2b0b1744ef --- /dev/null +++ b/src/Symfony/Component/HttpKernel/TerminableInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Terminable extends the Kernel request/response cycle with dispatching a post + * response event after sending the response and before shutting down the kernel. + * + * @author Jordi Boggiano + * @author Pierre Minnieur + * + * @api + */ +interface TerminableInterface +{ + /** + * Terminates a request/response cycle. + * + * Should be called after sending the response and before shutting down the kernel. + * + * @api + */ + function terminate(); +} diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php index 512d6ee8ee08..18180f38fa2c 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php @@ -11,10 +11,43 @@ namespace Symfony\Tests\Component\HttpKernel\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\StoreInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; require_once __DIR__.'/HttpCacheTestCase.php'; class HttpCacheTest extends HttpCacheTestCase { + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + $storeMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface') + ->disableOriginalConstructor() + ->getMock(); + + // does not implement TerminableInterface + $kernelMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpKernelInterface') + ->disableOriginalConstructor() + ->getMock(); + + $kernelMock->expects($this->never()) + ->method('terminate'); + + $kernel = new HttpCache($kernelMock, $storeMock); + $kernel->terminate(); + + // implements TerminableInterface + $kernelMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Kernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate', 'registerBundles', 'registerContainerConfiguration')) + ->getMock(); + + $kernelMock->expects($this->once()) + ->method('terminate'); + + $kernel = new HttpCache($kernelMock, $storeMock); + $kernel->terminate(); + } + public function testPassesOnNonGetHeadRequests() { $this->setNextResponse(200); diff --git a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php index 2572044b4336..fea6e1199dbb 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php @@ -652,6 +652,66 @@ public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() $kernel->initializeBundles(); } + public function testTerminateReturnsSilentlyIfKernelIsNotBooted() + { + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->never()) + ->method('getHttpKernel'); + + $kernel->setIsBooted(false); + $kernel->terminate(); + } + + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + // does not implement TerminableInterface + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface') + ->disableOriginalConstructor() + ->getMock(); + + $httpKernelMock + ->expects($this->never()) + ->method('terminate'); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->setIsBooted(true); + $kernel->terminate(); + + // implements TerminableInterface + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate')) + ->getMock(); + + $httpKernelMock + ->expects($this->once()) + ->method('terminate'); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->exactly(2)) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->setIsBooted(true); + $kernel->terminate(); + } + protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) { $bundle = $this