diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 3a7fc18058..4106683661 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -263,6 +263,7 @@ class DoctrineExtensionTest extends TestCase return new ContainerBuilder(new ParameterBag(array_merge([ 'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], 'kernel.cache_dir' => __DIR__, + 'kernel.build_dir' => __DIR__, 'kernel.container_class' => 'kernel', 'kernel.project_dir' => __DIR__, ], $data))); diff --git a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php index 2219b4e70e..31afae4d93 100644 --- a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php +++ b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php @@ -114,6 +114,7 @@ class DebugExtensionTest extends TestCase { $container = new ContainerBuilder(new ParameterBag([ 'kernel.cache_dir' => __DIR__, + 'kernel.build_dir' => __DIR__, 'kernel.charset' => 'UTF-8', 'kernel.debug' => true, 'kernel.project_dir' => __DIR__, diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 12c13024a1..44a53a542e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -61,6 +61,12 @@ EOT /** @var KernelInterface $kernel */ $kernel = $this->getApplication()->getKernel(); + if (method_exists($kernel, 'getBuildDir')) { + $buildDir = $kernel->getBuildDir(); + } else { + $buildDir = $kernel->getCacheDir(); + } + $rows = [ ['Symfony'], new TableSeparator(), @@ -76,6 +82,7 @@ EOT ['Debug', $kernel->isDebug() ? 'true' : 'false'], ['Charset', $kernel->getCharset()], ['Cache directory', self::formatPath($kernel->getCacheDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getCacheDir()).')'], + ['Build directory', self::formatPath($buildDir, $kernel->getProjectDir()).' ('.self::formatFileSize($buildDir).')'], ['Log directory', self::formatPath($kernel->getLogDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getLogDir()).')'], new TableSeparator(), ['PHP'], diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index 29791ab119..0dada3d7d1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -79,17 +79,23 @@ EOF $io = new SymfonyStyle($input, $output); $kernel = $this->getApplication()->getKernel(); + $realBuildDir = $kernel->getContainer()->getParameter('kernel.build_dir'); $realCacheDir = $kernel->getContainer()->getParameter('kernel.cache_dir'); // the old cache dir name must not be longer than the real one to avoid exceeding // the maximum length of a directory or file path within it (esp. Windows MAX_PATH) + $oldBuildDir = substr($realBuildDir, 0, -1).('~' === substr($realBuildDir, -1) ? '+' : '~'); $oldCacheDir = substr($realCacheDir, 0, -1).('~' === substr($realCacheDir, -1) ? '+' : '~'); - $fs->remove($oldCacheDir); + $fs->remove([$oldBuildDir, $oldCacheDir]); + if (!is_writable($realBuildDir)) { + throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realBuildDir)); + } if (!is_writable($realCacheDir)) { throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realCacheDir)); } $io->comment(sprintf('Clearing the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $this->cacheClearer->clear($realBuildDir); $this->cacheClearer->clear($realCacheDir); // The current event dispatcher is stale, let's not use it anymore @@ -155,17 +161,31 @@ EOF } } + if ($oldBuildDir) { + $fs->rename($realBuildDir, $oldBuildDir); + } else { + $fs->remove($realBuildDir); + } if ($oldCacheDir) { $fs->rename($realCacheDir, $oldCacheDir); } else { $fs->remove($realCacheDir); } $fs->rename($warmupDir, $realCacheDir); + // Copy the content of the warmed cache in the build dir + $fs->copy($realCacheDir, $realBuildDir); if ($output->isVerbose()) { - $io->comment('Removing old cache directory...'); + $io->comment('Removing old build and cache directory...'); } + try { + $fs->remove($oldBuildDir); + } catch (IOException $e) { + if ($output->isVerbose()) { + $io->warning($e->getMessage()); + } + } try { $fs->remove($oldCacheDir); } catch (IOException $e) { @@ -184,7 +204,7 @@ EOF return 0; } - private function warmup(string $warmupDir, string $realCacheDir, bool $enableOptionalWarmers = true) + private function warmup(string $warmupDir, string $realBuildDir, bool $enableOptionalWarmers = true) { // create a temporary kernel $kernel = $this->getApplication()->getKernel(); @@ -207,7 +227,7 @@ EOF // fix references to cached files with the real cache directory name $search = [$warmupDir, str_replace('\\', '\\\\', $warmupDir)]; - $replace = str_replace('\\', '/', $realCacheDir); + $replace = str_replace('\\', '/', $realBuildDir); foreach (Finder::create()->files()->in($warmupDir) as $file) { $content = str_replace($search, $replace, file_get_contents($file), $count); if ($count) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 878b2592f6..26dab6327b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1561,6 +1561,7 @@ abstract class FrameworkExtensionTest extends TestCase 'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], 'kernel.bundles_metadata' => ['FrameworkBundle' => ['namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..']], 'kernel.cache_dir' => __DIR__, + 'kernel.build_dir' => __DIR__, 'kernel.project_dir' => __DIR__, 'kernel.debug' => false, 'kernel.environment' => 'test', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php index 66f9422732..cfa6b5e028 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php @@ -125,6 +125,7 @@ class AddSessionDomainConstraintPassTest extends TestCase $container = new ContainerBuilder(); $container->setParameter('kernel.bundles_metadata', []); $container->setParameter('kernel.cache_dir', __DIR__); + $container->setParameter('kernel.build_dir', __DIR__); $container->setParameter('kernel.charset', 'UTF-8'); $container->setParameter('kernel.container_class', 'cc'); $container->setParameter('kernel.debug', true); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index 6c6fc7f0b4..0c81d82d7d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -62,6 +62,7 @@ class WebProfilerExtensionTest extends TestCase $this->container->register('twig', 'Twig\Environment')->addArgument(new Reference('twig_loader'))->setPublic(true); $this->container->setParameter('kernel.bundles', []); $this->container->setParameter('kernel.cache_dir', __DIR__); + $this->container->setParameter('kernel.build_dir', __DIR__); $this->container->setParameter('kernel.debug', false); $this->container->setParameter('kernel.project_dir', __DIR__); $this->container->setParameter('kernel.charset', 'UTF-8'); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 8f7ff96808..ac83e9ed53 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -314,7 +314,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ public function setAnnotatedClassCache(array $annotatedClasses) { - file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('warmupDir ?: $this->getBuildDir()).'/annotations.map', sprintf('getProjectDir().'/var/cache/'.$this->environment; } + /** + * Gets the build directory. + */ + public function getBuildDir(): string + { + // Returns $this->getCacheDir() for backward compatibility + return $this->getCacheDir(); + } + /** * {@inheritdoc} */ @@ -419,14 +428,14 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl /** * Initializes the service container. * - * The cached version of the service container is used when fresh, otherwise the + * The built version of the service container is used when fresh, otherwise the * container is built. */ protected function initializeContainer() { $class = $this->getContainerClass(); - $cacheDir = $this->warmupDir ?: $this->getCacheDir(); - $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); + $buildDir = $this->warmupDir ?: $this->getBuildDir(); + $cache = new ConfigCache($buildDir.'/'.$class.'.php', $this->debug); $cachePath = $cache->getPath(); // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors @@ -448,7 +457,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl $oldContainer = \is_object($this->container) ? new \ReflectionClass($this->container) : $this->container = null; try { - is_dir($cacheDir) ?: mkdir($cacheDir, 0777, true); + is_dir($buildDir) ?: mkdir($buildDir, 0777, true); if ($lock = fopen($cachePath.'.lock', 'w')) { flock($lock, LOCK_EX | LOCK_NB, $wouldBlock); @@ -533,8 +542,8 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl if ($collectDeprecations) { restore_error_handler(); - file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); - file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); + file_put_contents($buildDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); + file_put_contents($buildDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); } } @@ -570,7 +579,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl $preload = array_merge($preload, (array) $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'))); } - if ($preload && method_exists(Preloader::class, 'append') && file_exists($preloadFile = $cacheDir.'/'.$class.'.preload.php')) { + if ($preload && method_exists(Preloader::class, 'append') && file_exists($preloadFile = $buildDir.'/'.$class.'.preload.php')) { Preloader::append($preloadFile, $preload); } } @@ -597,7 +606,8 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), 'kernel.environment' => $this->environment, 'kernel.debug' => $this->debug, - 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, + 'kernel.build_dir' => realpath($buildDir = $this->warmupDir ?: $this->getBuildDir()) ?: $buildDir, + 'kernel.cache_dir' => realpath($this->getCacheDir()) ?: $this->getCacheDir(), 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), 'kernel.bundles' => $bundles, 'kernel.bundles_metadata' => $bundlesMetadata, @@ -615,7 +625,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ protected function buildContainer() { - foreach (['cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()] as $name => $dir) { + foreach (['cache' => $this->getCacheDir(), 'build' => $this->warmupDir ?: $this->getBuildDir(), 'logs' => $this->getLogDir()] as $name => $dir) { if (!is_dir($dir)) { if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { throw new \RuntimeException(sprintf('Unable to create the "%s" directory (%s).', $name, $dir)); diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/Component/HttpKernel/KernelInterface.php index cea86f687a..c1be3aff43 100644 --- a/src/Symfony/Component/HttpKernel/KernelInterface.php +++ b/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -20,6 +20,10 @@ use Symfony\Component\HttpKernel\Bundle\BundleInterface; * * It manages an environment made of application kernel and bundles. * + * @method string getBuildDir() Returns the build directory - not implementing it is deprecated since Symfony 5.2. + * This directory should be used to store build artifacts, and can be read-only at runtime. + * Caches written at runtime should be stored in the "cache directory" ({@see KernelInterface::getCacheDir()}). + * * @author Fabien Potencier */ interface KernelInterface extends HttpKernelInterface @@ -121,6 +125,10 @@ interface KernelInterface extends HttpKernelInterface /** * Gets the cache directory. * + * Since Symfony 5.2, the cache directory should be used for caches that are written at runtime. + * For caches and artifacts that can be warmed at compile-time and deployed as read-only, + * use the new "build directory" returned by the {@see getBuildDir()} method. + * * @return string The cache directory */ public function getCacheDir(); diff --git a/src/Symfony/Component/HttpKernel/RebootableInterface.php b/src/Symfony/Component/HttpKernel/RebootableInterface.php index 0dc46c3cef..e257237da9 100644 --- a/src/Symfony/Component/HttpKernel/RebootableInterface.php +++ b/src/Symfony/Component/HttpKernel/RebootableInterface.php @@ -21,10 +21,10 @@ interface RebootableInterface /** * Reboots a kernel. * - * The getCacheDir() method of a rebootable kernel should not be called - * while building the container. Use the %kernel.cache_dir% parameter instead. + * The getBuildDir() method of a rebootable kernel should not be called + * while building the container. Use the %kernel.build_dir% parameter instead. * - * @param string|null $warmupDir pass null to reboot in the regular cache directory + * @param string|null $warmupDir pass null to reboot in the regular build directory */ public function reboot(?string $warmupDir); }