first commit
This commit is contained in:
750
vendor/wikimedia/composer-merge-plugin/src/ExtraPackage.php
vendored
Normal file
750
vendor/wikimedia/composer-merge-plugin/src/ExtraPackage.php
vendored
Normal file
@ -0,0 +1,750 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\CompletePackage;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\Loader\ArrayLoader;
|
||||
use Composer\Package\RootAliasPackage;
|
||||
use Composer\Package\RootPackage;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Semver\Intervals;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Processing for a composer.json file that will be merged into
|
||||
* a RootPackageInterface
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class ExtraPackage
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var Logger $logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var string $path
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array $json
|
||||
*/
|
||||
protected $json;
|
||||
|
||||
/**
|
||||
* @var CompletePackage $package
|
||||
*/
|
||||
protected $package;
|
||||
|
||||
/**
|
||||
* @var array<string, bool> $mergedRequirements
|
||||
*/
|
||||
protected $mergedRequirements = [];
|
||||
|
||||
/**
|
||||
* @var VersionParser $versionParser
|
||||
*/
|
||||
protected $versionParser;
|
||||
|
||||
/**
|
||||
* @param string $path Path to composer.json file
|
||||
* @param Composer $composer
|
||||
* @param Logger $logger
|
||||
*/
|
||||
public function __construct($path, Composer $composer, Logger $logger)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->composer = $composer;
|
||||
$this->logger = $logger;
|
||||
$this->json = $this->readPackageJson($path);
|
||||
$this->package = $this->loadPackage($this->json);
|
||||
$this->versionParser = new VersionParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of additional packages to include if precessing recursively.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIncludes()
|
||||
{
|
||||
return isset($this->json['extra']['merge-plugin']['include']) ?
|
||||
$this->fixRelativePaths($this->json['extra']['merge-plugin']['include']) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of additional packages to require if precessing recursively.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequires()
|
||||
{
|
||||
return isset($this->json['extra']['merge-plugin']['require']) ?
|
||||
$this->fixRelativePaths($this->json['extra']['merge-plugin']['require']) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of merged requirements from this package.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMergedRequirements()
|
||||
{
|
||||
return array_keys($this->mergedRequirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the contents of a composer.json style file into an array.
|
||||
*
|
||||
* The package contents are fixed up to be usable to create a Package
|
||||
* object by providing dummy "name" and "version" values if they have not
|
||||
* been provided in the file. This is consistent with the default root
|
||||
* package loading behavior of Composer.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function readPackageJson($path)
|
||||
{
|
||||
$file = new JsonFile($path);
|
||||
$json = $file->read();
|
||||
if (!isset($json['name'])) {
|
||||
$json['name'] = 'merge-plugin/' .
|
||||
strtr($path, DIRECTORY_SEPARATOR, '-');
|
||||
}
|
||||
if (!isset($json['version'])) {
|
||||
$json['version'] = '1.0.0';
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $json
|
||||
* @return CompletePackage
|
||||
*/
|
||||
protected function loadPackage(array $json)
|
||||
{
|
||||
$loader = new ArrayLoader();
|
||||
$package = $loader->load($json);
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$package instanceof CompletePackage) {
|
||||
throw new UnexpectedValueException(
|
||||
'Expected instance of CompletePackage, got ' .
|
||||
get_class($package)
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this package into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeInto(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$this->prependRepositories($root);
|
||||
|
||||
$this->mergeRequires('require', $root, $state);
|
||||
|
||||
$this->mergePackageLinks('conflict', $root);
|
||||
|
||||
if ($state->shouldMergeReplace()) {
|
||||
$this->mergePackageLinks('replace', $root);
|
||||
}
|
||||
|
||||
$this->mergePackageLinks('provide', $root);
|
||||
|
||||
$this->mergeSuggests($root);
|
||||
|
||||
$this->mergeAutoload('autoload', $root);
|
||||
|
||||
$this->mergeExtra($root, $state);
|
||||
|
||||
$this->mergeScripts($root, $state);
|
||||
|
||||
if ($state->isDevMode()) {
|
||||
$this->mergeDevInto($root, $state);
|
||||
} else {
|
||||
$this->mergeReferences($root);
|
||||
$this->mergeAliases($root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge just the dev portion into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeDevInto(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$this->mergeRequires('require-dev', $root, $state);
|
||||
$this->mergeAutoload('devAutoload', $root);
|
||||
$this->mergeReferences($root);
|
||||
$this->mergeAliases($root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a collection of repositories described by the given configuration
|
||||
* to the given package and the global repository manager.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function prependRepositories(RootPackageInterface $root)
|
||||
{
|
||||
if (!isset($this->json['repositories'])) {
|
||||
return;
|
||||
}
|
||||
$repoManager = $this->composer->getRepositoryManager();
|
||||
$newRepos = [];
|
||||
|
||||
foreach ($this->json['repositories'] as $repoJson) {
|
||||
if (!isset($repoJson['type'])) {
|
||||
continue;
|
||||
}
|
||||
if ($repoJson['type'] === 'path' && isset($repoJson['url'])) {
|
||||
$repoJson['url'] = $this->fixRelativePaths(array($repoJson['url']))[0];
|
||||
}
|
||||
$this->logger->info("Prepending {$repoJson['type']} repository");
|
||||
$repo = $repoManager->createRepository(
|
||||
$repoJson['type'],
|
||||
$repoJson
|
||||
);
|
||||
$repoManager->prependRepository($repo);
|
||||
$newRepos[] = $repo;
|
||||
}
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setRepositories');
|
||||
$unwrapped->setRepositories(array_merge(
|
||||
$newRepos,
|
||||
$root->getRepositories()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge require or require-dev into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'require' or 'require-dev'
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
protected function mergeRequires(
|
||||
$type,
|
||||
RootPackageInterface $root,
|
||||
PluginState $state
|
||||
) {
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$getter = 'get' . ucfirst($linkType['method']);
|
||||
$setter = 'set' . ucfirst($linkType['method']);
|
||||
|
||||
$requires = $this->package->{$getter}();
|
||||
if (empty($requires)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->mergeStabilityFlags($root, $requires);
|
||||
|
||||
$requires = $this->replaceSelfVersionDependencies(
|
||||
$type,
|
||||
$requires,
|
||||
$root
|
||||
);
|
||||
|
||||
$root->{$setter}($this->mergeOrDefer(
|
||||
$type,
|
||||
$root->{$getter}(),
|
||||
$requires,
|
||||
$state
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two collections of package links and collect duplicates for
|
||||
* subsequent processing.
|
||||
*
|
||||
* @param string $type 'require' or 'require-dev'
|
||||
* @param array $origin Primary collection
|
||||
* @param array $merge Additional collection
|
||||
* @param PluginState $state
|
||||
* @return array Merged collection
|
||||
*/
|
||||
protected function mergeOrDefer(
|
||||
$type,
|
||||
array $origin,
|
||||
array $merge,
|
||||
PluginState $state
|
||||
) {
|
||||
if ($state->ignoreDuplicateLinks() && $state->replaceDuplicateLinks()) {
|
||||
$this->logger->warning("Both replace and ignore-duplicates are true. These are mutually exclusive.");
|
||||
$this->logger->warning("Duplicate packages will be ignored.");
|
||||
}
|
||||
|
||||
foreach ($merge as $name => $link) {
|
||||
if (isset($origin[$name])) {
|
||||
if ($state->ignoreDuplicateLinks()) {
|
||||
$this->logger->info("Ignoring duplicate <comment>{$name}</comment>");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$this->logger->info("Replacing <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $link;
|
||||
} else {
|
||||
$this->logger->info("Merging <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $this->mergeConstraints($origin[$name], $link, $state);
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Adding <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$state->isComposer1()) {
|
||||
Intervals::clear();
|
||||
}
|
||||
|
||||
return $origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge package constraints.
|
||||
*
|
||||
* Adapted from Composer's UpdateCommand::appendConstraintToLink
|
||||
*
|
||||
* @param Link $origin The base package link.
|
||||
* @param Link $merge The related package link to merge.
|
||||
* @param PluginState $state
|
||||
* @return Link Merged link.
|
||||
*/
|
||||
protected function mergeConstraints(Link $origin, Link $merge, PluginState $state)
|
||||
{
|
||||
$oldPrettyString = $origin->getConstraint()->getPrettyString();
|
||||
$newPrettyString = $merge->getConstraint()->getPrettyString();
|
||||
|
||||
if ($state->isComposer1()) {
|
||||
$constraintClass = MultiConstraint::class;
|
||||
} else {
|
||||
$constraintClass = \Composer\Semver\Constraint\MultiConstraint::class;
|
||||
|
||||
if (Intervals::isSubsetOf($origin->getConstraint(), $merge->getConstraint())) {
|
||||
return $origin;
|
||||
}
|
||||
|
||||
if (Intervals::isSubsetOf($merge->getConstraint(), $origin->getConstraint())) {
|
||||
return $merge;
|
||||
}
|
||||
}
|
||||
|
||||
$newConstraint = $constraintClass::create([
|
||||
$origin->getConstraint(),
|
||||
$merge->getConstraint()
|
||||
], true);
|
||||
$newConstraint->setPrettyString($oldPrettyString.', '.$newPrettyString);
|
||||
|
||||
return new Link(
|
||||
$origin->getSource(),
|
||||
$origin->getTarget(),
|
||||
$newConstraint,
|
||||
$origin->getDescription(),
|
||||
$origin->getPrettyConstraint() . ', ' . $newPrettyString
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge autoload or autoload-dev into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'autoload' or 'devAutoload'
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeAutoload($type, RootPackageInterface $root)
|
||||
{
|
||||
$getter = 'get' . ucfirst($type);
|
||||
$setter = 'set' . ucfirst($type);
|
||||
|
||||
$autoload = $this->package->{$getter}();
|
||||
if (empty($autoload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, $setter);
|
||||
$unwrapped->{$setter}(array_merge_recursive(
|
||||
$root->{$getter}(),
|
||||
$this->fixRelativePaths($autoload)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix a collection of paths that are relative to this package to be
|
||||
* relative to the base package.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return array
|
||||
*/
|
||||
protected function fixRelativePaths(array $paths)
|
||||
{
|
||||
$base = dirname($this->path);
|
||||
$base = ($base === '.') ? '' : "{$base}/";
|
||||
|
||||
array_walk_recursive(
|
||||
$paths,
|
||||
function (&$path) use ($base) {
|
||||
$path = "{$base}{$path}";
|
||||
}
|
||||
);
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and merge stability flags from the given collection of
|
||||
* requires and merge them into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param array $requires
|
||||
*/
|
||||
protected function mergeStabilityFlags(
|
||||
RootPackageInterface $root,
|
||||
array $requires
|
||||
) {
|
||||
$flags = $root->getStabilityFlags();
|
||||
$sf = new StabilityFlags($flags, $root->getMinimumStability());
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setStabilityFlags');
|
||||
$unwrapped->setStabilityFlags(array_merge(
|
||||
$flags,
|
||||
$sf->extractAll($requires)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge package links of the given type into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'conflict', 'replace' or 'provide'
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergePackageLinks($type, RootPackageInterface $root)
|
||||
{
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$getter = 'get' . ucfirst($linkType['method']);
|
||||
$setter = 'set' . ucfirst($linkType['method']);
|
||||
|
||||
$links = $this->package->{$getter}();
|
||||
if (!empty($links)) {
|
||||
$unwrapped = self::unwrapIfNeeded($root, $setter);
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($root !== $unwrapped) {
|
||||
$this->logger->warning(
|
||||
'This Composer version does not support ' .
|
||||
"'{$type}' merging for aliased packages."
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
$unwrapped->{$setter}(array_merge(
|
||||
$root->{$getter}(),
|
||||
$this->replaceSelfVersionDependencies($type, $links, $root)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge suggested packages into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeSuggests(RootPackageInterface $root)
|
||||
{
|
||||
$suggests = $this->package->getSuggests();
|
||||
if (!empty($suggests)) {
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setSuggests');
|
||||
$unwrapped->setSuggests(array_merge(
|
||||
$root->getSuggests(),
|
||||
$suggests
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge extra config into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeExtra(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$extra = $this->package->getExtra();
|
||||
unset($extra['merge-plugin']);
|
||||
if (!$state->shouldMergeExtra() || empty($extra)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootExtra = $root->getExtra();
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setExtra');
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$unwrapped->setExtra(
|
||||
self::mergeExtraArray($state->shouldMergeExtraDeep(), $rootExtra, $extra)
|
||||
);
|
||||
} else {
|
||||
if (!$state->shouldMergeExtraDeep()) {
|
||||
foreach (array_intersect(
|
||||
array_keys($extra),
|
||||
array_keys($rootExtra)
|
||||
) as $key) {
|
||||
$this->logger->info(
|
||||
"Ignoring duplicate <comment>{$key}</comment> in ".
|
||||
"<comment>{$this->path}</comment> extra config."
|
||||
);
|
||||
}
|
||||
}
|
||||
$unwrapped->setExtra(
|
||||
self::mergeExtraArray($state->shouldMergeExtraDeep(), $extra, $rootExtra)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge scripts config into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeScripts(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$scripts = $this->package->getScripts();
|
||||
if (!$state->shouldMergeScripts() || empty($scripts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScripts = $root->getScripts();
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setScripts');
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$unwrapped->setScripts(
|
||||
array_merge($rootScripts, $scripts)
|
||||
);
|
||||
} else {
|
||||
$unwrapped->setScripts(
|
||||
array_merge($scripts, $rootScripts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two arrays either via arrayMergeDeep or via array_merge.
|
||||
*
|
||||
* @param bool $mergeDeep
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return array
|
||||
*/
|
||||
public static function mergeExtraArray($mergeDeep, $array1, $array2)
|
||||
{
|
||||
if ($mergeDeep) {
|
||||
return NestedArray::mergeDeep($array1, $array2);
|
||||
}
|
||||
|
||||
return array_merge($array1, $array2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Links with a 'self.version' constraint with the root package's
|
||||
* version.
|
||||
*
|
||||
* @param string $type Link type
|
||||
* @param array $links
|
||||
* @param RootPackageInterface $root
|
||||
* @return array
|
||||
*/
|
||||
protected function replaceSelfVersionDependencies(
|
||||
$type,
|
||||
array $links,
|
||||
RootPackageInterface $root
|
||||
) {
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$version = $root->getVersion();
|
||||
$prettyVersion = $root->getPrettyVersion();
|
||||
$vp = $this->versionParser;
|
||||
|
||||
$method = 'get' . ucfirst($linkType['method']);
|
||||
$packages = $root->$method();
|
||||
|
||||
return array_map(
|
||||
static function ($link) use ($linkType, $version, $prettyVersion, $vp, $packages) {
|
||||
if ('self.version' === $link->getPrettyConstraint()) {
|
||||
if (isset($packages[$link->getSource()])) {
|
||||
/** @var Link $package */
|
||||
$package = $packages[$link->getSource()];
|
||||
return new Link(
|
||||
$link->getSource(),
|
||||
$link->getTarget(),
|
||||
$vp->parseConstraints($package->getConstraint()->getPrettyString()),
|
||||
$linkType['description'],
|
||||
$package->getPrettyConstraint()
|
||||
);
|
||||
}
|
||||
|
||||
return new Link(
|
||||
$link->getSource(),
|
||||
$link->getTarget(),
|
||||
$vp->parseConstraints($version),
|
||||
$linkType['description'],
|
||||
$prettyVersion
|
||||
);
|
||||
}
|
||||
return $link;
|
||||
},
|
||||
$links
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full featured Package from a RootPackageInterface.
|
||||
*
|
||||
* In Composer versions before 599ad77 the RootPackageInterface only
|
||||
* defines a sub-set of operations needed by composer-merge-plugin and
|
||||
* RootAliasPackage only implemented those methods defined by the
|
||||
* interface. Most of the unimplemented methods in RootAliasPackage can be
|
||||
* worked around because the getter methods that are implemented proxy to
|
||||
* the aliased package which we can modify by unwrapping. The exception
|
||||
* being modifying the 'conflicts', 'provides' and 'replaces' collections.
|
||||
* We have no way to actually modify those collections unfortunately in
|
||||
* older versions of Composer.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param string $method Method needed
|
||||
* @return RootPackageInterface|RootPackage
|
||||
*/
|
||||
public static function unwrapIfNeeded(
|
||||
RootPackageInterface $root,
|
||||
$method = 'setExtra'
|
||||
) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($root instanceof RootAliasPackage &&
|
||||
!method_exists($root, $method)
|
||||
) {
|
||||
// Unwrap and return the aliased RootPackage.
|
||||
$root = $root->getAliasOf();
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $root;
|
||||
}
|
||||
|
||||
protected function mergeAliases(RootPackageInterface $root)
|
||||
{
|
||||
$aliases = [];
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setAliases');
|
||||
foreach (array('require', 'require-dev') as $linkType) {
|
||||
$linkInfo = BasePackage::$supportedLinkTypes[$linkType];
|
||||
$method = 'get'.ucfirst($linkInfo['method']);
|
||||
$links = [];
|
||||
foreach ($unwrapped->$method() as $link) {
|
||||
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
||||
}
|
||||
$aliases = $this->extractAliases($links, $aliases);
|
||||
}
|
||||
$unwrapped->setAliases($aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract aliases from version constraints (dev-branch as 1.0.0).
|
||||
*
|
||||
* @param array $requires
|
||||
* @param array $aliases
|
||||
* @return array
|
||||
* @see RootPackageLoader::extractAliases()
|
||||
*/
|
||||
protected function extractAliases(array $requires, array $aliases)
|
||||
{
|
||||
foreach ($requires as $reqName => $reqVersion) {
|
||||
if (preg_match('{^([^,\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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the root packages reference information.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeReferences(RootPackageInterface $root)
|
||||
{
|
||||
// Merge source reference information for merged packages.
|
||||
// @see RootPackageLoader::load
|
||||
$references = [];
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setReferences');
|
||||
foreach (['require', 'require-dev'] as $linkType) {
|
||||
$linkInfo = BasePackage::$supportedLinkTypes[$linkType];
|
||||
$method = 'get'.ucfirst($linkInfo['method']);
|
||||
$links = [];
|
||||
foreach ($unwrapped->$method() as $link) {
|
||||
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
||||
}
|
||||
$references = $this->extractReferences($links, $references);
|
||||
}
|
||||
$unwrapped->setReferences($references);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract vcs revision from version constraint (dev-master#abc123.
|
||||
*
|
||||
* @param array $requires
|
||||
* @param array $references
|
||||
* @return array
|
||||
* @see RootPackageLoader::extractReferences()
|
||||
*/
|
||||
protected function extractReferences(array $requires, array $references)
|
||||
{
|
||||
foreach ($requires as $reqName => $reqVersion) {
|
||||
$reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
|
||||
$stabilityName = VersionParser::parseStability($reqVersion);
|
||||
if ($stabilityName === 'dev' &&
|
||||
preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match)
|
||||
) {
|
||||
$name = strtolower($reqName);
|
||||
$references[$name] = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $references;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
102
vendor/wikimedia/composer-merge-plugin/src/Logger.php
vendored
Normal file
102
vendor/wikimedia/composer-merge-plugin/src/Logger.php
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* Simple logging wrapper for Composer\IO\IOInterface
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
/**
|
||||
* @var string $name
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var IOInterface $inputOutput
|
||||
*/
|
||||
protected $inputOutput;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param IOInterface $io
|
||||
*/
|
||||
public function __construct($name, IOInterface $io)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->inputOutput = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a debug message
|
||||
*
|
||||
* Messages will be output at the "very verbose" logging level (eg `-vv`
|
||||
* needed on the Composer command).
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function debug($message)
|
||||
{
|
||||
if ($this->inputOutput->isVeryVerbose()) {
|
||||
$message = " <info>[{$this->name}]</info> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an informative message
|
||||
*
|
||||
* Messages will be output at the "verbose" logging level (eg `-v` needed
|
||||
* on the Composer command).
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function info($message)
|
||||
{
|
||||
if ($this->inputOutput->isVerbose()) {
|
||||
$message = " <info>[{$this->name}]</info> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning message
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function warning($message)
|
||||
{
|
||||
$message = " <error>[{$this->name}]</error> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function log($message)
|
||||
{
|
||||
if (method_exists($this->inputOutput, 'writeError')) {
|
||||
$this->inputOutput->writeError($message);
|
||||
} else {
|
||||
// @codeCoverageIgnoreStart
|
||||
// Backwards compatibility for Composer before cb336a5
|
||||
$this->inputOutput->write($message);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
396
vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
vendored
Normal file
396
vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
vendored
Normal file
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\EventDispatcher\Event as BaseEvent;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\Factory;
|
||||
use Composer\Installer;
|
||||
use Composer\Installer\PackageEvent;
|
||||
use Composer\Installer\PackageEvents;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
use Composer\Script\Event as ScriptEvent;
|
||||
use Composer\Script\ScriptEvents;
|
||||
|
||||
/**
|
||||
* Composer plugin that allows merging multiple composer.json files.
|
||||
*
|
||||
* When installed, this plugin will look for a "merge-plugin" key in the
|
||||
* composer configuration's "extra" section. The value for this key is
|
||||
* a set of options configuring the plugin.
|
||||
*
|
||||
* An "include" setting is required. The value of this setting can be either
|
||||
* a single value or an array of values. Each value is treated as a glob()
|
||||
* pattern identifying additional composer.json style configuration files to
|
||||
* merge into the configuration for the current compser execution.
|
||||
*
|
||||
* The "autoload", "autoload-dev", "conflict", "provide", "replace",
|
||||
* "repositories", "require", "require-dev", and "suggest" sections of the
|
||||
* found configuration files will be merged into the root package
|
||||
* configuration as though they were directly included in the top-level
|
||||
* composer.json file.
|
||||
*
|
||||
* If included files specify conflicting package versions for "require" or
|
||||
* "require-dev", the normal Composer dependency solver process will be used
|
||||
* to attempt to resolve the conflict. Specifying the 'replace' key as true will
|
||||
* change this default behaviour so that the last-defined version of a package
|
||||
* will win, allowing for force-overrides of package defines.
|
||||
*
|
||||
* By default the "extra" section is not merged. This can be enabled by
|
||||
* setitng the 'merge-extra' key to true. In normal mode, when the same key is
|
||||
* found in both the original and the imported extra section, the version in
|
||||
* the original config is used and the imported version is skipped. If
|
||||
* 'replace' mode is active, this behaviour changes so the imported version of
|
||||
* the key is used, replacing the version in the original config.
|
||||
*
|
||||
*
|
||||
* @code
|
||||
* {
|
||||
* "require": {
|
||||
* "wikimedia/composer-merge-plugin": "dev-master"
|
||||
* },
|
||||
* "extra": {
|
||||
* "merge-plugin": {
|
||||
* "include": [
|
||||
* "composer.local.json"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class MergePlugin implements PluginInterface, EventSubscriberInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Official package name
|
||||
*/
|
||||
public const PACKAGE_NAME = 'wikimedia/composer-merge-plugin';
|
||||
|
||||
/**
|
||||
* Priority that plugin uses to register callbacks.
|
||||
*/
|
||||
private const CALLBACK_PRIORITY = 50000;
|
||||
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var PluginState $state
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @var Logger $logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Files that have already been fully processed
|
||||
*
|
||||
* @var array<string, bool> $loaded
|
||||
*/
|
||||
protected $loaded = [];
|
||||
|
||||
/**
|
||||
* Files that have already been partially processed
|
||||
*
|
||||
* @var array<string, bool> $loadedNoDev
|
||||
*/
|
||||
protected $loadedNoDev = [];
|
||||
|
||||
/**
|
||||
* Nested packages to restrict update operations.
|
||||
*
|
||||
* @var array<string, bool> $updateAllowList
|
||||
*/
|
||||
protected $updateAllowList = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->state = new PluginState($this->composer);
|
||||
$this->logger = new Logger('merge-plugin', $io);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
PluginEvents::INIT =>
|
||||
['onInit', self::CALLBACK_PRIORITY],
|
||||
PackageEvents::POST_PACKAGE_INSTALL =>
|
||||
['onPostPackageInstall', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::POST_INSTALL_CMD =>
|
||||
['onPostInstallOrUpdate', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::POST_UPDATE_CMD =>
|
||||
['onPostInstallOrUpdate', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_AUTOLOAD_DUMP =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_INSTALL_CMD =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_UPDATE_CMD =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of packages to restrict update operations.
|
||||
*
|
||||
* @return string[]
|
||||
* @see \Composer\Installer::setUpdateAllowList()
|
||||
*/
|
||||
public function getUpdateAllowList()
|
||||
{
|
||||
return array_keys($this->updateAllowList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback for initialization.
|
||||
*
|
||||
* @param BaseEvent $event
|
||||
*/
|
||||
public function onInit(BaseEvent $event)
|
||||
{
|
||||
$this->state->loadSettings();
|
||||
// It is not possible to know if the user specified --dev or --no-dev
|
||||
// so assume it is false. The dev section will be merged later when
|
||||
// the other events fire.
|
||||
$this->state->setDevMode(false);
|
||||
$this->mergeFiles($this->state->getIncludes(), false);
|
||||
$this->mergeFiles($this->state->getRequires(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback for an install, update or dump command by
|
||||
* checking for "merge-plugin" in the "extra" data and merging package
|
||||
* contents if found.
|
||||
*
|
||||
* @param ScriptEvent $event
|
||||
*/
|
||||
public function onInstallUpdateOrDump(ScriptEvent $event)
|
||||
{
|
||||
$this->state->loadSettings();
|
||||
$this->state->setDevMode($event->isDevMode());
|
||||
$this->mergeFiles($this->state->getIncludes(), false);
|
||||
$this->mergeFiles($this->state->getRequires(), true);
|
||||
|
||||
if ($event->getName() === ScriptEvents::PRE_AUTOLOAD_DUMP) {
|
||||
$this->state->setDumpAutoloader(true);
|
||||
$flags = $event->getFlags();
|
||||
if (isset($flags['optimize'])) {
|
||||
$this->state->setOptimizeAutoloader($flags['optimize']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find configuration files matching the configured glob patterns and
|
||||
* merge their contents with the master package.
|
||||
*
|
||||
* @param array $patterns List of files/glob patterns
|
||||
* @param bool $required Are the patterns required to match files?
|
||||
* @throws MissingFileException when required and a pattern returns no
|
||||
* results
|
||||
*/
|
||||
protected function mergeFiles(array $patterns, $required = false)
|
||||
{
|
||||
$root = $this->composer->getPackage();
|
||||
|
||||
$files = array_map(
|
||||
static function ($files, $pattern) use ($required) {
|
||||
if ($required && !$files) {
|
||||
throw new MissingFileException(
|
||||
"merge-plugin: No files matched required '{$pattern}'"
|
||||
);
|
||||
}
|
||||
return $files;
|
||||
},
|
||||
array_map('glob', $patterns),
|
||||
$patterns
|
||||
);
|
||||
|
||||
foreach (array_reduce($files, 'array_merge', []) as $path) {
|
||||
$this->mergeFile($root, $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a JSON file and merge its contents
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param string $path
|
||||
*/
|
||||
protected function mergeFile(RootPackageInterface $root, $path)
|
||||
{
|
||||
if (isset($this->loaded[$path]) ||
|
||||
(isset($this->loadedNoDev[$path]) && !$this->state->isDevMode())
|
||||
) {
|
||||
$this->logger->debug(
|
||||
"Already merged <comment>$path</comment> completely"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$package = new ExtraPackage($path, $this->composer, $this->logger);
|
||||
|
||||
if (isset($this->loadedNoDev[$path])) {
|
||||
$this->logger->info(
|
||||
"Loading -dev sections of <comment>{$path}</comment>..."
|
||||
);
|
||||
$package->mergeDevInto($root, $this->state);
|
||||
} else {
|
||||
$this->logger->info("Loading <comment>{$path}</comment>...");
|
||||
$package->mergeInto($root, $this->state);
|
||||
}
|
||||
|
||||
$requirements = $package->getMergedRequirements();
|
||||
if (!empty($requirements)) {
|
||||
$this->updateAllowList = array_replace(
|
||||
$this->updateAllowList,
|
||||
array_fill_keys($requirements, true)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->state->isDevMode()) {
|
||||
$this->loaded[$path] = true;
|
||||
} else {
|
||||
$this->loadedNoDev[$path] = true;
|
||||
}
|
||||
|
||||
if ($this->state->recurseIncludes()) {
|
||||
$this->mergeFiles($package->getIncludes(), false);
|
||||
$this->mergeFiles($package->getRequires(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback following installation of a new package by
|
||||
* checking to see if the package that was installed was our plugin.
|
||||
*
|
||||
* @param PackageEvent $event
|
||||
*/
|
||||
public function onPostPackageInstall(PackageEvent $event)
|
||||
{
|
||||
$op = $event->getOperation();
|
||||
if ($op instanceof InstallOperation) {
|
||||
$package = $op->getPackage()->getName();
|
||||
if ($package === self::PACKAGE_NAME) {
|
||||
$this->logger->info('composer-merge-plugin installed');
|
||||
$this->state->setFirstInstall(true);
|
||||
$this->state->setLocked(
|
||||
$event->getComposer()->getLocker()->isLocked()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback following an install or update command. If our
|
||||
* plugin was installed during the run then trigger an update command to
|
||||
* process any merge-patterns in the current config.
|
||||
*
|
||||
* @param ScriptEvent $event
|
||||
*/
|
||||
public function onPostInstallOrUpdate(ScriptEvent $event)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->state->isFirstInstall()) {
|
||||
$this->state->setFirstInstall(false);
|
||||
|
||||
$requirements = $this->getUpdateAllowList();
|
||||
if (empty($requirements)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->log("\n".'<info>Running composer update to apply merge settings</info>');
|
||||
|
||||
$lockBackup = null;
|
||||
$lock = null;
|
||||
if (!$this->state->isComposer1()) {
|
||||
$file = Factory::getComposerFile();
|
||||
$lock = Factory::getLockFile($file);
|
||||
if (file_exists($lock)) {
|
||||
$lockBackup = file_get_contents($lock);
|
||||
}
|
||||
}
|
||||
|
||||
$config = $this->composer->getConfig();
|
||||
$preferSource = $config->get('preferred-install') === 'source';
|
||||
$preferDist = $config->get('preferred-install') === 'dist';
|
||||
|
||||
$installer = Installer::create(
|
||||
$event->getIO(),
|
||||
// Create a new Composer instance to ensure full processing of
|
||||
// the merged files.
|
||||
Factory::create($event->getIO(), null, false)
|
||||
);
|
||||
|
||||
$installer->setPreferSource($preferSource);
|
||||
$installer->setPreferDist($preferDist);
|
||||
$installer->setDevMode($event->isDevMode());
|
||||
$installer->setDumpAutoloader($this->state->shouldDumpAutoloader());
|
||||
$installer->setOptimizeAutoloader(
|
||||
$this->state->shouldOptimizeAutoloader()
|
||||
);
|
||||
|
||||
$installer->setUpdate(true);
|
||||
|
||||
if ($this->state->isComposer1()) {
|
||||
// setUpdateWhitelist() only exists in composer 1.x. Configure as to run phan against composer 2.x
|
||||
// @phan-suppress-next-line PhanUndeclaredMethod
|
||||
$installer->setUpdateWhitelist($requirements);
|
||||
} else {
|
||||
$installer->setUpdateAllowList($requirements);
|
||||
}
|
||||
|
||||
$status = $installer->run();
|
||||
if (( $status !== 0 ) && $lockBackup && $lock && !$this->state->isComposer1()) {
|
||||
$this->logger->log(
|
||||
"\n".'<error>'.
|
||||
'Update to apply merge settings failed, reverting '.$lock.' to its original content.'.
|
||||
'</error>'
|
||||
);
|
||||
file_put_contents($lock, $lockBackup);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
20
vendor/wikimedia/composer-merge-plugin/src/MissingFileException.php
vendored
Normal file
20
vendor/wikimedia/composer-merge-plugin/src/MissingFileException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class MissingFileException extends RuntimeException
|
||||
{
|
||||
}
|
114
vendor/wikimedia/composer-merge-plugin/src/MultiConstraint.php
vendored
Normal file
114
vendor/wikimedia/composer-merge-plugin/src/MultiConstraint.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2021 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\EmptyConstraint;
|
||||
use Composer\Semver\Constraint\MultiConstraint as SemverMultiConstraint;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Adapted from Composer's v2 MultiConstraint::create for Composer v1
|
||||
* @link https://github.com/composer/semver/blob/3.2.4/src/Constraint/MultiConstraint.php
|
||||
* @author Chauncey McAskill <chauncey@mcaskill.ca>
|
||||
*/
|
||||
class MultiConstraint extends SemverMultiConstraint
|
||||
{
|
||||
/**
|
||||
* Tries to optimize the constraints as much as possible, meaning
|
||||
* reducing/collapsing congruent constraints etc.
|
||||
* Does not necessarily return a MultiConstraint instance if
|
||||
* things can be reduced to a simple constraint
|
||||
*
|
||||
* @param ConstraintInterface[] $constraints A set of constraints
|
||||
* @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
|
||||
*
|
||||
* @return ConstraintInterface
|
||||
*/
|
||||
public static function create(array $constraints, $conjunctive = true)
|
||||
{
|
||||
if (count($constraints) === 0) {
|
||||
// EmptyConstraint only exists in composer 1.x. Configure as to run phan against composer 2.x
|
||||
// @phan-suppress-next-line PhanTypeMismatchReturn, PhanUndeclaredClassMethod
|
||||
return new EmptyConstraint();
|
||||
}
|
||||
|
||||
if (count($constraints) === 1) {
|
||||
return $constraints[0];
|
||||
}
|
||||
|
||||
$optimized = self::optimizeConstraints($constraints, $conjunctive);
|
||||
if ($optimized !== null) {
|
||||
list($constraints, $conjunctive) = $optimized;
|
||||
if (count($constraints) === 1) {
|
||||
return $constraints[0];
|
||||
}
|
||||
}
|
||||
|
||||
return new self($constraints, $conjunctive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
private static function optimizeConstraints(array $constraints, $conjunctive)
|
||||
{
|
||||
// parse the two OR groups and if they are contiguous we collapse
|
||||
// them into one constraint
|
||||
// [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4]
|
||||
if (!$conjunctive) {
|
||||
$left = $constraints[0];
|
||||
$mergedConstraints = [];
|
||||
$optimized = false;
|
||||
for ($i = 1, $l = count($constraints); $i < $l; $i++) {
|
||||
$right = $constraints[$i];
|
||||
if ($left instanceof SemverMultiConstraint
|
||||
&& $left->conjunctive
|
||||
&& $right instanceof SemverMultiConstraint
|
||||
&& $right->conjunctive
|
||||
&& count($left->constraints) === 2
|
||||
&& count($right->constraints) === 2
|
||||
&& ($left0 = (string) $left->constraints[0])
|
||||
&& $left0[0] === '>' && $left0[1] === '='
|
||||
&& ($left1 = (string) $left->constraints[1])
|
||||
&& $left1[0] === '<'
|
||||
&& ($right0 = (string) $right->constraints[0])
|
||||
&& $right0[0] === '>' && $right0[1] === '='
|
||||
&& ($right1 = (string) $right->constraints[1])
|
||||
&& $right1[0] === '<'
|
||||
&& substr($left1, 2) === substr($right0, 3)
|
||||
) {
|
||||
$optimized = true;
|
||||
$left = new MultiConstraint(
|
||||
[
|
||||
$left->constraints[0],
|
||||
$right->constraints[1],
|
||||
],
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$mergedConstraints[] = $left;
|
||||
$left = $right;
|
||||
}
|
||||
}
|
||||
if ($optimized) {
|
||||
$mergedConstraints[] = $left;
|
||||
return [$mergedConstraints, false];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Here's the place to put more optimizations
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
114
vendor/wikimedia/composer-merge-plugin/src/NestedArray.php
vendored
Normal file
114
vendor/wikimedia/composer-merge-plugin/src/NestedArray.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
/**
|
||||
* Adapted from
|
||||
* http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Component/Utility/NestedArray.php
|
||||
* @ f86a4d650d5af0b82a3981e09977055fa63f6f2e
|
||||
*/
|
||||
class NestedArray
|
||||
{
|
||||
|
||||
/**
|
||||
* Merges multiple arrays, recursively, and returns the merged array.
|
||||
*
|
||||
* This function is similar to PHP's array_merge_recursive() function, but
|
||||
* it handles non-array values differently. When merging values that are
|
||||
* not both arrays, the latter value replaces the former rather than
|
||||
* merging with it.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* $link_options_1 = ['fragment' => 'x', 'attributes' => ['title' => t('X'), 'class' => ['a', 'b']]];
|
||||
* $link_options_2 = ['fragment' => 'y', 'attributes' => ['title' => t('Y'), 'class' => ['c', 'd']]];
|
||||
*
|
||||
* // This results in ['fragment' => ['x', 'y'], 'attributes' =>
|
||||
* // ['title' => [t('X'), t('Y')], 'class' => ['a', 'b',
|
||||
* // 'c', 'd']]].
|
||||
* $incorrect = array_merge_recursive($link_options_1, $link_options_2);
|
||||
*
|
||||
* // This results in ['fragment' => 'y', 'attributes' =>
|
||||
* // ['title' => t('Y'), 'class' => ['a', 'b', 'c', 'd']]].
|
||||
* $correct = NestedArray::mergeDeep($link_options_1, $link_options_2);
|
||||
* @endcode
|
||||
*
|
||||
* @param mixed ...$params Arrays to merge.
|
||||
*
|
||||
* @return array The merged array.
|
||||
*
|
||||
* @see NestedArray::mergeDeepArray()
|
||||
*/
|
||||
public static function mergeDeep(...$params)
|
||||
{
|
||||
return self::mergeDeepArray($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges multiple arrays, recursively, and returns the merged array.
|
||||
*
|
||||
* This function is equivalent to NestedArray::mergeDeep(), except the
|
||||
* input arrays are passed as a single array parameter rather than
|
||||
* a variable parameter list.
|
||||
*
|
||||
* The following are equivalent:
|
||||
* - NestedArray::mergeDeep($a, $b);
|
||||
* - NestedArray::mergeDeepArray([$a, $b]);
|
||||
*
|
||||
* The following are also equivalent:
|
||||
* - call_user_func_array('NestedArray::mergeDeep', $arrays_to_merge);
|
||||
* - NestedArray::mergeDeepArray($arrays_to_merge);
|
||||
*
|
||||
* @param array $arrays
|
||||
* An arrays of arrays to merge.
|
||||
* @param bool $preserveIntegerKeys
|
||||
* (optional) If given, integer keys will be preserved and merged
|
||||
* instead of appended. Defaults to false.
|
||||
*
|
||||
* @return array
|
||||
* The merged array.
|
||||
*
|
||||
* @see NestedArray::mergeDeep()
|
||||
*/
|
||||
public static function mergeDeepArray(
|
||||
array $arrays,
|
||||
$preserveIntegerKeys = false
|
||||
) {
|
||||
$result = [];
|
||||
foreach ($arrays as $array) {
|
||||
foreach ($array as $key => $value) {
|
||||
// Renumber integer keys as array_merge_recursive() does
|
||||
// unless $preserveIntegerKeys is set to TRUE. Note that PHP
|
||||
// automatically converts array keys that are integer strings
|
||||
// (e.g., '1') to integers.
|
||||
if (is_int($key) && !$preserveIntegerKeys) {
|
||||
$result[] = $value;
|
||||
} elseif (isset($result[$key]) &&
|
||||
is_array($result[$key]) &&
|
||||
is_array($value)
|
||||
) {
|
||||
// Recurse when both values are arrays.
|
||||
$result[$key] = self::mergeDeepArray(
|
||||
[$result[$key], $value],
|
||||
$preserveIntegerKeys
|
||||
);
|
||||
} else {
|
||||
// Otherwise, use the latter value, overriding any
|
||||
// previous value.
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
422
vendor/wikimedia/composer-merge-plugin/src/PluginState.php
vendored
Normal file
422
vendor/wikimedia/composer-merge-plugin/src/PluginState.php
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
|
||||
/**
|
||||
* Mutable plugin state
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class PluginState
|
||||
{
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var bool $isComposer1
|
||||
*/
|
||||
protected $isComposer1;
|
||||
|
||||
/**
|
||||
* @var array $includes
|
||||
*/
|
||||
protected $includes = [];
|
||||
|
||||
/**
|
||||
* @var array $requires
|
||||
*/
|
||||
protected $requires = [];
|
||||
|
||||
/**
|
||||
* @var bool $devMode
|
||||
*/
|
||||
protected $devMode = false;
|
||||
|
||||
/**
|
||||
* @var bool $recurse
|
||||
*/
|
||||
protected $recurse = true;
|
||||
|
||||
/**
|
||||
* @var bool $replace
|
||||
*/
|
||||
protected $replace = false;
|
||||
|
||||
/**
|
||||
* @var bool $ignore
|
||||
*/
|
||||
protected $ignore = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the -dev sections.
|
||||
* @var bool $mergeDev
|
||||
*/
|
||||
protected $mergeDev = true;
|
||||
|
||||
/**
|
||||
* Whether to merge the extra section.
|
||||
*
|
||||
* By default, the extra section is not merged and there will be many
|
||||
* cases where the merge of the extra section is performed too late
|
||||
* to be of use to other plugins. When enabled, merging uses one of
|
||||
* two strategies - either 'first wins' or 'last wins'. When enabled,
|
||||
* 'first wins' is the default behaviour. If Replace mode is activated
|
||||
* then 'last wins' is used.
|
||||
*
|
||||
* @var bool $mergeExtra
|
||||
*/
|
||||
protected $mergeExtra = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the extra section in a deep / recursive way.
|
||||
*
|
||||
* By default the extra section is merged with array_merge() and duplicate
|
||||
* keys are ignored. When enabled this allows to merge the arrays recursively
|
||||
* using the following rule: Integer keys are merged, while array values are
|
||||
* replaced where the later values overwrite the former.
|
||||
*
|
||||
* This is useful especially for the extra section when plugins use larger
|
||||
* structures like a 'patches' key with the packages as sub-keys and the
|
||||
* patches as values.
|
||||
*
|
||||
* When 'replace' mode is activated the order of array merges is exchanged.
|
||||
*
|
||||
* @var bool $mergeExtraDeep
|
||||
*/
|
||||
protected $mergeExtraDeep = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the replace section.
|
||||
*
|
||||
* @var bool $mergeReplace
|
||||
*/
|
||||
protected $mergeReplace = true;
|
||||
|
||||
/**
|
||||
* Whether to merge the scripts section.
|
||||
*
|
||||
* @var bool $mergeScripts
|
||||
*/
|
||||
protected $mergeScripts = false;
|
||||
|
||||
/**
|
||||
* @var bool $firstInstall
|
||||
*/
|
||||
protected $firstInstall = false;
|
||||
|
||||
/**
|
||||
* @var bool $locked
|
||||
*/
|
||||
protected $locked = false;
|
||||
|
||||
/**
|
||||
* @var bool $dumpAutoloader
|
||||
*/
|
||||
protected $dumpAutoloader = false;
|
||||
|
||||
/**
|
||||
* @var bool $optimizeAutoloader
|
||||
*/
|
||||
protected $optimizeAutoloader = false;
|
||||
|
||||
/**
|
||||
* @param Composer $composer
|
||||
*/
|
||||
public function __construct(Composer $composer)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->isComposer1 = version_compare(PluginInterface::PLUGIN_API_VERSION, '2.0.0', '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this plugin runs within Composer 1.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isComposer1()
|
||||
{
|
||||
return $this->isComposer1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin settings
|
||||
*/
|
||||
public function loadSettings()
|
||||
{
|
||||
$extra = $this->composer->getPackage()->getExtra();
|
||||
$config = array_merge(
|
||||
[
|
||||
'include' => [],
|
||||
'require' => [],
|
||||
'recurse' => true,
|
||||
'replace' => false,
|
||||
'ignore-duplicates' => false,
|
||||
'merge-dev' => true,
|
||||
'merge-extra' => false,
|
||||
'merge-extra-deep' => false,
|
||||
'merge-replace' => true,
|
||||
'merge-scripts' => false,
|
||||
],
|
||||
$extra['merge-plugin'] ?? []
|
||||
);
|
||||
|
||||
$this->includes = (is_array($config['include'])) ?
|
||||
$config['include'] : [$config['include']];
|
||||
$this->requires = (is_array($config['require'])) ?
|
||||
$config['require'] : [$config['require']];
|
||||
$this->recurse = (bool)$config['recurse'];
|
||||
$this->replace = (bool)$config['replace'];
|
||||
$this->ignore = (bool)$config['ignore-duplicates'];
|
||||
$this->mergeDev = (bool)$config['merge-dev'];
|
||||
$this->mergeExtra = (bool)$config['merge-extra'];
|
||||
$this->mergeExtraDeep = (bool)$config['merge-extra-deep'];
|
||||
$this->mergeReplace = (bool)$config['merge-replace'];
|
||||
$this->mergeScripts = (bool)$config['merge-scripts'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of filenames and/or glob patterns to include
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIncludes()
|
||||
{
|
||||
return $this->includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of filenames and/or glob patterns to require
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequires()
|
||||
{
|
||||
return $this->requires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the first install flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setFirstInstall($flag)
|
||||
{
|
||||
$this->firstInstall = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the first time that the plugin has been installed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirstInstall()
|
||||
{
|
||||
return $this->firstInstall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locked flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setLocked($flag)
|
||||
{
|
||||
$this->locked = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was a lockfile present when the plugin was installed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocked()
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should an update be forced?
|
||||
*
|
||||
* @return true If packages are not locked
|
||||
*/
|
||||
public function forceUpdate()
|
||||
{
|
||||
return !$this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the devMode flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setDevMode($flag)
|
||||
{
|
||||
$this->devMode = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should devMode settings be processed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDevMode()
|
||||
{
|
||||
return $this->shouldMergeDev() && $this->devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should devMode settings be merged?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeDev()
|
||||
{
|
||||
return $this->mergeDev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dumpAutoloader flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setDumpAutoloader($flag)
|
||||
{
|
||||
$this->dumpAutoloader = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the autoloader file supposed to be written out?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDumpAutoloader()
|
||||
{
|
||||
return $this->dumpAutoloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the optimizeAutoloader flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setOptimizeAutoloader($flag)
|
||||
{
|
||||
$this->optimizeAutoloader = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the autoloader be optimized?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldOptimizeAutoloader()
|
||||
{
|
||||
return $this->optimizeAutoloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should includes be recursively processed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function recurseIncludes()
|
||||
{
|
||||
return $this->recurse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should duplicate links be replaced in a 'last definition wins' order?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function replaceDuplicateLinks()
|
||||
{
|
||||
return $this->replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should duplicate links be ignored?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ignoreDuplicateLinks()
|
||||
{
|
||||
return $this->ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the extra section be merged?
|
||||
*
|
||||
* By default, the extra section is not merged and there will be many
|
||||
* cases where the merge of the extra section is performed too late
|
||||
* to be of use to other plugins. When enabled, merging uses one of
|
||||
* two strategies - either 'first wins' or 'last wins'. When enabled,
|
||||
* 'first wins' is the default behaviour. If Replace mode is activated
|
||||
* then 'last wins' is used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeExtra()
|
||||
{
|
||||
return $this->mergeExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the extra section be merged deep / recursively?
|
||||
*
|
||||
* By default the extra section is merged with array_merge() and duplicate
|
||||
* keys are ignored. When enabled this allows to merge the arrays recursively
|
||||
* using the following rule: Integer keys are merged, while array values are
|
||||
* replaced where the later values overwrite the former.
|
||||
*
|
||||
* This is useful especially for the extra section when plugins use larger
|
||||
* structures like a 'patches' key with the packages as sub-keys and the
|
||||
* patches as values.
|
||||
*
|
||||
* When 'replace' mode is activated the order of array merges is exchanged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeExtraDeep()
|
||||
{
|
||||
return $this->mergeExtraDeep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the replace section be merged?
|
||||
*
|
||||
* By default, the replace section is merged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeReplace()
|
||||
{
|
||||
return $this->mergeReplace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the scripts section be merged?
|
||||
*
|
||||
* By default, the scripts section is not merged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeScripts()
|
||||
{
|
||||
return $this->mergeScripts;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
173
vendor/wikimedia/composer-merge-plugin/src/StabilityFlags.php
vendored
Normal file
173
vendor/wikimedia/composer-merge-plugin/src/StabilityFlags.php
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
|
||||
/**
|
||||
* Adapted from Composer's RootPackageLoader::extractStabilityFlags
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class StabilityFlags
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array Current package name => stability mappings
|
||||
*/
|
||||
protected $stabilityFlags;
|
||||
|
||||
/**
|
||||
* @var int Current default minimum stability
|
||||
*/
|
||||
protected $minimumStability;
|
||||
|
||||
/**
|
||||
* @var string Regex to extract an explicit stability flag (eg '@dev')
|
||||
*/
|
||||
protected $explicitStabilityRe;
|
||||
|
||||
/**
|
||||
* @param array $stabilityFlags Current package name => stability mappings
|
||||
* @param int|string $minimumStability Current default minimum stability
|
||||
*/
|
||||
public function __construct(
|
||||
array $stabilityFlags = [],
|
||||
$minimumStability = BasePackage::STABILITY_STABLE
|
||||
) {
|
||||
$this->stabilityFlags = $stabilityFlags;
|
||||
$this->minimumStability = $this->getStabilityInt((string)$minimumStability);
|
||||
$this->explicitStabilityRe = '/^[^@]*?@(' .
|
||||
implode('|', array_keys(BasePackage::$stabilities)) .
|
||||
')$/i';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stability value for a given string.
|
||||
*
|
||||
* @param string $name Stability name
|
||||
* @return int Stability value
|
||||
*/
|
||||
protected function getStabilityInt($name)
|
||||
{
|
||||
$name = VersionParser::normalizeStability($name);
|
||||
return BasePackage::$stabilities[$name] ?? BasePackage::STABILITY_STABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and merge stability flags from the given collection of
|
||||
* requires with another collection of stability flags.
|
||||
*
|
||||
* @param array $requires New package name => link mappings
|
||||
* @return array Unified package name => stability mappings
|
||||
*/
|
||||
public function extractAll(array $requires)
|
||||
{
|
||||
$flags = [];
|
||||
|
||||
foreach ($requires as $name => $link) {
|
||||
$name = strtolower($name);
|
||||
$version = $link->getPrettyConstraint();
|
||||
|
||||
$stability = $this->getExplicitStability($version);
|
||||
|
||||
if ($stability === null) {
|
||||
$stability = $this->getParsedStability($version);
|
||||
}
|
||||
|
||||
$flags[$name] = max($stability, $this->getCurrentStability($name));
|
||||
}
|
||||
|
||||
// Filter out null stability values
|
||||
return array_filter($flags, function ($v) {
|
||||
return $v !== null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the most unstable explicit stability (eg '@dev') from a version
|
||||
* specification.
|
||||
*
|
||||
* @param string $version
|
||||
* @return int|null Stability or null if no explict stability found
|
||||
*/
|
||||
protected function getExplicitStability($version)
|
||||
{
|
||||
$found = null;
|
||||
$constraints = $this->splitConstraints($version);
|
||||
foreach ($constraints as $constraint) {
|
||||
if (preg_match($this->explicitStabilityRe, $constraint, $match)) {
|
||||
$stability = $this->getStabilityInt($match[1]);
|
||||
$found = max($stability, $found);
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a version specification into a list of version constraints.
|
||||
*
|
||||
* @param string $version
|
||||
* @return array
|
||||
*/
|
||||
protected function splitConstraints($version)
|
||||
{
|
||||
$found = [];
|
||||
$orConstraints = preg_split('/\s*\|\|?\s*/', trim($version));
|
||||
foreach ($orConstraints as $constraints) {
|
||||
$andConstraints = preg_split(
|
||||
'/(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)/',
|
||||
$constraints
|
||||
);
|
||||
foreach ($andConstraints as $constraint) {
|
||||
$found[] = $constraint;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stability of a version
|
||||
*
|
||||
* @param string $version
|
||||
* @return int|null Stability or null if STABLE or less than minimum
|
||||
*/
|
||||
protected function getParsedStability($version)
|
||||
{
|
||||
// Drop aliasing if used
|
||||
$version = preg_replace('/^([^,\s@]+) as .+$/', '$1', $version);
|
||||
$stability = $this->getStabilityInt(
|
||||
VersionParser::parseStability($version)
|
||||
);
|
||||
|
||||
if ($stability === BasePackage::STABILITY_STABLE ||
|
||||
$this->minimumStability > $stability
|
||||
) {
|
||||
// Ignore if 'stable' or more stable than the global
|
||||
// minimum
|
||||
$stability = null;
|
||||
}
|
||||
|
||||
return $stability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current stability of a given package.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int|null Stability of null if not set
|
||||
*/
|
||||
protected function getCurrentStability($name)
|
||||
{
|
||||
return $this->stabilityFlags[$name] ?? null;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
Reference in New Issue
Block a user