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

Breadcrumb

  1. Drupal Core 11.1.x

RootPackageLoader.php

Namespace

Composer\Package\Loader

File

vendor/composer/composer/src/Composer/Package/Loader/RootPackageLoader.php

View source
<?php

declare (strict_types=1);

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Composer\Package\Loader;

use Composer\Package\BasePackage;
use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Package\RootAliasPackage;
use Composer\Pcre\Preg;
use Composer\Repository\RepositoryFactory;
use Composer\Package\Version\VersionGuesser;
use Composer\Package\Version\VersionParser;
use Composer\Package\RootPackage;
use Composer\Repository\RepositoryManager;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;

/**
 * ArrayLoader built for the sole purpose of loading the root package
 *
 * Sets additional defaults and loads repositories
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class RootPackageLoader extends ArrayLoader {
    
    /**
     * @var RepositoryManager
     */
    private $manager;
    
    /**
     * @var Config
     */
    private $config;
    
    /**
     * @var VersionGuesser
     */
    private $versionGuesser;
    
    /**
     * @var IOInterface|null
     */
    private $io;
    public function __construct(RepositoryManager $manager, Config $config, ?VersionParser $parser = null, ?VersionGuesser $versionGuesser = null, ?IOInterface $io = null) {
        parent::__construct($parser);
        $this->manager = $manager;
        $this->config = $config;
        $this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor($io), $this->versionParser, $io);
        $this->io = $io;
    }
    
    /**
     * @inheritDoc
     *
     * @return RootPackage|RootAliasPackage
     *
     * @phpstan-param class-string<RootPackage> $class
     */
    public function load(array $config, string $class = 'Composer\\Package\\RootPackage', ?string $cwd = null) : BasePackage {
        if ($class !== 'Composer\\Package\\RootPackage') {
            trigger_error('The $class arg is deprecated, please reach out to Composer maintainers ASAP if you still need this.', E_USER_DEPRECATED);
        }
        if (!isset($config['name'])) {
            $config['name'] = '__root__';
        }
        elseif ($err = ValidatingArrayLoader::hasPackageNamingError($config['name'])) {
            throw new \RuntimeException('Your package name ' . $err);
        }
        $autoVersioned = false;
        if (!isset($config['version'])) {
            $commit = null;
            // override with env var if available
            if (Platform::getEnv('COMPOSER_ROOT_VERSION')) {
                $config['version'] = $this->versionGuesser
                    ->getRootVersionFromEnv();
            }
            else {
                $versionData = $this->versionGuesser
                    ->guessVersion($config, $cwd ?? Platform::getCwd(true));
                if ($versionData) {
                    $config['version'] = $versionData['pretty_version'];
                    $config['version_normalized'] = $versionData['version'];
                    $commit = $versionData['commit'];
                }
            }
            if (!isset($config['version'])) {
                if ($this->io !== null && $config['name'] !== '__root__' && 'project' !== ($config['type'] ?? '')) {
                    $this->io
                        ->warning(sprintf("Composer could not detect the root package (%s) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version", $config['name']));
                }
                $config['version'] = '1.0.0';
                $autoVersioned = true;
            }
            if ($commit) {
                $config['source'] = [
                    'type' => '',
                    'url' => '',
                    'reference' => $commit,
                ];
                $config['dist'] = [
                    'type' => '',
                    'url' => '',
                    'reference' => $commit,
                ];
            }
        }
        
        /** @var RootPackage|RootAliasPackage $package */
        $package = parent::load($config, $class);
        if ($package instanceof RootAliasPackage) {
            $realPackage = $package->getAliasOf();
        }
        else {
            $realPackage = $package;
        }
        if (!$realPackage instanceof RootPackage) {
            throw new \LogicException('Expecting a Composer\\Package\\RootPackage at this point');
        }
        if ($autoVersioned) {
            $realPackage->replaceVersion($realPackage->getVersion(), RootPackage::DEFAULT_PRETTY_VERSION);
        }
        if (isset($config['minimum-stability'])) {
            $realPackage->setMinimumStability(VersionParser::normalizeStability($config['minimum-stability']));
        }
        $aliases = [];
        $stabilityFlags = [];
        $references = [];
        foreach ([
            'require',
            'require-dev',
        ] as $linkType) {
            if (isset($config[$linkType])) {
                $linkInfo = BasePackage::$supportedLinkTypes[$linkType];
                $method = 'get' . ucfirst($linkInfo['method']);
                $links = [];
                foreach ($realPackage->{$method}() as $link) {
                    $links[$link->getTarget()] = $link->getConstraint()
                        ->getPrettyString();
                }
                $aliases = $this->extractAliases($links, $aliases);
                $stabilityFlags = self::extractStabilityFlags($links, $realPackage->getMinimumStability(), $stabilityFlags);
                $references = self::extractReferences($links, $references);
                if (isset($links[$config['name']])) {
                    throw new \RuntimeException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL . 'Did you accidentally name your root package after an external package?', $config['name']));
                }
            }
        }
        foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) {
            if (isset($config[$linkType])) {
                foreach ($config[$linkType] as $linkName => $constraint) {
                    if ($err = ValidatingArrayLoader::hasPackageNamingError($linkName, true)) {
                        throw new \RuntimeException($linkType . '.' . $err);
                    }
                }
            }
        }
        $realPackage->setAliases($aliases);
        $realPackage->setStabilityFlags($stabilityFlags);
        $realPackage->setReferences($references);
        if (isset($config['prefer-stable'])) {
            $realPackage->setPreferStable((bool) $config['prefer-stable']);
        }
        if (isset($config['config'])) {
            $realPackage->setConfig($config['config']);
        }
        $repos = RepositoryFactory::defaultRepos(null, $this->config, $this->manager);
        foreach ($repos as $repo) {
            $this->manager
                ->addRepository($repo);
        }
        $realPackage->setRepositories($this->config
            ->getRepositories());
        return $package;
    }
    
    /**
     * @param array<string, string>                                                                  $requires
     * @param list<array{package: string, version: string, alias: string, alias_normalized: string}> $aliases
     *
     * @return list<array{package: string, version: string, alias: string, alias_normalized: string}>
     */
    private function extractAliases(array $requires, array $aliases) : array {
        foreach ($requires as $reqName => $reqVersion) {
            if (Preg::isMatchStrictGroups('{(?:^|\\| *|, *)([^,\\s#|]+)(?:#[^ ]+)? +as +([^,\\s|]+)(?:$| *\\|| *,)}', $reqVersion, $match)) {
                $aliases[] = [
                    'package' => strtolower($reqName),
                    'version' => $this->versionParser
                        ->normalize($match[1], $reqVersion),
                    'alias' => $match[2],
                    'alias_normalized' => $this->versionParser
                        ->normalize($match[2], $reqVersion),
                ];
            }
            elseif (strpos($reqVersion, ' as ') !== false) {
                throw new \UnexpectedValueException('Invalid alias definition in "' . $reqName . '": "' . $reqVersion . '". Aliases should be in the form "exact-version as other-exact-version".');
            }
        }
        return $aliases;
    }
    
    /**
     * @internal
     *
     * @param array<string, string> $requires
     * @param array<string, int>    $stabilityFlags
     * @param key-of<BasePackage::STABILITIES> $minimumStability
     *
     * @return array<string, int>
     *
     * @phpstan-param array<string, BasePackage::STABILITY_*> $stabilityFlags
     * @phpstan-return array<string, BasePackage::STABILITY_*>
     */
    public static function extractStabilityFlags(array $requires, string $minimumStability, array $stabilityFlags) : array {
        $stabilities = BasePackage::STABILITIES;
        $minimumStability = $stabilities[$minimumStability];
        foreach ($requires as $reqName => $reqVersion) {
            $constraints = [];
            // extract all sub-constraints in case it is an OR/AND multi-constraint
            $orSplit = Preg::split('{\\s*\\|\\|?\\s*}', trim($reqVersion));
            foreach ($orSplit as $orConstraint) {
                $andSplit = Preg::split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $orConstraint);
                foreach ($andSplit as $andConstraint) {
                    $constraints[] = $andConstraint;
                }
            }
            // parse explicit stability flags to the most unstable
            $matched = false;
            foreach ($constraints as $constraint) {
                if (Preg::isMatchStrictGroups('{^[^@]*?@(' . implode('|', array_keys($stabilities)) . ')$}i', $constraint, $match)) {
                    $name = strtolower($reqName);
                    $stability = $stabilities[VersionParser::normalizeStability($match[1])];
                    if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) {
                        continue;
                    }
                    $stabilityFlags[$name] = $stability;
                    $matched = true;
                }
            }
            if ($matched) {
                continue;
            }
            foreach ($constraints as $constraint) {
                // infer flags for requirements that have an explicit -dev or -beta version specified but only
                // for those that are more unstable than the minimumStability or existing flags
                $reqVersion = Preg::replace('{^([^,\\s@]+) as .+$}', '$1', $constraint);
                if (Preg::isMatch('{^[^,\\s@]+$}', $reqVersion) && 'stable' !== ($stabilityName = VersionParser::parseStability($reqVersion))) {
                    $name = strtolower($reqName);
                    $stability = $stabilities[$stabilityName];
                    if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability || $minimumStability > $stability) {
                        continue;
                    }
                    $stabilityFlags[$name] = $stability;
                }
            }
        }
        return $stabilityFlags;
    }
    
    /**
     * @internal
     *
     * @param array<string, string> $requires
     * @param array<string, string> $references
     *
     * @return array<string, string>
     */
    public static function extractReferences(array $requires, array $references) : array {
        foreach ($requires as $reqName => $reqVersion) {
            $reqVersion = Preg::replace('{^([^,\\s@]+) as .+$}', '$1', $reqVersion);
            if (Preg::isMatchStrictGroups('{^[^,\\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === VersionParser::parseStability($reqVersion)) {
                $name = strtolower($reqName);
                $references[$name] = $match[1];
            }
        }
        return $references;
    }

}

Classes

Title Deprecated Summary
RootPackageLoader ArrayLoader built for the sole purpose of loading the root package
RSS feed
Powered by Drupal