Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. RemoveCommand.php

function RemoveCommand::execute

Throws

\Seld\JsonLint\ParsingException

Overrides Command::execute

File

vendor/composer/composer/src/Composer/Command/RemoveCommand.php, line 87

Class

RemoveCommand
@author Pierre du Plessis <pdples@gmail.com> @author Jordi Boggiano <j.boggiano@seld.be>

Namespace

Composer\Command

Code

protected function execute(InputInterface $input, OutputInterface $output) : int {
    if ($input->getArgument('packages') === [] && !$input->getOption('unused')) {
        throw new InvalidArgumentException('Not enough arguments (missing: "packages").');
    }
    $packages = $input->getArgument('packages');
    $packages = array_map('strtolower', $packages);
    if ($input->getOption('unused')) {
        $composer = $this->requireComposer();
        $locker = $composer->getLocker();
        if (!$locker->isLocked()) {
            throw new \UnexpectedValueException('A valid composer.lock file is required to run this command with --unused');
        }
        $lockedPackages = $locker->getLockedRepository()
            ->getPackages();
        $required = [];
        foreach (array_merge($composer->getPackage()
            ->getRequires(), $composer->getPackage()
            ->getDevRequires()) as $link) {
            $required[$link->getTarget()] = true;
        }
        do {
            $found = false;
            foreach ($lockedPackages as $index => $package) {
                foreach ($package->getNames() as $name) {
                    if (isset($required[$name])) {
                        foreach ($package->getRequires() as $link) {
                            $required[$link->getTarget()] = true;
                        }
                        $found = true;
                        unset($lockedPackages[$index]);
                        break;
                    }
                }
            }
        } while ($found);
        $unused = [];
        foreach ($lockedPackages as $package) {
            $unused[] = $package->getName();
        }
        $packages = array_merge($packages, $unused);
        if (count($packages) === 0) {
            $this->getIO()
                ->writeError('<info>No unused packages to remove</info>');
            return 0;
        }
    }
    $file = Factory::getComposerFile();
    $jsonFile = new JsonFile($file);
    
    /** @var array{require?: array<string, string>, require-dev?: array<string, string>} $composer */
    $composer = $jsonFile->read();
    $composerBackup = file_get_contents($jsonFile->getPath());
    $json = new JsonConfigSource($jsonFile);
    $type = $input->getOption('dev') ? 'require-dev' : 'require';
    $altType = !$input->getOption('dev') ? 'require-dev' : 'require';
    $io = $this->getIO();
    if ($input->getOption('update-with-dependencies')) {
        $io->writeError('<warning>You are using the deprecated option "update-with-dependencies". This is now default behaviour. The --no-update-with-dependencies option can be used to remove a package without its dependencies.</warning>');
    }
    // make sure name checks are done case insensitively
    foreach ([
        'require',
        'require-dev',
    ] as $linkType) {
        if (isset($composer[$linkType])) {
            foreach ($composer[$linkType] as $name => $version) {
                $composer[$linkType][strtolower($name)] = $name;
            }
        }
    }
    $dryRun = $input->getOption('dry-run');
    $toRemove = [];
    foreach ($packages as $package) {
        if (isset($composer[$type][$package])) {
            if ($dryRun) {
                $toRemove[$type][] = $composer[$type][$package];
            }
            else {
                $json->removeLink($type, $composer[$type][$package]);
            }
        }
        elseif (isset($composer[$altType][$package])) {
            $io->writeError('<warning>' . $composer[$altType][$package] . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>');
            if ($io->isInteractive()) {
                if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ')) {
                    if ($dryRun) {
                        $toRemove[$altType][] = $composer[$altType][$package];
                    }
                    else {
                        $json->removeLink($altType, $composer[$altType][$package]);
                    }
                }
            }
        }
        elseif (isset($composer[$type]) && count($matches = Preg::grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$type]))) > 0) {
            foreach ($matches as $matchedPackage) {
                if ($dryRun) {
                    $toRemove[$type][] = $matchedPackage;
                }
                else {
                    $json->removeLink($type, $matchedPackage);
                }
            }
        }
        elseif (isset($composer[$altType]) && count($matches = Preg::grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$altType]))) > 0) {
            foreach ($matches as $matchedPackage) {
                $io->writeError('<warning>' . $matchedPackage . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>');
                if ($io->isInteractive()) {
                    if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ')) {
                        if ($dryRun) {
                            $toRemove[$altType][] = $matchedPackage;
                        }
                        else {
                            $json->removeLink($altType, $matchedPackage);
                        }
                    }
                }
            }
        }
        else {
            $io->writeError('<warning>' . $package . ' is not required in your composer.json and has not been removed</warning>');
        }
    }
    $io->writeError('<info>' . $file . ' has been updated</info>');
    if ($input->getOption('no-update')) {
        return 0;
    }
    if ($composer = $this->tryComposer()) {
        $composer->getPluginManager()
            ->deactivateInstalledPlugins();
    }
    // Update packages
    $this->resetComposer();
    $composer = $this->requireComposer();
    if ($dryRun) {
        $rootPackage = $composer->getPackage();
        $links = [
            'require' => $rootPackage->getRequires(),
            'require-dev' => $rootPackage->getDevRequires(),
        ];
        foreach ($toRemove as $type => $names) {
            foreach ($names as $name) {
                unset($links[$type][$name]);
            }
        }
        $rootPackage->setRequires($links['require']);
        $rootPackage->setDevRequires($links['require-dev']);
    }
    $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
    $composer->getEventDispatcher()
        ->dispatch($commandEvent->getName(), $commandEvent);
    $allowPlugins = $composer->getConfig()
        ->get('allow-plugins');
    $removedPlugins = is_array($allowPlugins) ? array_intersect(array_keys($allowPlugins), $packages) : [];
    if (!$dryRun && is_array($allowPlugins) && count($removedPlugins) > 0) {
        if (count($allowPlugins) === count($removedPlugins)) {
            $json->removeConfigSetting('allow-plugins');
        }
        else {
            foreach ($removedPlugins as $plugin) {
                $json->removeConfigSetting('allow-plugins.' . $plugin);
            }
        }
    }
    $composer->getInstallationManager()
        ->setOutputProgress(!$input->getOption('no-progress'));
    $install = Installer::create($io, $composer);
    $updateDevMode = !$input->getOption('update-no-dev');
    $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()
        ->get('optimize-autoloader');
    $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()
        ->get('classmap-authoritative');
    $apcuPrefix = $input->getOption('apcu-autoloader-prefix');
    $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $composer->getConfig()
        ->get('apcu-autoloader');
    $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
    $flags = '';
    if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) {
        $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
        $flags .= ' --with-all-dependencies';
    }
    elseif ($input->getOption('no-update-with-dependencies')) {
        $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
        $flags .= ' --with-dependencies';
    }
    $io->writeError('<info>Running composer update ' . implode(' ', $packages) . $flags . '</info>');
    $install->setVerbose($input->getOption('verbose'))
        ->setDevMode($updateDevMode)
        ->setOptimizeAutoloader($optimize)
        ->setClassMapAuthoritative($authoritative)
        ->setApcuAutoloader($apcu, $apcuPrefix)
        ->setUpdate(true)
        ->setInstall(!$input->getOption('no-install'))
        ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
        ->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input))
        ->setDryRun($dryRun)
        ->setAudit(!$input->getOption('no-audit'))
        ->setAuditFormat($this->getAuditFormat($input))
        ->setMinimalUpdate($input->getOption('minimal-changes'));
    // if no lock is present, we do not do a partial update as
    // this is not supported by the Installer
    if ($composer->getLocker()
        ->isLocked()) {
        $install->setUpdateAllowList($packages);
    }
    $status = $install->run();
    if ($status !== 0) {
        $io->writeError("\n" . '<error>Removal failed, reverting ' . $file . ' to its original content.</error>');
        file_put_contents($jsonFile->getPath(), $composerBackup);
    }
    if (!$dryRun) {
        foreach ($packages as $package) {
            if ($composer->getRepositoryManager()
                ->getLocalRepository()
                ->findPackages($package)) {
                $io->writeError('<error>Removal failed, ' . $package . ' is still present, it may be required by another package. See `composer why ' . $package . '`.</error>');
                return 2;
            }
        }
    }
    return $status;
}
RSS feed
Powered by Drupal