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

Breadcrumb

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

class BumpCommand

@author Jordi Boggiano <j.boggiano@seld.be>

Hierarchy

  • class \Symfony\Component\Console\Command\Command
    • class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
      • class \Composer\Command\BumpCommand extends \Composer\Command\BaseCommand uses \Composer\Command\CompletionTrait

Expanded class hierarchy of BumpCommand

File

vendor/composer/composer/src/Composer/Command/BumpCommand.php, line 35

Namespace

Composer\Command
View source
final class BumpCommand extends BaseCommand {
    private const ERROR_GENERIC = 1;
    private const ERROR_LOCK_OUTDATED = 2;
    use CompletionTrait;
    protected function configure() : void {
        $this->setName('bump')
            ->setDescription('Increases the lower limit of your composer.json requirements to the currently installed versions')
            ->setDefinition([
            new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name(s) to restrict which packages are bumped.', null, $this->suggestRootRequirement()),
            new InputOption('dev-only', 'D', InputOption::VALUE_NONE, 'Only bump requirements in "require-dev".'),
            new InputOption('no-dev-only', 'R', InputOption::VALUE_NONE, 'Only bump requirements in "require".'),
            new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the packages to bump, but will not execute anything.'),
        ])
            ->setHelp(<<<EOT
The <info>bump</info> command increases the lower limit of your composer.json requirements
to the currently installed versions. This helps to ensure your dependencies do not
accidentally get downgraded due to some other conflict, and can slightly improve
dependency resolution performance as it limits the amount of package versions
Composer has to look at.

Running this blindly on libraries is **NOT** recommended as it will narrow down
your allowed dependencies, which may cause dependency hell for your users.
Running it with <info>--dev-only</info> on libraries may be fine however as dev requirements
are local to the library and do not affect consumers of the package.

EOT
);
    }
    
    /**
     * @throws \Seld\JsonLint\ParsingException
     */
    protected function execute(InputInterface $input, OutputInterface $output) : int {
        return $this->doBump($this->getIO(), $input->getOption('dev-only'), $input->getOption('no-dev-only'), $input->getOption('dry-run'), $input->getArgument('packages'));
    }
    
    /**
     * @param string[] $packagesFilter
     * @throws \Seld\JsonLint\ParsingException
     */
    public function doBump(IOInterface $io, bool $devOnly, bool $noDevOnly, bool $dryRun, array $packagesFilter) : int {
        
        /** @readonly */
        $composerJsonPath = Factory::getComposerFile();
        if (!Filesystem::isReadable($composerJsonPath)) {
            $io->writeError('<error>' . $composerJsonPath . ' is not readable.</error>');
            return self::ERROR_GENERIC;
        }
        $composerJson = new JsonFile($composerJsonPath);
        $contents = file_get_contents($composerJson->getPath());
        if (false === $contents) {
            $io->writeError('<error>' . $composerJsonPath . ' is not readable.</error>');
            return self::ERROR_GENERIC;
        }
        // check for writability by writing to the file as is_writable can not be trusted on network-mounts
        // see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
        if (!is_writable($composerJsonPath) && false === Silencer::call('file_put_contents', $composerJsonPath, $contents)) {
            $io->writeError('<error>' . $composerJsonPath . ' is not writable.</error>');
            return self::ERROR_GENERIC;
        }
        unset($contents);
        $composer = $this->requireComposer();
        if ($composer->getLocker()
            ->isLocked()) {
            if (!$composer->getLocker()
                ->isFresh()) {
                $io->writeError('<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>');
                return self::ERROR_LOCK_OUTDATED;
            }
            $repo = $composer->getLocker()
                ->getLockedRepository(true);
        }
        else {
            $repo = $composer->getRepositoryManager()
                ->getLocalRepository();
        }
        if ($composer->getPackage()
            ->getType() !== 'project' && !$devOnly) {
            $io->writeError('<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>');
            $contents = $composerJson->read();
            if (!isset($contents['type'])) {
                $io->writeError('<warning>If your package is not a library, you can explicitly specify the "type" by using "composer config type project".</warning>');
                $io->writeError('<warning>Alternatively you can use --dev-only to only bump dependencies within "require-dev".</warning>');
            }
            unset($contents);
        }
        $bumper = new VersionBumper();
        $tasks = [];
        if (!$devOnly) {
            $tasks['require'] = $composer->getPackage()
                ->getRequires();
        }
        if (!$noDevOnly) {
            $tasks['require-dev'] = $composer->getPackage()
                ->getDevRequires();
        }
        if (count($packagesFilter) > 0) {
            $pattern = BasePackage::packageNamesToRegexp(array_unique(array_map('strtolower', $packagesFilter)));
            foreach ($tasks as $key => $reqs) {
                foreach ($reqs as $pkgName => $link) {
                    if (!Preg::isMatch($pattern, $pkgName)) {
                        unset($tasks[$key][$pkgName]);
                    }
                }
            }
        }
        $updates = [];
        foreach ($tasks as $key => $reqs) {
            foreach ($reqs as $pkgName => $link) {
                if (PlatformRepository::isPlatformPackage($pkgName)) {
                    continue;
                }
                $currentConstraint = $link->getPrettyConstraint();
                $package = $repo->findPackage($pkgName, '*');
                // name must be provided or replaced
                if (null === $package) {
                    continue;
                }
                while ($package instanceof AliasPackage) {
                    $package = $package->getAliasOf();
                }
                $bumped = $bumper->bumpRequirement($link->getConstraint(), $package);
                if ($bumped === $currentConstraint) {
                    continue;
                }
                $updates[$key][$pkgName] = $bumped;
            }
        }
        if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) {
            $composerDefinition = $composerJson->read();
            foreach ($updates as $key => $packages) {
                foreach ($packages as $package => $version) {
                    $composerDefinition[$key][$package] = $version;
                }
            }
            $composerJson->write($composerDefinition);
        }
        $changeCount = array_sum(array_map('count', $updates));
        if ($changeCount > 0) {
            if ($dryRun) {
                $io->write('<info>' . $composerJsonPath . ' would be updated with:</info>');
                foreach ($updates as $requireType => $packages) {
                    foreach ($packages as $package => $version) {
                        $io->write(sprintf('<info> - %s.%s: %s</info>', $requireType, $package, $version));
                    }
                }
            }
            else {
                $io->write('<info>' . $composerJsonPath . ' has been updated (' . $changeCount . ' changes).</info>');
            }
        }
        else {
            $io->write('<info>No requirements to update in ' . $composerJsonPath . '.</info>');
        }
        if (!$dryRun && $composer->getLocker()
            ->isLocked() && $composer->getConfig()
            ->get('lock') && $changeCount > 0) {
            $composer->getLocker()
                ->updateHash($composerJson);
        }
        if ($dryRun && $changeCount > 0) {
            return self::ERROR_GENERIC;
        }
        return 0;
    }
    
    /**
     * @param array<'require'|'require-dev', array<string, string>> $updates
     */
    private function updateFileCleanly(JsonFile $json, array $updates) : bool {
        $contents = file_get_contents($json->getPath());
        if (false === $contents) {
            throw new \RuntimeException('Unable to read ' . $json->getPath() . ' contents.');
        }
        $manipulator = new JsonManipulator($contents);
        foreach ($updates as $key => $packages) {
            foreach ($packages as $package => $version) {
                if (!$manipulator->addLink($key, $package, $version)) {
                    return false;
                }
            }
        }
        if (false === file_put_contents($json->getPath(), $manipulator->getContents())) {
            throw new \RuntimeException('Unable to write new ' . $json->getPath() . ' contents.');
        }
        return true;
    }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
BaseCommand::$composer private property
BaseCommand::$io private property
BaseCommand::complete public function @inheritdoc Overrides Command::complete 1
BaseCommand::createComposerInstance protected function Calls {
BaseCommand::formatRequirements protected function
BaseCommand::getApplication public function Gets the application instance for this command. Overrides Command::getApplication
BaseCommand::getAuditFormat protected function @internal
BaseCommand::getComposer Deprecated public function
BaseCommand::getIO public function
BaseCommand::getPlatformRequirementFilter protected function
BaseCommand::getPreferredInstallOptions protected function Returns preferSource and preferDist values based on the configuration.
BaseCommand::getTerminalWidth protected function
BaseCommand::initialize protected function @inheritDoc Overrides Command::initialize 1
BaseCommand::isProxyCommand public function Whether or not this command is meant to call another command. 2
BaseCommand::normalizeRequirements protected function
BaseCommand::renderTable protected function
BaseCommand::resetComposer public function Removes the cached composer instance
BaseCommand::setComposer public function
BaseCommand::setIO public function
BaseCommand::tryComposer public function Retrieves the default Composer\Composer instance or null
BumpCommand::configure protected function Configures the current command. Overrides Command::configure
BumpCommand::doBump public function
BumpCommand::ERROR_GENERIC private constant
BumpCommand::ERROR_LOCK_OUTDATED private constant
BumpCommand::execute protected function Overrides Command::execute
BumpCommand::updateFileCleanly private function
Command::$aliases private property 1
Command::$application private property
Command::$code private property
Command::$definition private property
Command::$description private property 1
Command::$fullDefinition private property
Command::$help private property
Command::$helperSet private property
Command::$hidden private property
Command::$ignoreValidationErrors private property 2
Command::$name private property
Command::$processTitle private property
Command::$synopsis private property
Command::$usages private property
Command::addArgument public function Adds an argument. 2
Command::addOption public function Adds an option. 2
Command::addUsage public function Add a command usage example, it&#039;ll be prefixed with the command name. 2
Command::FAILURE public constant
Command::getAliases public function Returns the aliases for the command.
Command::getDefaultDescription public static function
Command::getDefaultName public static function
Command::getDefinition public function Gets the InputDefinition attached to this Command. 2
Command::getDescription public function Returns the description for the command.
Command::getHelp public function Returns the help for the command. 2
Command::getHelper public function Gets a helper instance by name. 2
Command::getHelperSet public function Gets the helper set. 1
Command::getName public function Returns the command name.
Command::getNativeDefinition public function Gets the InputDefinition to be used to create representations of this Command. 2
Command::getProcessedHelp public function Returns the processed help for the command replacing the %command.name% and
%command.full_name% patterns with the real values dynamically.
2
Command::getSynopsis public function Returns the synopsis for the command. 2
Command::getUsages public function Returns alternative usages of the command. 2
Command::ignoreValidationErrors public function Ignores validation errors. 2
Command::interact protected function Interacts with the user. 5
Command::INVALID public constant
Command::isEnabled public function Checks whether the command is enabled or not in the current environment. 2
Command::isHidden public function
Command::mergeApplicationDefinition public function Merges the application definition with the command definition. 2
Command::run public function Runs the command. 4
Command::setAliases public function Sets the aliases for the command.
Command::setApplication public function 2
Command::setCode public function Sets the code to execute when running this command. 2
Command::setDefinition public function Sets an array of argument and option instances. 2
Command::setDescription public function Sets the description for the command.
Command::setHelp public function Sets the help for the command. 2
Command::setHelperSet public function 2
Command::setHidden public function
Command::setName public function Sets the name of the command.
Command::setProcessTitle public function Sets the process title of the command. 2
Command::SUCCESS public constant
Command::validateName private function Validates a command name.
Command::__construct public function 15
CompletionTrait::requireComposer abstract public function
CompletionTrait::suggestAvailablePackage private function Suggest package names available on all configured repositories.
CompletionTrait::suggestAvailablePackageInclPlatform private function Suggest package names available on all configured repositories or
platform packages from the ones available on the currently-running PHP
CompletionTrait::suggestInstalledPackage private function Suggest package names from installed.
CompletionTrait::suggestInstalledPackageTypes private function Suggest package names from installed.
CompletionTrait::suggestPlatformPackage private function Suggest platform packages from the ones available on the currently-running PHP
CompletionTrait::suggestPreferInstall private function Suggestion values for &quot;prefer-install&quot; option
CompletionTrait::suggestRootRequirement private function Suggest package names from root requirements.

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal