123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Theme\Console\Command;
- use Magento\Framework\App\Cache;
- use Magento\Framework\App\Console\MaintenanceModeEnabler;
- use Magento\Framework\App\ObjectManager;
- use Magento\Framework\App\MaintenanceMode;
- use Magento\Framework\App\State\CleanupFiles;
- use Magento\Framework\Composer\ComposerInformation;
- use Magento\Framework\Composer\DependencyChecker;
- use Magento\Theme\Model\Theme\Data\Collection;
- use Magento\Theme\Model\Theme\ThemePackageInfo;
- use Magento\Theme\Model\Theme\ThemeUninstaller;
- use Magento\Theme\Model\Theme\ThemeDependencyChecker;
- use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
- use Symfony\Component\Console\Input\InputOption;
- use Symfony\Component\Console\Input\InputArgument;
- use Magento\Framework\Setup\BackupRollbackFactory;
- use Magento\Theme\Model\ThemeValidator;
- /**
- * Command for uninstalling theme and backup-code feature
- *
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
- class ThemeUninstallCommand extends Command
- {
- /**
- * Names of input arguments or options
- */
- const INPUT_KEY_BACKUP_CODE = 'backup-code';
- const INPUT_KEY_THEMES = 'theme';
- const INPUT_KEY_CLEAR_STATIC_CONTENT = 'clear-static-content';
- /**
- * Composer general dependency checker
- *
- * @var DependencyChecker
- */
- private $dependencyChecker;
- /**
- * Root composer.json information
- *
- * @var ComposerInformation
- */
- private $composer;
- /**
- * Theme collection in filesystem
- *
- * @var Collection
- */
- private $themeCollection;
- /**
- * System cache model
- *
- * @var Cache
- */
- private $cache;
- /**
- * Cleaning up application state service
- *
- * @var CleanupFiles
- */
- private $cleanupFiles;
- /**
- * BackupRollback factory
- *
- * @var BackupRollbackFactory
- */
- private $backupRollbackFactory;
- /**
- * Theme Validator
- *
- * @var ThemeValidator
- */
- private $themeValidator;
- /**
- * Package name finder
- *
- * @var ThemePackageInfo
- */
- private $themePackageInfo;
- /**
- * Theme Uninstaller
- *
- * @var ThemeUninstaller
- */
- private $themeUninstaller;
- /**
- * Theme Dependency Checker
- *
- * @var ThemeDependencyChecker
- */
- private $themeDependencyChecker;
- /**
- * @var MaintenanceModeEnabler
- */
- private $maintenanceModeEnabler;
- /**
- * Constructor
- *
- * @param Cache $cache
- * @param CleanupFiles $cleanupFiles
- * @param ComposerInformation $composer
- * @param MaintenanceMode $maintenanceMode deprecated, use $maintenanceModeEnabler instead
- * @param DependencyChecker $dependencyChecker
- * @param Collection $themeCollection
- * @param BackupRollbackFactory $backupRollbackFactory
- * @param ThemeValidator $themeValidator
- * @param ThemePackageInfo $themePackageInfo
- * @param ThemeUninstaller $themeUninstaller
- * @param ThemeDependencyChecker $themeDependencyChecker
- * @param MaintenanceModeEnabler $maintenanceModeEnabler
- *
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function __construct(
- Cache $cache,
- CleanupFiles $cleanupFiles,
- ComposerInformation $composer,
- MaintenanceMode $maintenanceMode,
- DependencyChecker $dependencyChecker,
- Collection $themeCollection,
- BackupRollbackFactory $backupRollbackFactory,
- ThemeValidator $themeValidator,
- ThemePackageInfo $themePackageInfo,
- ThemeUninstaller $themeUninstaller,
- ThemeDependencyChecker $themeDependencyChecker,
- MaintenanceModeEnabler $maintenanceModeEnabler = null
- ) {
- $this->cache = $cache;
- $this->cleanupFiles = $cleanupFiles;
- $this->composer = $composer;
- $this->dependencyChecker = $dependencyChecker;
- $this->themeCollection = $themeCollection;
- $this->backupRollbackFactory = $backupRollbackFactory;
- $this->themeValidator = $themeValidator;
- $this->themePackageInfo = $themePackageInfo;
- $this->themeUninstaller = $themeUninstaller;
- $this->themeDependencyChecker = $themeDependencyChecker;
- $this->maintenanceModeEnabler =
- $maintenanceModeEnabler ?: ObjectManager::getInstance()->get(MaintenanceModeEnabler::class);
- parent::__construct();
- }
- /**
- * {@inheritdoc}
- */
- protected function configure()
- {
- $this->setName('theme:uninstall');
- $this->setDescription('Uninstalls theme');
- $this->addOption(
- self::INPUT_KEY_BACKUP_CODE,
- null,
- InputOption::VALUE_NONE,
- 'Take code backup (excluding temporary files)'
- );
- $this->addArgument(
- self::INPUT_KEY_THEMES,
- InputArgument::IS_ARRAY | InputArgument::REQUIRED,
- 'Path of the theme. Theme path should be specified as full path which is area/vendor/name.'
- . ' For example, frontend/Magento/blank'
- );
- $this->addOption(
- self::INPUT_KEY_CLEAR_STATIC_CONTENT,
- 'c',
- InputOption::VALUE_NONE,
- 'Clear generated static view files.'
- );
- parent::configure();
- }
- /**
- * {@inheritdoc}
- */
- protected function execute(InputInterface $input, OutputInterface $output)
- {
- $messages = [];
- $themePaths = $input->getArgument(self::INPUT_KEY_THEMES);
- $messages = array_merge($messages, $this->validate($themePaths));
- if (!empty($messages)) {
- $output->writeln($messages);
- // we must have an exit code higher than zero to indicate something was wrong
- return \Magento\Framework\Console\Cli::RETURN_FAILURE;
- }
- $messages = array_merge(
- $messages,
- $this->themeValidator->validateIsThemeInUse($themePaths),
- $this->themeDependencyChecker->checkChildTheme($themePaths),
- $this->checkDependencies($themePaths)
- );
- if (!empty($messages)) {
- $output->writeln(
- '<error>Unable to uninstall. Please resolve the following issues:</error>'
- . PHP_EOL . implode(PHP_EOL, $messages)
- );
- // we must have an exit code higher than zero to indicate something was wrong
- return \Magento\Framework\Console\Cli::RETURN_FAILURE;
- }
- $result = $this->maintenanceModeEnabler->executeInMaintenanceMode(
- function () use ($input, $output, $themePaths) {
- try {
- if ($input->getOption(self::INPUT_KEY_BACKUP_CODE)) {
- $time = time();
- $codeBackup = $this->backupRollbackFactory->create($output);
- $codeBackup->codeBackup($time);
- }
- $this->themeUninstaller->uninstallRegistry($output, $themePaths);
- $this->themeUninstaller->uninstallCode($output, $themePaths);
- $this->cleanup($input, $output);
- return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
- } catch (\Exception $e) {
- $output->writeln('<error>' . $e->getMessage() . '</error>');
- $output->writeln('<error>Please disable maintenance mode after you resolved above issues</error>');
- // we must have an exit code higher than zero to indicate something was wrong
- return \Magento\Framework\Console\Cli::RETURN_FAILURE;
- }
- },
- $output,
- true
- );
- return $result;
- }
- /**
- * Validate given full theme paths
- *
- * @param string[] $themePaths
- * @return string[]
- */
- private function validate($themePaths)
- {
- $messages = [];
- $incorrectThemes = $this->getIncorrectThemes($themePaths);
- if (!empty($incorrectThemes)) {
- $text = 'Theme path should be specified as full path which is area/vendor/name.';
- $messages[] = '<error>Incorrect theme(s) format: ' . implode(', ', $incorrectThemes)
- . '. ' . $text . '</error>';
- return $messages;
- }
- $unknownPackages = $this->getUnknownPackages($themePaths);
- $unknownThemes = $this->getUnknownThemes($themePaths);
- $unknownPackages = array_diff($unknownPackages, $unknownThemes);
- if (!empty($unknownPackages)) {
- $text = count($unknownPackages) > 1 ?
- ' are not installed Composer packages' : ' is not an installed Composer package';
- $messages[] = '<error>' . implode(', ', $unknownPackages) . $text . '</error>';
- }
- if (!empty($unknownThemes)) {
- $messages[] = '<error>Unknown theme(s): ' . implode(', ', $unknownThemes) . '</error>';
- }
- return $messages;
- }
- /**
- * Retrieve list of themes with wrong name format
- *
- * @param string[] $themePaths
- * @return string[]
- */
- protected function getIncorrectThemes($themePaths)
- {
- $result = [];
- foreach ($themePaths as $themePath) {
- if (!preg_match('/^[^\/]+\/[^\/]+\/[^\/]+$/', $themePath)) {
- $result[] = $themePath;
- continue;
- }
- }
- return $result;
- }
- /**
- * Retrieve list of unknown packages
- *
- * @param string[] $themePaths
- * @return string[]
- */
- protected function getUnknownPackages($themePaths)
- {
- $installedPackages = $this->composer->getRootRequiredPackages();
- $result = [];
- foreach ($themePaths as $themePath) {
- if (array_search($this->themePackageInfo->getPackageName($themePath), $installedPackages) === false) {
- $result[] = $themePath;
- }
- }
- return $result;
- }
- /**
- * Retrieve list of unknown themes
- *
- * @param string[] $themePaths
- * @return string[]
- */
- protected function getUnknownThemes($themePaths)
- {
- $result = [];
- foreach ($themePaths as $themePath) {
- if (!$this->themeCollection->hasTheme($this->themeCollection->getThemeByFullPath($themePath))) {
- $result[] = $themePath;
- }
- }
- return $result;
- }
- /**
- * Check dependencies to given full theme paths
- *
- * @param string[] $themePaths
- * @return string[]
- */
- private function checkDependencies($themePaths)
- {
- $messages = [];
- $packageToPath = [];
- foreach ($themePaths as $themePath) {
- $packageToPath[$this->themePackageInfo->getPackageName($themePath)] = $themePath;
- }
- $dependencies = $this->dependencyChecker->checkDependencies(array_keys($packageToPath), true);
- foreach ($dependencies as $package => $dependingPackages) {
- if (!empty($dependingPackages)) {
- $messages[] =
- '<error>' . $packageToPath[$package] .
- " has the following dependent package(s):</error>" .
- PHP_EOL . "\t<error>" . implode('</error>' . PHP_EOL . "\t<error>", $dependingPackages)
- . "</error>";
- }
- }
- return $messages;
- }
- /**
- * Cleanup after updated modules status
- *
- * @param InputInterface $input
- * @param OutputInterface $output
- * @return void
- */
- private function cleanup(InputInterface $input, OutputInterface $output)
- {
- $this->cache->clean();
- $output->writeln('<info>Cache cleared successfully.</info>');
- if ($input->getOption(self::INPUT_KEY_CLEAR_STATIC_CONTENT)) {
- $this->cleanupFiles->clearMaterializedViewFiles();
- $output->writeln('<info>Generated static view files cleared successfully.</info>');
- } else {
- $output->writeln(
- '<error>Alert: Generated static view files were not cleared.'
- . ' You can clear them using the --' . self::INPUT_KEY_CLEAR_STATIC_CONTENT . ' option.'
- . ' Failure to clear static view files might cause display issues in the Admin and storefront.</error>'
- );
- }
- }
- }
|