From 9ef2d0b63b9e855ba351e770a603d89699115801 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 Jan 2026 15:10:02 +0100 Subject: [PATCH 1/2] [FrameworkBundle] Clean `http_cache` dir in `KernelTestCase::ensureKernelShutdown()` --- Test/KernelTestCase.php | 10 ++ Tests/Test/KernelTestCaseHttpCacheTest.php | 145 +++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 Tests/Test/KernelTestCaseHttpCacheTest.php diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index 632adc696..5c337b730 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\Service\ResetInterface; @@ -147,6 +148,11 @@ protected static function ensureKernelShutdown() static::$kernel->boot(); $container = static::$kernel->getContainer(); + $httpCacheDir = null; + if ($container->has('http_cache')) { + $httpCacheDir = static::$kernel->getCacheDir().'/http_cache'; + } + if ($container->has('services_resetter')) { // Instantiate the service because Container::reset() only resets services that have been used $container->get('services_resetter'); @@ -158,6 +164,10 @@ protected static function ensureKernelShutdown() if ($container instanceof ResetInterface) { $container->reset(); } + + if (null !== $httpCacheDir && is_dir($httpCacheDir)) { + (new Filesystem())->remove($httpCacheDir); + } } } } diff --git a/Tests/Test/KernelTestCaseHttpCacheTest.php b/Tests/Test/KernelTestCaseHttpCacheTest.php new file mode 100644 index 000000000..1fa91d8b7 --- /dev/null +++ b/Tests/Test/KernelTestCaseHttpCacheTest.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Test; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; + +class KernelTestCaseHttpCacheTest extends KernelTestCase +{ + private static string $baseDir; + + public static function setUpBeforeClass(): void + { + self::$baseDir = sys_get_temp_dir().'/sf_http_cache_kernel_testcase_'.uniqid('', true); + } + + public static function tearDownAfterClass(): void + { + if (isset(self::$baseDir)) { + (new Filesystem())->remove(self::$baseDir); + } + + parent::tearDownAfterClass(); + } + + protected static function createKernel(array $options = []): KernelInterface + { + return new HttpCacheTestKernel(self::$baseDir, $options['environment'] ?? 'test', $options['debug'] ?? true); + } + + public function testHttpCacheIsClearedBetweenKernelShutdowns() + { + DynamicHttpKernel::$counter = 0; + + $kernel = $this->bootKernelForHttpCache(); + $response = $kernel->handle(Request::create('/')); + + $this->assertSame('count: 1', $response->getContent()); + + static::ensureKernelShutdown(); + + $kernel = $this->bootKernelForHttpCache(); + $response = $kernel->handle(Request::create('/')); + + $this->assertSame('count: 2', $response->getContent()); + } + + private function bootKernelForHttpCache(): KernelInterface + { + $kernel = static::createKernel(); + $kernel->boot(); + static::$kernel = $kernel; + static::$booted = true; + + return $kernel; + } +} + +class HttpCacheTestKernel extends Kernel +{ + public function __construct( + private readonly string $baseDir, + string $environment, + bool $debug, + ) { + parent::__construct($environment, $debug); + } + + public function registerBundles(): iterable + { + return []; + } + + public function registerContainerConfiguration(\Symfony\Component\Config\Loader\LoaderInterface $loader): void + { + $loader->load(function (ContainerBuilder $container): void { + $container->register('kernel', KernelInterface::class) + ->setSynthetic(true) + ->setPublic(true); + + $container->register('http_kernel', DynamicHttpKernel::class) + ->setPublic(true); + + $container->register('http_cache.store', Store::class) + ->setPublic(true) + ->setArguments([$this->getCacheDir().'/http_cache']); + + $container->register('http_cache', HttpCache::class) + ->setPublic(true) + ->setArguments([ + new Reference('kernel'), + new Reference('http_cache.store'), + null, + [], + ]); + }); + } + + public function getProjectDir(): string + { + return $this->baseDir; + } + + public function getCacheDir(): string + { + return $this->baseDir.'/cache'; + } + + public function getLogDir(): string + { + return $this->baseDir.'/log'; + } +} + +class DynamicHttpKernel implements HttpKernelInterface +{ + public static int $counter = 0; + + public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response + { + $response = new Response('count: '.++self::$counter); + $response->setPublic(); + $response->setMaxAge(60); + + return $response; + } +} From 970f742064631d9d4f32e5488838781b28558515 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 Jan 2026 13:53:15 +0100 Subject: [PATCH 2/2] [PropertyInfo] Conflict with phpdocumentor/reflection-docblock >= 6 --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index a00bac1c3..3c546f70e 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "require-dev": { "doctrine/persistence": "^1.3|^2|^3", "dragonmantank/cron-expression": "^3.1", + "phpdocumentor/reflection-docblock": "^5.2", "seld/jsonlint": "^1.10", "symfony/asset": "^6.4|^7.0", "symfony/asset-mapper": "^6.4|^7.0", @@ -74,13 +75,12 @@ "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "twig/twig": "^3.12" }, "conflict": { "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", + "phpdocumentor/reflection-docblock": "<5.2|>=6", + "phpdocumentor/type-resolver": "<1.5.1", "symfony/asset": "<6.4", "symfony/asset-mapper": "<6.4", "symfony/clock": "<6.4",