Skip to content

Commit

Permalink
[BUGIFX] Simplify and harden cache:flush command
Browse files Browse the repository at this point in the history
This command needs to be as resilient as possible,
therefore many dependencies from the classes have been
removed, especially the dependency to Extbase DI API,
which requires caching and lead to chicken/egg issues,
when we changed our API.

Additionally we now removed the --force flag, as in practice
this options is used all the time, so we make it default
and non optional to low level remove files and truncate db tables.

Fixes: TYPO3-Console#770
  • Loading branch information
helhum committed Jan 4, 2019
1 parent d09b2fd commit f4c35d6
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 237 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
: [];
Expand Down
126 changes: 126 additions & 0 deletions Classes/Console/Command/Cache/CacheFlushCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace Helhum\Typo3Console\Command\Cache;

/*
* This file is part of the TYPO3 Console project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read
* LICENSE file that was distributed with this source code.
*
*/

use Helhum\Typo3Console\Command\AbstractConvertedCommand;
use Helhum\Typo3Console\Core\Booting\RunLevel;
use Helhum\Typo3Console\Mvc\Cli\Symfony\Application;
use Helhum\Typo3Console\Service\CacheLowLevelCleaner;
use Helhum\Typo3Console\Service\CacheService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class CacheFlushCommand extends AbstractConvertedCommand
{
protected function configure()
{
$this->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('<warning>All options and arguments are deprecated and have no effect any more.</warning>');
}
}
}
84 changes: 2 additions & 82 deletions Classes/Console/Command/CacheCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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('<warning>Could not load extension configuration.</warning>');
$this->output->getSymfonyConsoleOutput()->getErrorOutput()->writeln('<warning>Some caches might not have been flushed.</warning>');
}
} 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();
}
}

/**
Expand All @@ -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
Expand Down Expand Up @@ -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) . '".');
Expand Down
10 changes: 10 additions & 0 deletions Classes/Console/Mvc/Cli/Symfony/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions Classes/Console/Service/CacheLowLevelCleaner.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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'])
: [];
Expand Down

0 comments on commit f4c35d6

Please sign in to comment.