summaryrefslogtreecommitdiff
path: root/vendor/wikimedia/composer-merge-plugin/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wikimedia/composer-merge-plugin/src')
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Logger.php102
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php487
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php18
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php327
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php181
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php427
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: