vendor/symfony/framework-bundle/Command/CacheClearCommand.php line 44

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bundle\FrameworkBundle\Command;
  11. use Symfony\Component\Console\Command\Command;
  12. use Symfony\Component\Console\Exception\RuntimeException;
  13. use Symfony\Component\Console\Input\InputInterface;
  14. use Symfony\Component\Console\Input\InputOption;
  15. use Symfony\Component\Console\Output\OutputInterface;
  16. use Symfony\Component\Console\Style\SymfonyStyle;
  17. use Symfony\Component\DependencyInjection\Dumper\Preloader;
  18. use Symfony\Component\EventDispatcher\EventDispatcher;
  19. use Symfony\Component\Filesystem\Exception\IOException;
  20. use Symfony\Component\Filesystem\Filesystem;
  21. use Symfony\Component\Finder\Finder;
  22. use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
  23. use Symfony\Component\HttpKernel\RebootableInterface;
  24. /**
  25.  * Clear and Warmup the cache.
  26.  *
  27.  * @author Francis Besset <francis.besset@gmail.com>
  28.  * @author Fabien Potencier <fabien@symfony.com>
  29.  *
  30.  * @final
  31.  */
  32. class CacheClearCommand extends Command
  33. {
  34.     protected static $defaultName 'cache:clear';
  35.     protected static $defaultDescription 'Clear the cache';
  36.     private $cacheClearer;
  37.     private $filesystem;
  38.     public function __construct(CacheClearerInterface $cacheClearerFilesystem $filesystem null)
  39.     {
  40.         parent::__construct();
  41.         $this->cacheClearer $cacheClearer;
  42.         $this->filesystem $filesystem ?? new Filesystem();
  43.     }
  44.     /**
  45.      * {@inheritdoc}
  46.      */
  47.     protected function configure()
  48.     {
  49.         $this
  50.             ->setDefinition([
  51.                 new InputOption('no-warmup'''InputOption::VALUE_NONE'Do not warm up the cache'),
  52.                 new InputOption('no-optional-warmers'''InputOption::VALUE_NONE'Skip optional cache warmers (faster)'),
  53.             ])
  54.             ->setDescription(self::$defaultDescription)
  55.             ->setHelp(<<<'EOF'
  56. The <info>%command.name%</info> command clears and warms up the application cache for a given environment
  57. and debug mode:
  58.   <info>php %command.full_name% --env=dev</info>
  59.   <info>php %command.full_name% --env=prod --no-debug</info>
  60. EOF
  61.             )
  62.         ;
  63.     }
  64.     /**
  65.      * {@inheritdoc}
  66.      */
  67.     protected function execute(InputInterface $inputOutputInterface $output): int
  68.     {
  69.         $fs $this->filesystem;
  70.         $io = new SymfonyStyle($input$output);
  71.         $kernel $this->getApplication()->getKernel();
  72.         $realCacheDir $kernel->getContainer()->getParameter('kernel.cache_dir');
  73.         $realBuildDir $kernel->getContainer()->hasParameter('kernel.build_dir') ? $kernel->getContainer()->getParameter('kernel.build_dir') : $realCacheDir;
  74.         // the old cache dir name must not be longer than the real one to avoid exceeding
  75.         // the maximum length of a directory or file path within it (esp. Windows MAX_PATH)
  76.         $oldCacheDir substr($realCacheDir0, -1).(str_ends_with($realCacheDir'~') ? '+' '~');
  77.         $fs->remove($oldCacheDir);
  78.         if (!is_writable($realCacheDir)) {
  79.             throw new RuntimeException(sprintf('Unable to write in the "%s" directory.'$realCacheDir));
  80.         }
  81.         $useBuildDir $realBuildDir !== $realCacheDir;
  82.         $oldBuildDir substr($realBuildDir0, -1).('~' === substr($realBuildDir, -1) ? '+' '~');
  83.         if ($useBuildDir) {
  84.             $fs->remove($oldBuildDir);
  85.             if (!is_writable($realBuildDir)) {
  86.                 throw new RuntimeException(sprintf('Unable to write in the "%s" directory.'$realBuildDir));
  87.             }
  88.             if ($this->isNfs($realCacheDir)) {
  89.                 $fs->remove($realCacheDir);
  90.             } else {
  91.                 $fs->rename($realCacheDir$oldCacheDir);
  92.             }
  93.             $fs->mkdir($realCacheDir);
  94.         }
  95.         $io->comment(sprintf('Clearing the cache for the <info>%s</info> environment with debug <info>%s</info>'$kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
  96.         if ($useBuildDir) {
  97.             $this->cacheClearer->clear($realBuildDir);
  98.         }
  99.         $this->cacheClearer->clear($realCacheDir);
  100.         // The current event dispatcher is stale, let's not use it anymore
  101.         $this->getApplication()->setDispatcher(new EventDispatcher());
  102.         $containerFile = (new \ReflectionObject($kernel->getContainer()))->getFileName();
  103.         $containerDir basename(\dirname($containerFile));
  104.         // the warmup cache dir name must have the same length as the real one
  105.         // to avoid the many problems in serialized resources files
  106.         $warmupDir substr($realBuildDir0, -1).('_' === substr($realBuildDir, -1) ? '-' '_');
  107.         if ($output->isVerbose() && $fs->exists($warmupDir)) {
  108.             $io->comment('Clearing outdated warmup directory...');
  109.         }
  110.         $fs->remove($warmupDir);
  111.         if ($_SERVER['REQUEST_TIME'] <= filemtime($containerFile) && filemtime($containerFile) <= time()) {
  112.             if ($output->isVerbose()) {
  113.                 $io->comment('Cache is fresh.');
  114.             }
  115.             if (!$input->getOption('no-warmup') && !$input->getOption('no-optional-warmers')) {
  116.                 if ($output->isVerbose()) {
  117.                     $io->comment('Warming up optional cache...');
  118.                 }
  119.                 $warmer $kernel->getContainer()->get('cache_warmer');
  120.                 // non optional warmers already ran during container compilation
  121.                 $warmer->enableOnlyOptionalWarmers();
  122.                 $preload = (array) $warmer->warmUp($realCacheDir);
  123.                 if ($preload && file_exists($preloadFile $realCacheDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) {
  124.                     Preloader::append($preloadFile$preload);
  125.                 }
  126.             }
  127.         } else {
  128.             $fs->mkdir($warmupDir);
  129.             if (!$input->getOption('no-warmup')) {
  130.                 if ($output->isVerbose()) {
  131.                     $io->comment('Warming up cache...');
  132.                 }
  133.                 $this->warmup($warmupDir$realCacheDir, !$input->getOption('no-optional-warmers'));
  134.             }
  135.             if (!$fs->exists($warmupDir.'/'.$containerDir)) {
  136.                 $fs->rename($realBuildDir.'/'.$containerDir$warmupDir.'/'.$containerDir);
  137.                 touch($warmupDir.'/'.$containerDir.'.legacy');
  138.             }
  139.             if ($this->isNfs($realBuildDir)) {
  140.                 $io->note('For better performances, you should move the cache and log directories to a non-shared folder of the VM.');
  141.                 $fs->remove($realBuildDir);
  142.             } else {
  143.                 $fs->rename($realBuildDir$oldBuildDir);
  144.             }
  145.             $fs->rename($warmupDir$realBuildDir);
  146.             if ($output->isVerbose()) {
  147.                 $io->comment('Removing old build and cache directory...');
  148.             }
  149.             if ($useBuildDir) {
  150.                 try {
  151.                     $fs->remove($oldBuildDir);
  152.                 } catch (IOException $e) {
  153.                     if ($output->isVerbose()) {
  154.                         $io->warning($e->getMessage());
  155.                     }
  156.                 }
  157.             }
  158.             try {
  159.                 $fs->remove($oldCacheDir);
  160.             } catch (IOException $e) {
  161.                 if ($output->isVerbose()) {
  162.                     $io->warning($e->getMessage());
  163.                 }
  164.             }
  165.         }
  166.         if ($output->isVerbose()) {
  167.             $io->comment('Finished');
  168.         }
  169.         $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.'$kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
  170.         return 0;
  171.     }
  172.     private function isNfs(string $dir): bool
  173.     {
  174.         static $mounts null;
  175.         if (null === $mounts) {
  176.             $mounts = [];
  177.             if ('/' === \DIRECTORY_SEPARATOR && $files = @file('/proc/mounts')) {
  178.                 foreach ($files as $mount) {
  179.                     $mount \array_slice(explode(' '$mount), 1, -3);
  180.                     if (!\in_array(array_pop($mount), ['vboxsf''nfs'])) {
  181.                         continue;
  182.                     }
  183.                     $mounts[] = implode(' '$mount).'/';
  184.                 }
  185.             }
  186.         }
  187.         foreach ($mounts as $mount) {
  188.             if (=== strpos($dir$mount)) {
  189.                 return true;
  190.             }
  191.         }
  192.         return false;
  193.     }
  194.     private function warmup(string $warmupDirstring $realBuildDirbool $enableOptionalWarmers true)
  195.     {
  196.         // create a temporary kernel
  197.         $kernel $this->getApplication()->getKernel();
  198.         if (!$kernel instanceof RebootableInterface) {
  199.             throw new \LogicException('Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is not supported.');
  200.         }
  201.         $kernel->reboot($warmupDir);
  202.         // warmup temporary dir
  203.         if ($enableOptionalWarmers) {
  204.             $warmer $kernel->getContainer()->get('cache_warmer');
  205.             // non optional warmers already ran during container compilation
  206.             $warmer->enableOnlyOptionalWarmers();
  207.             $preload = (array) $warmer->warmUp($warmupDir);
  208.             if ($preload && file_exists($preloadFile $warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) {
  209.                 Preloader::append($preloadFile$preload);
  210.             }
  211.         }
  212.         // fix references to cached files with the real cache directory name
  213.         $search = [$warmupDirstr_replace('\\''\\\\'$warmupDir)];
  214.         $replace str_replace('\\''/'$realBuildDir);
  215.         foreach (Finder::create()->files()->in($warmupDir) as $file) {
  216.             $content str_replace($search$replacefile_get_contents($file), $count);
  217.             if ($count) {
  218.                 file_put_contents($file$content);
  219.             }
  220.         }
  221.     }
  222. }