diff options
Diffstat (limited to 'vendor/wikimedia/composer-merge-plugin/src/Merge')
4 files changed, 1013 insertions, 0 deletions
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php new file mode 100644 index 00000000..ebecdff5 --- /dev/null +++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php @@ -0,0 +1,487 @@ +<?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; + +use Wikimedia\Composer\Logger; + +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 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; + + /** + * @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); + } + + /** + * Get list of additional packages to include if precessing recursively. + * + * @return array + */ + public function getIncludes() + { + return isset($this->json['extra']['merge-plugin']['include']) ? + $this->json['extra']['merge-plugin']['include'] : array(); + } + + /** + * Get list of additional packages to require if precessing recursively. + * + * @return array + */ + public function getRequires() + { + return isset($this->json['extra']['merge-plugin']['require']) ? + $this->json['extra']['merge-plugin']['require'] : array(); + } + + /** + * 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; + } + + /** + * @return CompletePackage + */ + protected function loadPackage($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->addRepositories($root); + + $this->mergeRequires('require', $root, $state); + if ($state->isDevMode()) { + $this->mergeRequires('require-dev', $root, $state); + } + + $this->mergePackageLinks('conflict', $root); + $this->mergePackageLinks('replace', $root); + $this->mergePackageLinks('provide', $root); + + $this->mergeSuggests($root); + + $this->mergeAutoload('autoload', $root); + if ($state->isDevMode()) { + $this->mergeAutoload('devAutoload', $root); + } + + $this->mergeExtra($root, $state); + } + + /** + * Add a collection of repositories described by the given configuration + * to the given package and the global repository manager. + * + * @param RootPackageInterface $root + */ + protected function addRepositories(RootPackageInterface $root) + { + if (!isset($this->json['repositories'])) { + return; + } + $repoManager = $this->composer->getRepositoryManager(); + $newRepos = array(); + + foreach ($this->json['repositories'] as $repoJson) { + if (!isset($repoJson['type'])) { + continue; + } + $this->logger->info("Adding {$repoJson['type']} repository"); + $repo = $repoManager->createRepository( + $repoJson['type'], + $repoJson + ); + $repoManager->addRepository($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, + $state + ) { + $dups = array(); + foreach ($merge as $name => $link) { + if (!isset($origin[$name]) || $state->replaceDuplicateLinks()) { + $this->logger->info("Merging <comment>{$name}</comment>"); + $origin[$name] = $link; + } else { + // Defer to solver. + $this->logger->info( + "Deferring duplicate <comment>{$name}</comment>" + ); + $dups[] = $link; + } + } + $state->addDuplicateLinks($type, $dups); + return $origin; + } + + /** + * 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); + if ($root !== $unwrapped) { + $this->logger->warning( + 'This Composer version does not support ' . + "'{$type}' merging for aliased packages." + ); + } + $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( + array_merge($rootExtra, $extra) + ); + + } else { + 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( + array_merge($extra, $rootExtra) + ); + } + } + + /** + * 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 = new VersionParser(); + + return array_map( + function ($link) use ($linkType, $version, $prettyVersion, $vp) { + if ('self.version' === $link->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' + ) { + if ($root instanceof RootAliasPackage && + !method_exists($root, $method) + ) { + // Unwrap and return the aliased RootPackage. + $root = $root->getAliasOf(); + } + return $root; + } +} +// vim:sw=4:ts=4:sts=4:et: diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php new file mode 100644 index 00000000..873719d7 --- /dev/null +++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php @@ -0,0 +1,18 @@ +<?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; + +/** + * @author Bryan Davis <bd808@bd808.com> + */ +class MissingFileException extends \RuntimeException +{ +} diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php new file mode 100644 index 00000000..a191c1e8 --- /dev/null +++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php @@ -0,0 +1,327 @@ +<?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; + +use Composer\Composer; + +/** + * Mutable plugin state + * + * @author Bryan Davis <bd808@bd808.com> + */ +class PluginState +{ + /** + * @var Composer $composer + */ + protected $composer; + + /** + * @var array $includes + */ + protected $includes = array(); + + /** + * @var array $requires + */ + protected $requires = array(); + + /** + * @var array $duplicateLinks + */ + protected $duplicateLinks = array(); + + /** + * @var bool $devMode + */ + protected $devMode = false; + + /** + * @var bool $recurse + */ + protected $recurse = true; + + /** + * @var bool $replace + */ + protected $replace = 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; + + /** + * @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; + } + + /** + * Load plugin settings + */ + public function loadSettings() + { + $extra = $this->composer->getPackage()->getExtra(); + $config = array_merge( + array( + 'include' => array(), + 'require' => array(), + 'recurse' => true, + 'replace' => false, + 'merge-dev' => true, + 'merge-extra' => false, + ), + isset($extra['merge-plugin']) ? $extra['merge-plugin'] : array() + ); + + $this->includes = (is_array($config['include'])) ? + $config['include'] : array($config['include']); + $this->requires = (is_array($config['require'])) ? + $config['require'] : array($config['require']); + $this->recurse = (bool)$config['recurse']; + $this->replace = (bool)$config['replace']; + $this->mergeDev = (bool)$config['merge-dev']; + $this->mergeExtra = (bool)$config['merge-extra']; + } + + /** + * 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->mergeDev && $this->devMode; + } + + /** + * 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; + } + + /** + * Add duplicate packages + * + * @param string $type Package type + * @param array $packages + */ + public function addDuplicateLinks($type, array $packages) + { + if (!isset($this->duplicateLinks[$type])) { + $this->duplicateLinks[$type] = array(); + } + $this->duplicateLinks[$type] = + array_merge($this->duplicateLinks[$type], $packages); + } + + /** + * Get duplicate packages + * + * @param string $type Package type + * @return array + */ + public function getDuplicateLinks($type) + { + return isset($this->duplicateLinks[$type]) ? + $this->duplicateLinks[$type] : array(); + } + + /** + * 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 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; + } +} +// vim:sw=4:ts=4:sts=4:et: diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php new file mode 100644 index 00000000..1c106e02 --- /dev/null +++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php @@ -0,0 +1,181 @@ +<?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; + +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 explict stability flag (eg '@dev') + */ + protected $explicitStabilityRe; + + + /** + * @param array $stabilityFlags Current package name => stability mappings + * @param int $minimumStability Current default minimum stability + */ + public function __construct( + array $stabilityFlags = array(), + $minimumStability = BasePackage::STABILITY_STABLE + ) { + $this->stabilityFlags = $stabilityFlags; + $this->minimumStability = $this->getStabilityInt($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 isset(BasePackage::$stabilities[$name]) ? + 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 = array(); + + 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 = array(); + $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 isset($this->stabilityFlags[$name]) ? + $this->stabilityFlags[$name] : null; + } +} +// vim:sw=4:ts=4:sts=4:et: |