updated core and modules

This commit is contained in:
Bachir Soussi Chiadmi
2017-09-09 16:27:51 +02:00
parent 027aa99b32
commit 41863f872c
7810 changed files with 318922 additions and 83631 deletions
+31 -7
View File
@@ -24,6 +24,8 @@ extensions which may be managed via Composer.
Installation
------------
Composer Merge Plugin requires [Composer 1.0.0](https://getcomposer.org/) or newer.
```
$ composer require wikimedia/composer-merge-plugin
```
@@ -32,7 +34,7 @@ $ composer require wikimedia/composer-merge-plugin
Usage
-----
```
```json
{
"require": {
"wikimedia/composer-merge-plugin": "dev-master"
@@ -49,7 +51,9 @@ Usage
"recurse": true,
"replace": false,
"merge-dev": true,
"merge-extra": false
"merge-extra": false,
"merge-extra-deep": false,
"merge-scripts": false
}
}
}
@@ -88,11 +92,13 @@ in the top-level composer.json file:
* [suggest](https://getcomposer.org/doc/04-schema.md#suggest)
* [extra](https://getcomposer.org/doc/04-schema.md#extra)
(optional, see [merge-extra](#merge-extra) below)
* [scripts](https://getcomposer.org/doc/04-schema.md#scripts)
(optional, see [merge-scripts](#merge-scripts) below)
### require
The `require` setting is identical to `[include](#include)` except when
The `require` setting is identical to [`include`](#include) except when
a pattern fails to match at least one file then it will cause an error.
### recurse
@@ -127,13 +133,30 @@ section is to accept the first version of any key found (e.g. a key in the
master config wins over the version found in any imported config). If
`replace` mode is active ([see above](#replace)) then this behavior changes
and the last key found will win (e.g. the key in the master config is replaced
by the key in the imported config). The usefulness of merging the extra
section will vary depending on the Composer plugins being used and the order
in which they are processed by Composer.
by the key in the imported config). If `"merge-extra-deep": true` is specified
then, the sections are merged similar to array_merge_recursive() - however
duplicate string array keys are replaced instead of merged, while numeric
array keys are merged as usual. The usefulness of merging the extra section
will vary depending on the Composer plugins being used and the order in which
they are processed by Composer.
Note that `merge-plugin` sections are excluded from the merge process, but are
always processed by the plugin unless [recursion](#recurse) is disabled.
### merge-scripts
A `"merge-scripts": true` setting enables merging the contents of the
`scripts` section of included files as well. The normal merge mode for the
scripts section is to accept the first version of any key found (e.g. a key in
the master config wins over the version found in any imported config). If
`replace` mode is active ([see above](#replace)) then this behavior changes
and the last key found will win (e.g. the key in the master config is replaced
by the key in the imported config).
Note: [custom commands][] added by merged configuration will work when invoked
as `composer run-script my-cool-command` but will not be available using the
normal `composer my-cool-command` shortcut.
Running tests
-------------
@@ -167,7 +190,7 @@ GitHub as well.
License
-------
Composer Merge plugin is licensed under the MIT license. See the `LICENSE`
Composer Merge plugin is licensed under the MIT license. See the [`LICENSE`](LICENSE)
file for more details.
@@ -181,3 +204,4 @@ file for more details.
[License]: https://img.shields.io/packagist/l/wikimedia/composer-merge-plugin.svg?style=flat
[Build Status]: https://img.shields.io/travis/wikimedia/composer-merge-plugin.svg?style=flat
[Code Coverage]: https://img.shields.io/scrutinizer/coverage/g/wikimedia/composer-merge-plugin/master.svg?style=flat
[custom commands]: https://getcomposer.org/doc/articles/scripts.md#writing-custom-commands
+1 -1
View File
@@ -16,7 +16,7 @@
"php": ">=5.3.2"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
"composer/composer": "~1.0.0",
"jakub-onderka/php-parallel-lint": "~0.8",
"phpunit/phpunit": "~4.8|~5.0",
"squizlabs/php_codesniffer": "~2.1.0"
@@ -58,6 +58,11 @@ class ExtraPackage
*/
protected $package;
/**
* @var VersionParser $versionParser
*/
protected $versionParser;
/**
* @param string $path Path to composer.json file
* @param Composer $composer
@@ -70,6 +75,7 @@ class ExtraPackage
$this->logger = $logger;
$this->json = $this->readPackageJson($path);
$this->package = $this->loadPackage($this->json);
$this->versionParser = new VersionParser();
}
/**
@@ -120,10 +126,10 @@ class ExtraPackage
}
/**
* @param string $json
* @param array $json
* @return CompletePackage
*/
protected function loadPackage($json)
protected function loadPackage(array $json)
{
$loader = new ArrayLoader();
$package = $loader->load($json);
@@ -146,12 +152,9 @@ class ExtraPackage
*/
public function mergeInto(RootPackageInterface $root, PluginState $state)
{
$this->addRepositories($root);
$this->prependRepositories($root);
$this->mergeRequires('require', $root, $state);
if ($state->isDevMode()) {
$this->mergeRequires('require-dev', $root, $state);
}
$this->mergePackageLinks('conflict', $root);
$this->mergePackageLinks('replace', $root);
@@ -160,11 +163,29 @@ class ExtraPackage
$this->mergeSuggests($root);
$this->mergeAutoload('autoload', $root);
if ($state->isDevMode()) {
$this->mergeAutoload('devAutoload', $root);
}
$this->mergeExtra($root, $state);
$this->mergeScripts($root, $state);
if ($state->isDevMode()) {
$this->mergeDevInto($root, $state);
} else {
$this->mergeReferences($root);
}
}
/**
* Merge just the dev portion into a RootPackageInterface
*
* @param RootPackageInterface $root
* @param PluginState $state
*/
public function mergeDevInto(RootPackageInterface $root, PluginState $state)
{
$this->mergeRequires('require-dev', $root, $state);
$this->mergeAutoload('devAutoload', $root);
$this->mergeReferences($root);
}
/**
@@ -173,7 +194,7 @@ class ExtraPackage
*
* @param RootPackageInterface $root
*/
protected function addRepositories(RootPackageInterface $root)
protected function prependRepositories(RootPackageInterface $root)
{
if (!isset($this->json['repositories'])) {
return;
@@ -185,12 +206,12 @@ class ExtraPackage
if (!isset($repoJson['type'])) {
continue;
}
$this->logger->info("Adding {$repoJson['type']} repository");
$this->logger->info("Prepending {$repoJson['type']} repository");
$repo = $repoManager->createRepository(
$repoJson['type'],
$repoJson
);
$repoManager->addRepository($repo);
$repoManager->prependRepository($repo);
$newRepos[] = $repo;
}
@@ -402,25 +423,70 @@ class ExtraPackage
if ($state->replaceDuplicateLinks()) {
$unwrapped->setExtra(
array_merge($rootExtra, $extra)
self::mergeExtraArray($state->shouldMergeExtraDeep(), $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."
);
if (!$state->shouldMergeExtraDeep()) {
foreach (array_intersect(
array_keys($extra),
array_keys($rootExtra)
) as $key) {
$this->logger->info(
"Ignoring duplicate <comment>{$key}</comment> in ".
"<comment>{$this->path}</comment> extra config."
);
}
}
$unwrapped->setExtra(
array_merge($extra, $rootExtra)
self::mergeExtraArray($state->shouldMergeExtraDeep(), $extra, $rootExtra)
);
}
}
/**
* Merge scripts config into a RootPackageInterface
*
* @param RootPackageInterface $root
* @param PluginState $state
*/
public function mergeScripts(RootPackageInterface $root, PluginState $state)
{
$scripts = $this->package->getScripts();
if (!$state->shouldMergeScripts() || empty($scripts)) {
return;
}
$rootScripts = $root->getScripts();
$unwrapped = self::unwrapIfNeeded($root, 'setScripts');
if ($state->replaceDuplicateLinks()) {
$unwrapped->setScripts(
array_merge($rootScripts, $scripts)
);
} else {
$unwrapped->setScripts(
array_merge($scripts, $rootScripts)
);
}
}
/**
* Merges two arrays either via arrayMergeDeep or via array_merge.
*
* @param bool $mergeDeep
* @param array $array1
* @param array $array2
* @return array
*/
public static function mergeExtraArray($mergeDeep, $array1, $array2)
{
if ($mergeDeep) {
return NestedArray::mergeDeep($array1, $array2);
}
return array_merge($array1, $array2);
}
/**
* Update Links with a 'self.version' constraint with the root package's
* version.
@@ -438,7 +504,7 @@ class ExtraPackage
$linkType = BasePackage::$supportedLinkTypes[$type];
$version = $root->getVersion();
$prettyVersion = $root->getPrettyVersion();
$vp = new VersionParser();
$vp = $this->versionParser;
$method = 'get' . ucfirst($linkType['method']);
$packages = $root->$method();
@@ -503,5 +569,53 @@ class ExtraPackage
// @codeCoverageIgnoreEnd
return $root;
}
/**
* Update the root packages reference information.
*
* @param RootPackageInterface $root
*/
protected function mergeReferences(RootPackageInterface $root)
{
// Merge source reference information for merged packages.
// @see RootPackageLoader::load
$references = array();
$unwrapped = $this->unwrapIfNeeded($root, 'setReferences');
foreach (array('require', 'require-dev') as $linkType) {
$linkInfo = BasePackage::$supportedLinkTypes[$linkType];
$method = 'get'.ucfirst($linkInfo['method']);
$links = array();
foreach ($unwrapped->$method() as $link) {
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
}
$references = $this->extractReferences($links, $references);
}
$unwrapped->setReferences($references);
}
/**
* Extract vcs revision from version constraint (dev-master#abc123.
*
* @param array $requires
* @param array $references
* @return array
* @see RootPackageLoader::extractReferences()
*/
protected function extractReferences(array $requires, array $references)
{
foreach ($requires as $reqName => $reqVersion) {
$reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
$stabilityName = VersionParser::parseStability($reqVersion);
if (
preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) &&
$stabilityName === 'dev'
) {
$name = strtolower($reqName);
$references[$name] = $match[1];
}
}
return $references;
}
}
// vim:sw=4:ts=4:sts=4:et:
@@ -0,0 +1,116 @@
<?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;
/**
* Adapted from
* http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Component/Utility/NestedArray.php
* @ f86a4d650d5af0b82a3981e09977055fa63f6f2e
*/
class NestedArray
{
/**
* Merges multiple arrays, recursively, and returns the merged array.
*
* This function is similar to PHP's array_merge_recursive() function, but
* it handles non-array values differently. When merging values that are
* not both arrays, the latter value replaces the former rather than
* merging with it.
*
* Example:
*
* @code
* $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b')));
* $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd')));
*
* // This results in array('fragment' => array('x', 'y'), 'attributes' =>
* // array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b',
* // 'c', 'd'))).
* $incorrect = array_merge_recursive($link_options_1, $link_options_2);
*
* // This results in array('fragment' => 'y', 'attributes' =>
* // array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))).
* $correct = NestedArray::mergeDeep($link_options_1, $link_options_2);
* @endcode
*
* @param array ...
* Arrays to merge.
*
* @return array
* The merged array.
*
* @see NestedArray::mergeDeepArray()
*/
public static function mergeDeep()
{
return self::mergeDeepArray(func_get_args());
}
/**
* Merges multiple arrays, recursively, and returns the merged array.
*
* This function is equivalent to NestedArray::mergeDeep(), except the
* input arrays are passed as a single array parameter rather than
* a variable parameter list.
*
* The following are equivalent:
* - NestedArray::mergeDeep($a, $b);
* - NestedArray::mergeDeepArray(array($a, $b));
*
* The following are also equivalent:
* - call_user_func_array('NestedArray::mergeDeep', $arrays_to_merge);
* - NestedArray::mergeDeepArray($arrays_to_merge);
*
* @param array $arrays
* An arrays of arrays to merge.
* @param bool $preserveIntegerKeys
* (optional) If given, integer keys will be preserved and merged
* instead of appended. Defaults to false.
*
* @return array
* The merged array.
*
* @see NestedArray::mergeDeep()
*/
public static function mergeDeepArray(
array $arrays,
$preserveIntegerKeys = false
) {
$result = array();
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
// Renumber integer keys as array_merge_recursive() does
// unless $preserveIntegerKeys is set to TRUE. Note that PHP
// automatically converts array keys that are integer strings
// (e.g., '1') to integers.
if (is_integer($key) && !$preserveIntegerKeys) {
$result[] = $value;
} elseif (isset($result[$key]) &&
is_array($result[$key]) &&
is_array($value)
) {
// Recurse when both values are arrays.
$result[$key] = self::mergeDeepArray(
array($result[$key], $value),
$preserveIntegerKeys
);
} else {
// Otherwise, use the latter value, overriding any
// previous value.
$result[$key] = $value;
}
}
}
return $result;
}
}
// vim:sw=4:ts=4:sts=4:et:
@@ -74,6 +74,31 @@ class PluginState
*/
protected $mergeExtra = false;
/**
* Whether to merge the extra section in a deep / recursive way.
*
* By default the extra section is merged with array_merge() and duplicate
* keys are ignored. When enabled this allows to merge the arrays recursively
* using the following rule: Integer keys are merged, while array values are
* replaced where the later values overwrite the former.
*
* This is useful especially for the extra section when plugins use larger
* structures like a 'patches' key with the packages as sub-keys and the
* patches as values.
*
* When 'replace' mode is activated the order of array merges is exchanged.
*
* @var bool $mergeExtraDeep
*/
protected $mergeExtraDeep = false;
/**
* Whether to merge the scripts section.
*
* @var bool $mergeScripts
*/
protected $mergeScripts = false;
/**
* @var bool $firstInstall
*/
@@ -116,6 +141,8 @@ class PluginState
'replace' => false,
'merge-dev' => true,
'merge-extra' => false,
'merge-extra-deep' => false,
'merge-scripts' => false,
),
isset($extra['merge-plugin']) ? $extra['merge-plugin'] : array()
);
@@ -128,6 +155,8 @@ class PluginState
$this->replace = (bool)$config['replace'];
$this->mergeDev = (bool)$config['merge-dev'];
$this->mergeExtra = (bool)$config['merge-extra'];
$this->mergeExtraDeep = (bool)$config['merge-extra-deep'];
$this->mergeScripts = (bool)$config['merge-scripts'];
}
/**
@@ -217,7 +246,17 @@ class PluginState
*/
public function isDevMode()
{
return $this->mergeDev && $this->devMode;
return $this->shouldMergeDev() && $this->devMode;
}
/**
* Should devMode settings be merged?
*
* @return bool
*/
public function shouldMergeDev()
{
return $this->mergeDev;
}
/**
@@ -323,5 +362,39 @@ class PluginState
{
return $this->mergeExtra;
}
/**
* Should the extra section be merged deep / recursively?
*
* By default the extra section is merged with array_merge() and duplicate
* keys are ignored. When enabled this allows to merge the arrays recursively
* using the following rule: Integer keys are merged, while array values are
* replaced where the later values overwrite the former.
*
* This is useful especially for the extra section when plugins use larger
* structures like a 'patches' key with the packages as sub-keys and the
* patches as values.
*
* When 'replace' mode is activated the order of array merges is exchanged.
*
* @return bool
*/
public function shouldMergeExtraDeep()
{
return $this->mergeExtraDeep;
}
/**
* Should the scripts section be merged?
*
* By default, the scripts section is not merged.
*
* @return bool
*/
public function shouldMergeScripts()
{
return $this->mergeScripts;
}
}
// vim:sw=4:ts=4:sts=4:et:
+89 -22
View File
@@ -16,6 +16,7 @@ use Wikimedia\Composer\Merge\PluginState;
use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\EventDispatcher\Event as BaseEvent;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Factory;
use Composer\Installer;
@@ -26,7 +27,7 @@ use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Package\RootPackageInterface;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\Event as ScriptEvent;
use Composer\Script\ScriptEvents;
/**
@@ -86,6 +87,16 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
*/
const PACKAGE_NAME = 'wikimedia/composer-merge-plugin';
/**
* Name of the composer 1.1 init event.
*/
const COMPAT_PLUGINEVENTS_INIT = 'init';
/**
* Priority that plugin uses to register callbacks.
*/
const CALLBACK_PRIORITY = 50000;
/**
* @var Composer $composer
*/
@@ -102,11 +113,18 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
protected $logger;
/**
* Files that have already been processed
* Files that have already been fully processed
*
* @var string[] $loadedFiles
* @var string[] $loaded
*/
protected $loadedFiles = array();
protected $loaded = array();
/**
* Files that have already been partially processed
*
* @var string[] $loadedNoDev
*/
protected $loadedNoDev = array();
/**
* {@inheritdoc}
@@ -124,24 +142,52 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
InstallerEvents::PRE_DEPENDENCIES_SOLVING => 'onDependencySolve',
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',
// Use our own constant to make this event optional. Once
// composer-1.1 is required, this can use PluginEvents::INIT
// instead.
self::COMPAT_PLUGINEVENTS_INIT =>
array('onInit', self::CALLBACK_PRIORITY),
InstallerEvents::PRE_DEPENDENCIES_SOLVING =>
array('onDependencySolve', self::CALLBACK_PRIORITY),
PackageEvents::POST_PACKAGE_INSTALL =>
array('onPostPackageInstall', self::CALLBACK_PRIORITY),
ScriptEvents::POST_INSTALL_CMD =>
array('onPostInstallOrUpdate', self::CALLBACK_PRIORITY),
ScriptEvents::POST_UPDATE_CMD =>
array('onPostInstallOrUpdate', self::CALLBACK_PRIORITY),
ScriptEvents::PRE_AUTOLOAD_DUMP =>
array('onInstallUpdateOrDump', self::CALLBACK_PRIORITY),
ScriptEvents::PRE_INSTALL_CMD =>
array('onInstallUpdateOrDump', self::CALLBACK_PRIORITY),
ScriptEvents::PRE_UPDATE_CMD =>
array('onInstallUpdateOrDump', self::CALLBACK_PRIORITY),
);
}
/**
* Handle an event callback for initialization.
*
* @param \Composer\EventDispatcher\Event $event
*/
public function onInit(BaseEvent $event)
{
$this->state->loadSettings();
// It is not possible to know if the user specified --dev or --no-dev
// so assume it is false. The dev section will be merged later when
// the other events fire.
$this->state->setDevMode(false);
$this->mergeFiles($this->state->getIncludes(), false);
$this->mergeFiles($this->state->getRequires(), true);
}
/**
* Handle an event callback for an install, update or dump command by
* checking for "merge-plugin" in the "extra" data and merging package
* contents if found.
*
* @param Event $event
* @param ScriptEvent $event
*/
public function onInstallUpdateOrDump(Event $event)
public function onInstallUpdateOrDump(ScriptEvent $event)
{
$this->state->loadSettings();
$this->state->setDevMode($event->isDevMode());
@@ -196,16 +242,32 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
*/
protected function mergeFile(RootPackageInterface $root, $path)
{
if (isset($this->loadedFiles[$path])) {
$this->logger->debug("Already merged <comment>$path</comment>");
if (isset($this->loaded[$path]) ||
(isset($this->loadedNoDev[$path]) && !$this->state->isDevMode())
) {
$this->logger->debug(
"Already merged <comment>$path</comment> completely"
);
return;
} else {
$this->loadedFiles[$path] = true;
}
$this->logger->info("Loading <comment>{$path}</comment>...");
$package = new ExtraPackage($path, $this->composer, $this->logger);
$package->mergeInto($root, $this->state);
if (isset($this->loadedNoDev[$path])) {
$this->logger->info(
"Loading -dev sections of <comment>{$path}</comment>..."
);
$package->mergeDevInto($root, $this->state);
} else {
$this->logger->info("Loading <comment>{$path}</comment>...");
$package->mergeInto($root, $this->state);
}
if ($this->state->isDevMode()) {
$this->loaded[$path] = true;
} else {
$this->loadedNoDev[$path] = true;
}
if ($this->state->recurseIncludes()) {
$this->mergeFiles($package->getIncludes(), false);
@@ -230,7 +292,12 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
);
$request->install($link->getTarget(), $link->getConstraint());
}
if ($this->state->isDevMode()) {
// Issue #113: Check devMode of event rather than our global state.
// Composer fires the PRE_DEPENDENCIES_SOLVING event twice for
// `--no-dev` operations to decide which packages are dev only
// requirements.
if ($this->state->shouldMergeDev() && $event->isDevMode()) {
foreach ($this->state->getDuplicateLinks('require-dev') as $link) {
$this->logger->info(
"Adding dev dependency <comment>{$link}</comment>"
@@ -266,9 +333,9 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
* plugin was installed during the run then trigger an update command to
* process any merge-patterns in the current config.
*
* @param Event $event
* @param ScriptEvent $event
*/
public function onPostInstallOrUpdate(Event $event)
public function onPostInstallOrUpdate(ScriptEvent $event)
{
// @codeCoverageIgnoreStart
if ($this->state->isFirstInstall()) {