diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2015-12-17 09:15:42 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2015-12-17 09:44:51 +0100 |
commit | a1789ddde42033f1b05cc4929491214ee6e79383 (patch) | |
tree | 63615735c4ddffaaabf2428946bb26f90899f7bf /vendor/wikimedia/composer-merge-plugin/src | |
parent | 9e06a62f265e3a2aaabecc598d4bc617e06fa32d (diff) |
Update to MediaWiki 1.26.0
Diffstat (limited to 'vendor/wikimedia/composer-merge-plugin/src')
6 files changed, 1280 insertions, 262 deletions
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Logger.php b/vendor/wikimedia/composer-merge-plugin/src/Logger.php new file mode 100644 index 00000000..1635a2b0 --- /dev/null +++ b/vendor/wikimedia/composer-merge-plugin/src/Logger.php @@ -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; + +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 + */ + protected function log($message) + { + if (method_exists($this->inputOutput, 'writeError')) { + $this->inputOutput->writeError($message); + } else { + // @codeCoverageIgnoreStart + // Backwards compatiblity for Composer before cb336a5 + $this->inputOutput->write($message); + // @codeCoverageIgnoreEnd + } + } +} +// vim:sw=4:ts=4:sts=4:et: 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: diff --git a/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php b/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php index 04c55886..ea41d41a 100644 --- a/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php +++ b/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php @@ -2,7 +2,7 @@ /** * This file is part of the Composer Merge plugin. * - * Copyright (C) 2014 Bryan Davis, Wikimedia Foundation, and contributors + * 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. @@ -10,39 +10,56 @@ namespace Wikimedia\Composer; +use Wikimedia\Composer\Merge\ExtraPackage; +use Wikimedia\Composer\Merge\MissingFileException; +use Wikimedia\Composer\Merge\PluginState; + use Composer\Composer; -use Composer\Config; +use Composer\DependencyResolver\Operation\InstallOperation; use Composer\EventDispatcher\EventSubscriberInterface; +use Composer\Factory; +use Composer\Installer; use Composer\Installer\InstallerEvent; use Composer\Installer\InstallerEvents; +use Composer\Installer\PackageEvent; +use Composer\Installer\PackageEvents; use Composer\IO\IOInterface; -use Composer\Json\JsonFile; -use Composer\Package\BasePackage; -use Composer\Package\CompletePackage; -use Composer\Package\Loader\ArrayLoader; use Composer\Package\RootPackageInterface; -use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginInterface; -use Composer\Script\CommandEvent; +use Composer\Script\Event; use Composer\Script\ScriptEvents; /** * Composer plugin that allows merging multiple composer.json files. * - * When installed, this plugin will look for a "merge-patterns" key in the - * composer configuration's "extra" section. 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. + * 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 "require", "require-dev", "repositories" and "suggest" sections of the + * 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. + * 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 * { @@ -65,36 +82,24 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface { /** - * @var Composer $composer + * Offical package name */ - protected $composer; + const PACKAGE_NAME = 'wikimedia/composer-merge-plugin'; /** - * @var IOInterface $inputOutput - */ - protected $inputOutput; - - /** - * @var ArrayLoader $loader - */ - protected $loader; - - /** - * @var array $duplicateLinks + * @var Composer $composer */ - protected $duplicateLinks; + protected $composer; /** - * @var bool $devMode + * @var PluginState $state */ - protected $devMode; + protected $state; /** - * Whether to recursively include dependencies - * - * @var bool $recurse + * @var Logger $logger */ - protected $recurse = true; + protected $logger; /** * Files that have already been processed @@ -109,7 +114,8 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface public function activate(Composer $composer, IOInterface $io) { $this->composer = $composer; - $this->inputOutput = $io; + $this->state = new PluginState($this->composer); + $this->logger = new Logger('merge-plugin', $io); } /** @@ -119,69 +125,66 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface { return array( InstallerEvents::PRE_DEPENDENCIES_SOLVING => 'onDependencySolve', - ScriptEvents::PRE_INSTALL_CMD => 'onInstallOrUpdate', - ScriptEvents::PRE_UPDATE_CMD => 'onInstallOrUpdate', + PackageEvents::POST_PACKAGE_INSTALL => 'onPostPackageInstall', + ScriptEvents::POST_INSTALL_CMD => 'onPostInstallOrUpdate', + ScriptEvents::POST_UPDATE_CMD => 'onPostInstallOrUpdate', + ScriptEvents::PRE_AUTOLOAD_DUMP => 'onInstallUpdateOrDump', + ScriptEvents::PRE_INSTALL_CMD => 'onInstallUpdateOrDump', + ScriptEvents::PRE_UPDATE_CMD => 'onInstallUpdateOrDump', ); } /** - * Handle an event callback for an install or update command by checking - * for "merge-patterns" in the "extra" data and merging package contents - * if found. + * 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 CommandEvent $event + * @param Event $event */ - public function onInstallOrUpdate(CommandEvent $event) + public function onInstallUpdateOrDump(Event $event) { - $config = $this->readConfig($this->composer->getPackage()); - if (isset($config['recurse'])) { - $this->recurse = (bool)$config['recurse']; - } - if ($config['include']) { - $this->loader = new ArrayLoader(); - $this->duplicateLinks = array( - 'require' => array(), - 'require-dev' => array(), - ); - $this->devMode = $event->isDevMode(); - $this->mergePackages($config); - } - } - - /** - * @param RootPackageInterface $package - * @return array - */ - protected function readConfig(RootPackageInterface $package) - { - $config = array( - 'include' => array(), - ); - $extra = $package->getExtra(); - if (isset($extra['merge-plugin'])) { - $config = array_merge($config, $extra['merge-plugin']); - if (!is_array($config['include'])) { - $config['include'] = array($config['include']); + $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']); } } - return $config; } /** * Find configuration files matching the configured glob patterns and * merge their contents with the master package. * - * @param array $config + * @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 mergePackages(array $config) + protected function mergeFiles(array $patterns, $required = false) { $root = $this->composer->getPackage(); - foreach (array_reduce( - array_map('glob', $config['include']), - 'array_merge', - array() - ) as $path) { - $this->loadFile($root, $path); + + $files = array_map( + 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', array()) as $path) { + $this->mergeFile($root, $path); } } @@ -191,221 +194,121 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface * @param RootPackageInterface $root * @param string $path */ - protected function loadFile($root, $path) + protected function mergeFile(RootPackageInterface $root, $path) { - if (in_array($path, $this->loadedFiles)) { - $this->debug("Skipping duplicate <comment>$path</comment>..."); + if (isset($this->loadedFiles[$path])) { + $this->logger->debug("Already merged <comment>$path</comment>"); return; } else { - $this->loadedFiles[] = $path; + $this->loadedFiles[$path] = true; } - $this->debug("Loading <comment>{$path}</comment>..."); - $json = $this->readPackageJson($path); - $package = $this->loader->load($json); - - $this->mergeRequires($root, $package); - $this->mergeDevRequires($root, $package); + $this->logger->info("Loading <comment>{$path}</comment>..."); - if (isset($json['repositories'])) { - $this->addRepositories($json['repositories'], $root); - } + $package = new ExtraPackage($path, $this->composer, $this->logger); + $package->mergeInto($root, $this->state); - if ($package->getSuggests()) { - $root->setSuggests(array_merge( - $root->getSuggests(), - $package->getSuggests() - )); - } - - if ($this->recurse && isset($json['extra']['merge-plugin'])) { - $this->mergePackages($json['extra']['merge-plugin']); + if ($this->state->recurseIncludes()) { + $this->mergeFiles($package->getIncludes(), false); + $this->mergeFiles($package->getRequires(), true); } } /** - * 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. + * Handle an event callback for pre-dependency solving phase of an install + * or update by adding any duplicate package dependencies found during + * initial merge processing to the request that will be processed by the + * dependency solver. * - * @param string $path - * @return array + * @param InstallerEvent $event */ - protected function readPackageJson($path) + public function onDependencySolve(InstallerEvent $event) { - $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 RootPackageInterface $root - * @param CompletePackage $package - */ - protected function mergeRequires( - RootPackageInterface $root, - CompletePackage $package - ) { - $requires = $package->getRequires(); - if (!$requires) { - return; + $request = $event->getRequest(); + foreach ($this->state->getDuplicateLinks('require') as $link) { + $this->logger->info( + "Adding dependency <comment>{$link}</comment>" + ); + $request->install($link->getTarget(), $link->getConstraint()); } - - $this->mergeStabilityFlags($root, $requires); - - $root->setRequires($this->mergeLinks( - $root->getRequires(), - $requires, - $this->duplicateLinks['require'] - )); - } - - /** - * @param RootPackageInterface $root - * @param CompletePackage $package - */ - protected function mergeDevRequires( - RootPackageInterface $root, - CompletePackage $package - ) { - $requires = $package->getDevRequires(); - if (!$requires) { - return; + if ($this->state->isDevMode()) { + foreach ($this->state->getDuplicateLinks('require-dev') as $link) { + $this->logger->info( + "Adding dev dependency <comment>{$link}</comment>" + ); + $request->install($link->getTarget(), $link->getConstraint()); + } } - - $this->mergeStabilityFlags($root, $requires); - - $root->setDevRequires($this->mergeLinks( - $root->getDevRequires(), - $requires, - $this->duplicateLinks['require-dev'] - )); } /** - * Extract and merge stability flags from the given collection of - * requires. + * Handle an event callback following installation of a new package by + * checking to see if the package that was installed was our plugin. * - * @param RootPackageInterface $root - * @param array $requires + * @param PackageEvent $event */ - protected function mergeStabilityFlags( - RootPackageInterface $root, - array $requires - ) { - $flags = $root->getStabilityFlags(); - foreach ($requires as $name => $link) { - $name = strtolower($name); - $version = $link->getPrettyConstraint(); - $stability = VersionParser::parseStability($version); - $flags[$name] = BasePackage::$stabilities[$stability]; + 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() + ); + } } - $root->setStabilityFlags($flags); } /** - * Add a collection of repositories described by the given configuration - * to the given package and the global repository manager. + * 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 array $repositories - * @param RootPackageInterface $root + * @param Event $event */ - protected function addRepositories( - array $repositories, - RootPackageInterface $root - ) { - $repoManager = $this->composer->getRepositoryManager(); - $newRepos = array(); - - foreach ($repositories as $repoJson) { - $this->debug("Adding {$repoJson['type']} repository"); - $repo = $repoManager->createRepository( - $repoJson['type'], - $repoJson + public function onPostInstallOrUpdate(Event $event) + { + // @codeCoverageIgnoreStart + if ($this->state->isFirstInstall()) { + $this->state->setFirstInstall(false); + $this->logger->info( + '<comment>' . + 'Running additional update to apply merge settings' . + '</comment>' ); - $repoManager->addRepository($repo); - $newRepos[] = $repo; - } - $root->setRepositories(array_merge( - $newRepos, - $root->getRepositories() - )); - } + $config = $this->composer->getConfig(); - /** - * Merge two collections of package links and collect duplicates for - * subsequent processing. - * - * @param array $origin Primary collection - * @param array $merge Additional collection - * @param array &dups Duplicate storage - * @return array Merged collection - */ - protected function mergeLinks(array $origin, array $merge, array &$dups) - { - foreach ($merge as $name => $link) { - if (!isset($origin[$name])) { - $this->debug("Merging <comment>{$name}</comment>"); - $origin[$name] = $link; - } else { - // Defer to solver. - $this->debug("Deferring duplicate <comment>{$name}</comment>"); - $dups[] = $link; - } - } - return $origin; - } + $preferSource = $config->get('preferred-install') == 'source'; + $preferDist = $config->get('preferred-install') == 'dist'; - /** - * Handle an event callback for pre-dependency solving phase of an install - * or update by adding any duplicate package dependencies found during - * initial merge processing to the request that will be processed by the - * dependency solver. - * - * @param InstallerEvent $event - */ - public function onDependencySolve(InstallerEvent $event) - { - if (!$this->duplicateLinks) { - return; - } + $installer = Installer::create( + $event->getIO(), + // Create a new Composer instance to ensure full processing of + // the merged files. + Factory::create($event->getIO(), null, false) + ); - $request = $event->getRequest(); - foreach ($this->duplicateLinks['require'] as $link) { - $this->debug("Adding dependency <comment>{$link}</comment>"); - $request->install($link->getTarget(), $link->getConstraint()); - } - if ($this->devMode) { - foreach ($this->duplicateLinks['require-dev'] as $link) { - $this->debug("Adding dev dependency <comment>{$link}</comment>"); - $request->install($link->getTarget(), $link->getConstraint()); + $installer->setPreferSource($preferSource); + $installer->setPreferDist($preferDist); + $installer->setDevMode($event->isDevMode()); + $installer->setDumpAutoloader($this->state->shouldDumpAutoloader()); + $installer->setOptimizeAutoloader( + $this->state->shouldOptimizeAutoloader() + ); + + if ($this->state->forceUpdate()) { + // Force update mode so that new packages are processed rather + // than just telling the user that composer.json and + // composer.lock don't match. + $installer->setUpdate(true); } - } - } - /** - * Log a debug message - * - * Messages will be output at the "verbose" logging level (eg `-v` needed - * on the Composer command). - * - * @param string $message - */ - protected function debug($message) - { - if ($this->inputOutput->isVerbose()) { - $this->inputOutput->write(" <info>[merge]</info> {$message}"); + $installer->run(); } + // @codeCoverageIgnoreEnd } } // vim:sw=4:ts=4:sts=4:et: |