diff --git a/Classes/Compatibility/TYPO3v87/Service/CacheLowLevelCleaner.php b/Classes/Compatibility/TYPO3v87/Service/CacheLowLevelCleaner.php index b7cc7c917..d10aeb02f 100644 --- a/Classes/Compatibility/TYPO3v87/Service/CacheLowLevelCleaner.php +++ b/Classes/Compatibility/TYPO3v87/Service/CacheLowLevelCleaner.php @@ -45,7 +45,7 @@ public function forceFlushDatabaseCacheTables() $connection->truncate($tableName); } } - // check tables on other connections + // Check tables on other connections $remappedTables = isset($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']) ? array_keys((array)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']) : []; diff --git a/Classes/Console/Command/Cache/CacheFlushCommand.php b/Classes/Console/Command/Cache/CacheFlushCommand.php new file mode 100644 index 000000000..4ed9dbe99 --- /dev/null +++ b/Classes/Console/Command/Cache/CacheFlushCommand.php @@ -0,0 +1,126 @@ +setDescription('Flush all caches'); + $this->setHelp( + <<<'EOH' +Flushes TYPO3 core caches first and after that, flushes caches from extensions. +EOH + ); + + $this->setDefinition($this->createCompleteInputDefinition()); + } + + protected function createNativeDefinition(): array + { + return [ + new InputOption( + 'files-only', + null, + InputOption::VALUE_NONE, + 'Only file caches are flushed' + ), + ]; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $filesOnly = $input->getOption('files-only'); + $application = $this->getApplication(); + if (!$application instanceof Application) { + throw new \RuntimeException('Fatal error. Application is not properly initialized.', 1546617606); + } + $filesOnly = $filesOnly || !$application->isFullyCapable(); + + $io = new SymfonyStyle($input, $output); + + $lowLevelCleaner = new CacheLowLevelCleaner(); + $lowLevelCleaner->forceFlushCachesFiles(); + if ($filesOnly) { + $io->writeln('Flushed all file caches.'); + // No need to proceed, as files only flush is requested + return; + } + + $lowLevelCleaner->forceFlushDatabaseCacheTables(); + $application->boot(RunLevel::LEVEL_FULL); + + $cacheService = new CacheService(); + $cacheService->flush(); + $cacheService->flushCachesWithDataHandler(); + + $io->writeln('Flushed all caches.'); + } + + /** + * @deprecated will be removed with 6.0 + * + * @return array + */ + protected function createDeprecatedDefinition(): array + { + return [ + new InputOption( + 'force', + null, + InputOption::VALUE_NONE, + 'Cache is forcibly flushed (low level operations are performed)' + ), + new InputArgument( + 'force', + null, + 'Cache is forcibly flushed (low level operations are performed)', + false + ), + new InputArgument( + 'filesOnly', + null, + 'Only file caches are flushed', + false + ), + ]; + } + + /** + * @deprecated will be removed with 6.0 + */ + protected function handleDeprecatedArgumentsAndOptions(InputInterface $input, OutputInterface $output) + { + if ($input->getArgument('force') + || $input->getArgument('filesOnly') + || $input->getOption('files-only') + || $input->getOption('force') + ) { + $io = new SymfonyStyle($input, $output); + $io->getErrorStyle()->writeln('All options and arguments are deprecated and have no effect any more.'); + } + } +} diff --git a/Classes/Console/Command/CacheCommandController.php b/Classes/Console/Command/CacheCommandController.php index dce799e0e..7d7f301be 100644 --- a/Classes/Console/Command/CacheCommandController.php +++ b/Classes/Console/Command/CacheCommandController.php @@ -14,8 +14,6 @@ * */ -use Helhum\Typo3Console\Mvc\Cli\CommandDispatcher; -use Helhum\Typo3Console\Mvc\Cli\FailedSubProcessCommandException; use Helhum\Typo3Console\Mvc\Controller\CommandController; use Helhum\Typo3Console\Service\CacheService; use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException; @@ -30,86 +28,9 @@ class CacheCommandController extends CommandController */ private $cacheService; - /** - * @var CommandDispatcher - */ - private $commandDispatcher; - - /** - * @param CacheService $cacheService - * @param CommandDispatcher $commandDispatcher - */ - public function __construct(CacheService $cacheService, CommandDispatcher $commandDispatcher = null) + public function __construct(CacheService $cacheService) { $this->cacheService = $cacheService; - $this->commandDispatcher = $commandDispatcher ?: CommandDispatcher::createFromCommandRun(); - } - - /** - * Flush all caches - * - * Flushes TYPO3 core caches first and after that, flushes caches from extensions. - * - * @param bool $force Cache is forcibly flushed (low level operations are performed) - * @param bool $filesOnly Only file caches are flushed - * @throws FailedSubProcessCommandException - */ - public function flushCommand($force = false, $filesOnly = false) - { - $exitCode = 0; - $isApplicationFullyCapable = $this->isApplicationFullyCapable(); - if (!$isApplicationFullyCapable) { - $filesOnly = true; - } - if ($filesOnly) { - $this->cacheService->flushFileCaches($force); - try { - $this->commandDispatcher->executeCommand('cache:flushcomplete', ['--files-only']); - } catch (FailedSubProcessCommandException $e) { - if ($isApplicationFullyCapable) { - throw $e; - } - $this->output->getSymfonyConsoleOutput()->getErrorOutput()->writeln('Could not load extension configuration.'); - $this->output->getSymfonyConsoleOutput()->getErrorOutput()->writeln('Some caches might not have been flushed.'); - } - } else { - $this->cacheService->flush($force); - $this->commandDispatcher->executeCommand('cache:flushcomplete'); - } - - $this->outputLine('%slushed all %scaches.', [$force ? 'Force f' : 'F', $filesOnly ? 'file ' : '']); - $this->quit($exitCode); - } - - /** - * Check if we have all mandatory files to assume we have a fully configured / installed TYPO3 - * - * @return bool - * @deprecated can be removed once this is converted into a native Symfony command. We can use the Application API then. - */ - private function isApplicationFullyCapable(): bool - { - return file_exists(PATH_site . 'typo3conf/PackageStates.php') && file_exists(PATH_site . 'typo3conf/LocalConfiguration.php'); - } - - /** - * Called only internally in a sub process of the cache:flush command - * - * This command will then use the full TYPO3 bootstrap. - * - * @param bool $filesOnly Only file caches are flushed - * @internal - */ - public function flushCompleteCommand($filesOnly = false) - { - // Flush a second time to have extension caches and previously disabled core caches cleared when clearing not forced - if ($filesOnly) { - $this->cacheService->flushFileCaches(); - } else { - $this->cacheService->flush(); - // Also call the data handler API to cover legacy hook subscriber code - $this->cacheService->flushCachesWithDataHandler(); - } } /** @@ -118,7 +39,6 @@ public function flushCompleteCommand($filesOnly = false) * Flushes all caches in specified groups. * Valid group names are by default: * - * - all * - lowlevel * - pages * - system @@ -175,7 +95,7 @@ public function listGroupsCommand() switch (count($groups)) { case 0: - $this->outputLine('No cache group is registered.'); + $this->outputLine('No cache groups are registered.'); break; case 1: $this->outputLine('The following cache group is registered: "' . implode('", "', $groups) . '".'); diff --git a/Classes/Console/Mvc/Cli/Symfony/Application.php b/Classes/Console/Mvc/Cli/Symfony/Application.php index 2a46358d9..3f98a05f0 100644 --- a/Classes/Console/Mvc/Cli/Symfony/Application.php +++ b/Classes/Console/Mvc/Cli/Symfony/Application.php @@ -15,6 +15,7 @@ */ use Helhum\Typo3Console\Core\Booting\RunLevel; +use Helhum\Typo3Console\Core\Booting\StepFailedException; use Helhum\Typo3Console\Error\ExceptionRenderer; use Helhum\Typo3Console\Exception\CommandNotAvailableException; use Helhum\Typo3Console\Mvc\Cli\Symfony\Command\HelpCommand; @@ -79,6 +80,15 @@ public function hasErrors(): bool return $this->runLevel->getError() !== null; } + /** + * @param string $runLevel + * @throws StepFailedException + */ + public function boot(string $runLevel) + { + $this->runLevel->runSequence($runLevel); + } + /** * Whether this application is composer managed. * Can be used to enable or disable commands or arguments/ options diff --git a/Classes/Console/Service/CacheLowLevelCleaner.php b/Classes/Console/Service/CacheLowLevelCleaner.php index 58ec0d6f0..c45126b60 100644 --- a/Classes/Console/Service/CacheLowLevelCleaner.php +++ b/Classes/Console/Service/CacheLowLevelCleaner.php @@ -28,8 +28,7 @@ class CacheLowLevelCleaner */ public function forceFlushCachesFiles() { - // Delete typo3temp/Cache - GeneralUtility::flushDirectory(Environment::getVarPath() . '/cache', true, true); + GeneralUtility::flushDirectory(Environment::getVarPath() . '/cache', true); } /** @@ -46,7 +45,7 @@ public function forceFlushDatabaseCacheTables() $connection->truncate($tableName); } } - // check tables on other connections + // Check tables on other connections $remappedTables = isset($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']) ? array_keys((array)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']) : []; diff --git a/Classes/Console/Service/CacheService.php b/Classes/Console/Service/CacheService.php index 36803243b..49e6cbbd0 100644 --- a/Classes/Console/Service/CacheService.php +++ b/Classes/Console/Service/CacheService.php @@ -14,16 +14,8 @@ * */ -use Helhum\Typo3Console\Core\Booting\CompatibilityScripts; -use Helhum\Typo3Console\Core\Booting\Scripts; -use Helhum\Typo3Console\Service\Configuration\ConfigurationService; -use Symfony\Component\Console\Exception\RuntimeException; -use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; -use TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException; -use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; -use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -39,67 +31,28 @@ class CacheService implements SingletonInterface protected $cacheManager; /** - * @var ConfigurationService + * @var array */ - protected $configurationService; + protected $cacheConfiguration; - /** - * @var Bootstrap - */ - private $bootstrap; - - /** - * @var CacheLowLevelCleaner - */ - private $lowLevelCleaner; - - /** - * Builds the dependencies correctly - * - * @param ConfigurationService $configurationService - */ - public function __construct(ConfigurationService $configurationService, Bootstrap $bootstrap = null, CacheLowLevelCleaner $lowLevelCleaner = null) + public function __construct() { // We need a new instance here to get the real caches instead of the disabled ones $this->cacheManager = new CacheManager(); - $this->cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); - $this->configurationService = $configurationService; - $this->bootstrap = $bootstrap ?: Bootstrap::getInstance(); - $this->lowLevelCleaner = $lowLevelCleaner ?: new CacheLowLevelCleaner(); + $this->cacheConfiguration = $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']; + $this->cacheManager->setCacheConfigurations($this->cacheConfiguration); } /** * Flushes all caches - * - * @param bool $force */ - public function flush($force = false) + public function flush() { - $this->ensureDatabaseIsInitialized(); - if ($force) { - $this->lowLevelCleaner->forceFlushCachesFiles(); - $this->lowLevelCleaner->forceFlushDatabaseCacheTables(); - } $this->cacheManager->flushCaches(); } /** - * Flushes all file based caches - * - * @param bool $force - */ - public function flushFileCaches($force = false) - { - if ($force) { - $this->lowLevelCleaner->forceFlushCachesFiles(); - } - foreach ($this->getFileCaches() as $cache) { - $cache->flush(); - } - } - - /** - * Flushes caches using the data handler. This should not be necessary any more in the future. + * Flushes caches using the data handler. * Although we trigger the cache flush API here, the real intention is to trigger * hook subscribers, so that they can do their job (flushing "other" caches when cache is flushed. * For example realurl subscribes to these hooks. @@ -111,18 +64,12 @@ public function flushFileCaches($force = false) * However if you find a valid use case for us to also call "pages" here, then please create * a pull request and describe this case. "system" or "temp_cached" will not be added however * because these are deprecated since TYPO3 8.x - * - * Besides that, this DataHandler API is probably something to be removed in TYPO3, - * so we deprecate and mark this method as internal at the same time. - * - * @deprecated Will be removed once DataHandler cache flush methods are removed in supported TYPO3 versions - * @internal */ public function flushCachesWithDataHandler() { - $this->ensureDatabaseIsInitialized(); - $this->ensureBackendUserIsInitialized(); - self::createDataHandlerFromGlobals()->clear_cacheCmd('all'); + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start([], []); + $dataHandler->clear_cacheCmd('all'); } /** @@ -134,7 +81,6 @@ public function flushCachesWithDataHandler() public function flushGroups(array $groups) { $this->ensureCacheGroupsExist($groups); - $this->ensureDatabaseIsInitialized(); foreach ($groups as $group) { $this->cacheManager->flushCachesInGroup($group); } @@ -148,7 +94,6 @@ public function flushGroups(array $groups) */ public function flushByTags(array $tags, $group = null) { - $this->ensureDatabaseIsInitialized(); foreach ($tags as $tag) { if ($group === null) { $this->cacheManager->flushCachesByTag($tag); @@ -182,35 +127,13 @@ public function flushByTagsAndGroups(array $tags, array $groups = null) public function getValidCacheGroups() { $validGroups = []; - foreach ($this->configurationService->getActive('SYS/caching/cacheConfigurations') as $cacheConfiguration) { + foreach ($this->cacheConfiguration as $cacheConfiguration) { if (isset($cacheConfiguration['groups']) && is_array($cacheConfiguration['groups'])) { - $validGroups = array_merge($validGroups, $cacheConfiguration['groups']); + $validGroups[] = $cacheConfiguration['groups']; } } - return array_unique($validGroups); - } - - /** - * @deprecated can be removed when TYPO3 8 support is removed - */ - private function ensureDatabaseIsInitialized() - { - if (!empty($GLOBALS['TYPO3_DB'])) { - // Already initialized - return; - } - CompatibilityScripts::initializeDatabaseConnection($this->bootstrap); - } - - private function ensureBackendUserIsInitialized() - { - if (!empty($GLOBALS['BE_USER'])) { - // Already initialized - return; - } - Scripts::initializePersistence($this->bootstrap); - Scripts::initializeAuthenticatedOperations($this->bootstrap); + return array_unique(array_merge(...$validGroups)); } /** @@ -226,45 +149,4 @@ private function ensureCacheGroupsExist($groups) throw new NoSuchCacheGroupException('Invalid cache groups "' . implode(', ', $invalidGroups) . '".', 1399630162); } } - - /** - * @return FrontendInterface[] - */ - private function getFileCaches() - { - $fileCaches = []; - foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] as $identifier => $cacheConfiguration) { - if ( - isset($cacheConfiguration['backend']) - && ( - $cacheConfiguration['backend'] === SimpleFileBackend::class - || is_subclass_of($cacheConfiguration['backend'], SimpleFileBackend::class) - ) - ) { - $fileCaches[] = $this->cacheManager->getCache($identifier); - } - } - - return $fileCaches; - } - - /** - * Create a data handler instance from global state (with user being admin) - * - * @internal - * @throws RuntimeException - * @return DataHandler - */ - private static function createDataHandlerFromGlobals() - { - if (empty($GLOBALS['BE_USER']) || !$GLOBALS['BE_USER'] instanceof BackendUserAuthentication) { - throw new RuntimeException('No backend user initialized. flushCachesWithDataHandler needs fully initialized TYPO3', 1477066610); - } - $user = clone $GLOBALS['BE_USER']; - $user->admin = 1; - $dataHandler = GeneralUtility::makeInstance(DataHandler::class); - $dataHandler->start([], [], $user); - - return $dataHandler; - } } diff --git a/Configuration/Commands.php b/Configuration/Commands.php index 9c89d3d6d..f66adb4f2 100644 --- a/Configuration/Commands.php +++ b/Configuration/Commands.php @@ -56,20 +56,10 @@ ], 'cache:flush' => [ 'vendor' => 'typo3_console', - 'class' => \Helhum\Typo3Console\Mvc\Cli\Symfony\Command\DummyCommand::class, + 'class' => \Helhum\Typo3Console\Command\Cache\CacheFlushCommand::class, 'schedulable' => false, - 'controller' => \Helhum\Typo3Console\Command\CacheCommandController::class, - 'controllerCommandName' => 'flush', 'runLevel' => \Helhum\Typo3Console\Core\Booting\RunLevel::LEVEL_COMPILE, ], - 'cache:flushcomplete' => [ - 'vendor' => 'typo3_console', - 'class' => \Helhum\Typo3Console\Mvc\Cli\Symfony\Command\DummyCommand::class, - 'schedulable' => false, - 'controller' => \Helhum\Typo3Console\Command\CacheCommandController::class, - 'controllerCommandName' => 'flushComplete', - 'runLevel' => \Helhum\Typo3Console\Core\Booting\RunLevel::LEVEL_MINIMAL, - ], 'cache:flushgroups' => [ 'vendor' => 'typo3_console', 'class' => \Helhum\Typo3Console\Mvc\Cli\Symfony\Command\DummyCommand::class, diff --git a/Documentation/CommandReference/Index.rst b/Documentation/CommandReference/Index.rst index 4dc90a902..6e1e38448 100644 --- a/Documentation/CommandReference/Index.rst +++ b/Documentation/CommandReference/Index.rst @@ -319,14 +319,6 @@ Flushes TYPO3 core caches first and after that, flushes caches from extensions. Options ~~~~~~~ -`--force` - Cache is forcibly flushed (low level operations are performed) - -- Accept value: no -- Is value required: no -- Is multiple: no -- Default: false - `--files-only` Only file caches are flushed @@ -350,7 +342,6 @@ Options Flushes all caches in specified groups. Valid group names are by default: -- all - lowlevel - pages - system