first commit
This commit is contained in:
19
vendor/wikimedia/composer-merge-plugin/LICENSE
vendored
Normal file
19
vendor/wikimedia/composer-merge-plugin/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
251
vendor/wikimedia/composer-merge-plugin/README.md
vendored
Normal file
251
vendor/wikimedia/composer-merge-plugin/README.md
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
[![Latest Stable Version]](https://packagist.org/packages/wikimedia/composer-merge-plugin) [![License]](https://github.com/wikimedia/composer-merge-plugin/blob/master/LICENSE)
|
||||
[![Build Status]](https://github.com/wikimedia/composer-merge-plugin/actions/workflows/CI.yaml)
|
||||
[![Code Coverage]](https://scrutinizer-ci.com/g/wikimedia/composer-merge-plugin/?branch=master)
|
||||
|
||||
Composer Merge Plugin
|
||||
=====================
|
||||
|
||||
Merge multiple composer.json files at [Composer] runtime.
|
||||
|
||||
Composer Merge Plugin is intended to allow easier dependency management for
|
||||
applications which ship a composer.json file and expect some deployments to
|
||||
install additional Composer managed libraries. It does this by allowing the
|
||||
application's top level `composer.json` file to provide a list of optional
|
||||
additional configuration files. When Composer is run it will parse these files
|
||||
and merge their configuration settings into the base configuration. This
|
||||
combined configuration will then be used when downloading additional libraries
|
||||
and generating the autoloader.
|
||||
|
||||
Composer Merge Plugin was created to help with installation of [MediaWiki]
|
||||
which has core library requirements as well as optional libraries and
|
||||
extensions which may be managed via Composer.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Composer Merge Plugin 1.4.x (and older) requires Composer 1.x.
|
||||
|
||||
Composer Merge Plugin 2.0.x (and newer) is compatible with both Composer 2.x and 1.x.
|
||||
|
||||
```
|
||||
$ composer require wikimedia/composer-merge-plugin
|
||||
```
|
||||
|
||||
### Upgrading from Composer 1 to 2
|
||||
|
||||
If you are already using Composer Merge Plugin 1.4 (or older) and you are updating the plugin to 2.0 (or newer), it is recommended that you update the plugin first using Composer 1.
|
||||
|
||||
If you update the incompatible plugin using Composer 2, the plugin will be ignored:
|
||||
|
||||
> The "wikimedia/composer-merge-plugin" plugin was skipped because it requires a Plugin API version ("^1.0") that does not match your Composer installation ("2.0.0"). You may need to run composer update with the "--no-plugins" option.
|
||||
|
||||
Consequently, Composer will be unaware of the merged dependencies and will remove them requiring you to run `composer update` again to reinstall merged dependencies.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"wikimedia/composer-merge-plugin": "dev-master"
|
||||
},
|
||||
"extra": {
|
||||
"merge-plugin": {
|
||||
"include": [
|
||||
"composer.local.json",
|
||||
"extensions/*/composer.json"
|
||||
],
|
||||
"require": [
|
||||
"submodule/composer.json"
|
||||
],
|
||||
"recurse": true,
|
||||
"replace": false,
|
||||
"ignore-duplicates": false,
|
||||
"merge-dev": true,
|
||||
"merge-extra": false,
|
||||
"merge-extra-deep": false,
|
||||
"merge-replace": true,
|
||||
"merge-scripts": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Updating sub-levels `composer.json` files
|
||||
|
||||
|
||||
In order for Composer Merge Plugin to install dependencies from updated or newly created sub-level `composer.json` files in your project you need to run the command:
|
||||
|
||||
```
|
||||
$ composer update
|
||||
```
|
||||
|
||||
This will [instruct Composer to recalculate the file hash](https://getcomposer.org/doc/03-cli.md#update) for the top-level `composer.json` thus triggering Composer Merge Plugin to look for the sub-level configuration files and update your dependencies.
|
||||
|
||||
|
||||
Plugin configuration
|
||||
--------------------
|
||||
|
||||
The plugin reads its configuration from the `merge-plugin` section of your
|
||||
composer.json's `extra` section. An `include` setting is required to tell
|
||||
Composer Merge Plugin which file(s) to merge.
|
||||
|
||||
|
||||
### include
|
||||
|
||||
The `include` setting can specify either a single value or an array of values.
|
||||
Each value is treated as a PHP `glob()` pattern identifying additional
|
||||
composer.json style configuration files to merge into the root package
|
||||
configuration for the current Composer execution.
|
||||
|
||||
The following sections of the found configuration files will be merged into
|
||||
the Composer root package configuration as though they were directly included
|
||||
in the top-level composer.json file:
|
||||
|
||||
* [autoload](https://getcomposer.org/doc/04-schema.md#autoload)
|
||||
* [autoload-dev](https://getcomposer.org/doc/04-schema.md#autoload-dev)
|
||||
(optional, see [merge-dev](#merge-dev) below)
|
||||
* [conflict](https://getcomposer.org/doc/04-schema.md#conflict)
|
||||
* [provide](https://getcomposer.org/doc/04-schema.md#provide)
|
||||
* [replace](https://getcomposer.org/doc/04-schema.md#replace)
|
||||
(optional, see [merge-replace](#merge-replace) below)
|
||||
* [repositories](https://getcomposer.org/doc/04-schema.md#repositories)
|
||||
* [require](https://getcomposer.org/doc/04-schema.md#require)
|
||||
* [require-dev](https://getcomposer.org/doc/04-schema.md#require-dev)
|
||||
(optional, see [merge-dev](#merge-dev) below)
|
||||
* [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
|
||||
a pattern fails to match at least one file then it will cause an error.
|
||||
|
||||
### recurse
|
||||
|
||||
By default the merge plugin is recursive; if an included file has
|
||||
a `merge-plugin` section it will also be processed. This functionality can be
|
||||
disabled by adding a `"recurse": false` setting.
|
||||
|
||||
|
||||
### replace
|
||||
|
||||
By default, Composer's conflict resolution engine is used to determine which
|
||||
version of a package should be installed when multiple files specify the same
|
||||
package. A `"replace": true` setting can be provided to change to a "last
|
||||
version specified wins" conflict resolution strategy. In this mode, duplicate
|
||||
package declarations found in merged files will overwrite the declarations
|
||||
made by earlier files. Files are loaded in the order specified by the
|
||||
`include` setting with globbed files being processed in alphabetical order.
|
||||
|
||||
### ignore-duplicates
|
||||
|
||||
By default, Composer's conflict resolution engine is used to determine which
|
||||
version of a package should be installed when multiple files specify the same
|
||||
package. An `"ignore-duplicates": true` setting can be provided to change to
|
||||
a "first version specified wins" conflict resolution strategy. In this mode,
|
||||
duplicate package declarations found in merged files will be ignored in favor
|
||||
of the declarations made by earlier files. Files are loaded in the order
|
||||
specified by the `include` setting with globbed files being processed in
|
||||
alphabetical order.
|
||||
|
||||
Note: `"replace": true` and `"ignore-duplicates": true` modes are mutually
|
||||
exclusive. If both are set, `"ignore-duplicates": true` will be used.
|
||||
|
||||
### merge-dev
|
||||
|
||||
By default, `autoload-dev` and `require-dev` sections of included files are
|
||||
merged. A `"merge-dev": false` setting will disable this behavior.
|
||||
|
||||
|
||||
### merge-extra
|
||||
|
||||
A `"merge-extra": true` setting enables the merging the contents of the
|
||||
`extra` section of included files as well. The normal merge mode for the extra
|
||||
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). 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-replace
|
||||
|
||||
By default, the `replace` section of included files are merged.
|
||||
A `"merge-replace": false` setting will disable this behavior.
|
||||
|
||||
### 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
|
||||
-------------
|
||||
|
||||
```
|
||||
$ composer install
|
||||
$ composer test
|
||||
```
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Bug, feature requests and other issues should be reported to the [GitHub
|
||||
project]. We accept code and documentation contributions via Pull Requests on
|
||||
GitHub as well.
|
||||
|
||||
- [PSR-2 Coding Standard][] is used by the project. The included test
|
||||
configuration uses [PHP_CodeSniffer][] to validate the conventions.
|
||||
- Tests are encouraged. Our test coverage isn't perfect but we'd like it to
|
||||
get better rather than worse, so please try to include tests with your
|
||||
changes.
|
||||
- Keep the documentation up to date. Make sure `README.md` and other
|
||||
relevant documentation is kept up to date with your changes.
|
||||
- One pull request per feature. Try to keep your changes focused on solving
|
||||
a single problem. This will make it easier for us to review the change and
|
||||
easier for you to make sure you have updated the necessary tests and
|
||||
documentation.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Composer Merge plugin is licensed under the MIT license. See the
|
||||
[`LICENSE`](LICENSE) file for more details.
|
||||
|
||||
|
||||
---
|
||||
[Composer]: https://getcomposer.org/
|
||||
[MediaWiki]: https://www.mediawiki.org/wiki/MediaWiki
|
||||
[GitHub project]: https://github.com/wikimedia/composer-merge-plugin
|
||||
[PSR-2 Coding Standard]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
|
||||
[PHP_CodeSniffer]: http://pear.php.net/package/PHP_CodeSniffer
|
||||
[Latest Stable Version]: https://img.shields.io/packagist/v/wikimedia/composer-merge-plugin.svg?style=flat
|
||||
[License]: https://img.shields.io/packagist/l/wikimedia/composer-merge-plugin.svg?style=flat
|
||||
[Build Status]: https://github.com/wikimedia/composer-merge-plugin/actions/workflows/CI.yaml/badge.svg
|
||||
[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
|
57
vendor/wikimedia/composer-merge-plugin/composer.json
vendored
Normal file
57
vendor/wikimedia/composer-merge-plugin/composer.json
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "wikimedia/composer-merge-plugin",
|
||||
"description": "Composer plugin to merge multiple composer.json files",
|
||||
"type": "composer-plugin",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bryan Davis",
|
||||
"email": "bd808@wikimedia.org"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"composer-plugin-api": "^1.1||^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-json": "*",
|
||||
"composer/composer": "^1.1||^2.0",
|
||||
"mediawiki/mediawiki-phan-config": "0.11.1",
|
||||
"php-parallel-lint/php-parallel-lint": "~1.3.1",
|
||||
"phpspec/prophecy": "~1.15.0",
|
||||
"phpunit/phpunit": "^8.5||^9.0",
|
||||
"squizlabs/php_codesniffer": "~3.7.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Wikimedia\\Composer\\Merge\\V2\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
},
|
||||
"class": "Wikimedia\\Composer\\Merge\\V2\\MergePlugin"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"coverage": [
|
||||
"phpunit --log-junit=reports/unitreport.xml --coverage-text --coverage-html=reports/coverage --coverage-clover=reports/coverage.xml",
|
||||
"phpcs --encoding=utf-8 --standard=PSR2 --report-checkstyle=reports/checkstyle-phpcs.xml --report-full --extensions=php src/* tests/phpunit/*"
|
||||
],
|
||||
"phan": "phan -d . --long-progress-bar --allow-polyfill-parser",
|
||||
"phpcs": "phpcs --encoding=utf-8 --standard=PSR2 --extensions=php src/* tests/phpunit/*",
|
||||
"phpunit": "phpunit",
|
||||
"test": [
|
||||
"composer validate --no-interaction",
|
||||
"parallel-lint src tests",
|
||||
"@phpunit",
|
||||
"@phpcs"
|
||||
]
|
||||
}
|
||||
}
|
750
vendor/wikimedia/composer-merge-plugin/src/ExtraPackage.php
vendored
Normal file
750
vendor/wikimedia/composer-merge-plugin/src/ExtraPackage.php
vendored
Normal file
@ -0,0 +1,750 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\CompletePackage;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\Loader\ArrayLoader;
|
||||
use Composer\Package\RootAliasPackage;
|
||||
use Composer\Package\RootPackage;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Semver\Intervals;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Processing for a composer.json file that will be merged into
|
||||
* a RootPackageInterface
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class ExtraPackage
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var Logger $logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var string $path
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array $json
|
||||
*/
|
||||
protected $json;
|
||||
|
||||
/**
|
||||
* @var CompletePackage $package
|
||||
*/
|
||||
protected $package;
|
||||
|
||||
/**
|
||||
* @var array<string, bool> $mergedRequirements
|
||||
*/
|
||||
protected $mergedRequirements = [];
|
||||
|
||||
/**
|
||||
* @var VersionParser $versionParser
|
||||
*/
|
||||
protected $versionParser;
|
||||
|
||||
/**
|
||||
* @param string $path Path to composer.json file
|
||||
* @param Composer $composer
|
||||
* @param Logger $logger
|
||||
*/
|
||||
public function __construct($path, Composer $composer, Logger $logger)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->composer = $composer;
|
||||
$this->logger = $logger;
|
||||
$this->json = $this->readPackageJson($path);
|
||||
$this->package = $this->loadPackage($this->json);
|
||||
$this->versionParser = new VersionParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of additional packages to include if precessing recursively.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIncludes()
|
||||
{
|
||||
return isset($this->json['extra']['merge-plugin']['include']) ?
|
||||
$this->fixRelativePaths($this->json['extra']['merge-plugin']['include']) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of additional packages to require if precessing recursively.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequires()
|
||||
{
|
||||
return isset($this->json['extra']['merge-plugin']['require']) ?
|
||||
$this->fixRelativePaths($this->json['extra']['merge-plugin']['require']) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of merged requirements from this package.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMergedRequirements()
|
||||
{
|
||||
return array_keys($this->mergedRequirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the contents of a composer.json style file into an array.
|
||||
*
|
||||
* The package contents are fixed up to be usable to create a Package
|
||||
* object by providing dummy "name" and "version" values if they have not
|
||||
* been provided in the file. This is consistent with the default root
|
||||
* package loading behavior of Composer.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function readPackageJson($path)
|
||||
{
|
||||
$file = new JsonFile($path);
|
||||
$json = $file->read();
|
||||
if (!isset($json['name'])) {
|
||||
$json['name'] = 'merge-plugin/' .
|
||||
strtr($path, DIRECTORY_SEPARATOR, '-');
|
||||
}
|
||||
if (!isset($json['version'])) {
|
||||
$json['version'] = '1.0.0';
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $json
|
||||
* @return CompletePackage
|
||||
*/
|
||||
protected function loadPackage(array $json)
|
||||
{
|
||||
$loader = new ArrayLoader();
|
||||
$package = $loader->load($json);
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$package instanceof CompletePackage) {
|
||||
throw new UnexpectedValueException(
|
||||
'Expected instance of CompletePackage, got ' .
|
||||
get_class($package)
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this package into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeInto(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$this->prependRepositories($root);
|
||||
|
||||
$this->mergeRequires('require', $root, $state);
|
||||
|
||||
$this->mergePackageLinks('conflict', $root);
|
||||
|
||||
if ($state->shouldMergeReplace()) {
|
||||
$this->mergePackageLinks('replace', $root);
|
||||
}
|
||||
|
||||
$this->mergePackageLinks('provide', $root);
|
||||
|
||||
$this->mergeSuggests($root);
|
||||
|
||||
$this->mergeAutoload('autoload', $root);
|
||||
|
||||
$this->mergeExtra($root, $state);
|
||||
|
||||
$this->mergeScripts($root, $state);
|
||||
|
||||
if ($state->isDevMode()) {
|
||||
$this->mergeDevInto($root, $state);
|
||||
} else {
|
||||
$this->mergeReferences($root);
|
||||
$this->mergeAliases($root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge just the dev portion into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeDevInto(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$this->mergeRequires('require-dev', $root, $state);
|
||||
$this->mergeAutoload('devAutoload', $root);
|
||||
$this->mergeReferences($root);
|
||||
$this->mergeAliases($root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a collection of repositories described by the given configuration
|
||||
* to the given package and the global repository manager.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function prependRepositories(RootPackageInterface $root)
|
||||
{
|
||||
if (!isset($this->json['repositories'])) {
|
||||
return;
|
||||
}
|
||||
$repoManager = $this->composer->getRepositoryManager();
|
||||
$newRepos = [];
|
||||
|
||||
foreach ($this->json['repositories'] as $repoJson) {
|
||||
if (!isset($repoJson['type'])) {
|
||||
continue;
|
||||
}
|
||||
if ($repoJson['type'] === 'path' && isset($repoJson['url'])) {
|
||||
$repoJson['url'] = $this->fixRelativePaths(array($repoJson['url']))[0];
|
||||
}
|
||||
$this->logger->info("Prepending {$repoJson['type']} repository");
|
||||
$repo = $repoManager->createRepository(
|
||||
$repoJson['type'],
|
||||
$repoJson
|
||||
);
|
||||
$repoManager->prependRepository($repo);
|
||||
$newRepos[] = $repo;
|
||||
}
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setRepositories');
|
||||
$unwrapped->setRepositories(array_merge(
|
||||
$newRepos,
|
||||
$root->getRepositories()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge require or require-dev into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'require' or 'require-dev'
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
protected function mergeRequires(
|
||||
$type,
|
||||
RootPackageInterface $root,
|
||||
PluginState $state
|
||||
) {
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$getter = 'get' . ucfirst($linkType['method']);
|
||||
$setter = 'set' . ucfirst($linkType['method']);
|
||||
|
||||
$requires = $this->package->{$getter}();
|
||||
if (empty($requires)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->mergeStabilityFlags($root, $requires);
|
||||
|
||||
$requires = $this->replaceSelfVersionDependencies(
|
||||
$type,
|
||||
$requires,
|
||||
$root
|
||||
);
|
||||
|
||||
$root->{$setter}($this->mergeOrDefer(
|
||||
$type,
|
||||
$root->{$getter}(),
|
||||
$requires,
|
||||
$state
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two collections of package links and collect duplicates for
|
||||
* subsequent processing.
|
||||
*
|
||||
* @param string $type 'require' or 'require-dev'
|
||||
* @param array $origin Primary collection
|
||||
* @param array $merge Additional collection
|
||||
* @param PluginState $state
|
||||
* @return array Merged collection
|
||||
*/
|
||||
protected function mergeOrDefer(
|
||||
$type,
|
||||
array $origin,
|
||||
array $merge,
|
||||
PluginState $state
|
||||
) {
|
||||
if ($state->ignoreDuplicateLinks() && $state->replaceDuplicateLinks()) {
|
||||
$this->logger->warning("Both replace and ignore-duplicates are true. These are mutually exclusive.");
|
||||
$this->logger->warning("Duplicate packages will be ignored.");
|
||||
}
|
||||
|
||||
foreach ($merge as $name => $link) {
|
||||
if (isset($origin[$name])) {
|
||||
if ($state->ignoreDuplicateLinks()) {
|
||||
$this->logger->info("Ignoring duplicate <comment>{$name}</comment>");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$this->logger->info("Replacing <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $link;
|
||||
} else {
|
||||
$this->logger->info("Merging <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $this->mergeConstraints($origin[$name], $link, $state);
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Adding <comment>{$name}</comment>");
|
||||
$this->mergedRequirements[$name] = true;
|
||||
$origin[$name] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$state->isComposer1()) {
|
||||
Intervals::clear();
|
||||
}
|
||||
|
||||
return $origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge package constraints.
|
||||
*
|
||||
* Adapted from Composer's UpdateCommand::appendConstraintToLink
|
||||
*
|
||||
* @param Link $origin The base package link.
|
||||
* @param Link $merge The related package link to merge.
|
||||
* @param PluginState $state
|
||||
* @return Link Merged link.
|
||||
*/
|
||||
protected function mergeConstraints(Link $origin, Link $merge, PluginState $state)
|
||||
{
|
||||
$oldPrettyString = $origin->getConstraint()->getPrettyString();
|
||||
$newPrettyString = $merge->getConstraint()->getPrettyString();
|
||||
|
||||
if ($state->isComposer1()) {
|
||||
$constraintClass = MultiConstraint::class;
|
||||
} else {
|
||||
$constraintClass = \Composer\Semver\Constraint\MultiConstraint::class;
|
||||
|
||||
if (Intervals::isSubsetOf($origin->getConstraint(), $merge->getConstraint())) {
|
||||
return $origin;
|
||||
}
|
||||
|
||||
if (Intervals::isSubsetOf($merge->getConstraint(), $origin->getConstraint())) {
|
||||
return $merge;
|
||||
}
|
||||
}
|
||||
|
||||
$newConstraint = $constraintClass::create([
|
||||
$origin->getConstraint(),
|
||||
$merge->getConstraint()
|
||||
], true);
|
||||
$newConstraint->setPrettyString($oldPrettyString.', '.$newPrettyString);
|
||||
|
||||
return new Link(
|
||||
$origin->getSource(),
|
||||
$origin->getTarget(),
|
||||
$newConstraint,
|
||||
$origin->getDescription(),
|
||||
$origin->getPrettyConstraint() . ', ' . $newPrettyString
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge autoload or autoload-dev into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'autoload' or 'devAutoload'
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeAutoload($type, RootPackageInterface $root)
|
||||
{
|
||||
$getter = 'get' . ucfirst($type);
|
||||
$setter = 'set' . ucfirst($type);
|
||||
|
||||
$autoload = $this->package->{$getter}();
|
||||
if (empty($autoload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, $setter);
|
||||
$unwrapped->{$setter}(array_merge_recursive(
|
||||
$root->{$getter}(),
|
||||
$this->fixRelativePaths($autoload)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix a collection of paths that are relative to this package to be
|
||||
* relative to the base package.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return array
|
||||
*/
|
||||
protected function fixRelativePaths(array $paths)
|
||||
{
|
||||
$base = dirname($this->path);
|
||||
$base = ($base === '.') ? '' : "{$base}/";
|
||||
|
||||
array_walk_recursive(
|
||||
$paths,
|
||||
function (&$path) use ($base) {
|
||||
$path = "{$base}{$path}";
|
||||
}
|
||||
);
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and merge stability flags from the given collection of
|
||||
* requires and merge them into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param array $requires
|
||||
*/
|
||||
protected function mergeStabilityFlags(
|
||||
RootPackageInterface $root,
|
||||
array $requires
|
||||
) {
|
||||
$flags = $root->getStabilityFlags();
|
||||
$sf = new StabilityFlags($flags, $root->getMinimumStability());
|
||||
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setStabilityFlags');
|
||||
$unwrapped->setStabilityFlags(array_merge(
|
||||
$flags,
|
||||
$sf->extractAll($requires)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge package links of the given type into a RootPackageInterface
|
||||
*
|
||||
* @param string $type 'conflict', 'replace' or 'provide'
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergePackageLinks($type, RootPackageInterface $root)
|
||||
{
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$getter = 'get' . ucfirst($linkType['method']);
|
||||
$setter = 'set' . ucfirst($linkType['method']);
|
||||
|
||||
$links = $this->package->{$getter}();
|
||||
if (!empty($links)) {
|
||||
$unwrapped = self::unwrapIfNeeded($root, $setter);
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($root !== $unwrapped) {
|
||||
$this->logger->warning(
|
||||
'This Composer version does not support ' .
|
||||
"'{$type}' merging for aliased packages."
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
$unwrapped->{$setter}(array_merge(
|
||||
$root->{$getter}(),
|
||||
$this->replaceSelfVersionDependencies($type, $links, $root)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge suggested packages into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeSuggests(RootPackageInterface $root)
|
||||
{
|
||||
$suggests = $this->package->getSuggests();
|
||||
if (!empty($suggests)) {
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setSuggests');
|
||||
$unwrapped->setSuggests(array_merge(
|
||||
$root->getSuggests(),
|
||||
$suggests
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge extra config into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeExtra(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$extra = $this->package->getExtra();
|
||||
unset($extra['merge-plugin']);
|
||||
if (!$state->shouldMergeExtra() || empty($extra)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootExtra = $root->getExtra();
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setExtra');
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$unwrapped->setExtra(
|
||||
self::mergeExtraArray($state->shouldMergeExtraDeep(), $rootExtra, $extra)
|
||||
);
|
||||
} else {
|
||||
if (!$state->shouldMergeExtraDeep()) {
|
||||
foreach (array_intersect(
|
||||
array_keys($extra),
|
||||
array_keys($rootExtra)
|
||||
) as $key) {
|
||||
$this->logger->info(
|
||||
"Ignoring duplicate <comment>{$key}</comment> in ".
|
||||
"<comment>{$this->path}</comment> extra config."
|
||||
);
|
||||
}
|
||||
}
|
||||
$unwrapped->setExtra(
|
||||
self::mergeExtraArray($state->shouldMergeExtraDeep(), $extra, $rootExtra)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge scripts config into a RootPackageInterface
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param PluginState $state
|
||||
*/
|
||||
public function mergeScripts(RootPackageInterface $root, PluginState $state)
|
||||
{
|
||||
$scripts = $this->package->getScripts();
|
||||
if (!$state->shouldMergeScripts() || empty($scripts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScripts = $root->getScripts();
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setScripts');
|
||||
|
||||
if ($state->replaceDuplicateLinks()) {
|
||||
$unwrapped->setScripts(
|
||||
array_merge($rootScripts, $scripts)
|
||||
);
|
||||
} else {
|
||||
$unwrapped->setScripts(
|
||||
array_merge($scripts, $rootScripts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two arrays either via arrayMergeDeep or via array_merge.
|
||||
*
|
||||
* @param bool $mergeDeep
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return array
|
||||
*/
|
||||
public static function mergeExtraArray($mergeDeep, $array1, $array2)
|
||||
{
|
||||
if ($mergeDeep) {
|
||||
return NestedArray::mergeDeep($array1, $array2);
|
||||
}
|
||||
|
||||
return array_merge($array1, $array2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Links with a 'self.version' constraint with the root package's
|
||||
* version.
|
||||
*
|
||||
* @param string $type Link type
|
||||
* @param array $links
|
||||
* @param RootPackageInterface $root
|
||||
* @return array
|
||||
*/
|
||||
protected function replaceSelfVersionDependencies(
|
||||
$type,
|
||||
array $links,
|
||||
RootPackageInterface $root
|
||||
) {
|
||||
$linkType = BasePackage::$supportedLinkTypes[$type];
|
||||
$version = $root->getVersion();
|
||||
$prettyVersion = $root->getPrettyVersion();
|
||||
$vp = $this->versionParser;
|
||||
|
||||
$method = 'get' . ucfirst($linkType['method']);
|
||||
$packages = $root->$method();
|
||||
|
||||
return array_map(
|
||||
static function ($link) use ($linkType, $version, $prettyVersion, $vp, $packages) {
|
||||
if ('self.version' === $link->getPrettyConstraint()) {
|
||||
if (isset($packages[$link->getSource()])) {
|
||||
/** @var Link $package */
|
||||
$package = $packages[$link->getSource()];
|
||||
return new Link(
|
||||
$link->getSource(),
|
||||
$link->getTarget(),
|
||||
$vp->parseConstraints($package->getConstraint()->getPrettyString()),
|
||||
$linkType['description'],
|
||||
$package->getPrettyConstraint()
|
||||
);
|
||||
}
|
||||
|
||||
return new Link(
|
||||
$link->getSource(),
|
||||
$link->getTarget(),
|
||||
$vp->parseConstraints($version),
|
||||
$linkType['description'],
|
||||
$prettyVersion
|
||||
);
|
||||
}
|
||||
return $link;
|
||||
},
|
||||
$links
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full featured Package from a RootPackageInterface.
|
||||
*
|
||||
* In Composer versions before 599ad77 the RootPackageInterface only
|
||||
* defines a sub-set of operations needed by composer-merge-plugin and
|
||||
* RootAliasPackage only implemented those methods defined by the
|
||||
* interface. Most of the unimplemented methods in RootAliasPackage can be
|
||||
* worked around because the getter methods that are implemented proxy to
|
||||
* the aliased package which we can modify by unwrapping. The exception
|
||||
* being modifying the 'conflicts', 'provides' and 'replaces' collections.
|
||||
* We have no way to actually modify those collections unfortunately in
|
||||
* older versions of Composer.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param string $method Method needed
|
||||
* @return RootPackageInterface|RootPackage
|
||||
*/
|
||||
public static function unwrapIfNeeded(
|
||||
RootPackageInterface $root,
|
||||
$method = 'setExtra'
|
||||
) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($root instanceof RootAliasPackage &&
|
||||
!method_exists($root, $method)
|
||||
) {
|
||||
// Unwrap and return the aliased RootPackage.
|
||||
$root = $root->getAliasOf();
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $root;
|
||||
}
|
||||
|
||||
protected function mergeAliases(RootPackageInterface $root)
|
||||
{
|
||||
$aliases = [];
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setAliases');
|
||||
foreach (array('require', 'require-dev') as $linkType) {
|
||||
$linkInfo = BasePackage::$supportedLinkTypes[$linkType];
|
||||
$method = 'get'.ucfirst($linkInfo['method']);
|
||||
$links = [];
|
||||
foreach ($unwrapped->$method() as $link) {
|
||||
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
||||
}
|
||||
$aliases = $this->extractAliases($links, $aliases);
|
||||
}
|
||||
$unwrapped->setAliases($aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract aliases from version constraints (dev-branch as 1.0.0).
|
||||
*
|
||||
* @param array $requires
|
||||
* @param array $aliases
|
||||
* @return array
|
||||
* @see RootPackageLoader::extractAliases()
|
||||
*/
|
||||
protected function extractAliases(array $requires, array $aliases)
|
||||
{
|
||||
foreach ($requires as $reqName => $reqVersion) {
|
||||
if (preg_match('{^([^,\s#]+)(?:#[^ ]+)? +as +([^,\s]+)$}', $reqVersion, $match)) {
|
||||
$aliases[] = [
|
||||
'package' => strtolower($reqName),
|
||||
'version' => $this->versionParser->normalize($match[1], $reqVersion),
|
||||
'alias' => $match[2],
|
||||
'alias_normalized' => $this->versionParser->normalize($match[2], $reqVersion),
|
||||
];
|
||||
} elseif (strpos($reqVersion, ' as ') !== false) {
|
||||
throw new UnexpectedValueException(
|
||||
'Invalid alias definition in "'.$reqName.'": "'.$reqVersion.'". '
|
||||
. 'Aliases should be in the form "exact-version as other-exact-version".'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the root packages reference information.
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
*/
|
||||
protected function mergeReferences(RootPackageInterface $root)
|
||||
{
|
||||
// Merge source reference information for merged packages.
|
||||
// @see RootPackageLoader::load
|
||||
$references = [];
|
||||
$unwrapped = self::unwrapIfNeeded($root, 'setReferences');
|
||||
foreach (['require', 'require-dev'] as $linkType) {
|
||||
$linkInfo = BasePackage::$supportedLinkTypes[$linkType];
|
||||
$method = 'get'.ucfirst($linkInfo['method']);
|
||||
$links = [];
|
||||
foreach ($unwrapped->$method() as $link) {
|
||||
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
||||
}
|
||||
$references = $this->extractReferences($links, $references);
|
||||
}
|
||||
$unwrapped->setReferences($references);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract vcs revision from version constraint (dev-master#abc123.
|
||||
*
|
||||
* @param array $requires
|
||||
* @param array $references
|
||||
* @return array
|
||||
* @see RootPackageLoader::extractReferences()
|
||||
*/
|
||||
protected function extractReferences(array $requires, array $references)
|
||||
{
|
||||
foreach ($requires as $reqName => $reqVersion) {
|
||||
$reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
|
||||
$stabilityName = VersionParser::parseStability($reqVersion);
|
||||
if ($stabilityName === 'dev' &&
|
||||
preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match)
|
||||
) {
|
||||
$name = strtolower($reqName);
|
||||
$references[$name] = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $references;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
102
vendor/wikimedia/composer-merge-plugin/src/Logger.php
vendored
Normal file
102
vendor/wikimedia/composer-merge-plugin/src/Logger.php
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* Simple logging wrapper for Composer\IO\IOInterface
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
/**
|
||||
* @var string $name
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var IOInterface $inputOutput
|
||||
*/
|
||||
protected $inputOutput;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param IOInterface $io
|
||||
*/
|
||||
public function __construct($name, IOInterface $io)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->inputOutput = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a debug message
|
||||
*
|
||||
* Messages will be output at the "very verbose" logging level (eg `-vv`
|
||||
* needed on the Composer command).
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function debug($message)
|
||||
{
|
||||
if ($this->inputOutput->isVeryVerbose()) {
|
||||
$message = " <info>[{$this->name}]</info> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an informative message
|
||||
*
|
||||
* Messages will be output at the "verbose" logging level (eg `-v` needed
|
||||
* on the Composer command).
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function info($message)
|
||||
{
|
||||
if ($this->inputOutput->isVerbose()) {
|
||||
$message = " <info>[{$this->name}]</info> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning message
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function warning($message)
|
||||
{
|
||||
$message = " <error>[{$this->name}]</error> {$message}";
|
||||
$this->log($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function log($message)
|
||||
{
|
||||
if (method_exists($this->inputOutput, 'writeError')) {
|
||||
$this->inputOutput->writeError($message);
|
||||
} else {
|
||||
// @codeCoverageIgnoreStart
|
||||
// Backwards compatibility for Composer before cb336a5
|
||||
$this->inputOutput->write($message);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
396
vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
vendored
Normal file
396
vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
vendored
Normal file
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\EventDispatcher\Event as BaseEvent;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\Factory;
|
||||
use Composer\Installer;
|
||||
use Composer\Installer\PackageEvent;
|
||||
use Composer\Installer\PackageEvents;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
use Composer\Script\Event as ScriptEvent;
|
||||
use Composer\Script\ScriptEvents;
|
||||
|
||||
/**
|
||||
* Composer plugin that allows merging multiple composer.json files.
|
||||
*
|
||||
* When installed, this plugin will look for a "merge-plugin" key in the
|
||||
* composer configuration's "extra" section. The value for this key is
|
||||
* a set of options configuring the plugin.
|
||||
*
|
||||
* An "include" setting is required. The value of this setting can be either
|
||||
* a single value or an array of values. Each value is treated as a glob()
|
||||
* pattern identifying additional composer.json style configuration files to
|
||||
* merge into the configuration for the current compser execution.
|
||||
*
|
||||
* The "autoload", "autoload-dev", "conflict", "provide", "replace",
|
||||
* "repositories", "require", "require-dev", and "suggest" sections of the
|
||||
* found configuration files will be merged into the root package
|
||||
* configuration as though they were directly included in the top-level
|
||||
* composer.json file.
|
||||
*
|
||||
* If included files specify conflicting package versions for "require" or
|
||||
* "require-dev", the normal Composer dependency solver process will be used
|
||||
* to attempt to resolve the conflict. Specifying the 'replace' key as true will
|
||||
* change this default behaviour so that the last-defined version of a package
|
||||
* will win, allowing for force-overrides of package defines.
|
||||
*
|
||||
* By default the "extra" section is not merged. This can be enabled by
|
||||
* setitng the 'merge-extra' key to true. In normal mode, when the same key is
|
||||
* found in both the original and the imported extra section, the version in
|
||||
* the original config is used and the imported version is skipped. If
|
||||
* 'replace' mode is active, this behaviour changes so the imported version of
|
||||
* the key is used, replacing the version in the original config.
|
||||
*
|
||||
*
|
||||
* @code
|
||||
* {
|
||||
* "require": {
|
||||
* "wikimedia/composer-merge-plugin": "dev-master"
|
||||
* },
|
||||
* "extra": {
|
||||
* "merge-plugin": {
|
||||
* "include": [
|
||||
* "composer.local.json"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class MergePlugin implements PluginInterface, EventSubscriberInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Official package name
|
||||
*/
|
||||
public const PACKAGE_NAME = 'wikimedia/composer-merge-plugin';
|
||||
|
||||
/**
|
||||
* Priority that plugin uses to register callbacks.
|
||||
*/
|
||||
private const CALLBACK_PRIORITY = 50000;
|
||||
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var PluginState $state
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @var Logger $logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Files that have already been fully processed
|
||||
*
|
||||
* @var array<string, bool> $loaded
|
||||
*/
|
||||
protected $loaded = [];
|
||||
|
||||
/**
|
||||
* Files that have already been partially processed
|
||||
*
|
||||
* @var array<string, bool> $loadedNoDev
|
||||
*/
|
||||
protected $loadedNoDev = [];
|
||||
|
||||
/**
|
||||
* Nested packages to restrict update operations.
|
||||
*
|
||||
* @var array<string, bool> $updateAllowList
|
||||
*/
|
||||
protected $updateAllowList = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->state = new PluginState($this->composer);
|
||||
$this->logger = new Logger('merge-plugin', $io);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
PluginEvents::INIT =>
|
||||
['onInit', self::CALLBACK_PRIORITY],
|
||||
PackageEvents::POST_PACKAGE_INSTALL =>
|
||||
['onPostPackageInstall', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::POST_INSTALL_CMD =>
|
||||
['onPostInstallOrUpdate', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::POST_UPDATE_CMD =>
|
||||
['onPostInstallOrUpdate', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_AUTOLOAD_DUMP =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_INSTALL_CMD =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
ScriptEvents::PRE_UPDATE_CMD =>
|
||||
['onInstallUpdateOrDump', self::CALLBACK_PRIORITY],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of packages to restrict update operations.
|
||||
*
|
||||
* @return string[]
|
||||
* @see \Composer\Installer::setUpdateAllowList()
|
||||
*/
|
||||
public function getUpdateAllowList()
|
||||
{
|
||||
return array_keys($this->updateAllowList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback for initialization.
|
||||
*
|
||||
* @param BaseEvent $event
|
||||
*/
|
||||
public function onInit(BaseEvent $event)
|
||||
{
|
||||
$this->state->loadSettings();
|
||||
// It is not possible to know if the user specified --dev or --no-dev
|
||||
// so assume it is false. The dev section will be merged later when
|
||||
// the other events fire.
|
||||
$this->state->setDevMode(false);
|
||||
$this->mergeFiles($this->state->getIncludes(), false);
|
||||
$this->mergeFiles($this->state->getRequires(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback for an install, update or dump command by
|
||||
* checking for "merge-plugin" in the "extra" data and merging package
|
||||
* contents if found.
|
||||
*
|
||||
* @param ScriptEvent $event
|
||||
*/
|
||||
public function onInstallUpdateOrDump(ScriptEvent $event)
|
||||
{
|
||||
$this->state->loadSettings();
|
||||
$this->state->setDevMode($event->isDevMode());
|
||||
$this->mergeFiles($this->state->getIncludes(), false);
|
||||
$this->mergeFiles($this->state->getRequires(), true);
|
||||
|
||||
if ($event->getName() === ScriptEvents::PRE_AUTOLOAD_DUMP) {
|
||||
$this->state->setDumpAutoloader(true);
|
||||
$flags = $event->getFlags();
|
||||
if (isset($flags['optimize'])) {
|
||||
$this->state->setOptimizeAutoloader($flags['optimize']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find configuration files matching the configured glob patterns and
|
||||
* merge their contents with the master package.
|
||||
*
|
||||
* @param array $patterns List of files/glob patterns
|
||||
* @param bool $required Are the patterns required to match files?
|
||||
* @throws MissingFileException when required and a pattern returns no
|
||||
* results
|
||||
*/
|
||||
protected function mergeFiles(array $patterns, $required = false)
|
||||
{
|
||||
$root = $this->composer->getPackage();
|
||||
|
||||
$files = array_map(
|
||||
static function ($files, $pattern) use ($required) {
|
||||
if ($required && !$files) {
|
||||
throw new MissingFileException(
|
||||
"merge-plugin: No files matched required '{$pattern}'"
|
||||
);
|
||||
}
|
||||
return $files;
|
||||
},
|
||||
array_map('glob', $patterns),
|
||||
$patterns
|
||||
);
|
||||
|
||||
foreach (array_reduce($files, 'array_merge', []) as $path) {
|
||||
$this->mergeFile($root, $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a JSON file and merge its contents
|
||||
*
|
||||
* @param RootPackageInterface $root
|
||||
* @param string $path
|
||||
*/
|
||||
protected function mergeFile(RootPackageInterface $root, $path)
|
||||
{
|
||||
if (isset($this->loaded[$path]) ||
|
||||
(isset($this->loadedNoDev[$path]) && !$this->state->isDevMode())
|
||||
) {
|
||||
$this->logger->debug(
|
||||
"Already merged <comment>$path</comment> completely"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$package = new ExtraPackage($path, $this->composer, $this->logger);
|
||||
|
||||
if (isset($this->loadedNoDev[$path])) {
|
||||
$this->logger->info(
|
||||
"Loading -dev sections of <comment>{$path}</comment>..."
|
||||
);
|
||||
$package->mergeDevInto($root, $this->state);
|
||||
} else {
|
||||
$this->logger->info("Loading <comment>{$path}</comment>...");
|
||||
$package->mergeInto($root, $this->state);
|
||||
}
|
||||
|
||||
$requirements = $package->getMergedRequirements();
|
||||
if (!empty($requirements)) {
|
||||
$this->updateAllowList = array_replace(
|
||||
$this->updateAllowList,
|
||||
array_fill_keys($requirements, true)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->state->isDevMode()) {
|
||||
$this->loaded[$path] = true;
|
||||
} else {
|
||||
$this->loadedNoDev[$path] = true;
|
||||
}
|
||||
|
||||
if ($this->state->recurseIncludes()) {
|
||||
$this->mergeFiles($package->getIncludes(), false);
|
||||
$this->mergeFiles($package->getRequires(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback following installation of a new package by
|
||||
* checking to see if the package that was installed was our plugin.
|
||||
*
|
||||
* @param PackageEvent $event
|
||||
*/
|
||||
public function onPostPackageInstall(PackageEvent $event)
|
||||
{
|
||||
$op = $event->getOperation();
|
||||
if ($op instanceof InstallOperation) {
|
||||
$package = $op->getPackage()->getName();
|
||||
if ($package === self::PACKAGE_NAME) {
|
||||
$this->logger->info('composer-merge-plugin installed');
|
||||
$this->state->setFirstInstall(true);
|
||||
$this->state->setLocked(
|
||||
$event->getComposer()->getLocker()->isLocked()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event callback following an install or update command. If our
|
||||
* plugin was installed during the run then trigger an update command to
|
||||
* process any merge-patterns in the current config.
|
||||
*
|
||||
* @param ScriptEvent $event
|
||||
*/
|
||||
public function onPostInstallOrUpdate(ScriptEvent $event)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->state->isFirstInstall()) {
|
||||
$this->state->setFirstInstall(false);
|
||||
|
||||
$requirements = $this->getUpdateAllowList();
|
||||
if (empty($requirements)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->log("\n".'<info>Running composer update to apply merge settings</info>');
|
||||
|
||||
$lockBackup = null;
|
||||
$lock = null;
|
||||
if (!$this->state->isComposer1()) {
|
||||
$file = Factory::getComposerFile();
|
||||
$lock = Factory::getLockFile($file);
|
||||
if (file_exists($lock)) {
|
||||
$lockBackup = file_get_contents($lock);
|
||||
}
|
||||
}
|
||||
|
||||
$config = $this->composer->getConfig();
|
||||
$preferSource = $config->get('preferred-install') === 'source';
|
||||
$preferDist = $config->get('preferred-install') === 'dist';
|
||||
|
||||
$installer = Installer::create(
|
||||
$event->getIO(),
|
||||
// Create a new Composer instance to ensure full processing of
|
||||
// the merged files.
|
||||
Factory::create($event->getIO(), null, false)
|
||||
);
|
||||
|
||||
$installer->setPreferSource($preferSource);
|
||||
$installer->setPreferDist($preferDist);
|
||||
$installer->setDevMode($event->isDevMode());
|
||||
$installer->setDumpAutoloader($this->state->shouldDumpAutoloader());
|
||||
$installer->setOptimizeAutoloader(
|
||||
$this->state->shouldOptimizeAutoloader()
|
||||
);
|
||||
|
||||
$installer->setUpdate(true);
|
||||
|
||||
if ($this->state->isComposer1()) {
|
||||
// setUpdateWhitelist() only exists in composer 1.x. Configure as to run phan against composer 2.x
|
||||
// @phan-suppress-next-line PhanUndeclaredMethod
|
||||
$installer->setUpdateWhitelist($requirements);
|
||||
} else {
|
||||
$installer->setUpdateAllowList($requirements);
|
||||
}
|
||||
|
||||
$status = $installer->run();
|
||||
if (( $status !== 0 ) && $lockBackup && $lock && !$this->state->isComposer1()) {
|
||||
$this->logger->log(
|
||||
"\n".'<error>'.
|
||||
'Update to apply merge settings failed, reverting '.$lock.' to its original content.'.
|
||||
'</error>'
|
||||
);
|
||||
file_put_contents($lock, $lockBackup);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
20
vendor/wikimedia/composer-merge-plugin/src/MissingFileException.php
vendored
Normal file
20
vendor/wikimedia/composer-merge-plugin/src/MissingFileException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class MissingFileException extends RuntimeException
|
||||
{
|
||||
}
|
114
vendor/wikimedia/composer-merge-plugin/src/MultiConstraint.php
vendored
Normal file
114
vendor/wikimedia/composer-merge-plugin/src/MultiConstraint.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2021 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\EmptyConstraint;
|
||||
use Composer\Semver\Constraint\MultiConstraint as SemverMultiConstraint;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Adapted from Composer's v2 MultiConstraint::create for Composer v1
|
||||
* @link https://github.com/composer/semver/blob/3.2.4/src/Constraint/MultiConstraint.php
|
||||
* @author Chauncey McAskill <chauncey@mcaskill.ca>
|
||||
*/
|
||||
class MultiConstraint extends SemverMultiConstraint
|
||||
{
|
||||
/**
|
||||
* Tries to optimize the constraints as much as possible, meaning
|
||||
* reducing/collapsing congruent constraints etc.
|
||||
* Does not necessarily return a MultiConstraint instance if
|
||||
* things can be reduced to a simple constraint
|
||||
*
|
||||
* @param ConstraintInterface[] $constraints A set of constraints
|
||||
* @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
|
||||
*
|
||||
* @return ConstraintInterface
|
||||
*/
|
||||
public static function create(array $constraints, $conjunctive = true)
|
||||
{
|
||||
if (count($constraints) === 0) {
|
||||
// EmptyConstraint only exists in composer 1.x. Configure as to run phan against composer 2.x
|
||||
// @phan-suppress-next-line PhanTypeMismatchReturn, PhanUndeclaredClassMethod
|
||||
return new EmptyConstraint();
|
||||
}
|
||||
|
||||
if (count($constraints) === 1) {
|
||||
return $constraints[0];
|
||||
}
|
||||
|
||||
$optimized = self::optimizeConstraints($constraints, $conjunctive);
|
||||
if ($optimized !== null) {
|
||||
list($constraints, $conjunctive) = $optimized;
|
||||
if (count($constraints) === 1) {
|
||||
return $constraints[0];
|
||||
}
|
||||
}
|
||||
|
||||
return new self($constraints, $conjunctive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
private static function optimizeConstraints(array $constraints, $conjunctive)
|
||||
{
|
||||
// parse the two OR groups and if they are contiguous we collapse
|
||||
// them into one constraint
|
||||
// [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4]
|
||||
if (!$conjunctive) {
|
||||
$left = $constraints[0];
|
||||
$mergedConstraints = [];
|
||||
$optimized = false;
|
||||
for ($i = 1, $l = count($constraints); $i < $l; $i++) {
|
||||
$right = $constraints[$i];
|
||||
if ($left instanceof SemverMultiConstraint
|
||||
&& $left->conjunctive
|
||||
&& $right instanceof SemverMultiConstraint
|
||||
&& $right->conjunctive
|
||||
&& count($left->constraints) === 2
|
||||
&& count($right->constraints) === 2
|
||||
&& ($left0 = (string) $left->constraints[0])
|
||||
&& $left0[0] === '>' && $left0[1] === '='
|
||||
&& ($left1 = (string) $left->constraints[1])
|
||||
&& $left1[0] === '<'
|
||||
&& ($right0 = (string) $right->constraints[0])
|
||||
&& $right0[0] === '>' && $right0[1] === '='
|
||||
&& ($right1 = (string) $right->constraints[1])
|
||||
&& $right1[0] === '<'
|
||||
&& substr($left1, 2) === substr($right0, 3)
|
||||
) {
|
||||
$optimized = true;
|
||||
$left = new MultiConstraint(
|
||||
[
|
||||
$left->constraints[0],
|
||||
$right->constraints[1],
|
||||
],
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$mergedConstraints[] = $left;
|
||||
$left = $right;
|
||||
}
|
||||
}
|
||||
if ($optimized) {
|
||||
$mergedConstraints[] = $left;
|
||||
return [$mergedConstraints, false];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Here's the place to put more optimizations
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
114
vendor/wikimedia/composer-merge-plugin/src/NestedArray.php
vendored
Normal file
114
vendor/wikimedia/composer-merge-plugin/src/NestedArray.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
/**
|
||||
* Adapted from
|
||||
* http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Component/Utility/NestedArray.php
|
||||
* @ f86a4d650d5af0b82a3981e09977055fa63f6f2e
|
||||
*/
|
||||
class NestedArray
|
||||
{
|
||||
|
||||
/**
|
||||
* Merges multiple arrays, recursively, and returns the merged array.
|
||||
*
|
||||
* This function is similar to PHP's array_merge_recursive() function, but
|
||||
* it handles non-array values differently. When merging values that are
|
||||
* not both arrays, the latter value replaces the former rather than
|
||||
* merging with it.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* $link_options_1 = ['fragment' => 'x', 'attributes' => ['title' => t('X'), 'class' => ['a', 'b']]];
|
||||
* $link_options_2 = ['fragment' => 'y', 'attributes' => ['title' => t('Y'), 'class' => ['c', 'd']]];
|
||||
*
|
||||
* // This results in ['fragment' => ['x', 'y'], 'attributes' =>
|
||||
* // ['title' => [t('X'), t('Y')], 'class' => ['a', 'b',
|
||||
* // 'c', 'd']]].
|
||||
* $incorrect = array_merge_recursive($link_options_1, $link_options_2);
|
||||
*
|
||||
* // This results in ['fragment' => 'y', 'attributes' =>
|
||||
* // ['title' => t('Y'), 'class' => ['a', 'b', 'c', 'd']]].
|
||||
* $correct = NestedArray::mergeDeep($link_options_1, $link_options_2);
|
||||
* @endcode
|
||||
*
|
||||
* @param mixed ...$params Arrays to merge.
|
||||
*
|
||||
* @return array The merged array.
|
||||
*
|
||||
* @see NestedArray::mergeDeepArray()
|
||||
*/
|
||||
public static function mergeDeep(...$params)
|
||||
{
|
||||
return self::mergeDeepArray($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges multiple arrays, recursively, and returns the merged array.
|
||||
*
|
||||
* This function is equivalent to NestedArray::mergeDeep(), except the
|
||||
* input arrays are passed as a single array parameter rather than
|
||||
* a variable parameter list.
|
||||
*
|
||||
* The following are equivalent:
|
||||
* - NestedArray::mergeDeep($a, $b);
|
||||
* - NestedArray::mergeDeepArray([$a, $b]);
|
||||
*
|
||||
* The following are also equivalent:
|
||||
* - call_user_func_array('NestedArray::mergeDeep', $arrays_to_merge);
|
||||
* - NestedArray::mergeDeepArray($arrays_to_merge);
|
||||
*
|
||||
* @param array $arrays
|
||||
* An arrays of arrays to merge.
|
||||
* @param bool $preserveIntegerKeys
|
||||
* (optional) If given, integer keys will be preserved and merged
|
||||
* instead of appended. Defaults to false.
|
||||
*
|
||||
* @return array
|
||||
* The merged array.
|
||||
*
|
||||
* @see NestedArray::mergeDeep()
|
||||
*/
|
||||
public static function mergeDeepArray(
|
||||
array $arrays,
|
||||
$preserveIntegerKeys = false
|
||||
) {
|
||||
$result = [];
|
||||
foreach ($arrays as $array) {
|
||||
foreach ($array as $key => $value) {
|
||||
// Renumber integer keys as array_merge_recursive() does
|
||||
// unless $preserveIntegerKeys is set to TRUE. Note that PHP
|
||||
// automatically converts array keys that are integer strings
|
||||
// (e.g., '1') to integers.
|
||||
if (is_int($key) && !$preserveIntegerKeys) {
|
||||
$result[] = $value;
|
||||
} elseif (isset($result[$key]) &&
|
||||
is_array($result[$key]) &&
|
||||
is_array($value)
|
||||
) {
|
||||
// Recurse when both values are arrays.
|
||||
$result[$key] = self::mergeDeepArray(
|
||||
[$result[$key], $value],
|
||||
$preserveIntegerKeys
|
||||
);
|
||||
} else {
|
||||
// Otherwise, use the latter value, overriding any
|
||||
// previous value.
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
422
vendor/wikimedia/composer-merge-plugin/src/PluginState.php
vendored
Normal file
422
vendor/wikimedia/composer-merge-plugin/src/PluginState.php
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
|
||||
/**
|
||||
* Mutable plugin state
|
||||
*
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class PluginState
|
||||
{
|
||||
/**
|
||||
* @var Composer $composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* @var bool $isComposer1
|
||||
*/
|
||||
protected $isComposer1;
|
||||
|
||||
/**
|
||||
* @var array $includes
|
||||
*/
|
||||
protected $includes = [];
|
||||
|
||||
/**
|
||||
* @var array $requires
|
||||
*/
|
||||
protected $requires = [];
|
||||
|
||||
/**
|
||||
* @var bool $devMode
|
||||
*/
|
||||
protected $devMode = false;
|
||||
|
||||
/**
|
||||
* @var bool $recurse
|
||||
*/
|
||||
protected $recurse = true;
|
||||
|
||||
/**
|
||||
* @var bool $replace
|
||||
*/
|
||||
protected $replace = false;
|
||||
|
||||
/**
|
||||
* @var bool $ignore
|
||||
*/
|
||||
protected $ignore = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the -dev sections.
|
||||
* @var bool $mergeDev
|
||||
*/
|
||||
protected $mergeDev = true;
|
||||
|
||||
/**
|
||||
* Whether to merge the extra section.
|
||||
*
|
||||
* By default, the extra section is not merged and there will be many
|
||||
* cases where the merge of the extra section is performed too late
|
||||
* to be of use to other plugins. When enabled, merging uses one of
|
||||
* two strategies - either 'first wins' or 'last wins'. When enabled,
|
||||
* 'first wins' is the default behaviour. If Replace mode is activated
|
||||
* then 'last wins' is used.
|
||||
*
|
||||
* @var bool $mergeExtra
|
||||
*/
|
||||
protected $mergeExtra = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the extra section in a deep / recursive way.
|
||||
*
|
||||
* By default the extra section is merged with array_merge() and duplicate
|
||||
* keys are ignored. When enabled this allows to merge the arrays recursively
|
||||
* using the following rule: Integer keys are merged, while array values are
|
||||
* replaced where the later values overwrite the former.
|
||||
*
|
||||
* This is useful especially for the extra section when plugins use larger
|
||||
* structures like a 'patches' key with the packages as sub-keys and the
|
||||
* patches as values.
|
||||
*
|
||||
* When 'replace' mode is activated the order of array merges is exchanged.
|
||||
*
|
||||
* @var bool $mergeExtraDeep
|
||||
*/
|
||||
protected $mergeExtraDeep = false;
|
||||
|
||||
/**
|
||||
* Whether to merge the replace section.
|
||||
*
|
||||
* @var bool $mergeReplace
|
||||
*/
|
||||
protected $mergeReplace = true;
|
||||
|
||||
/**
|
||||
* Whether to merge the scripts section.
|
||||
*
|
||||
* @var bool $mergeScripts
|
||||
*/
|
||||
protected $mergeScripts = false;
|
||||
|
||||
/**
|
||||
* @var bool $firstInstall
|
||||
*/
|
||||
protected $firstInstall = false;
|
||||
|
||||
/**
|
||||
* @var bool $locked
|
||||
*/
|
||||
protected $locked = false;
|
||||
|
||||
/**
|
||||
* @var bool $dumpAutoloader
|
||||
*/
|
||||
protected $dumpAutoloader = false;
|
||||
|
||||
/**
|
||||
* @var bool $optimizeAutoloader
|
||||
*/
|
||||
protected $optimizeAutoloader = false;
|
||||
|
||||
/**
|
||||
* @param Composer $composer
|
||||
*/
|
||||
public function __construct(Composer $composer)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->isComposer1 = version_compare(PluginInterface::PLUGIN_API_VERSION, '2.0.0', '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this plugin runs within Composer 1.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isComposer1()
|
||||
{
|
||||
return $this->isComposer1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin settings
|
||||
*/
|
||||
public function loadSettings()
|
||||
{
|
||||
$extra = $this->composer->getPackage()->getExtra();
|
||||
$config = array_merge(
|
||||
[
|
||||
'include' => [],
|
||||
'require' => [],
|
||||
'recurse' => true,
|
||||
'replace' => false,
|
||||
'ignore-duplicates' => false,
|
||||
'merge-dev' => true,
|
||||
'merge-extra' => false,
|
||||
'merge-extra-deep' => false,
|
||||
'merge-replace' => true,
|
||||
'merge-scripts' => false,
|
||||
],
|
||||
$extra['merge-plugin'] ?? []
|
||||
);
|
||||
|
||||
$this->includes = (is_array($config['include'])) ?
|
||||
$config['include'] : [$config['include']];
|
||||
$this->requires = (is_array($config['require'])) ?
|
||||
$config['require'] : [$config['require']];
|
||||
$this->recurse = (bool)$config['recurse'];
|
||||
$this->replace = (bool)$config['replace'];
|
||||
$this->ignore = (bool)$config['ignore-duplicates'];
|
||||
$this->mergeDev = (bool)$config['merge-dev'];
|
||||
$this->mergeExtra = (bool)$config['merge-extra'];
|
||||
$this->mergeExtraDeep = (bool)$config['merge-extra-deep'];
|
||||
$this->mergeReplace = (bool)$config['merge-replace'];
|
||||
$this->mergeScripts = (bool)$config['merge-scripts'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of filenames and/or glob patterns to include
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIncludes()
|
||||
{
|
||||
return $this->includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of filenames and/or glob patterns to require
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequires()
|
||||
{
|
||||
return $this->requires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the first install flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setFirstInstall($flag)
|
||||
{
|
||||
$this->firstInstall = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the first time that the plugin has been installed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirstInstall()
|
||||
{
|
||||
return $this->firstInstall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locked flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setLocked($flag)
|
||||
{
|
||||
$this->locked = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was a lockfile present when the plugin was installed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocked()
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should an update be forced?
|
||||
*
|
||||
* @return true If packages are not locked
|
||||
*/
|
||||
public function forceUpdate()
|
||||
{
|
||||
return !$this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the devMode flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setDevMode($flag)
|
||||
{
|
||||
$this->devMode = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should devMode settings be processed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDevMode()
|
||||
{
|
||||
return $this->shouldMergeDev() && $this->devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should devMode settings be merged?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeDev()
|
||||
{
|
||||
return $this->mergeDev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dumpAutoloader flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setDumpAutoloader($flag)
|
||||
{
|
||||
$this->dumpAutoloader = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the autoloader file supposed to be written out?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDumpAutoloader()
|
||||
{
|
||||
return $this->dumpAutoloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the optimizeAutoloader flag
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setOptimizeAutoloader($flag)
|
||||
{
|
||||
$this->optimizeAutoloader = (bool)$flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the autoloader be optimized?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldOptimizeAutoloader()
|
||||
{
|
||||
return $this->optimizeAutoloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should includes be recursively processed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function recurseIncludes()
|
||||
{
|
||||
return $this->recurse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should duplicate links be replaced in a 'last definition wins' order?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function replaceDuplicateLinks()
|
||||
{
|
||||
return $this->replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should duplicate links be ignored?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ignoreDuplicateLinks()
|
||||
{
|
||||
return $this->ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the extra section be merged?
|
||||
*
|
||||
* By default, the extra section is not merged and there will be many
|
||||
* cases where the merge of the extra section is performed too late
|
||||
* to be of use to other plugins. When enabled, merging uses one of
|
||||
* two strategies - either 'first wins' or 'last wins'. When enabled,
|
||||
* 'first wins' is the default behaviour. If Replace mode is activated
|
||||
* then 'last wins' is used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeExtra()
|
||||
{
|
||||
return $this->mergeExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the extra section be merged deep / recursively?
|
||||
*
|
||||
* By default the extra section is merged with array_merge() and duplicate
|
||||
* keys are ignored. When enabled this allows to merge the arrays recursively
|
||||
* using the following rule: Integer keys are merged, while array values are
|
||||
* replaced where the later values overwrite the former.
|
||||
*
|
||||
* This is useful especially for the extra section when plugins use larger
|
||||
* structures like a 'patches' key with the packages as sub-keys and the
|
||||
* patches as values.
|
||||
*
|
||||
* When 'replace' mode is activated the order of array merges is exchanged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeExtraDeep()
|
||||
{
|
||||
return $this->mergeExtraDeep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the replace section be merged?
|
||||
*
|
||||
* By default, the replace section is merged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeReplace()
|
||||
{
|
||||
return $this->mergeReplace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the scripts section be merged?
|
||||
*
|
||||
* By default, the scripts section is not merged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldMergeScripts()
|
||||
{
|
||||
return $this->mergeScripts;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
173
vendor/wikimedia/composer-merge-plugin/src/StabilityFlags.php
vendored
Normal file
173
vendor/wikimedia/composer-merge-plugin/src/StabilityFlags.php
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the Composer Merge plugin.
|
||||
*
|
||||
* Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the MIT
|
||||
* license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Wikimedia\Composer\Merge\V2;
|
||||
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
|
||||
/**
|
||||
* Adapted from Composer's RootPackageLoader::extractStabilityFlags
|
||||
* @author Bryan Davis <bd808@bd808.com>
|
||||
*/
|
||||
class StabilityFlags
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array Current package name => stability mappings
|
||||
*/
|
||||
protected $stabilityFlags;
|
||||
|
||||
/**
|
||||
* @var int Current default minimum stability
|
||||
*/
|
||||
protected $minimumStability;
|
||||
|
||||
/**
|
||||
* @var string Regex to extract an explicit stability flag (eg '@dev')
|
||||
*/
|
||||
protected $explicitStabilityRe;
|
||||
|
||||
/**
|
||||
* @param array $stabilityFlags Current package name => stability mappings
|
||||
* @param int|string $minimumStability Current default minimum stability
|
||||
*/
|
||||
public function __construct(
|
||||
array $stabilityFlags = [],
|
||||
$minimumStability = BasePackage::STABILITY_STABLE
|
||||
) {
|
||||
$this->stabilityFlags = $stabilityFlags;
|
||||
$this->minimumStability = $this->getStabilityInt((string)$minimumStability);
|
||||
$this->explicitStabilityRe = '/^[^@]*?@(' .
|
||||
implode('|', array_keys(BasePackage::$stabilities)) .
|
||||
')$/i';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stability value for a given string.
|
||||
*
|
||||
* @param string $name Stability name
|
||||
* @return int Stability value
|
||||
*/
|
||||
protected function getStabilityInt($name)
|
||||
{
|
||||
$name = VersionParser::normalizeStability($name);
|
||||
return BasePackage::$stabilities[$name] ?? BasePackage::STABILITY_STABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and merge stability flags from the given collection of
|
||||
* requires with another collection of stability flags.
|
||||
*
|
||||
* @param array $requires New package name => link mappings
|
||||
* @return array Unified package name => stability mappings
|
||||
*/
|
||||
public function extractAll(array $requires)
|
||||
{
|
||||
$flags = [];
|
||||
|
||||
foreach ($requires as $name => $link) {
|
||||
$name = strtolower($name);
|
||||
$version = $link->getPrettyConstraint();
|
||||
|
||||
$stability = $this->getExplicitStability($version);
|
||||
|
||||
if ($stability === null) {
|
||||
$stability = $this->getParsedStability($version);
|
||||
}
|
||||
|
||||
$flags[$name] = max($stability, $this->getCurrentStability($name));
|
||||
}
|
||||
|
||||
// Filter out null stability values
|
||||
return array_filter($flags, function ($v) {
|
||||
return $v !== null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the most unstable explicit stability (eg '@dev') from a version
|
||||
* specification.
|
||||
*
|
||||
* @param string $version
|
||||
* @return int|null Stability or null if no explict stability found
|
||||
*/
|
||||
protected function getExplicitStability($version)
|
||||
{
|
||||
$found = null;
|
||||
$constraints = $this->splitConstraints($version);
|
||||
foreach ($constraints as $constraint) {
|
||||
if (preg_match($this->explicitStabilityRe, $constraint, $match)) {
|
||||
$stability = $this->getStabilityInt($match[1]);
|
||||
$found = max($stability, $found);
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a version specification into a list of version constraints.
|
||||
*
|
||||
* @param string $version
|
||||
* @return array
|
||||
*/
|
||||
protected function splitConstraints($version)
|
||||
{
|
||||
$found = [];
|
||||
$orConstraints = preg_split('/\s*\|\|?\s*/', trim($version));
|
||||
foreach ($orConstraints as $constraints) {
|
||||
$andConstraints = preg_split(
|
||||
'/(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)/',
|
||||
$constraints
|
||||
);
|
||||
foreach ($andConstraints as $constraint) {
|
||||
$found[] = $constraint;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stability of a version
|
||||
*
|
||||
* @param string $version
|
||||
* @return int|null Stability or null if STABLE or less than minimum
|
||||
*/
|
||||
protected function getParsedStability($version)
|
||||
{
|
||||
// Drop aliasing if used
|
||||
$version = preg_replace('/^([^,\s@]+) as .+$/', '$1', $version);
|
||||
$stability = $this->getStabilityInt(
|
||||
VersionParser::parseStability($version)
|
||||
);
|
||||
|
||||
if ($stability === BasePackage::STABILITY_STABLE ||
|
||||
$this->minimumStability > $stability
|
||||
) {
|
||||
// Ignore if 'stable' or more stable than the global
|
||||
// minimum
|
||||
$stability = null;
|
||||
}
|
||||
|
||||
return $stability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current stability of a given package.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int|null Stability of null if not set
|
||||
*/
|
||||
protected function getCurrentStability($name)
|
||||
{
|
||||
return $this->stabilityFlags[$name] ?? null;
|
||||
}
|
||||
}
|
||||
// vim:sw=4:ts=4:sts=4:et:
|
Reference in New Issue
Block a user