23 use Symfony\Component\Console\Input\InputInterface;
24 use Symfony\Component\Console\Input\InputOption;
25 use Symfony\Component\Console\Output\OutputInterface;
26 use Symfony\Component\Console\Question\ConfirmationQuestion;
50 private $deploymentConfig;
57 private $fullModuleList;
78 private $dependencyChecker;
92 private $backupRollbackFactory;
99 private $moduleUninstaller;
106 private $moduleRegistryUninstaller;
111 private $maintenanceModeEnabler;
116 private $patchApplier;
144 parent::__construct($objectManagerProvider);
145 $this->composer = $composer;
147 $this->fullModuleList = $fullModuleList;
148 $this->packageInfo = $this->objectManager->get(\
Magento\Framework\
Module\PackageInfoFactory::class)->create();
149 $this->collector = $collector;
150 $this->dependencyChecker = $this->objectManager->get(\
Magento\Framework\
Module\DependencyChecker::class);
151 $this->backupRollbackFactory = $this->objectManager->get(\
Magento\Framework\Setup\BackupRollbackFactory::class);
152 $this->moduleUninstaller = $moduleUninstaller;
153 $this->moduleRegistryUninstaller = $moduleRegistryUninstaller;
154 $this->maintenanceModeEnabler =
155 $maintenanceModeEnabler ?: $this->objectManager->get(MaintenanceModeEnabler::class);
161 private function getPatchApplier()
163 if (!$this->patchApplier) {
164 $this->patchApplier = $this
165 ->objectManager->get(PatchApplier::class);
168 return $this->patchApplier;
178 self::INPUT_KEY_REMOVE_DATA,
180 InputOption::VALUE_NONE,
181 'Remove data installed by module(s)' 184 self::INPUT_KEY_BACKUP_CODE,
186 InputOption::VALUE_NONE,
187 'Take code and configuration files backup (excluding temporary files)' 190 self::INPUT_KEY_BACKUP_MEDIA,
192 InputOption::VALUE_NONE,
196 self::INPUT_KEY_BACKUP_DB,
198 InputOption::VALUE_NONE,
199 'Take complete database backup' 202 self::INPUT_KEY_NON_COMPOSER_MODULE,
204 InputOption::VALUE_NONE,
205 'All modules, that will be past here will be non composer based' 208 $this->setName(
'module:uninstall')
209 ->setDescription(
'Uninstalls modules installed by composer')
229 if (!$this->deploymentConfig->isAvailable()) {
231 '<error>You cannot run this command because the Magento application is not installed.</error>' 237 $modules = $input->getArgument(self::INPUT_KEY_MODULES);
239 if ($input->getOption(self::INPUT_KEY_NON_COMPOSER_MODULE)) {
240 foreach ($modules as $moduleName) {
241 $this->getPatchApplier()->revertDataPatches($moduleName);
248 $messages = $this->
validate($modules);
249 if (!empty($messages)) {
256 $dependencyMessages = $this->checkDependencies($modules);
257 if (!empty($dependencyMessages)) {
258 $output->writeln($dependencyMessages);
263 $helper = $this->getHelper(
'question');
264 $question =
new ConfirmationQuestion(
265 'You are about to remove code and/or database tables. Are you sure?[y/N]',
268 if (!
$helper->ask($input,
$output, $question) && $input->isInteractive()) {
272 $result = $this->maintenanceModeEnabler->executeInMaintenanceMode(
275 $this->takeBackup($input,
$output);
276 $dbBackupOption = $input->getOption(self::INPUT_KEY_BACKUP_DB);
277 if ($input->getOption(self::INPUT_KEY_REMOVE_DATA)) {
278 $this->removeData($modules,
$output, $dbBackupOption);
280 if (!empty($this->collector->collectUninstall())) {
281 $question =
new ConfirmationQuestion(
282 'You are about to remove a module(s) that might have database data. ' 283 .
'Do you want to remove the data from database?[y/N]',
286 if (
$helper->ask($input,
$output, $question) || !$input->isInteractive()) {
287 $this->removeData($modules,
$output, $dbBackupOption);
291 '<info>You are about to remove a module(s) that might have database data. ' 292 .
'Remove the database data manually after uninstalling, if desired.</info>' 296 $this->moduleRegistryUninstaller->removeModulesFromDb(
$output, $modules);
297 $this->moduleRegistryUninstaller->removeModulesFromDeploymentConfig(
$output, $modules);
298 $this->moduleUninstaller->uninstallCode(
$output, $modules);
303 $output->writeln(
'<error>' . $e->getMessage() .
'</error>');
304 $output->writeln(
'<error>Please disable maintenance mode after you resolved above issues</error>');
322 private function takeBackup(InputInterface $input, OutputInterface
$output)
325 if ($input->getOption(self::INPUT_KEY_BACKUP_CODE)) {
326 $codeBackup = $this->backupRollbackFactory->create(
$output);
327 $codeBackup->codeBackup($time);
329 if ($input->getOption(self::INPUT_KEY_BACKUP_MEDIA)) {
330 $mediaBackup = $this->backupRollbackFactory->create(
$output);
333 if ($input->getOption(self::INPUT_KEY_BACKUP_DB)) {
334 $dbBackup = $this->backupRollbackFactory->create(
$output);
335 $this->setAreaCode();
336 $dbBackup->dbBackup($time);
348 private function removeData(array $modules, OutputInterface
$output, $dbBackupOption)
350 if (!$dbBackupOption) {
351 $output->writeln(
'<error>You are removing data without a database backup.</error>');
353 $output->writeln(
'<info>Removing data</info>');
355 $this->moduleUninstaller->uninstallData(
$output, $modules);
367 $unknownPackages = [];
368 $unknownModules = [];
369 $installedPackages = $this->composer->getRootRequiredPackages();
370 foreach ($modules as $module) {
371 if (array_search($this->packageInfo->getPackageName($module), $installedPackages) ===
false) {
372 $unknownPackages[] = $module;
374 if (!$this->fullModuleList->has($module)) {
375 $unknownModules[] = $module;
378 $unknownPackages = array_diff($unknownPackages, $unknownModules);
379 if (!empty($unknownPackages)) {
380 $text = count($unknownPackages) > 1 ?
381 ' are not installed composer packages' :
' is not an installed composer package';
382 $messages[] =
'<error>' . implode(
', ', $unknownPackages) .
$text .
'</error>';
384 if (!empty($unknownModules)) {
385 $messages[] =
'<error>Unknown module(s): ' . implode(
', ', $unknownModules) .
'</error>';
396 private function checkDependencies(array $modules)
399 $dependencies = $this->dependencyChecker->checkDependenciesWhenDisableModules(
401 $this->fullModuleList->getNames()
403 foreach ($dependencies as $module => $dependingModules) {
404 if (!empty($dependingModules)) {
406 "<error>Cannot uninstall module '$module' because the following module(s) depend on it:</error>" .
407 PHP_EOL .
"\t<error>" . implode(
'</error>' . PHP_EOL .
"\t<error>", array_keys($dependingModules)) .
419 private function setAreaCode()
421 $areaCode =
'adminhtml';
423 $appState = $this->objectManager->get(\
Magento\Framework\
App\State::class);
424 $appState->setAreaCode($areaCode);
426 $configLoader = $this->objectManager->get(\
Magento\Framework\ObjectManager\ConfigLoaderInterface::class);
427 $this->objectManager->configure($configLoader->load($areaCode));
__construct(ComposerInformation $composer, DeploymentConfig $deploymentConfig, FullModuleList $fullModuleList, MaintenanceMode $maintenanceMode, ObjectManagerProvider $objectManagerProvider, UninstallCollector $collector, ModuleUninstaller $moduleUninstaller, ModuleRegistryUninstaller $moduleRegistryUninstaller, MaintenanceModeEnabler $maintenanceModeEnabler=null)
cleanup($repo, $mainline)
const INPUT_KEY_BACKUP_DB
const INPUT_KEY_BACKUP_CODE
const INPUT_KEY_NON_COMPOSER_MODULE
const INPUT_KEY_BACKUP_MEDIA
execute(InputInterface $input, OutputInterface $output)
const INPUT_KEY_REMOVE_DATA