PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/magento/magento2-base/setup/src/Magento/Setup/Console/Command/ModuleUninstallCommand.php

https://gitlab.com/yousafsyed/easternglamor
PHP | 379 lines | 232 code | 26 blank | 121 comment | 21 complexity | 10961d100d38922057f5b89c4b839b2d MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Setup\Console\Command;
  7. use Magento\Framework\App\DeploymentConfig;
  8. use Magento\Framework\App\MaintenanceMode;
  9. use Magento\Framework\Backup\Factory;
  10. use Magento\Framework\Composer\ComposerInformation;
  11. use Magento\Framework\Module\DependencyChecker;
  12. use Magento\Framework\Module\FullModuleList;
  13. use Magento\Framework\Module\PackageInfo;
  14. use Magento\Framework\Setup\BackupRollbackFactory;
  15. use Magento\Setup\Model\ModuleRegistryUninstaller;
  16. use Magento\Setup\Model\ModuleUninstaller;
  17. use Magento\Setup\Model\ObjectManagerProvider;
  18. use Magento\Setup\Model\UninstallCollector;
  19. use Symfony\Component\Console\Input\InputInterface;
  20. use Symfony\Component\Console\Input\InputOption;
  21. use Symfony\Component\Console\Output\OutputInterface;
  22. use Symfony\Component\Console\Question\ConfirmationQuestion;
  23. /**
  24. * Command for uninstalling modules
  25. *
  26. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  27. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  28. */
  29. class ModuleUninstallCommand extends AbstractModuleCommand
  30. {
  31. /**
  32. * Names of input options
  33. */
  34. const INPUT_KEY_REMOVE_DATA = 'remove-data';
  35. const INPUT_KEY_BACKUP_CODE = 'backup-code';
  36. const INPUT_KEY_BACKUP_MEDIA = 'backup-media';
  37. const INPUT_KEY_BACKUP_DB = 'backup-db';
  38. /**
  39. * Maintenance mode
  40. *
  41. * @var MaintenanceMode
  42. */
  43. private $maintenanceMode;
  44. /**
  45. * Deployment Configuration
  46. *
  47. * @var DeploymentConfig
  48. */
  49. private $deploymentConfig;
  50. /**
  51. * Full module list
  52. *
  53. * @var FullModuleList
  54. */
  55. private $fullModuleList;
  56. /**
  57. * Module package info
  58. *
  59. * @var PackageInfo
  60. */
  61. private $packageInfo;
  62. /**
  63. * Uninstall classes collector
  64. *
  65. * @var UninstallCollector
  66. */
  67. private $collector;
  68. /**
  69. * Composer general dependency checker
  70. *
  71. * @var DependencyChecker
  72. */
  73. private $dependencyChecker;
  74. /**
  75. * Root composer.json information
  76. *
  77. * @var ComposerInformation
  78. */
  79. private $composer;
  80. /**
  81. * BackupRollback factory
  82. *
  83. * @var BackupRollbackFactory
  84. */
  85. private $backupRollbackFactory;
  86. /**
  87. * Module Uninstaller
  88. *
  89. * @var ModuleUninstaller
  90. */
  91. private $moduleUninstaller;
  92. /**
  93. * Module Registry uninstaller
  94. *
  95. * @var ModuleRegistryUninstaller
  96. */
  97. private $moduleRegistryUninstaller;
  98. /**
  99. * Constructor
  100. *
  101. * @param ComposerInformation $composer
  102. * @param DeploymentConfig $deploymentConfig
  103. * @param FullModuleList $fullModuleList
  104. * @param MaintenanceMode $maintenanceMode
  105. * @param ObjectManagerProvider $objectManagerProvider
  106. * @param UninstallCollector $collector
  107. * @param ModuleUninstaller $moduleUninstaller
  108. * @param ModuleRegistryUninstaller $moduleRegistryUninstaller
  109. */
  110. public function __construct(
  111. ComposerInformation $composer,
  112. DeploymentConfig $deploymentConfig,
  113. FullModuleList $fullModuleList,
  114. MaintenanceMode $maintenanceMode,
  115. ObjectManagerProvider $objectManagerProvider,
  116. UninstallCollector $collector,
  117. ModuleUninstaller $moduleUninstaller,
  118. ModuleRegistryUninstaller $moduleRegistryUninstaller
  119. ) {
  120. parent::__construct($objectManagerProvider);
  121. $this->composer = $composer;
  122. $this->deploymentConfig = $deploymentConfig;
  123. $this->maintenanceMode = $maintenanceMode;
  124. $this->fullModuleList = $fullModuleList;
  125. $this->packageInfo = $this->objectManager->get('Magento\Framework\Module\PackageInfoFactory')->create();
  126. $this->collector = $collector;
  127. $this->dependencyChecker = $this->objectManager->get('Magento\Framework\Module\DependencyChecker');
  128. $this->backupRollbackFactory = $this->objectManager->get('Magento\Framework\Setup\BackupRollbackFactory');
  129. $this->moduleUninstaller = $moduleUninstaller;
  130. $this->moduleRegistryUninstaller = $moduleRegistryUninstaller;
  131. }
  132. /**
  133. * {@inheritdoc}
  134. */
  135. protected function configure()
  136. {
  137. $options = [
  138. new InputOption(
  139. self::INPUT_KEY_REMOVE_DATA,
  140. 'r',
  141. InputOption::VALUE_NONE,
  142. 'Remove data installed by module(s)'
  143. ),
  144. new InputOption(
  145. self::INPUT_KEY_BACKUP_CODE,
  146. null,
  147. InputOption::VALUE_NONE,
  148. 'Take code and configuration files backup (excluding temporary files)'
  149. ),
  150. new InputOption(
  151. self::INPUT_KEY_BACKUP_MEDIA,
  152. null,
  153. InputOption::VALUE_NONE,
  154. 'Take media backup'
  155. ),
  156. new InputOption(
  157. self::INPUT_KEY_BACKUP_DB,
  158. null,
  159. InputOption::VALUE_NONE,
  160. 'Take complete database backup'
  161. ),
  162. ];
  163. $this->setName('module:uninstall')
  164. ->setDescription('Uninstalls modules installed by composer')
  165. ->setDefinition($options);
  166. parent::configure();
  167. }
  168. /**
  169. * {@inheritdoc}
  170. */
  171. protected function isModuleRequired()
  172. {
  173. return true;
  174. }
  175. /**
  176. * {@inheritdoc}
  177. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  178. */
  179. protected function execute(InputInterface $input, OutputInterface $output)
  180. {
  181. if (!$this->deploymentConfig->isAvailable()) {
  182. $output->writeln(
  183. '<error>You cannot run this command because the Magento application is not installed.</error>'
  184. );
  185. return;
  186. }
  187. $modules = $input->getArgument(self::INPUT_KEY_MODULES);
  188. // validate modules input
  189. $messages = $this->validate($modules);
  190. if (!empty($messages)) {
  191. $output->writeln($messages);
  192. return;
  193. }
  194. // check dependencies
  195. $dependencyMessages = $this->checkDependencies($modules);
  196. if (!empty($dependencyMessages)) {
  197. $output->writeln($dependencyMessages);
  198. return;
  199. }
  200. $helper = $this->getHelper('question');
  201. $question = new ConfirmationQuestion(
  202. 'You are about to remove code and/or database tables. Are you sure?[y/N]',
  203. false
  204. );
  205. if (!$helper->ask($input, $output, $question) && $input->isInteractive()) {
  206. return;
  207. }
  208. try {
  209. $output->writeln('<info>Enabling maintenance mode</info>');
  210. $this->maintenanceMode->set(true);
  211. $this->takeBackup($input, $output);
  212. $dbBackupOption = $input->getOption(self::INPUT_KEY_BACKUP_DB);
  213. if ($input->getOption(self::INPUT_KEY_REMOVE_DATA)) {
  214. $this->removeData($modules, $output, $dbBackupOption);
  215. } else {
  216. if (!empty($this->collector->collectUninstall())) {
  217. $question = new ConfirmationQuestion(
  218. 'You are about to remove a module(s) that might have database data. '
  219. . 'Do you want to remove the data from database?[y/N]',
  220. false
  221. );
  222. if ($helper->ask($input, $output, $question) || !$input->isInteractive()) {
  223. $this->removeData($modules, $output, $dbBackupOption);
  224. }
  225. } else {
  226. $output->writeln(
  227. '<info>You are about to remove a module(s) that might have database data. '
  228. . 'Remove the database data manually after uninstalling, if desired.</info>'
  229. );
  230. }
  231. }
  232. $this->moduleRegistryUninstaller->removeModulesFromDb($output, $modules);
  233. $this->moduleRegistryUninstaller->removeModulesFromDeploymentConfig($output, $modules);
  234. $this->moduleUninstaller->uninstallCode($output, $modules);
  235. $this->cleanup($input, $output);
  236. $output->writeln('<info>Disabling maintenance mode</info>');
  237. $this->maintenanceMode->set(false);
  238. } catch (\Exception $e) {
  239. $output->writeln('<error>' . $e->getMessage() . '</error>');
  240. $output->writeln('<error>Please disable maintenance mode after you resolved above issues</error>');
  241. }
  242. }
  243. /**
  244. * Check backup options and take backup appropriately
  245. *
  246. * @param InputInterface $input
  247. * @param OutputInterface $output
  248. * @return void
  249. */
  250. private function takeBackup(InputInterface $input, OutputInterface $output)
  251. {
  252. $time = time();
  253. if ($input->getOption(self::INPUT_KEY_BACKUP_CODE)) {
  254. $codeBackup = $this->backupRollbackFactory->create($output);
  255. $codeBackup->codeBackup($time);
  256. }
  257. if ($input->getOption(self::INPUT_KEY_BACKUP_MEDIA)) {
  258. $mediaBackup = $this->backupRollbackFactory->create($output);
  259. $mediaBackup->codeBackup($time, Factory::TYPE_MEDIA);
  260. }
  261. if ($input->getOption(self::INPUT_KEY_BACKUP_DB)) {
  262. $dbBackup = $this->backupRollbackFactory->create($output);
  263. $this->setAreaCode();
  264. $dbBackup->dbBackup($time);
  265. }
  266. }
  267. /**
  268. * Invoke remove data routine in each specified module
  269. *
  270. * @param string[] $modules
  271. * @param OutputInterface $output
  272. * @param bool $dbBackupOption
  273. * @return void
  274. */
  275. private function removeData(array $modules, OutputInterface $output, $dbBackupOption)
  276. {
  277. if (!$dbBackupOption) {
  278. $output->writeln('<error>You are removing data without a database backup.</error>');
  279. } else {
  280. $output->writeln('<info>Removing data</info>');
  281. }
  282. $this->moduleUninstaller->uninstallData($output, $modules);
  283. }
  284. /**
  285. * Validate list of modules against installed composer packages and return error messages
  286. *
  287. * @param string[] $modules
  288. * @return string[]
  289. */
  290. protected function validate(array $modules)
  291. {
  292. $messages = [];
  293. $unknownPackages = [];
  294. $unknownModules = [];
  295. $installedPackages = $this->composer->getRootRequiredPackages();
  296. foreach ($modules as $module) {
  297. if (array_search($this->packageInfo->getPackageName($module), $installedPackages) === false) {
  298. $unknownPackages[] = $module;
  299. }
  300. if (!$this->fullModuleList->has($module)) {
  301. $unknownModules[] = $module;
  302. }
  303. }
  304. $unknownPackages = array_diff($unknownPackages, $unknownModules);
  305. if (!empty($unknownPackages)) {
  306. $text = count($unknownPackages) > 1 ?
  307. ' are not installed composer packages' : ' is not an installed composer package';
  308. $messages[] = '<error>' . implode(', ', $unknownPackages) . $text . '</error>';
  309. }
  310. if (!empty($unknownModules)) {
  311. $messages[] = '<error>Unknown module(s): ' . implode(', ', $unknownModules) . '</error>';
  312. }
  313. return $messages;
  314. }
  315. /**
  316. * Check for dependencies to modules, return error messages
  317. *
  318. * @param string[] $modules
  319. * @return string[]
  320. */
  321. private function checkDependencies(array $modules)
  322. {
  323. $messages = [];
  324. $dependencies = $this->dependencyChecker->checkDependenciesWhenDisableModules(
  325. $modules,
  326. $this->fullModuleList->getNames()
  327. );
  328. foreach ($dependencies as $module => $dependingModules) {
  329. if (!empty($dependingModules)) {
  330. $messages[] =
  331. "<error>Cannot uninstall module '$module' because the following module(s) depend on it:</error>" .
  332. PHP_EOL . "\t<error>" . implode('</error>' . PHP_EOL . "\t<error>", array_keys($dependingModules)) .
  333. "</error>";
  334. }
  335. }
  336. return $messages;
  337. }
  338. /**
  339. * Sets area code to start a session for database backup and rollback
  340. *
  341. * @return void
  342. */
  343. private function setAreaCode()
  344. {
  345. $areaCode = 'adminhtml';
  346. /** @var \Magento\Framework\App\State $appState */
  347. $appState = $this->objectManager->get('Magento\Framework\App\State');
  348. $appState->setAreaCode($areaCode);
  349. /** @var \Magento\Framework\ObjectManager\ConfigLoaderInterface $configLoader */
  350. $configLoader = $this->objectManager->get('Magento\Framework\ObjectManager\ConfigLoaderInterface');
  351. $this->objectManager->configure($configLoader->load($areaCode));
  352. }
  353. }