Merge branch 'master' into filter-packages
commit
9a04ecefbf
|
@ -11,6 +11,7 @@ on:
|
||||||
env:
|
env:
|
||||||
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist"
|
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist"
|
||||||
COMPOSER_UPDATE_FLAGS: ""
|
COMPOSER_UPDATE_FLAGS: ""
|
||||||
|
COMPOSER_TESTS_ARE_RUNNING: "1"
|
||||||
SYMFONY_PHPUNIT_VERSION: "8.3"
|
SYMFONY_PHPUNIT_VERSION: "8.3"
|
||||||
SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1"
|
SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1"
|
||||||
|
|
||||||
|
@ -108,7 +109,7 @@ jobs:
|
||||||
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
|
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
|
||||||
|
|
||||||
- name: "Cache dependencies installed with composer"
|
- name: "Cache dependencies installed with composer"
|
||||||
uses: "actions/cache@v1"
|
uses: "actions/cache@v2"
|
||||||
with:
|
with:
|
||||||
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
|
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
|
||||||
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
|
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
|
||||||
|
|
|
@ -34,14 +34,13 @@ jobs:
|
||||||
extensions: "intl, zip"
|
extensions: "intl, zip"
|
||||||
ini-values: "memory_limit=-1"
|
ini-values: "memory_limit=-1"
|
||||||
php-version: "${{ matrix.php-version }}"
|
php-version: "${{ matrix.php-version }}"
|
||||||
tools: "cs2pr"
|
|
||||||
|
|
||||||
- name: "Determine composer cache directory"
|
- name: "Determine composer cache directory"
|
||||||
id: "determine-composer-cache-directory"
|
id: "determine-composer-cache-directory"
|
||||||
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
|
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
|
||||||
|
|
||||||
- name: "Cache dependencies installed with composer"
|
- name: "Cache dependencies installed with composer"
|
||||||
uses: "actions/cache@v1"
|
uses: "actions/cache@v2"
|
||||||
with:
|
with:
|
||||||
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
|
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
|
||||||
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
|
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
|
||||||
|
@ -52,5 +51,5 @@ jobs:
|
||||||
|
|
||||||
- name: Run PHPStan
|
- name: Run PHPStan
|
||||||
run: |
|
run: |
|
||||||
bin/composer require --dev phpstan/phpstan:^0.12.26 phpunit/phpunit:^7.5 --with-all-dependencies
|
bin/composer require --dev phpstan/phpstan:^0.12.37 phpunit/phpunit:^7.5.20 --with-all-dependencies
|
||||||
vendor/bin/phpstan analyse --configuration=phpstan/config.neon || vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr
|
vendor/bin/phpstan analyse --configuration=phpstan/config.neon
|
||||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -1,3 +1,14 @@
|
||||||
|
### [2.0.0-alpha3] 2020-08-03
|
||||||
|
|
||||||
|
* Breaking: Zip archives loaded by artifact repositories must now have a composer.json on top level, or a max of one folder on top level of the archive
|
||||||
|
* Added --no-dev support to `show` and `outdated` commands to skip dev requirements
|
||||||
|
* Added support for multiple --repository flags being passed into the `create-project` command, only useful in combination with `--add-repository` to persist them to composer.json
|
||||||
|
* Added a new optional `list` API endpoint for v2-format composer repositories, see [UPGRADE](UPGRADE-2.0.md) for details
|
||||||
|
* Fixed `show -a` command not listing anything
|
||||||
|
* Fixed solver bug where it ended in a "Reached invalid decision id 0"
|
||||||
|
* Fixed updates of git-installed packages on windows
|
||||||
|
* Lots of minor bug fixes
|
||||||
|
|
||||||
### [2.0.0-alpha2] 2020-06-24
|
### [2.0.0-alpha2] 2020-06-24
|
||||||
|
|
||||||
* Added parallel installation of packages (requires OSX/Linux/WSL, and that `unzip` is present in PATH)
|
* Added parallel installation of packages (requires OSX/Linux/WSL, and that `unzip` is present in PATH)
|
||||||
|
@ -46,6 +57,12 @@
|
||||||
* Fixed suggest output being very spammy, it now is only one line long and shows more rarely
|
* Fixed suggest output being very spammy, it now is only one line long and shows more rarely
|
||||||
* Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore
|
* Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore
|
||||||
|
|
||||||
|
### [1.10.10] 2020-08-03
|
||||||
|
|
||||||
|
* Fixed `create-project` not triggering events while installing the root package
|
||||||
|
* Fixed PHP 8 compatibility issue
|
||||||
|
* Fixed `self-update` to avoid automatically upgrading to the next major version once it becomes stable
|
||||||
|
|
||||||
### [1.10.9] 2020-07-16
|
### [1.10.9] 2020-07-16
|
||||||
|
|
||||||
* Fixed Bitbucket redirect loop when credentials are outdated
|
* Fixed Bitbucket redirect loop when credentials are outdated
|
||||||
|
@ -921,8 +938,10 @@
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
|
[2.0.0-alpha3]: https://github.com/composer/composer/compare/2.0.0-alpha2...2.0.0-alpha3
|
||||||
[2.0.0-alpha2]: https://github.com/composer/composer/compare/2.0.0-alpha1...2.0.0-alpha2
|
[2.0.0-alpha2]: https://github.com/composer/composer/compare/2.0.0-alpha1...2.0.0-alpha2
|
||||||
[2.0.0-alpha1]: https://github.com/composer/composer/compare/1.10.7...2.0.0-alpha1
|
[2.0.0-alpha1]: https://github.com/composer/composer/compare/1.10.7...2.0.0-alpha1
|
||||||
|
[1.10.10]: https://github.com/composer/composer/compare/1.10.9...1.10.10
|
||||||
[1.10.9]: https://github.com/composer/composer/compare/1.10.8...1.10.9
|
[1.10.9]: https://github.com/composer/composer/compare/1.10.8...1.10.9
|
||||||
[1.10.8]: https://github.com/composer/composer/compare/1.10.7...1.10.8
|
[1.10.8]: https://github.com/composer/composer/compare/1.10.7...1.10.8
|
||||||
[1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7
|
[1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7
|
||||||
|
|
|
@ -80,4 +80,6 @@ The providers-api is optional, but if you implement it it should return packages
|
||||||
|
|
||||||
This is also optional, it should accept an optional `?filter=xx` query param, which can contain `*` as wildcards matching any substring.
|
This is also optional, it should accept an optional `?filter=xx` query param, which can contain `*` as wildcards matching any substring.
|
||||||
|
|
||||||
It must return an array of package names as `{"packageNames": ["a/b", "c/d"]}`. See https://packagist.org/packages/list.json?filter=composer/* for example.
|
It must return an array of package names as `{"packageNames": ["a/b", "c/d"]}`. See <https://packagist.org/packages/list.json?filter=composer/*> for example.
|
||||||
|
|
||||||
|
It should return the names of package which names match the filter (or all names if no filter is present). Replace/provide rules should not be considered here.
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "composer/ca-bundle",
|
"name": "composer/ca-bundle",
|
||||||
"version": "1.2.7",
|
"version": "1.2.8",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/ca-bundle.git",
|
"url": "https://github.com/composer/ca-bundle.git",
|
||||||
"reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd"
|
"reference": "8a7ecad675253e4654ea05505233285377405215"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd",
|
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215",
|
||||||
"reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd",
|
"reference": "8a7ecad675253e4654ea05505233285377405215",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -63,19 +63,23 @@
|
||||||
"support": {
|
"support": {
|
||||||
"irc": "irc://irc.freenode.org/composer",
|
"irc": "irc://irc.freenode.org/composer",
|
||||||
"issues": "https://github.com/composer/ca-bundle/issues",
|
"issues": "https://github.com/composer/ca-bundle/issues",
|
||||||
"source": "https://github.com/composer/ca-bundle/tree/1.2.7"
|
"source": "https://github.com/composer/ca-bundle/tree/1.2.8"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://packagist.com",
|
"url": "https://packagist.com",
|
||||||
"type": "custom"
|
"type": "custom"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/composer",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-04-08T08:27:21+00:00"
|
"time": "2020-08-23T12:54:47+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
|
@ -239,16 +243,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/xdebug-handler",
|
"name": "composer/xdebug-handler",
|
||||||
"version": "1.4.2",
|
"version": "1.4.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/xdebug-handler.git",
|
"url": "https://github.com/composer/xdebug-handler.git",
|
||||||
"reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51"
|
"reference": "ebd27a9866ae8254e873866f795491f02418c5a5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
|
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ebd27a9866ae8254e873866f795491f02418c5a5",
|
||||||
"reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
|
"reference": "ebd27a9866ae8254e873866f795491f02418c5a5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -282,7 +286,7 @@
|
||||||
"support": {
|
"support": {
|
||||||
"irc": "irc://irc.freenode.org/composer",
|
"irc": "irc://irc.freenode.org/composer",
|
||||||
"issues": "https://github.com/composer/xdebug-handler/issues",
|
"issues": "https://github.com/composer/xdebug-handler/issues",
|
||||||
"source": "https://github.com/composer/xdebug-handler/tree/1.4.2"
|
"source": "https://github.com/composer/xdebug-handler/tree/1.4.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -298,7 +302,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-06-04T11:16:35+00:00"
|
"time": "2020-08-19T10:27:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "justinrainbow/json-schema",
|
"name": "justinrainbow/json-schema",
|
||||||
|
@ -470,16 +474,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "seld/jsonlint",
|
"name": "seld/jsonlint",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Seldaek/jsonlint.git",
|
"url": "https://github.com/Seldaek/jsonlint.git",
|
||||||
"reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1"
|
"reference": "3d5eb71705adfa34bd34b993400622932b2f62fd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1",
|
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/3d5eb71705adfa34bd34b993400622932b2f62fd",
|
||||||
"reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1",
|
"reference": "3d5eb71705adfa34bd34b993400622932b2f62fd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -529,7 +533,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-04-30T19:05:18+00:00"
|
"time": "2020-08-13T09:07:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "seld/phar-utils",
|
"name": "seld/phar-utils",
|
||||||
|
@ -810,7 +814,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.18.0",
|
"version": "v1.18.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
@ -889,7 +893,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.18.0",
|
"version": "v1.18.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
@ -949,7 +953,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/master"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.18.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -142,6 +142,9 @@ reinstalling the project you can feel confident the dependencies installed are
|
||||||
still working even if your dependencies released many new versions since then.
|
still working even if your dependencies released many new versions since then.
|
||||||
(See note below about using the `update` command.)
|
(See note below about using the `update` command.)
|
||||||
|
|
||||||
|
> **Note:** For libraries it is not necessary to commit the lock
|
||||||
|
> file, see also: [Libraries - Lock file](02-libraries.md#lock-file).
|
||||||
|
|
||||||
## Updating dependencies to their latest versions
|
## Updating dependencies to their latest versions
|
||||||
|
|
||||||
As mentioned above, the `composer.lock` file prevents you from automatically getting
|
As mentioned above, the `composer.lock` file prevents you from automatically getting
|
||||||
|
@ -165,9 +168,6 @@ If you only want to install, upgrade or remove one dependency, you can explicitl
|
||||||
php composer.phar update monolog/monolog [...]
|
php composer.phar update monolog/monolog [...]
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** For libraries it is not necessary to commit the lock
|
|
||||||
> file, see also: [Libraries - Lock file](02-libraries.md#lock-file).
|
|
||||||
|
|
||||||
## Packagist
|
## Packagist
|
||||||
|
|
||||||
[Packagist](https://packagist.org/) is the main Composer repository. A Composer
|
[Packagist](https://packagist.org/) is the main Composer repository. A Composer
|
||||||
|
|
|
@ -516,7 +516,7 @@ There are some cases, when there is no ability to have one of the previously
|
||||||
mentioned repository types online, even the VCS one. Typical example could be
|
mentioned repository types online, even the VCS one. Typical example could be
|
||||||
cross-organisation library exchange through built artifacts. Of course, most
|
cross-organisation library exchange through built artifacts. Of course, most
|
||||||
of the times they are private. To simplify maintenance, one can simply use a
|
of the times they are private. To simplify maintenance, one can simply use a
|
||||||
repository of type `artifact` with a folder containing ZIP archives of those
|
repository of type `artifact` with a folder containing ZIP or TAR archives of those
|
||||||
private packages:
|
private packages:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -85,11 +85,11 @@ gitlab.com the domain names must be also specified with the
|
||||||
|
|
||||||
## gitlab-token
|
## gitlab-token
|
||||||
|
|
||||||
A list of domain names and private tokens. Private token can be either simple
|
A list of domain names and private tokens. Private token can be either simple
|
||||||
string, or array with username and token. For example using `{"gitlab.com":
|
string, or array with username and token. For example using `{"gitlab.com":
|
||||||
"privatetoken"}` as the value of this option will use `privatetoken` to access
|
"privatetoken"}` as the value of this option will use `privatetoken` to access
|
||||||
private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser",
|
private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser",
|
||||||
"token": "privatetoken"}}` will use both username and token for gitlab deploy
|
"token": "privatetoken"}}` will use both username and token for gitlab deploy
|
||||||
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
|
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
|
||||||
Please note: If the package is not hosted at
|
Please note: If the package is not hosted at
|
||||||
gitlab.com the domain names must be also specified with the
|
gitlab.com the domain names must be also specified with the
|
||||||
|
@ -204,6 +204,10 @@ downloads. When the garbage collection is periodically ran, this is the maximum
|
||||||
size the cache will be able to use. Older (less used) files will be removed
|
size the cache will be able to use. Older (less used) files will be removed
|
||||||
first until the cache fits.
|
first until the cache fits.
|
||||||
|
|
||||||
|
## cache-read-only
|
||||||
|
|
||||||
|
Defaults to `false`. Whether to use the Composer cache in read-only mode.
|
||||||
|
|
||||||
## bin-compat
|
## bin-compat
|
||||||
|
|
||||||
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
|
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
|
||||||
|
|
|
@ -237,6 +237,10 @@
|
||||||
"type": ["string", "integer"],
|
"type": ["string", "integer"],
|
||||||
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
||||||
},
|
},
|
||||||
|
"cache-read-only": {
|
||||||
|
"type": ["boolean"],
|
||||||
|
"description": "Whether to use the Composer cache in read-only mode."
|
||||||
|
},
|
||||||
"bin-compat": {
|
"bin-compat": {
|
||||||
"enum": ["auto", "full"],
|
"enum": ["auto", "full"],
|
||||||
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
|
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
|
||||||
|
|
|
@ -30,19 +30,22 @@ class Cache
|
||||||
private $enabled = true;
|
private $enabled = true;
|
||||||
private $allowlist;
|
private $allowlist;
|
||||||
private $filesystem;
|
private $filesystem;
|
||||||
|
private $readOnly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IOInterface $io
|
* @param IOInterface $io
|
||||||
* @param string $cacheDir location of the cache
|
* @param string $cacheDir location of the cache
|
||||||
* @param string $allowlist List of characters that are allowed in path names (used in a regex character class)
|
* @param string $allowlist List of characters that are allowed in path names (used in a regex character class)
|
||||||
* @param Filesystem $filesystem optional filesystem instance
|
* @param Filesystem $filesystem optional filesystem instance
|
||||||
|
* @param bool $readOnly whether the cache is in readOnly mode
|
||||||
*/
|
*/
|
||||||
public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null)
|
public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null, $readOnly = false)
|
||||||
{
|
{
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->root = rtrim($cacheDir, '/\\') . '/';
|
$this->root = rtrim($cacheDir, '/\\') . '/';
|
||||||
$this->allowlist = $allowlist;
|
$this->allowlist = $allowlist;
|
||||||
$this->filesystem = $filesystem ?: new Filesystem();
|
$this->filesystem = $filesystem ?: new Filesystem();
|
||||||
|
$this->readOnly = (bool) $readOnly;
|
||||||
|
|
||||||
if (!self::isUsable($cacheDir)) {
|
if (!self::isUsable($cacheDir)) {
|
||||||
$this->enabled = false;
|
$this->enabled = false;
|
||||||
|
@ -59,6 +62,22 @@ class Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $readOnly
|
||||||
|
*/
|
||||||
|
public function setReadOnly($readOnly)
|
||||||
|
{
|
||||||
|
$this->readOnly = (bool) $readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isReadOnly()
|
||||||
|
{
|
||||||
|
return $this->readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
public static function isUsable($path)
|
public static function isUsable($path)
|
||||||
{
|
{
|
||||||
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
|
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
|
||||||
|
@ -90,7 +109,7 @@ class Cache
|
||||||
|
|
||||||
public function write($file, $contents)
|
public function write($file, $contents)
|
||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled && !$this->readOnly) {
|
||||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||||
|
|
||||||
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
|
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
|
||||||
|
@ -128,7 +147,7 @@ class Cache
|
||||||
*/
|
*/
|
||||||
public function copyFrom($file, $source)
|
public function copyFrom($file, $source)
|
||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled && !$this->readOnly) {
|
||||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||||
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ EOT
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$cache = new Cache($io, $cachePath);
|
$cache = new Cache($io, $cachePath);
|
||||||
|
$cache->setReadOnly($config->get('cache-read-only'));
|
||||||
if (!$cache->isEnabled()) {
|
if (!$cache->isEnabled()) {
|
||||||
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
|
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
|
||||||
|
|
||||||
|
|
|
@ -135,8 +135,36 @@ EOT
|
||||||
|
|
||||||
$latest = $versionsUtil->getLatest();
|
$latest = $versionsUtil->getLatest();
|
||||||
$latestStable = $versionsUtil->getLatest('stable');
|
$latestStable = $versionsUtil->getLatest('stable');
|
||||||
|
try {
|
||||||
|
$latestPreview = $versionsUtil->getLatest('preview');
|
||||||
|
} catch (\UnexpectedValueException $e) {
|
||||||
|
$latestPreview = $latestStable;
|
||||||
|
}
|
||||||
$latestVersion = $latest['version'];
|
$latestVersion = $latest['version'];
|
||||||
$updateVersion = $input->getArgument('version') ?: $latestVersion;
|
$updateVersion = $input->getArgument('version') ?: $latestVersion;
|
||||||
|
$currentMajorVersion = preg_replace('{^(\d+).*}', '$1', Composer::getVersion());
|
||||||
|
$updateMajorVersion = preg_replace('{^(\d+).*}', '$1', $updateVersion);
|
||||||
|
$previewMajorVersion = preg_replace('{^(\d+).*}', '$1', $latestPreview['version']);
|
||||||
|
|
||||||
|
if ($versionsUtil->getChannel() === 'stable' && !$input->getArgument('version')) {
|
||||||
|
// if requesting stable channel and no specific version, avoid automatically upgrading to the next major
|
||||||
|
// simply output a warning that the next major stable is available and let users upgrade to it manually
|
||||||
|
if ($currentMajorVersion < $updateMajorVersion) {
|
||||||
|
$skippedVersion = $updateVersion;
|
||||||
|
|
||||||
|
$versionsUtil->setChannel($currentMajorVersion);
|
||||||
|
|
||||||
|
$latest = $versionsUtil->getLatest();
|
||||||
|
$latestStable = $versionsUtil->getLatest('stable');
|
||||||
|
$latestVersion = $latest['version'];
|
||||||
|
$updateVersion = $latestVersion;
|
||||||
|
|
||||||
|
$io->writeError('<warning>A new stable major version of Composer is available ('.$skippedVersion.'), run "composer self-update --'.$updateMajorVersion.'" to update to it. See also https://github.com/composer/composer/releases for changelogs.</warning>');
|
||||||
|
} elseif ($currentMajorVersion < $previewMajorVersion) {
|
||||||
|
// promote next major version if available in preview
|
||||||
|
$io->writeError('<warning>A preview release of the next major version of Composer is available ('.$latestPreview['version'].'), run "composer self-update --preview" to give it a try. See also https://github.com/composer/composer/releases for changelogs.</warning>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($requestedChannel && is_numeric($requestedChannel) && substr($latestStable['version'], 0, 1) !== $requestedChannel) {
|
if ($requestedChannel && is_numeric($requestedChannel) && substr($latestStable['version'], 0, 1) !== $requestedChannel) {
|
||||||
$io->writeError('<warning>Warning: You forced the install of '.$latestVersion.' via --'.$requestedChannel.', but '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>');
|
$io->writeError('<warning>Warning: You forced the install of '.$latestVersion.' via --'.$requestedChannel.', but '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>');
|
||||||
|
@ -243,7 +271,12 @@ TAGSPUBKEY
|
||||||
$signature = json_decode($signature, true);
|
$signature = json_decode($signature, true);
|
||||||
$signature = base64_decode($signature['sha384']);
|
$signature = base64_decode($signature['sha384']);
|
||||||
$verified = 1 === openssl_verify(file_get_contents($tempFilename), $signature, $pubkeyid, $algo);
|
$verified = 1 === openssl_verify(file_get_contents($tempFilename), $signature, $pubkeyid, $algo);
|
||||||
openssl_free_key($pubkeyid);
|
|
||||||
|
// PHP 8 automatically frees the key instance and deprecates the function
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
openssl_free_key($pubkeyid);
|
||||||
|
}
|
||||||
|
|
||||||
if (!$verified) {
|
if (!$verified) {
|
||||||
throw new \RuntimeException('The phar signature did not match the file you downloaded, this means your public keys are outdated or that the phar file is corrupt/has been modified');
|
throw new \RuntimeException('The phar signature did not match the file you downloaded, this means your public keys are outdated or that the phar file is corrupt/has been modified');
|
||||||
}
|
}
|
||||||
|
|
|
@ -578,8 +578,8 @@ EOT
|
||||||
|
|
||||||
$matchedPackage = null;
|
$matchedPackage = null;
|
||||||
$versions = array();
|
$versions = array();
|
||||||
$matches = $repositorySet->findPackages($name, $constraint);
|
|
||||||
$pool = $repositorySet->createPoolForPackage($name);
|
$pool = $repositorySet->createPoolForPackage($name);
|
||||||
|
$matches = $pool->whatProvides($name, $constraint);
|
||||||
foreach ($matches as $index => $package) {
|
foreach ($matches as $index => $package) {
|
||||||
// select an exact match if it is in the installed repo and no specific version was required
|
// select an exact match if it is in the installed repo and no specific version was required
|
||||||
if (null === $version && $installedRepo->hasPackage($package)) {
|
if (null === $version && $installedRepo->hasPackage($package)) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Config
|
||||||
'cache-ttl' => 15552000, // 6 months
|
'cache-ttl' => 15552000, // 6 months
|
||||||
'cache-files-ttl' => null, // fallback to cache-ttl
|
'cache-files-ttl' => null, // fallback to cache-ttl
|
||||||
'cache-files-maxsize' => '300MiB',
|
'cache-files-maxsize' => '300MiB',
|
||||||
|
'cache-read-only' => false,
|
||||||
'bin-compat' => 'auto',
|
'bin-compat' => 'auto',
|
||||||
'discard-changes' => false,
|
'discard-changes' => false,
|
||||||
'autoloader-suffix' => null,
|
'autoloader-suffix' => null,
|
||||||
|
@ -211,6 +212,7 @@ class Config
|
||||||
public function get($key, $flags = 0)
|
public function get($key, $flags = 0)
|
||||||
{
|
{
|
||||||
switch ($key) {
|
switch ($key) {
|
||||||
|
// strings/paths with env var and {$refs} support
|
||||||
case 'vendor-dir':
|
case 'vendor-dir':
|
||||||
case 'bin-dir':
|
case 'bin-dir':
|
||||||
case 'process-timeout':
|
case 'process-timeout':
|
||||||
|
@ -234,20 +236,34 @@ class Config
|
||||||
|
|
||||||
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
||||||
|
|
||||||
|
// booleans with env var support
|
||||||
|
case 'cache-read-only':
|
||||||
case 'htaccess-protect':
|
case 'htaccess-protect':
|
||||||
$value = $this->getComposerEnv('COMPOSER_HTACCESS_PROTECT');
|
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
|
||||||
if (false === $value) {
|
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
|
||||||
$value = $this->config[$key];
|
|
||||||
}
|
|
||||||
return $value !== 'false' && (bool) $value;
|
|
||||||
|
|
||||||
|
$val = $this->getComposerEnv($env);
|
||||||
|
if (false === $val) {
|
||||||
|
$val = $this->config[$key];
|
||||||
|
}
|
||||||
|
return $val !== 'false' && (bool) $val;
|
||||||
|
|
||||||
|
// booleans without env var support
|
||||||
|
case 'disable-tls':
|
||||||
|
case 'secure-http':
|
||||||
|
case 'use-github-api':
|
||||||
|
case 'lock':
|
||||||
|
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
||||||
|
|
||||||
|
// ints without env var support
|
||||||
case 'cache-ttl':
|
case 'cache-ttl':
|
||||||
return (int) $this->config[$key];
|
return (int) $this->config[$key];
|
||||||
|
|
||||||
|
// numbers with kb/mb/gb support, without env var support
|
||||||
case 'cache-files-maxsize':
|
case 'cache-files-maxsize':
|
||||||
if (!preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $this->config[$key], $matches)) {
|
if (!preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $this->config[$key], $matches)) {
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
"Could not parse the value of 'cache-files-maxsize': {$this->config[$key]}"
|
"Could not parse the value of '$key': {$this->config[$key]}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$size = $matches[1];
|
$size = $matches[1];
|
||||||
|
@ -269,6 +285,7 @@ class Config
|
||||||
|
|
||||||
return $size;
|
return $size;
|
||||||
|
|
||||||
|
// special cases below
|
||||||
case 'cache-files-ttl':
|
case 'cache-files-ttl':
|
||||||
if (isset($this->config[$key])) {
|
if (isset($this->config[$key])) {
|
||||||
return (int) $this->config[$key];
|
return (int) $this->config[$key];
|
||||||
|
@ -326,14 +343,6 @@ class Config
|
||||||
|
|
||||||
return $protos;
|
return $protos;
|
||||||
|
|
||||||
case 'disable-tls':
|
|
||||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
|
||||||
case 'secure-http':
|
|
||||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
|
||||||
case 'use-github-api':
|
|
||||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
|
||||||
case 'lock':
|
|
||||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
|
||||||
default:
|
default:
|
||||||
if (!isset($this->config[$key])) {
|
if (!isset($this->config[$key])) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -22,6 +22,7 @@ use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Seld\JsonLint\ParsingException;
|
||||||
use Composer\Command;
|
use Composer\Command;
|
||||||
use Composer\Composer;
|
use Composer\Composer;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
|
@ -191,6 +192,20 @@ class Application extends BaseApplication
|
||||||
}
|
}
|
||||||
} catch (NoSslException $e) {
|
} catch (NoSslException $e) {
|
||||||
// suppress these as they are not relevant at this point
|
// suppress these as they are not relevant at this point
|
||||||
|
} catch (ParsingException $e) {
|
||||||
|
$details = $e->getDetails();
|
||||||
|
|
||||||
|
$file = realpath(Factory::getComposerFile());
|
||||||
|
|
||||||
|
$line = null;
|
||||||
|
if ($details && isset($details['line'])) {
|
||||||
|
$line = $details['line'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ghe = new GithubActionError($this->io);
|
||||||
|
$ghe->emit($e->getMessage(), $file, $line);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->hasPluginCommands = true;
|
$this->hasPluginCommands = true;
|
||||||
|
@ -237,7 +252,7 @@ class Application extends BaseApplication
|
||||||
if (function_exists('posix_getuid') && posix_getuid() === 0) {
|
if (function_exists('posix_getuid') && posix_getuid() === 0) {
|
||||||
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
||||||
$io->writeError('<warning>Do not run Composer as root/super user! See https://getcomposer.org/root for details</warning>');
|
$io->writeError('<warning>Do not run Composer as root/super user! See https://getcomposer.org/root for details</warning>');
|
||||||
|
|
||||||
if ($io->isInteractive()) {
|
if ($io->isInteractive()) {
|
||||||
if (!$io->askConfirmation('<info>Continue as root/super user</info> [<comment>yes</comment>]? ', true)) {
|
if (!$io->askConfirmation('<info>Continue as root/super user</info> [<comment>yes</comment>]? ', true)) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -307,8 +322,13 @@ class Application extends BaseApplication
|
||||||
} catch (ScriptExecutionException $e) {
|
} catch (ScriptExecutionException $e) {
|
||||||
return (int) $e->getCode();
|
return (int) $e->getCode();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
$ghe = new GithubActionError($this->io);
|
||||||
|
$ghe->emit($e->getMessage());
|
||||||
|
|
||||||
$this->hintCommonErrors($e);
|
$this->hintCommonErrors($e);
|
||||||
|
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Composer\Console;
|
||||||
|
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
|
||||||
|
final class GithubActionError
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var IOInterface
|
||||||
|
*/
|
||||||
|
protected $io;
|
||||||
|
|
||||||
|
public function __construct(IOInterface $io)
|
||||||
|
{
|
||||||
|
$this->io = $io;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message
|
||||||
|
* @param null|string $file
|
||||||
|
* @param null|int $line
|
||||||
|
*/
|
||||||
|
public function emit($message, $file = null, $line = null)
|
||||||
|
{
|
||||||
|
if (getenv('GITHUB_ACTIONS') && !getenv('COMPOSER_TESTS_ARE_RUNNING')) {
|
||||||
|
// newlines need to be encoded
|
||||||
|
// see https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448
|
||||||
|
$message = str_replace("\n", '%0A', $message);
|
||||||
|
|
||||||
|
if ($file && $line) {
|
||||||
|
$this->io->write("::error file=". $file .",line=". $line ."::". $message);
|
||||||
|
} elseif ($file) {
|
||||||
|
$this->io->write("::error file=". $file ."::". $message);
|
||||||
|
} else {
|
||||||
|
$this->io->write("::error ::". $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ class DefaultPolicy implements PolicyInterface
|
||||||
$sortedLiterals = $this->pruneRemoteAliases($pool, $sortedLiterals);
|
$sortedLiterals = $this->pruneRemoteAliases($pool, $sortedLiterals);
|
||||||
}
|
}
|
||||||
|
|
||||||
$selected = \call_user_func_array('array_merge', $packages);
|
$selected = \call_user_func_array('array_merge', array_values($packages));
|
||||||
|
|
||||||
// now sort the result across all packages to respect replaces across packages
|
// now sort the result across all packages to respect replaces across packages
|
||||||
usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) {
|
usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) {
|
||||||
|
|
|
@ -276,27 +276,19 @@ class Problem
|
||||||
|
|
||||||
// check if the package is found when bypassing stability checks
|
// check if the package is found when bypassing stability checks
|
||||||
if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
|
if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
|
||||||
|
// we must first verify if a valid package would be found in a lower priority repository
|
||||||
|
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
|
||||||
|
return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, $packages, $allReposPackages, 'minimum-stability');
|
||||||
|
}
|
||||||
|
|
||||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the package is found when bypassing the constraint check
|
// check if the package is found when bypassing the constraint and stability checks
|
||||||
if ($packages = $repositorySet->findPackages($packageName, null)) {
|
if ($packages = $repositorySet->findPackages($packageName, null, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
|
||||||
// we must first verify if a valid package would be found in a lower priority repository
|
// we must first verify if a valid package would be found in a lower priority repository
|
||||||
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
|
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
|
||||||
$higherRepoPackages = $repositorySet->findPackages($packageName, null);
|
return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, $packages, $allReposPackages, 'constraint');
|
||||||
$nextRepoPackages = array();
|
|
||||||
$nextRepo = null;
|
|
||||||
|
|
||||||
foreach ($allReposPackages as $package) {
|
|
||||||
if ($nextRepo === null || $nextRepo === $package->getRepository()) {
|
|
||||||
$nextRepoPackages[] = $package;
|
|
||||||
$nextRepo = $package->getRepository();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.');
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.');
|
||||||
|
@ -396,6 +388,24 @@ class Problem
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, array $higherRepoPackages, array $allReposPackages, $reason)
|
||||||
|
{
|
||||||
|
$nextRepoPackages = array();
|
||||||
|
$nextRepo = null;
|
||||||
|
|
||||||
|
foreach ($allReposPackages as $package) {
|
||||||
|
if ($nextRepo === null || $nextRepo === $package->getRepository()) {
|
||||||
|
$nextRepoPackages[] = $package;
|
||||||
|
$nextRepo = $package->getRepository();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your '.$reason.' and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a constraint into text usable in a sentence describing a request
|
* Turns a constraint into text usable in a sentence describing a request
|
||||||
*
|
*
|
||||||
|
|
|
@ -46,10 +46,17 @@ abstract class ArchiveDownloader extends FileDownloader
|
||||||
$this->io->writeError('Extracting archive', false);
|
$this->io->writeError('Extracting archive', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->filesystem->emptyDirectory($path);
|
$vendorDir = $this->config->get('vendor-dir');
|
||||||
|
|
||||||
|
// clean up the target directory, unless it contains the vendor dir, as the vendor dir contains
|
||||||
|
// the archive to be extracted. This is the case when installing with create-project in the current directory
|
||||||
|
// but in that case we ensure the directory is empty already in ProjectInstaller so no need to empty it here.
|
||||||
|
if (false === strpos($this->filesystem->normalizePath($vendorDir), $this->filesystem->normalizePath($path).DIRECTORY_SEPARATOR)) {
|
||||||
|
$this->filesystem->emptyDirectory($path);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
$temporaryDir = $vendorDir.'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||||
} while (is_dir($temporaryDir));
|
} while (is_dir($temporaryDir));
|
||||||
|
|
||||||
$this->addCleanupPath($package, $temporaryDir);
|
$this->addCleanupPath($package, $temporaryDir);
|
||||||
|
|
|
@ -187,7 +187,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
||||||
$url = reset($urls);
|
$url = reset($urls);
|
||||||
$cacheKey = $url['cacheKey'];
|
$cacheKey = $url['cacheKey'];
|
||||||
|
|
||||||
if ($cache) {
|
if ($cache && !$cache->isReadOnly()) {
|
||||||
$self->lastCacheWrites[$package->getName()] = $cacheKey;
|
$self->lastCacheWrites[$package->getName()] = $cacheKey;
|
||||||
$cache->copyFrom($cacheKey, $fileName);
|
$cache->copyFrom($cacheKey, $fileName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,10 +146,10 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
||||||
|
|
||||||
if (!empty($this->cachedPackages[$target->getId()][$ref])) {
|
if (!empty($this->cachedPackages[$target->getId()][$ref])) {
|
||||||
$msg = "Checking out ".$this->getShortHash($ref).' from cache';
|
$msg = "Checking out ".$this->getShortHash($ref).' from cache';
|
||||||
$command = 'git rev-parse --quiet --verify %ref% || (git remote set-url composer %cachePath% && git fetch composer && git fetch --tags composer); git remote set-url composer %sanitizedUrl%';
|
$command = '(git rev-parse --quiet --verify %ref% || (git remote set-url composer %cachePath% && git fetch composer && git fetch --tags composer)) && git remote set-url composer %sanitizedUrl%';
|
||||||
} else {
|
} else {
|
||||||
$msg = "Checking out ".$this->getShortHash($ref);
|
$msg = "Checking out ".$this->getShortHash($ref);
|
||||||
$command = 'git remote set-url composer %url% && git rev-parse --quiet --verify %ref% || (git fetch composer && git fetch --tags composer); git remote set-url composer %sanitizedUrl%';
|
$command = '(git remote set-url composer %url% && git rev-parse --quiet --verify %ref% || (git fetch composer && git fetch --tags composer)) && git remote set-url composer %sanitizedUrl%';
|
||||||
if (getenv('COMPOSER_DISABLE_NETWORK')) {
|
if (getenv('COMPOSER_DISABLE_NETWORK')) {
|
||||||
throw new \RuntimeException('The required git reference for '.$target->getName().' is not in cache and network is disabled, aborting');
|
throw new \RuntimeException('The required git reference for '.$target->getName().' is not in cache and network is disabled, aborting');
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Composer\Downloader;
|
||||||
|
|
||||||
|
class MaxFileSizeExceededException extends TransportException
|
||||||
|
{
|
||||||
|
}
|
|
@ -180,7 +180,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
||||||
{
|
{
|
||||||
$realUrl = realpath($package->getDistUrl());
|
$realUrl = realpath($package->getDistUrl());
|
||||||
|
|
||||||
if ($path === $realUrl) {
|
if (realpath($path) === $realUrl) {
|
||||||
if ($output) {
|
if ($output) {
|
||||||
$this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path");
|
$this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path");
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,6 +477,7 @@ class Factory
|
||||||
$cache = null;
|
$cache = null;
|
||||||
if ($config->get('cache-files-ttl') > 0) {
|
if ($config->get('cache-files-ttl') > 0) {
|
||||||
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
|
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
|
||||||
|
$cache->setReadOnly($config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$fs = new Filesystem($process);
|
$fs = new Filesystem($process);
|
||||||
|
|
|
@ -46,7 +46,7 @@ class InstalledVersions
|
||||||
*
|
*
|
||||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
* @param string $packageName
|
* @param string $packageName
|
||||||
* @param ?string $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer;
|
namespace Composer;
|
||||||
|
|
||||||
use Composer\Autoload\AutoloadGenerator;
|
use Composer\Autoload\AutoloadGenerator;
|
||||||
|
use Composer\Console\GithubActionError;
|
||||||
use Composer\DependencyResolver\DefaultPolicy;
|
use Composer\DependencyResolver\DefaultPolicy;
|
||||||
use Composer\DependencyResolver\LocalRepoTransaction;
|
use Composer\DependencyResolver\LocalRepoTransaction;
|
||||||
use Composer\DependencyResolver\LockTransaction;
|
use Composer\DependencyResolver\LockTransaction;
|
||||||
|
@ -387,9 +388,13 @@ class Installer
|
||||||
$this->io->writeError('<info>Updating dependencies</info>');
|
$this->io->writeError('<info>Updating dependencies</info>');
|
||||||
|
|
||||||
// if we're updating mirrors we want to keep exactly the same versions installed which are in the lock file, but we want current remote metadata
|
// if we're updating mirrors we want to keep exactly the same versions installed which are in the lock file, but we want current remote metadata
|
||||||
if ($this->updateMirrors) {
|
if ($this->updateMirrors && $lockedRepository) {
|
||||||
foreach ($lockedRepository->getPackages() as $lockedPackage) {
|
foreach ($lockedRepository->getPackages() as $lockedPackage) {
|
||||||
$request->requireName($lockedPackage->getName(), new Constraint('==', $lockedPackage->getVersion()));
|
// exclude alias packages here as for root aliases, both alias and aliased are
|
||||||
|
// present in the lock repo and we only want to require the aliased version
|
||||||
|
if (!$lockedPackage instanceof AliasPackage) {
|
||||||
|
$request->requireName($lockedPackage->getName(), new Constraint('==', $lockedPackage->getVersion()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
|
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
|
||||||
|
@ -412,12 +417,18 @@ class Installer
|
||||||
$ruleSetSize = $solver->getRuleSetSize();
|
$ruleSetSize = $solver->getRuleSetSize();
|
||||||
$solver = null;
|
$solver = null;
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
|
$err = 'Your requirements could not be resolved to an installable set of packages.';
|
||||||
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
|
$prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose());
|
||||||
|
|
||||||
|
$this->io->writeError('<error>'. $err .'</error>', true, IOInterface::QUIET);
|
||||||
|
$this->io->writeError($prettyProblem);
|
||||||
if (!$this->devMode) {
|
if (!$this->devMode) {
|
||||||
$this->io->writeError('<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>', true, IOInterface::QUIET);
|
$this->io->writeError('<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>', true, IOInterface::QUIET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ghe = new GithubActionError($this->io);
|
||||||
|
$ghe->emit($err."\n".$prettyProblem);
|
||||||
|
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,10 +582,16 @@ class Installer
|
||||||
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
||||||
$solver = null;
|
$solver = null;
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Unable to find a compatible set of packages based on your non-dev requirements alone.</error>', true, IOInterface::QUIET);
|
$err = 'Unable to find a compatible set of packages based on your non-dev requirements alone.';
|
||||||
|
$prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true);
|
||||||
|
|
||||||
|
$this->io->writeError('<error>'. $err .'</error>', true, IOInterface::QUIET);
|
||||||
$this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.');
|
$this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.');
|
||||||
$this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.');
|
$this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.');
|
||||||
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true));
|
$this->io->writeError($prettyProblem);
|
||||||
|
|
||||||
|
$ghe = new GithubActionError($this->io);
|
||||||
|
$ghe->emit($err."\n".$prettyProblem);
|
||||||
|
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
@ -637,8 +654,14 @@ class Installer
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Your lock file does not contain a compatible set of packages. Please run composer update.</error>', true, IOInterface::QUIET);
|
$err = 'Your lock file does not contain a compatible set of packages. Please run composer update.';
|
||||||
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
|
$prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose());
|
||||||
|
|
||||||
|
$this->io->writeError('<error>'. $err .'</error>', true, IOInterface::QUIET);
|
||||||
|
$this->io->writeError($prettyProblem);
|
||||||
|
|
||||||
|
$ghe = new GithubActionError($this->io);
|
||||||
|
$ghe->emit($err."\n".$prettyProblem);
|
||||||
|
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,25 +111,27 @@ class ValidatingArrayLoader implements LoaderInterface
|
||||||
if (is_array($this->config['license']) || is_string($this->config['license'])) {
|
if (is_array($this->config['license']) || is_string($this->config['license'])) {
|
||||||
$licenses = (array) $this->config['license'];
|
$licenses = (array) $this->config['license'];
|
||||||
|
|
||||||
// strip proprietary since it's not a valid SPDX identifier, but is accepted by composer
|
|
||||||
foreach ($licenses as $key => $license) {
|
|
||||||
if ('proprietary' === $license) {
|
|
||||||
unset($licenses[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$licenseValidator = new SpdxLicenses();
|
$licenseValidator = new SpdxLicenses();
|
||||||
if (count($licenses) === 1 && !$licenseValidator->validate($licenses) && $licenseValidator->validate(trim($licenses[0]))) {
|
foreach ($licenses as $license) {
|
||||||
$this->warnings[] = sprintf(
|
// replace proprietary by MIT for validation purposes since it's not a valid SPDX identifier, but is accepted by composer
|
||||||
'License %s must not contain extra spaces, make sure to trim it.',
|
if ('proprietary' === $license) {
|
||||||
json_encode($this->config['license'])
|
continue;
|
||||||
);
|
}
|
||||||
} elseif (array() !== $licenses && !$licenseValidator->validate($licenses)) {
|
$licenseToValidate = str_replace('proprietary', 'MIT', $license);
|
||||||
$this->warnings[] = sprintf(
|
if (!$licenseValidator->validate($licenseToValidate)) {
|
||||||
'License %s is not a valid SPDX license identifier, see https://spdx.org/licenses/ if you use an open license.' . PHP_EOL .
|
if ($licenseValidator->validate(trim($licenseToValidate))) {
|
||||||
'If the software is closed-source, you may use "proprietary" as license.',
|
$this->warnings[] = sprintf(
|
||||||
json_encode($this->config['license'])
|
'License %s must not contain extra spaces, make sure to trim it.',
|
||||||
);
|
json_encode($license)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->warnings[] = sprintf(
|
||||||
|
'License %s is not a valid SPDX license identifier, see https://spdx.org/licenses/ if you use an open license.' . PHP_EOL .
|
||||||
|
'If the software is closed-source, you may use "proprietary" as license.',
|
||||||
|
json_encode($license)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Platform;
|
||||||
|
|
||||||
|
use Composer\Util\Platform;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
|
|
||||||
|
class HhvmDetector
|
||||||
|
{
|
||||||
|
private static $hhvmVersion;
|
||||||
|
private $executableFinder;
|
||||||
|
private $processExecutor;
|
||||||
|
|
||||||
|
public function __construct(ExecutableFinder $executableFinder = null, ProcessExecutor $processExecutor = null)
|
||||||
|
{
|
||||||
|
$this->executableFinder = $executableFinder;
|
||||||
|
$this->processExecutor = $processExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
self::$hhvmVersion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion()
|
||||||
|
{
|
||||||
|
if (null !== self::$hhvmVersion) {
|
||||||
|
return self::$hhvmVersion ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null;
|
||||||
|
if (self::$hhvmVersion === null && !Platform::isWindows()) {
|
||||||
|
self::$hhvmVersion = false;
|
||||||
|
$this->executableFinder = $this->executableFinder ?: new ExecutableFinder();
|
||||||
|
$hhvmPath = $this->executableFinder->find('hhvm');
|
||||||
|
if ($hhvmPath !== null) {
|
||||||
|
$this->processExecutor = $this->processExecutor ?: new ProcessExecutor();
|
||||||
|
$exitCode = $this->processExecutor->execute(
|
||||||
|
ProcessExecutor::escape($hhvmPath).
|
||||||
|
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
|
||||||
|
self::$hhvmVersion
|
||||||
|
);
|
||||||
|
if ($exitCode !== 0) {
|
||||||
|
self::$hhvmVersion = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$hhvmVersion;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Platform;
|
||||||
|
|
||||||
|
class Runtime
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $constant
|
||||||
|
* @param class-string $class
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasConstant($constant, $class = null)
|
||||||
|
{
|
||||||
|
return defined(ltrim($class.'::'.$constant, ':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $constant
|
||||||
|
* @param class-string $class
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getConstant($constant, $class = null)
|
||||||
|
{
|
||||||
|
return constant(ltrim($class.'::'.$constant, ':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $fn
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasFunction($fn)
|
||||||
|
{
|
||||||
|
return function_exists($fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
* @param array $arguments
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function invoke($callable, array $arguments = array())
|
||||||
|
{
|
||||||
|
return call_user_func_array($callable, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param class-string $class
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasClass($class)
|
||||||
|
{
|
||||||
|
return class_exists($class, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param class-string $class
|
||||||
|
* @param array $arguments
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function construct($class, array $arguments = array())
|
||||||
|
{
|
||||||
|
if (empty($arguments)) {
|
||||||
|
return new $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$refl = new \ReflectionClass($class);
|
||||||
|
return $refl->newInstanceArgs($arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return string[] */
|
||||||
|
public function getExtensions()
|
||||||
|
{
|
||||||
|
return get_loaded_extensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtensionVersion($extension)
|
||||||
|
{
|
||||||
|
return phpversion($extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtensionInfo($extension)
|
||||||
|
{
|
||||||
|
$reflector = new \ReflectionExtension($extension);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$reflector->info();
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lars Strojny <lars@strojny.net>
|
||||||
|
*/
|
||||||
|
class Version
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $opensslVersion
|
||||||
|
* @param bool $isFips
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseOpenssl($opensslVersion, &$isFips)
|
||||||
|
{
|
||||||
|
$isFips = false;
|
||||||
|
|
||||||
|
if (!preg_match('/^(?<version>[0-9.]+)(?<patch>[a-z]{0,2})?(?<suffix>(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)?$/', $opensslVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isFips = strpos($matches['suffix'], 'fips') !== false;
|
||||||
|
$suffix = strtr('-'.ltrim($matches['suffix'], '-'), array('-fips' => '', '-pre' => '-alpha'));
|
||||||
|
$patch = self::convertAlphaVersionToIntVersion($matches['patch']);
|
||||||
|
|
||||||
|
return rtrim($matches['version'].'.'.$patch.$suffix, '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $libjpegVersion
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseLibjpeg($libjpegVersion)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^(?<major>\d+)(?<minor>[a-z]*)$/', $libjpegVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches['major'].'.'.self::convertAlphaVersionToIntVersion($matches['minor']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $zoneinfoVersion
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseZoneinfoVersion($zoneinfoVersion)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^(?<year>\d{4})(?<revision>[a-z]*)$/', $zoneinfoVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches['year'].'.'.self::convertAlphaVersionToIntVersion($matches['revision']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "" => 0, "a" => 1, "zg" => 33
|
||||||
|
*
|
||||||
|
* @param string $alpha
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private static function convertAlphaVersionToIntVersion($alpha)
|
||||||
|
{
|
||||||
|
return strlen($alpha) * (-ord('a')+1) + array_sum(array_map('ord', str_split($alpha)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $versionId
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertLibxpmVersionId($versionId)
|
||||||
|
{
|
||||||
|
return self::convertVersionId($versionId, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $versionId
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertOpenldapVersionId($versionId)
|
||||||
|
{
|
||||||
|
return self::convertVersionId($versionId, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function convertVersionId($versionId, $base)
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'%d.%d.%d',
|
||||||
|
$versionId / ($base * $base),
|
||||||
|
($versionId / $base) % $base,
|
||||||
|
$versionId % $base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ use Composer\IO\IOInterface;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\Loader\ArrayLoader;
|
use Composer\Package\Loader\ArrayLoader;
|
||||||
use Composer\Package\Loader\LoaderInterface;
|
use Composer\Package\Loader\LoaderInterface;
|
||||||
|
use Composer\Util\Tar;
|
||||||
use Composer\Util\Zip;
|
use Composer\Util\Zip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +67,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
|
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
|
||||||
$iterator = new \RecursiveIteratorIterator($directory);
|
$iterator = new \RecursiveIteratorIterator($directory);
|
||||||
$regex = new \RegexIterator($iterator, '/^.+\.(zip|phar)$/i');
|
$regex = new \RegexIterator($iterator, '/^.+\.(zip|phar|tar|gz|tgz)$/i');
|
||||||
foreach ($regex as $file) {
|
foreach ($regex as $file) {
|
||||||
/* @var $file \SplFileInfo */
|
/* @var $file \SplFileInfo */
|
||||||
if (!$file->isFile()) {
|
if (!$file->isFile()) {
|
||||||
|
@ -88,7 +89,26 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
private function getComposerInformation(\SplFileInfo $file)
|
private function getComposerInformation(\SplFileInfo $file)
|
||||||
{
|
{
|
||||||
$json = Zip::getComposerJson($file->getPathname());
|
$json = null;
|
||||||
|
$fileType = null;
|
||||||
|
$fileExtension = pathinfo($file->getPathname(), PATHINFO_EXTENSION);
|
||||||
|
if (in_array($fileExtension, array('gz', 'tar', 'tgz'), true)) {
|
||||||
|
$fileType = 'tar';
|
||||||
|
} else if ($fileExtension === 'zip') {
|
||||||
|
$fileType = 'zip';
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException('Files with "'.$fileExtension.'" extensions aren\'t supported. Only ZIP and TAR/TAR.GZ/TGZ archives are supported.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($fileType === 'tar') {
|
||||||
|
$json = Tar::getComposerJson($file->getPathname());
|
||||||
|
} else {
|
||||||
|
$json = Zip::getComposerJson($file->getPathname());
|
||||||
|
}
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$this->io->write('Failed loading package '.$file->getPathname().': '.$exception->getMessage(), false, IOInterface::VERBOSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (null === $json) {
|
if (null === $json) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -96,7 +116,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
$package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
|
$package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
|
||||||
$package['dist'] = array(
|
$package['dist'] = array(
|
||||||
'type' => 'zip',
|
'type' => $fileType,
|
||||||
'url' => strtr($file->getPathname(), '\\', '/'),
|
'url' => strtr($file->getPathname(), '\\', '/'),
|
||||||
'shasum' => sha1_file($file->getRealPath()),
|
'shasum' => sha1_file($file->getRealPath()),
|
||||||
);
|
);
|
||||||
|
|
|
@ -130,6 +130,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$~');
|
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$~');
|
||||||
|
$this->cache->setReadOnly($config->get('cache-read-only'));
|
||||||
$this->versionParser = new VersionParser();
|
$this->versionParser = new VersionParser();
|
||||||
$this->loader = new ArrayLoader($this->versionParser);
|
$this->loader = new ArrayLoader($this->versionParser);
|
||||||
$this->httpDownloader = $httpDownloader;
|
$this->httpDownloader = $httpDownloader;
|
||||||
|
@ -1082,7 +1083,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data = $response->decodeJson();
|
$data = $response->decodeJson();
|
||||||
HttpDownloader::outputWarnings($this->io, $this->url, $data);
|
HttpDownloader::outputWarnings($this->io, $this->url, $data);
|
||||||
|
|
||||||
if ($cacheKey) {
|
if ($cacheKey && !$this->cache->isReadOnly()) {
|
||||||
if ($storeLastModifiedTime) {
|
if ($storeLastModifiedTime) {
|
||||||
$lastModifiedDate = $response->getHeader('last-modified');
|
$lastModifiedDate = $response->getHeader('last-modified');
|
||||||
if ($lastModifiedDate) {
|
if ($lastModifiedDate) {
|
||||||
|
@ -1150,7 +1151,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
if (isset($options['http']['header'])) {
|
if (isset($options['http']['header'])) {
|
||||||
$options['http']['header'] = (array) $options['http']['header'];
|
$options['http']['header'] = (array) $options['http']['header'];
|
||||||
}
|
}
|
||||||
$options['http']['header'][] = array('If-Modified-Since: '.$lastModifiedTime);
|
$options['http']['header'][] = 'If-Modified-Since: '.$lastModifiedTime;
|
||||||
$response = $this->httpDownloader->get($filename, $options);
|
$response = $this->httpDownloader->get($filename, $options);
|
||||||
$json = $response->getBody();
|
$json = $response->getBody();
|
||||||
if ($json === '' && $response->getStatusCode() === 304) {
|
if ($json === '' && $response->getStatusCode() === 304) {
|
||||||
|
@ -1166,7 +1167,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data['last-modified'] = $lastModifiedDate;
|
$data['last-modified'] = $lastModifiedDate;
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
}
|
}
|
||||||
$this->cache->write($cacheKey, $json);
|
if (!$this->cache->isReadOnly()) {
|
||||||
|
$this->cache->write($cacheKey, $json);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -1213,7 +1216,13 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$filename = $preFileDownloadEvent->getProcessedUrl();
|
$filename = $preFileDownloadEvent->getProcessedUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
$options = $lastModifiedTime ? array('http' => array('header' => array('If-Modified-Since: '.$lastModifiedTime))) : array();
|
$options = $this->options;
|
||||||
|
if ($lastModifiedTime) {
|
||||||
|
if (isset($options['http']['header'])) {
|
||||||
|
$options['http']['header'] = (array) $options['http']['header'];
|
||||||
|
}
|
||||||
|
$options['http']['header'][] = 'If-Modified-Since: '.$lastModifiedTime;
|
||||||
|
}
|
||||||
|
|
||||||
$io = $this->io;
|
$io = $this->io;
|
||||||
$url = $this->url;
|
$url = $this->url;
|
||||||
|
@ -1243,7 +1252,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data['last-modified'] = $lastModifiedDate;
|
$data['last-modified'] = $lastModifiedDate;
|
||||||
$json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE);
|
$json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
$cache->write($cacheKey, $json);
|
if (!$cache->isReadOnly()) {
|
||||||
|
$cache->write($cacheKey, $json);
|
||||||
|
}
|
||||||
$repo->freshMetadataUrls[$filename] = true;
|
$repo->freshMetadataUrls[$filename] = true;
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -14,14 +14,16 @@ namespace Composer\Repository;
|
||||||
|
|
||||||
use Composer\Composer;
|
use Composer\Composer;
|
||||||
use Composer\Package\CompletePackage;
|
use Composer\Package\CompletePackage;
|
||||||
|
use Composer\Package\Link;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
|
use Composer\Platform\HhvmDetector;
|
||||||
|
use Composer\Platform\Runtime;
|
||||||
|
use Composer\Platform\Version;
|
||||||
use Composer\Plugin\PluginInterface;
|
use Composer\Plugin\PluginInterface;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Semver\Constraint\Constraint;
|
||||||
use Composer\Util\Silencer;
|
use Composer\Util\Silencer;
|
||||||
use Composer\Util\Platform;
|
|
||||||
use Composer\XdebugHandler\XdebugHandler;
|
use Composer\XdebugHandler\XdebugHandler;
|
||||||
use Symfony\Component\Process\ExecutableFinder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
@ -30,7 +32,9 @@ class PlatformRepository extends ArrayRepository
|
||||||
{
|
{
|
||||||
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD';
|
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD';
|
||||||
|
|
||||||
private static $hhvmVersion;
|
/**
|
||||||
|
* @var VersionParser
|
||||||
|
*/
|
||||||
private $versionParser;
|
private $versionParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,11 +46,13 @@ class PlatformRepository extends ArrayRepository
|
||||||
*/
|
*/
|
||||||
private $overrides = array();
|
private $overrides = array();
|
||||||
|
|
||||||
private $process;
|
private $runtime;
|
||||||
|
private $hhvmDetector;
|
||||||
|
|
||||||
public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null)
|
public function __construct(array $packages = array(), array $overrides = array(), Runtime $runtime = null, HhvmDetector $hhvmDetector = null)
|
||||||
{
|
{
|
||||||
$this->process = $process;
|
$this->runtime = $runtime ?: new Runtime();
|
||||||
|
$this->hhvmDetector = $hhvmDetector ?: new HhvmDetector();
|
||||||
foreach ($overrides as $name => $version) {
|
foreach ($overrides as $name => $version) {
|
||||||
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
|
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
|
||||||
}
|
}
|
||||||
|
@ -88,10 +94,10 @@ class PlatformRepository extends ArrayRepository
|
||||||
$this->addPackage($composerRuntimeApi);
|
$this->addPackage($composerRuntimeApi);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$prettyVersion = PHP_VERSION;
|
$prettyVersion = $this->runtime->getConstant('PHP_VERSION');
|
||||||
$version = $this->versionParser->normalize($prettyVersion);
|
$version = $this->versionParser->normalize($prettyVersion);
|
||||||
} catch (\UnexpectedValueException $e) {
|
} catch (\UnexpectedValueException $e) {
|
||||||
$prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', PHP_VERSION);
|
$prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', $this->runtime->getConstant('PHP_VERSION'));
|
||||||
$version = $this->versionParser->normalize($prettyVersion);
|
$version = $this->versionParser->normalize($prettyVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,19 +105,19 @@ class PlatformRepository extends ArrayRepository
|
||||||
$php->setDescription('The PHP interpreter');
|
$php->setDescription('The PHP interpreter');
|
||||||
$this->addPackage($php);
|
$this->addPackage($php);
|
||||||
|
|
||||||
if (PHP_DEBUG) {
|
if ($this->runtime->getConstant('PHP_DEBUG')) {
|
||||||
$phpdebug = new CompletePackage('php-debug', $version, $prettyVersion);
|
$phpdebug = new CompletePackage('php-debug', $version, $prettyVersion);
|
||||||
$phpdebug->setDescription('The PHP interpreter, with debugging symbols');
|
$phpdebug->setDescription('The PHP interpreter, with debugging symbols');
|
||||||
$this->addPackage($phpdebug);
|
$this->addPackage($phpdebug);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined('PHP_ZTS') && PHP_ZTS) {
|
if ($this->runtime->hasConstant('PHP_ZTS') && $this->runtime->getConstant('PHP_ZTS')) {
|
||||||
$phpzts = new CompletePackage('php-zts', $version, $prettyVersion);
|
$phpzts = new CompletePackage('php-zts', $version, $prettyVersion);
|
||||||
$phpzts->setDescription('The PHP interpreter, with Zend Thread Safety');
|
$phpzts->setDescription('The PHP interpreter, with Zend Thread Safety');
|
||||||
$this->addPackage($phpzts);
|
$this->addPackage($phpzts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PHP_INT_SIZE === 8) {
|
if ($this->runtime->getConstant('PHP_INT_SIZE') === 8) {
|
||||||
$php64 = new CompletePackage('php-64bit', $version, $prettyVersion);
|
$php64 = new CompletePackage('php-64bit', $version, $prettyVersion);
|
||||||
$php64->setDescription('The PHP interpreter, 64bit');
|
$php64->setDescription('The PHP interpreter, 64bit');
|
||||||
$this->addPackage($php64);
|
$this->addPackage($php64);
|
||||||
|
@ -119,13 +125,13 @@ class PlatformRepository extends ArrayRepository
|
||||||
|
|
||||||
// The AF_INET6 constant is only defined if ext-sockets is available but
|
// The AF_INET6 constant is only defined if ext-sockets is available but
|
||||||
// IPv6 support might still be available.
|
// IPv6 support might still be available.
|
||||||
if (defined('AF_INET6') || Silencer::call('inet_pton', '::') !== false) {
|
if ($this->runtime->hasConstant('AF_INET6') || Silencer::call(array($this->runtime, 'invoke'), 'inet_pton', array('::')) !== false) {
|
||||||
$phpIpv6 = new CompletePackage('php-ipv6', $version, $prettyVersion);
|
$phpIpv6 = new CompletePackage('php-ipv6', $version, $prettyVersion);
|
||||||
$phpIpv6->setDescription('The PHP interpreter, with IPv6 support');
|
$phpIpv6->setDescription('The PHP interpreter, with IPv6 support');
|
||||||
$this->addPackage($phpIpv6);
|
$this->addPackage($phpIpv6);
|
||||||
}
|
}
|
||||||
|
|
||||||
$loadedExtensions = get_loaded_extensions();
|
$loadedExtensions = $this->runtime->getExtensions();
|
||||||
|
|
||||||
// Extensions scanning
|
// Extensions scanning
|
||||||
foreach ($loadedExtensions as $name) {
|
foreach ($loadedExtensions as $name) {
|
||||||
|
@ -133,9 +139,7 @@ class PlatformRepository extends ArrayRepository
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$reflExt = new \ReflectionExtension($name);
|
$this->addExtension($name, $this->runtime->getExtensionVersion($name));
|
||||||
$prettyVersion = $reflExt->getVersion();
|
|
||||||
$this->addExtension($name, $prettyVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for Xdebug in a restarted process
|
// Check for Xdebug in a restarted process
|
||||||
|
@ -147,112 +151,317 @@ class PlatformRepository extends ArrayRepository
|
||||||
// Doing it this way to know that functions or constants exist before
|
// Doing it this way to know that functions or constants exist before
|
||||||
// relying on them.
|
// relying on them.
|
||||||
foreach ($loadedExtensions as $name) {
|
foreach ($loadedExtensions as $name) {
|
||||||
$prettyVersion = null;
|
|
||||||
$description = 'The '.$name.' PHP library';
|
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
|
case 'amqp':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// librabbitmq version => 0.9.0
|
||||||
|
if (preg_match('/^librabbitmq version => (?<version>.+)$/im', $info, $librabbitmqMatches)) {
|
||||||
|
$this->addLibrary($name.'-librabbitmq', $librabbitmqMatches['version'], 'AMQP librabbitmq version');
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMQP protocol version => 0-9-1
|
||||||
|
if (preg_match('/^AMQP protocol version => (?<version>.+)$/im', $info, $protocolMatches)) {
|
||||||
|
$this->addLibrary($name.'-protocol', str_replace('-', '.', $protocolMatches['version']), 'AMQP protocol version');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bz2':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// BZip2 Version => 1.0.6, 6-Sept-2010
|
||||||
|
if (preg_match('/^BZip2 Version => (?<version>.*),/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name, $matches['version']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'curl':
|
case 'curl':
|
||||||
$curlVersion = curl_version();
|
$curlVersion = $this->runtime->invoke('curl_version');
|
||||||
$prettyVersion = $curlVersion['version'];
|
$this->addLibrary($name, $curlVersion['version']);
|
||||||
|
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// SSL Version => OpenSSL/1.0.1t
|
||||||
|
if (preg_match('{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im', $info, $sslMatches)) {
|
||||||
|
$library = strtolower($sslMatches['library']);
|
||||||
|
if ($library === 'openssl') {
|
||||||
|
$parsedVersion = Version::parseOpenssl($sslMatches['version'], $isFips);
|
||||||
|
$this->addLibrary($name.'-openssl'.($isFips ? '-fips': ''), $parsedVersion, 'curl OpenSSL version ('.$parsedVersion.')', array(), $isFips ? array('curl-openssl'): array());
|
||||||
|
} else {
|
||||||
|
$this->addLibrary($name.'-'.$library, $sslMatches['version'], 'curl '.$library.' version ('.$sslMatches['version'].')', array('curl-openssl'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// libSSH Version => libssh2/1.4.3
|
||||||
|
if (preg_match('{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im', $info, $sshMatches)) {
|
||||||
|
$this->addLibrary($name.'-'.strtolower($sshMatches['library']), $sshMatches['version'], 'curl '.$sshMatches['library'].' version');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZLib Version => 1.2.8
|
||||||
|
if (preg_match('{^ZLib Version => (?<version>.+)$}im', $info, $zlibMatches)) {
|
||||||
|
$this->addLibrary($name.'-zlib', $zlibMatches['version'], 'curl zlib version');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'iconv':
|
case 'date':
|
||||||
$prettyVersion = ICONV_VERSION;
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// timelib version => 2018.03
|
||||||
|
if (preg_match('/^timelib version => (?<version>.+)$/im', $info, $timelibMatches)) {
|
||||||
|
$this->addLibrary($name.'-timelib', $timelibMatches['version'], 'date timelib version');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timezone Database => internal
|
||||||
|
if (preg_match('/^Timezone Database => (?<source>internal|external)$/im', $info, $zoneinfoSourceMatches)) {
|
||||||
|
$external = $zoneinfoSourceMatches['source'] === 'external';
|
||||||
|
if (preg_match('/^"Olson" Timezone Database Version => (?<version>.+?)(\.system)?$/im', $info, $zoneinfoMatches)) {
|
||||||
|
// If the timezonedb is provided by ext/timezonedb, register that version as a replacement
|
||||||
|
if ($external && in_array('timezonedb', $loadedExtensions, true)) {
|
||||||
|
$this->addLibrary('timezonedb-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date (replaced by timezonedb)', array($name.'-zoneinfo'));
|
||||||
|
} else {
|
||||||
|
$this->addLibrary($name.'-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'intl':
|
case 'fileinfo':
|
||||||
$name = 'ICU';
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
if (defined('INTL_ICU_VERSION')) {
|
|
||||||
$prettyVersion = INTL_ICU_VERSION;
|
|
||||||
} else {
|
|
||||||
$reflector = new \ReflectionExtension('intl');
|
|
||||||
|
|
||||||
ob_start();
|
// libmagic => 537
|
||||||
$reflector->info();
|
if (preg_match('/^libmagic => (?<version>.+)$/im', $info, $magicMatches)) {
|
||||||
$output = ob_get_clean();
|
$this->addLibrary($name.'-libmagic', $magicMatches['version'], 'fileinfo libmagic version');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
preg_match('/^ICU version => (.*)$/m', $output, $matches);
|
case 'gd':
|
||||||
$prettyVersion = $matches[1];
|
$this->addLibrary($name, $this->runtime->getConstant('GD_VERSION'));
|
||||||
|
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im', $info, $libjpegMatches)) {
|
||||||
|
$this->addLibrary($name.'-libjpeg', Version::parseLibjpeg($libjpegMatches['version']), 'libjpeg version for gd');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^libPNG Version => (?<version>.+)$/im', $info, $libpngMatches)) {
|
||||||
|
$this->addLibrary($name.'-libpng', $libpngMatches['version'], 'libpng version for gd');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^FreeType Version => (?<version>.+)$/im', $info, $freetypeMatches)) {
|
||||||
|
$this->addLibrary($name.'-freetype', $freetypeMatches['version'], 'freetype version for gd');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^libXpm Version => (?<versionId>\d+)$/im', $info, $libxpmMatches)) {
|
||||||
|
$this->addLibrary($name.'-libxpm', Version::convertLibxpmVersionId($libxpmMatches['versionId']), 'libxpm version for gd');
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'gmp':
|
||||||
|
$this->addLibrary($name, $this->runtime->getConstant('GMP_VERSION'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'iconv':
|
||||||
|
$this->addLibrary($name, $this->runtime->getConstant('ICONV_VERSION'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'intl':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
$description = 'The ICU unicode and globalization support library';
|
||||||
|
// Truthy check is for testing only so we can make the condition fail
|
||||||
|
if ($this->runtime->hasConstant('INTL_ICU_VERSION')) {
|
||||||
|
$this->addLibrary('icu', $this->runtime->getConstant('INTL_ICU_VERSION'), $description);
|
||||||
|
} elseif (preg_match('/^ICU version => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary('icu', $matches['version'], $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICU TZData version => 2019c
|
||||||
|
if (preg_match('/^ICU TZData version => (?<version>.*)$/im', $info, $zoneinfoMatches)) {
|
||||||
|
$this->addLibrary('icu-zoneinfo', Version::parseZoneinfoVersion($zoneinfoMatches['version']), 'zoneinfo ("Olson") database for icu');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a separate version for the CLDR library version
|
||||||
|
if ($this->runtime->hasClass('ResourceBundle')) {
|
||||||
|
$cldrVersion = $this->runtime->invoke(array('ResourceBundle', 'create'), array('root', 'ICUDATA', false))->get('Version');
|
||||||
|
$this->addLibrary('icu-cldr', $cldrVersion, 'ICU CLDR project version');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->runtime->hasClass('IntlChar')) {
|
||||||
|
$this->addLibrary('icu-unicode', implode('.', array_slice($this->runtime->invoke(array('IntlChar', 'getUnicodeVersion')), 0, 3)), 'ICU unicode version');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'imagick':
|
case 'imagick':
|
||||||
$imagick = new \Imagick();
|
$imageMagickVersion = $this->runtime->construct('Imagick')->getVersion();
|
||||||
$imageMagickVersion = $imagick->getVersion();
|
|
||||||
// 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
|
// 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
|
||||||
// 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
|
// 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
|
||||||
preg_match('/^ImageMagick ([\d.]+)(?:-(\d+))?/', $imageMagickVersion['versionString'], $matches);
|
preg_match('/^ImageMagick (?<version>[\d.]+)(?:-(?<patch>\d+))?/', $imageMagickVersion['versionString'], $matches);
|
||||||
if (isset($matches[2])) {
|
$version = $matches['version'];
|
||||||
$prettyVersion = "{$matches[1]}.{$matches[2]}";
|
if (isset($matches['patch'])) {
|
||||||
} else {
|
$version .= '.'.$matches['patch'];
|
||||||
$prettyVersion = $matches[1];
|
}
|
||||||
|
|
||||||
|
$this->addLibrary($name.'-imagemagick', $version, null, array('imagick'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ldap':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^Vendor Version => (?<versionId>\d+)$/im', $info, $matches) && preg_match('/^Vendor Name => (?<vendor>.+)$/im', $info, $vendorMatches)) {
|
||||||
|
$this->addLibrary($name.'-'.strtolower($vendorMatches['vendor']), Version::convertOpenldapVersionId($matches['versionId']), $vendorMatches['vendor'].' version of ldap');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'libxml':
|
case 'libxml':
|
||||||
$prettyVersion = LIBXML_DOTTED_VERSION;
|
// ext/dom, ext/simplexml, ext/xmlreader and ext/xmlwriter use the same libxml as the ext/libxml
|
||||||
|
$libxmlProvides = array_map(function($extension) {
|
||||||
|
return $extension . '-libxml';
|
||||||
|
}, array_intersect($loadedExtensions, array('dom', 'simplexml', 'xml', 'xmlreader', 'xmlwriter')));
|
||||||
|
$this->addLibrary($name, $this->runtime->getConstant('LIBXML_DOTTED_VERSION'), 'libxml library version', array(), $libxmlProvides);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'mbstring':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// libmbfl version => 1.3.2
|
||||||
|
if (preg_match('/^libmbfl version => (?<version>.+)$/im', $info, $libmbflMatches)) {
|
||||||
|
$this->addLibrary($name.'-libmbfl', $libmbflMatches['version'], 'mbstring libmbfl version');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->runtime->hasConstant('MB_ONIGURUMA_VERSION')) {
|
||||||
|
$this->addLibrary($name.'-oniguruma', $this->runtime->getConstant('MB_ONIGURUMA_VERSION'), 'mbstring oniguruma version');
|
||||||
|
|
||||||
|
// Multibyte regex (oniguruma) version => 5.9.5
|
||||||
|
// oniguruma version => 6.9.0
|
||||||
|
} elseif (preg_match('/^(?:oniguruma|Multibyte regex \(oniguruma\)) version => (?<version>.+)$/im', $info, $onigurumaMatches)) {
|
||||||
|
$this->addLibrary($name.'-oniguruma', $onigurumaMatches['version'], 'mbstring oniguruma version');
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'memcached':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// libmemcached version => 1.0.18
|
||||||
|
if (preg_match('/^libmemcached version => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-libmemcached', $matches['version'], 'libmemcached version');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'openssl':
|
case 'openssl':
|
||||||
$prettyVersion = preg_replace_callback('{^(?:OpenSSL|LibreSSL)?\s*([0-9.]+)([a-z]*).*}i', function ($match) {
|
// OpenSSL 1.1.1g 21 Apr 2020
|
||||||
if (empty($match[2])) {
|
if (preg_match('{^(?:OpenSSL|LibreSSL)?\s*(?<version>\S+)}i', $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), $matches)) {
|
||||||
return $match[1];
|
$parsedVersion = Version::parseOpenssl($matches['version'], $isFips);
|
||||||
}
|
$this->addLibrary($name.($isFips ? '-fips' : ''), $parsedVersion, $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), array(), $isFips ? array($name) : array());
|
||||||
|
}
|
||||||
// OpenSSL versions add another letter when they reach Z.
|
|
||||||
// e.g. OpenSSL 0.9.8zh 3 Dec 2015
|
|
||||||
|
|
||||||
if (!preg_match('{^z*[a-z]$}', $match[2])) {
|
|
||||||
// 0.9.8abc is garbage
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$len = strlen($match[2]);
|
|
||||||
$patchVersion = ($len - 1) * 26; // All Z
|
|
||||||
$patchVersion += ord($match[2][$len - 1]) - 96;
|
|
||||||
|
|
||||||
return $match[1].'.'.$patchVersion;
|
|
||||||
}, OPENSSL_VERSION_TEXT);
|
|
||||||
|
|
||||||
$description = OPENSSL_VERSION_TEXT;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'pcre':
|
case 'pcre':
|
||||||
$prettyVersion = preg_replace('{^(\S+).*}', '$1', PCRE_VERSION);
|
$this->addLibrary($name, preg_replace('{^(\S+).*}', '$1', $this->runtime->getConstant('PCRE_VERSION')));
|
||||||
|
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
// PCRE Unicode Version => 12.1.0
|
||||||
|
if (preg_match('/^PCRE Unicode Version => (?<version>.+)$/im', $info, $pcreUnicodeMatches)) {
|
||||||
|
$this->addLibrary($name.'-unicode', $pcreUnicodeMatches['version'], 'PCRE Unicode version support');
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'uuid':
|
case 'mysqlnd':
|
||||||
$prettyVersion = phpversion('uuid');
|
case 'pdo_mysql':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-mysqlnd', $matches['version'], 'mysqlnd library version for '.$name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'mongodb':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^libmongoc bundled version => (?<version>.+)$/im', $info, $libmongocMatches)) {
|
||||||
|
$this->addLibrary($name.'-libmongoc', $libmongocMatches['version'], 'libmongoc version of mongodb');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^libbson bundled version => (?<version>.+)$/im', $info, $libbsonMatches)) {
|
||||||
|
$this->addLibrary($name.'-libbson', $libbsonMatches['version'], 'libbson version of mongodb');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'pgsql':
|
||||||
|
case 'pdo_pgsql':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^PostgreSQL\(libpq\) Version => (?<version>.*)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-libpq', $matches['version'], 'libpq for '.$name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'libsodium':
|
||||||
|
case 'sodium':
|
||||||
|
$this->addLibrary('libsodium', $this->runtime->getConstant('SODIUM_LIBRARY_VERSION'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite3':
|
||||||
|
case 'pdo_sqlite':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^SQLite Library => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-sqlite', $matches['version']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ssh2':
|
||||||
|
$info = $this->runtime->getExtensionInfo($name);
|
||||||
|
|
||||||
|
if (preg_match('/^libssh2 version => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-libssh2', $matches['version']);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'xsl':
|
case 'xsl':
|
||||||
$prettyVersion = LIBXSLT_DOTTED_VERSION;
|
$this->addLibrary('libxslt', $this->runtime->getConstant('LIBXSLT_DOTTED_VERSION'), null, array('xsl'));
|
||||||
|
|
||||||
|
$info = $this->runtime->getExtensionInfo('xsl');
|
||||||
|
if (preg_match('/^libxslt compiled against libxml Version => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary('libxslt-libxml', $matches['version'], 'libxml version libxslt is compiled against');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'yaml':
|
||||||
|
$info = $this->runtime->getExtensionInfo('yaml');
|
||||||
|
|
||||||
|
if (preg_match('/^LibYAML Version => (?<version>.+)$/im', $info, $matches)) {
|
||||||
|
$this->addLibrary($name.'-libyaml', $matches['version'], 'libyaml version of yaml');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'zip':
|
case 'zip':
|
||||||
if (defined('ZipArchive::LIBZIP_VERSION')) {
|
if ($this->runtime->hasConstant('LIBZIP_VERSION', 'ZipArchive')) {
|
||||||
$prettyVersion = \ZipArchive::LIBZIP_VERSION;
|
$this->addLibrary($name.'-libzip', $this->runtime->getConstant('LIBZIP_VERSION','ZipArchive'), null, array('zip'));
|
||||||
} else {
|
|
||||||
continue 2;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'zlib':
|
||||||
|
if ($this->runtime->hasConstant('ZLIB_VERSION')) {
|
||||||
|
$this->addLibrary($name, $this->runtime->getConstant('ZLIB_VERSION'));
|
||||||
|
|
||||||
|
// Linked Version => 1.2.8
|
||||||
|
} elseif (preg_match('/^Linked Version => (?<version>.+)$/im', $this->runtime->getExtensionInfo($name), $matches)) {
|
||||||
|
$this->addLibrary($name, $matches['version']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// None handled extensions have no special cases, skip
|
break;
|
||||||
continue 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
$version = $this->versionParser->normalize($prettyVersion);
|
|
||||||
} catch (\UnexpectedValueException $e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
|
|
||||||
$lib->setDescription($description);
|
|
||||||
$this->addPackage($lib);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($hhvmVersion = self::getHHVMVersion($this->process)) {
|
$hhvmVersion = $this->hhvmDetector->getVersion();
|
||||||
|
if ($hhvmVersion) {
|
||||||
try {
|
try {
|
||||||
$prettyVersion = $hhvmVersion;
|
$prettyVersion = $hhvmVersion;
|
||||||
$version = $this->versionParser->normalize($prettyVersion);
|
$version = $this->versionParser->normalize($prettyVersion);
|
||||||
|
@ -337,6 +546,11 @@ class PlatformRepository extends ArrayRepository
|
||||||
$packageName = $this->buildPackageName($name);
|
$packageName = $this->buildPackageName($name);
|
||||||
$ext = new CompletePackage($packageName, $version, $prettyVersion);
|
$ext = new CompletePackage($packageName, $version, $prettyVersion);
|
||||||
$ext->setDescription('The '.$name.' PHP extension'.$extraDescription);
|
$ext->setDescription('The '.$name.' PHP extension'.$extraDescription);
|
||||||
|
|
||||||
|
if ($name === 'uuid') {
|
||||||
|
$ext->setReplaces(array(new Link('ext-uuid', 'lib-uuid', new Constraint('=', $version))));
|
||||||
|
}
|
||||||
|
|
||||||
$this->addPackage($ext);
|
$this->addPackage($ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,31 +560,37 @@ class PlatformRepository extends ArrayRepository
|
||||||
*/
|
*/
|
||||||
private function buildPackageName($name)
|
private function buildPackageName($name)
|
||||||
{
|
{
|
||||||
return 'ext-' . str_replace(' ', '-', $name);
|
return 'ext-' . str_replace(' ', '-', strtolower($name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getHHVMVersion(ProcessExecutor $process = null)
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $prettyVersion
|
||||||
|
* @param string|null $description
|
||||||
|
* @param string[] $replaces
|
||||||
|
* @param string[] $provides
|
||||||
|
*/
|
||||||
|
private function addLibrary($name, $prettyVersion, $description = null, array $replaces = array(), array $provides = array())
|
||||||
{
|
{
|
||||||
if (null !== self::$hhvmVersion) {
|
try {
|
||||||
return self::$hhvmVersion ?: null;
|
$version = $this->versionParser->normalize($prettyVersion);
|
||||||
|
} catch (\UnexpectedValueException $e) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null;
|
if ($description === null) {
|
||||||
if (self::$hhvmVersion === null && !Platform::isWindows()) {
|
$description = 'The '.$name.' library';
|
||||||
self::$hhvmVersion = false;
|
|
||||||
$finder = new ExecutableFinder();
|
|
||||||
$hhvmPath = $finder->find('hhvm');
|
|
||||||
if ($hhvmPath !== null) {
|
|
||||||
$process = $process ?: new ProcessExecutor();
|
|
||||||
$exitCode = $process->execute(
|
|
||||||
ProcessExecutor::escape($hhvmPath).
|
|
||||||
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
|
|
||||||
self::$hhvmVersion
|
|
||||||
);
|
|
||||||
if ($exitCode !== 0) {
|
|
||||||
self::$hhvmVersion = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
|
||||||
|
$lib->setDescription($description);
|
||||||
|
|
||||||
|
$links = function ($alias) use ($name, $version) {
|
||||||
|
return new Link('lib-'.$name, 'lib-'.$alias, new Constraint('=', $version));
|
||||||
|
};
|
||||||
|
$lib->setReplaces(array_map($links, $replaces));
|
||||||
|
$lib->setProvides(array_map($links, $provides));
|
||||||
|
|
||||||
|
$this->addPackage($lib);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ class RepositorySet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $candidates;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProviders($packageName)
|
public function getProviders($packageName)
|
||||||
|
|
|
@ -60,6 +60,7 @@ abstract class BitbucketDriver extends VcsDriver
|
||||||
$this->repository,
|
$this->repository,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,10 +121,14 @@ abstract class BitbucketDriver extends VcsDriver
|
||||||
|
|
||||||
if (!isset($this->infoCache[$identifier])) {
|
if (!isset($this->infoCache[$identifier])) {
|
||||||
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
||||||
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
|
$composer = JsonFile::parseJson($res);
|
||||||
}
|
} else {
|
||||||
|
$composer = $this->getBaseComposerInformation($identifier);
|
||||||
|
|
||||||
$composer = $this->getBaseComposerInformation($identifier);
|
if ($this->shouldCache($identifier)) {
|
||||||
|
$this->cache->write($identifier, json_encode($composer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
// specials for bitbucket
|
// specials for bitbucket
|
||||||
|
@ -174,10 +179,6 @@ abstract class BitbucketDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->infoCache[$identifier] = $composer;
|
$this->infoCache[$identifier] = $composer;
|
||||||
|
|
||||||
if ($this->shouldCache($identifier)) {
|
|
||||||
$this->cache->write($identifier, json_encode($composer));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->infoCache[$identifier];
|
return $this->infoCache[$identifier];
|
||||||
|
|
|
@ -75,6 +75,7 @@ class GitDriver extends VcsDriver
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,6 +58,7 @@ class GitHubDriver extends VcsDriver
|
||||||
$this->originUrl = 'github.com';
|
$this->originUrl = 'github.com';
|
||||||
}
|
}
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
if ( $this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'] ) ){
|
if ( $this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'] ) ){
|
||||||
$this->setupGitDriver($this->url);
|
$this->setupGitDriver($this->url);
|
||||||
|
@ -151,10 +152,14 @@ class GitHubDriver extends VcsDriver
|
||||||
|
|
||||||
if (!isset($this->infoCache[$identifier])) {
|
if (!isset($this->infoCache[$identifier])) {
|
||||||
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
||||||
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
|
$composer = JsonFile::parseJson($res);
|
||||||
}
|
} else {
|
||||||
|
$composer = $this->getBaseComposerInformation($identifier);
|
||||||
|
|
||||||
$composer = $this->getBaseComposerInformation($identifier);
|
if ($this->shouldCache($identifier)) {
|
||||||
|
$this->cache->write($identifier, json_encode($composer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
// specials for github
|
// specials for github
|
||||||
|
@ -173,10 +178,6 @@ class GitHubDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->shouldCache($identifier)) {
|
|
||||||
$this->cache->write($identifier, json_encode($composer));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->infoCache[$identifier] = $composer;
|
$this->infoCache[$identifier] = $composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,7 @@ class GitLabDriver extends VcsDriver
|
||||||
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository);
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
$this->fetchProject();
|
$this->fetchProject();
|
||||||
}
|
}
|
||||||
|
@ -131,10 +132,14 @@ class GitLabDriver extends VcsDriver
|
||||||
|
|
||||||
if (!isset($this->infoCache[$identifier])) {
|
if (!isset($this->infoCache[$identifier])) {
|
||||||
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
||||||
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
|
$composer = JsonFile::parseJson($res);
|
||||||
}
|
} else {
|
||||||
|
$composer = $this->getBaseComposerInformation($identifier);
|
||||||
|
|
||||||
$composer = $this->getBaseComposerInformation($identifier);
|
if ($this->shouldCache($identifier)) {
|
||||||
|
$this->cache->write($identifier, json_encode($composer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
// specials for gitlab (this data is only available if authentication is provided)
|
// specials for gitlab (this data is only available if authentication is provided)
|
||||||
|
@ -146,10 +151,6 @@ class GitLabDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->shouldCache($identifier)) {
|
|
||||||
$this->cache->write($identifier, json_encode($composer));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->infoCache[$identifier] = $composer;
|
$this->infoCache[$identifier] = $composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +449,7 @@ class GitLabDriver extends VcsDriver
|
||||||
if (!$moreThanGuestAccess) {
|
if (!$moreThanGuestAccess) {
|
||||||
$this->io->writeError('<warning>GitLab token with Guest only access detected</warning>');
|
$this->io->writeError('<warning>GitLab token with Guest only access detected</warning>');
|
||||||
|
|
||||||
return $this->attemptCloneFallback();
|
return $this->attemptCloneFallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ class SvnDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
$this->getTags();
|
$this->getTags();
|
||||||
|
|
|
@ -81,7 +81,7 @@ abstract class VcsDriver implements VcsDriverInterface
|
||||||
*/
|
*/
|
||||||
protected function shouldCache($identifier)
|
protected function shouldCache($identifier)
|
||||||
{
|
{
|
||||||
return $this->cache && preg_match('{[a-f0-9]{40}}i', $identifier);
|
return $this->cache && preg_match('{^[a-f0-9]{40}$}iD', $identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -265,6 +265,9 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
||||||
if ($e->getCode() === 404) {
|
if ($e->getCode() === 404) {
|
||||||
$this->emptyReferences[] = $identifier;
|
$this->emptyReferences[] = $identifier;
|
||||||
}
|
}
|
||||||
|
if ($e->getCode() === 401 || $e->getCode() === 403) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($isVeryVerbose) {
|
if ($isVeryVerbose) {
|
||||||
$this->io->writeError('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found (' . $e->getCode() . ' HTTP status code)' : $e->getMessage()).'</warning>');
|
$this->io->writeError('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found (' . $e->getCode() . ' HTTP status code)' : $e->getMessage()).'</warning>');
|
||||||
|
@ -350,6 +353,9 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
||||||
if ($e->getCode() === 404) {
|
if ($e->getCode() === 404) {
|
||||||
$this->emptyReferences[] = $identifier;
|
$this->emptyReferences[] = $identifier;
|
||||||
}
|
}
|
||||||
|
if ($e->getCode() === 401 || $e->getCode() === 403) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
if ($isVeryVerbose) {
|
if ($isVeryVerbose) {
|
||||||
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file was found (' . $e->getCode() . ' HTTP status code)</warning>');
|
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file was found (' . $e->getCode() . ' HTTP status code)</warning>');
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Versions
|
||||||
private $httpDownloader;
|
private $httpDownloader;
|
||||||
private $config;
|
private $config;
|
||||||
private $channel;
|
private $channel;
|
||||||
|
private $versionsData;
|
||||||
|
|
||||||
public function __construct(Config $config, HttpDownloader $httpDownloader)
|
public function __construct(Config $config, HttpDownloader $httpDownloader)
|
||||||
{
|
{
|
||||||
|
@ -63,12 +64,7 @@ class Versions
|
||||||
|
|
||||||
public function getLatest($channel = null)
|
public function getLatest($channel = null)
|
||||||
{
|
{
|
||||||
if ($this->config->get('disable-tls') === true) {
|
$versions = $this->getVersionsData();
|
||||||
$protocol = 'http';
|
|
||||||
} else {
|
|
||||||
$protocol = 'https';
|
|
||||||
}
|
|
||||||
$versions = $this->httpDownloader->get($protocol . '://getcomposer.org/versions')->decodeJson();
|
|
||||||
|
|
||||||
foreach ($versions[$channel ?: $this->getChannel()] as $version) {
|
foreach ($versions[$channel ?: $this->getChannel()] as $version) {
|
||||||
if ($version['min-php'] <= PHP_VERSION_ID) {
|
if ($version['min-php'] <= PHP_VERSION_ID) {
|
||||||
|
@ -76,6 +72,21 @@ class Versions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \LogicException('There is no version of Composer available for your PHP version ('.PHP_VERSION.')');
|
throw new \UnexpectedValueException('There is no version of Composer available for your PHP version ('.PHP_VERSION.')');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getVersionsData()
|
||||||
|
{
|
||||||
|
if (!$this->versionsData) {
|
||||||
|
if ($this->config->get('disable-tls') === true) {
|
||||||
|
$protocol = 'http';
|
||||||
|
} else {
|
||||||
|
$protocol = 'https';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->versionsData = $this->httpDownloader->get($protocol . '://getcomposer.org/versions')->decodeJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->versionsData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\Util\Http;
|
namespace Composer\Util\Http;
|
||||||
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
|
use Composer\Downloader\MaxFileSizeExceededException;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Downloader\TransportException;
|
use Composer\Downloader\TransportException;
|
||||||
use Composer\CaBundle\CaBundle;
|
use Composer\CaBundle\CaBundle;
|
||||||
|
@ -56,6 +57,8 @@ class CurlDownloader
|
||||||
'ssl' => array(
|
'ssl' => array(
|
||||||
'cafile' => CURLOPT_CAINFO,
|
'cafile' => CURLOPT_CAINFO,
|
||||||
'capath' => CURLOPT_CAPATH,
|
'capath' => CURLOPT_CAPATH,
|
||||||
|
'verify_peer' => CURLOPT_SSL_VERIFYPEER,
|
||||||
|
'verify_peer_name' => CURLOPT_SSL_VERIFYHOST,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -178,7 +181,11 @@ class CurlDownloader
|
||||||
foreach (self::$options as $type => $curlOptions) {
|
foreach (self::$options as $type => $curlOptions) {
|
||||||
foreach ($curlOptions as $name => $curlOption) {
|
foreach ($curlOptions as $name => $curlOption) {
|
||||||
if (isset($options[$type][$name])) {
|
if (isset($options[$type][$name])) {
|
||||||
curl_setopt($curlHandle, $curlOption, $options[$type][$name]);
|
if ($type === 'ssl' && $name === 'verify_peer_name') {
|
||||||
|
curl_setopt($curlHandle, $curlOption, $options[$type][$name] === true ? 2 : $options[$type][$name]);
|
||||||
|
} else {
|
||||||
|
curl_setopt($curlHandle, $curlOption, $options[$type][$name]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,6 +366,18 @@ class CurlDownloader
|
||||||
$previousProgress = $this->jobs[$i]['progress'];
|
$previousProgress = $this->jobs[$i]['progress'];
|
||||||
$this->jobs[$i]['progress'] = $progress;
|
$this->jobs[$i]['progress'] = $progress;
|
||||||
|
|
||||||
|
if (isset($this->jobs[$i]['options']['max_file_size'])) {
|
||||||
|
// Compare max_file_size with the content-length header this value will be -1 until the header is parsed
|
||||||
|
if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) {
|
||||||
|
throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare max_file_size with the download size in bytes
|
||||||
|
if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) {
|
||||||
|
throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
//$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
|
//$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\Util;
|
namespace Composer\Util;
|
||||||
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
|
use Composer\Downloader\MaxFileSizeExceededException;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Downloader\TransportException;
|
use Composer\Downloader\TransportException;
|
||||||
use Composer\CaBundle\CaBundle;
|
use Composer\CaBundle\CaBundle;
|
||||||
|
@ -244,6 +245,12 @@ class RemoteFilesystem
|
||||||
$degradedPackagist = true;
|
$degradedPackagist = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$maxFileSize = null;
|
||||||
|
if (isset($options['max_file_size'])) {
|
||||||
|
$maxFileSize = $options['max_file_size'];
|
||||||
|
unset($options['max_file_size']);
|
||||||
|
}
|
||||||
|
|
||||||
$ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
|
$ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
|
||||||
|
|
||||||
$actualContextOptions = stream_context_get_options($ctx);
|
$actualContextOptions = stream_context_get_options($ctx);
|
||||||
|
@ -273,7 +280,7 @@ class RemoteFilesystem
|
||||||
});
|
});
|
||||||
$http_response_header = array();
|
$http_response_header = array();
|
||||||
try {
|
try {
|
||||||
$result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header);
|
$result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header, $maxFileSize);
|
||||||
|
|
||||||
if (!empty($http_response_header[0])) {
|
if (!empty($http_response_header[0])) {
|
||||||
$statusCode = $this->findStatusCode($http_response_header);
|
$statusCode = $this->findStatusCode($http_response_header);
|
||||||
|
@ -532,23 +539,34 @@ class RemoteFilesystem
|
||||||
/**
|
/**
|
||||||
* Get contents of remote URL.
|
* Get contents of remote URL.
|
||||||
*
|
*
|
||||||
* @param string $originUrl The origin URL
|
* @param string $originUrl The origin URL
|
||||||
* @param string $fileUrl The file URL
|
* @param string $fileUrl The file URL
|
||||||
* @param resource $context The stream context
|
* @param resource $context The stream context
|
||||||
|
* @param int $maxFileSize The maximum allowed file size
|
||||||
*
|
*
|
||||||
* @return string|false The response contents or false on failure
|
* @return string|false The response contents or false on failure
|
||||||
*/
|
*/
|
||||||
protected function getRemoteContents($originUrl, $fileUrl, $context, array &$responseHeaders = null)
|
protected function getRemoteContents($originUrl, $fileUrl, $context, array &$responseHeaders = null, $maxFileSize = null)
|
||||||
{
|
{
|
||||||
$result = false;
|
$result = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$e = null;
|
$e = null;
|
||||||
$result = file_get_contents($fileUrl, false, $context);
|
if ($maxFileSize !== null) {
|
||||||
|
$result = file_get_contents($fileUrl, false, $context, 0, $maxFileSize);
|
||||||
|
} else {
|
||||||
|
// passing `null` to file_get_contents will convert `null` to `0` and return 0 bytes
|
||||||
|
$result = file_get_contents($fileUrl, false, $context);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($maxFileSize !== null && Platform::strlen($result) >= $maxFileSize) {
|
||||||
|
throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . Platform::strlen($result) . ' of allowed ' . $maxFileSize . ' bytes');
|
||||||
|
}
|
||||||
|
|
||||||
$responseHeaders = isset($http_response_header) ? $http_response_header : array();
|
$responseHeaders = isset($http_response_header) ? $http_response_header : array();
|
||||||
|
|
||||||
if (null !== $e) {
|
if (null !== $e) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ final class StreamContextFactory
|
||||||
* Creates a context supporting HTTP proxies
|
* Creates a context supporting HTTP proxies
|
||||||
*
|
*
|
||||||
* @param string $url URL the context is to be used for
|
* @param string $url URL the context is to be used for
|
||||||
|
* @psalm-param array{http: array{follow_location?: int, max_redirects?: int, header?: string|array<string, string|int>}} $defaultOptions
|
||||||
* @param array $defaultOptions Options to merge with the default
|
* @param array $defaultOptions Options to merge with the default
|
||||||
* @param array $defaultParams Parameters to specify on the context
|
* @param array $defaultParams Parameters to specify on the context
|
||||||
* @throws \RuntimeException if https proxy required and OpenSSL uninstalled
|
* @throws \RuntimeException if https proxy required and OpenSSL uninstalled
|
||||||
|
@ -56,7 +57,8 @@ final class StreamContextFactory
|
||||||
/**
|
/**
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return array ['http' => ['header' => [...], 'proxy' => '..', 'request_fulluri' => bool]] formatted as a stream context array
|
* @psalm-return array{http:{header: string[], proxy?: string, request_fulluri: bool}, ssl: array}
|
||||||
|
* @return array formatted as a stream context array
|
||||||
*/
|
*/
|
||||||
public static function initOptions($url, array $options)
|
public static function initOptions($url, array $options)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wissem Riahi <wissemr@gmail.com>
|
||||||
|
*/
|
||||||
|
class Tar
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $pathToArchive
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function getComposerJson($pathToArchive)
|
||||||
|
{
|
||||||
|
$phar = new \PharData($pathToArchive);
|
||||||
|
|
||||||
|
if (!$phar->valid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::extractComposerJsonFromFolder($phar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \PharData $phar
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function extractComposerJsonFromFolder(\PharData $phar)
|
||||||
|
{
|
||||||
|
if (isset($phar['composer.json'])) {
|
||||||
|
return $phar['composer.json']->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
$topLevelPaths = array();
|
||||||
|
foreach ($phar as $folderFile) {
|
||||||
|
$name = $folderFile->getBasename();
|
||||||
|
|
||||||
|
if ($folderFile->isDir()) {
|
||||||
|
$topLevelPaths[$name] = true;
|
||||||
|
if (\count($topLevelPaths) > 1) {
|
||||||
|
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$composerJsonPath = key($topLevelPaths).'/composer.json';
|
||||||
|
if ($topLevelPaths && isset($phar[$composerJsonPath])) {
|
||||||
|
return $phar[$composerJsonPath]->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('No composer.json found either at the top level or within the topmost directory');
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,42 +66,44 @@ class Zip
|
||||||
*
|
*
|
||||||
* @param \ZipArchive $zip
|
* @param \ZipArchive $zip
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
|
* @throws \RuntimeException
|
||||||
*
|
*
|
||||||
* @return bool|int
|
* @return int
|
||||||
*/
|
*/
|
||||||
private static function locateFile(\ZipArchive $zip, $filename)
|
private static function locateFile(\ZipArchive $zip, $filename)
|
||||||
{
|
{
|
||||||
$indexOfShortestMatch = false;
|
// return root composer.json if it is there and is a file
|
||||||
$lengthOfShortestMatch = -1;
|
if (false !== ($index = $zip->locateName($filename)) && $zip->getFromIndex($index) !== false) {
|
||||||
|
return $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
$topLevelPaths = array();
|
||||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||||
$stat = $zip->statIndex($i);
|
$name = $zip->getNameIndex($i);
|
||||||
if (strcmp(basename($stat['name']), $filename) === 0) {
|
$dirname = dirname($name);
|
||||||
$directoryName = dirname($stat['name']);
|
|
||||||
if ($directoryName === '.') {
|
|
||||||
//if composer.json is in root directory
|
|
||||||
//it has to be the one to use.
|
|
||||||
return $i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($directoryName, '\\') !== false ||
|
// handle archives with proper TOC
|
||||||
strpos($directoryName, '/') !== false) {
|
if ($dirname === '.') {
|
||||||
//composer.json files below first directory are rejected
|
$topLevelPaths[$name] = true;
|
||||||
continue;
|
if (\count($topLevelPaths) > 1) {
|
||||||
|
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$length = strlen($stat['name']);
|
// handle archives which do not have a TOC record for the directory itself
|
||||||
if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) {
|
if (false === strpos('\\', $dirname) && false === strpos('/', $dirname)) {
|
||||||
//Check it's not a directory.
|
$topLevelPaths[$dirname.'/'] = true;
|
||||||
$contents = $zip->getFromIndex($i);
|
if (\count($topLevelPaths) > 1) {
|
||||||
if ($contents !== false) {
|
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
||||||
$indexOfShortestMatch = $i;
|
|
||||||
$lengthOfShortestMatch = $length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $indexOfShortestMatch;
|
if ($topLevelPaths && false !== ($index = $zip->locateName(key($topLevelPaths).$filename)) && $zip->getFromIndex($index) !== false) {
|
||||||
|
return $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('No composer.json found either at the top level or within the topmost directory');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,7 +390,7 @@ class GitDownloaderTest extends TestCase
|
||||||
|
|
||||||
public function testUpdate()
|
public function testUpdate()
|
||||||
{
|
{
|
||||||
$expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
|
$expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
|
||||||
|
|
||||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||||
$packageMock->expects($this->any())
|
$packageMock->expects($this->any())
|
||||||
|
@ -421,7 +421,7 @@ class GitDownloaderTest extends TestCase
|
||||||
|
|
||||||
public function testUpdateWithNewRepoUrl()
|
public function testUpdateWithNewRepoUrl()
|
||||||
{
|
{
|
||||||
$expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
|
$expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
|
||||||
|
|
||||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||||
$packageMock->expects($this->any())
|
$packageMock->expects($this->any())
|
||||||
|
@ -496,8 +496,8 @@ composer https://github.com/old/url (push)
|
||||||
*/
|
*/
|
||||||
public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
|
public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
|
||||||
{
|
{
|
||||||
$expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
|
$expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
|
||||||
$expectedGitUpdateCommand2 = $this->winCompat("git remote set-url composer 'git@github.com:composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'git@github.com:composer/composer'");
|
$expectedGitUpdateCommand2 = $this->winCompat("(git remote set-url composer 'git@github.com:composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'git@github.com:composer/composer'");
|
||||||
|
|
||||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||||
$packageMock->expects($this->any())
|
$packageMock->expects($this->any())
|
||||||
|
@ -540,8 +540,8 @@ composer https://github.com/old/url (push)
|
||||||
|
|
||||||
public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
|
public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
|
||||||
{
|
{
|
||||||
$expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."'");
|
$expectedFirstGitUpdateCommand = $this->winCompat("(git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."'");
|
||||||
$expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
|
$expectedSecondGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
|
||||||
|
|
||||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||||
$packageMock->expects($this->any())
|
$packageMock->expects($this->any())
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
--TEST--
|
||||||
|
Partial update from lock file with root aliases should work
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "a/dep", "version": "1.0.0", "require": { "c/aliased": "2.0.0" } },
|
||||||
|
{ "name": "b/dep", "version": "1.0.0", "require": { "c/aliased": "1.0.0" } },
|
||||||
|
{ "name": "c/aliased", "version": "1.0.0" },
|
||||||
|
{ "name": "c/aliased", "version": "2.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"a/dep": "*",
|
||||||
|
"b/dep": "*",
|
||||||
|
"c/aliased": "1.0.0 as 2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "a/dep", "version": "1.0.0", "require": { "c/aliased": "2.0.0" } },
|
||||||
|
{ "name": "b/dep", "version": "1.0.0", "require": { "c/aliased": "1.0.0" } },
|
||||||
|
{ "name": "c/aliased", "version": "1.0.0" }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [
|
||||||
|
{
|
||||||
|
"package": "c/aliased",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"alias": "2.0.0",
|
||||||
|
"alias_normalized": "2.0.0.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
||||||
|
--INSTALLED--
|
||||||
|
[
|
||||||
|
{ "name": "a/dep", "version": "1.0.0", "require": { "c/aliased": "2.0.0" } },
|
||||||
|
{ "name": "b/dep", "version": "1.0.0", "require": { "c/aliased": "1.0.0" } },
|
||||||
|
{ "name": "c/aliased", "version": "1.0.0" }
|
||||||
|
]
|
||||||
|
--RUN--
|
||||||
|
update --lock
|
||||||
|
--EXPECT-LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "a/dep", "version": "1.0.0", "require": { "c/aliased": "2.0.0" }, "type": "library" },
|
||||||
|
{ "name": "b/dep", "version": "1.0.0", "require": { "c/aliased": "1.0.0" }, "type": "library" },
|
||||||
|
{ "name": "c/aliased", "version": "1.0.0", "type": "library" }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [
|
||||||
|
{
|
||||||
|
"package": "c/aliased",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"alias": "2.0.0",
|
||||||
|
"alias_normalized": "2.0.0.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
||||||
|
--EXPECT--
|
||||||
|
Marking c/aliased (2.0.0) as installed, alias of c/aliased (1.0.0)
|
|
@ -0,0 +1,59 @@
|
||||||
|
--TEST--
|
||||||
|
Packages found in a higher priority repository take precedence even if they are not found in the requested version case #2
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "ruflin/elastica",
|
||||||
|
"version": "dev-outdated-branch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "friendsofsymfony/elastica-bundle",
|
||||||
|
"version": "dev-foobar-master",
|
||||||
|
"require": {
|
||||||
|
"ruflin/elastica": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "ruflin/elastica",
|
||||||
|
"version": "2.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ruflin/elastica",
|
||||||
|
"version": "2.0.1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"friendsofsymfony/elastica-bundle": "dev-foobar-master"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
|
Problem 1
|
||||||
|
- Root composer.json requires friendsofsymfony/elastica-bundle dev-foobar-master -> satisfiable by friendsofsymfony/elastica-bundle[dev-foobar-master].
|
||||||
|
- friendsofsymfony/elastica-bundle dev-foobar-master requires ruflin/elastica 2.* -> satisfiable by ruflin/elastica[2.0.0, 2.0.1] from package repo (defining 2 packages) but ruflin/elastica[dev-outdated-branch] from package repo (defining 1 package) has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
|
@ -0,0 +1,35 @@
|
||||||
|
--TEST--
|
||||||
|
Packages found in a higher priority repository take precedence even if they are not found in the requested version case #3
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "foo/a", "version": "2.0.0-dev" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "foo/a", "version": "2.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"foo/a": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
|
Problem 1
|
||||||
|
- Root composer.json requires foo/a 2.*, it is satisfiable by foo/a[2.0.0] from package repo (defining 1 package) but foo/a[2.0.0-dev] from package repo (defining 1 package) has higher repository priority. The packages with higher priority do not match your minimum-stability and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Test\Platform;
|
||||||
|
|
||||||
|
use Composer\Platform\HhvmDetector;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
use Composer\Util\Platform;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
|
|
||||||
|
class HhvmDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
private $hhvmDetector;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->hhvmDetector = new HhvmDetector();
|
||||||
|
$this->hhvmDetector->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHHVMVersionWhenExecutingInHHVM()
|
||||||
|
{
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
self::markTestSkipped('Not running with HHVM');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$version = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertSame(self::versionIdToVersion(), $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHHVMVersionWhenExecutingInPHP()
|
||||||
|
{
|
||||||
|
if (defined('HHVM_VERSION_ID')) {
|
||||||
|
self::markTestSkipped('Running with HHVM');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PHP_VERSION_ID < 50400) {
|
||||||
|
self::markTestSkipped('Test only works on PHP 5.4+');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Platform::isWindows()) {
|
||||||
|
self::markTestSkipped('Test does not run on Windows');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$finder = new ExecutableFinder();
|
||||||
|
$hhvm = $finder->find('hhvm');
|
||||||
|
if ($hhvm === null) {
|
||||||
|
self::markTestSkipped('HHVM is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$detectedVersion = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertNotNull($detectedVersion, 'Failed to detect HHVM version');
|
||||||
|
|
||||||
|
$process = new ProcessExecutor();
|
||||||
|
$exitCode = $process->execute(
|
||||||
|
ProcessExecutor::escape($hhvm).
|
||||||
|
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
|
||||||
|
$version
|
||||||
|
);
|
||||||
|
self::assertSame(0, $exitCode);
|
||||||
|
|
||||||
|
self::assertSame(self::getVersionParser()->normalize($version), self::getVersionParser()->normalize($detectedVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @runInSeparateProcess */
|
||||||
|
public function testHHVMVersionWhenRunningInHHVMWithMockedConstant()
|
||||||
|
{
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
define('HHVM_VERSION', '2.2.1');
|
||||||
|
define('HHVM_VERSION_ID', 20201);
|
||||||
|
}
|
||||||
|
$version = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertSame(self::getVersionParser()->normalize(self::versionIdToVersion()), self::getVersionParser()->normalize($version));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function versionIdToVersion()
|
||||||
|
{
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'%d.%d.%d',
|
||||||
|
HHVM_VERSION_ID / 10000,
|
||||||
|
(HHVM_VERSION_ID / 100) % 100,
|
||||||
|
HHVM_VERSION_ID % 100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Test\Platform;
|
||||||
|
|
||||||
|
use Composer\Platform\Version;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lars Strojny <lars@strojny.net>
|
||||||
|
*/
|
||||||
|
class VersionTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create normalized test data set
|
||||||
|
*
|
||||||
|
* 1) Clone OpenSSL repository
|
||||||
|
* 2) git log --pretty=%h --all -- crypto/opensslv.h include/openssl/opensslv.h | while read hash ; do (git show $hash:crypto/opensslv.h; git show $hash:include/openssl/opensslv.h) | grep "define OPENSSL_VERSION_TEXT" ; done > versions.txt
|
||||||
|
* 3) cat versions.txt | awk -F "OpenSSL " '{print $2}' | awk -F " " '{print $1}' | sed -e "s:\([0-9]*\.[0-9]*\.[0-9]*\):1.2.3:g" -e "s:1\.2\.3[a-z]\(-.*\)\{0,1\}$:1.2.3a\1:g" -e "s:1\.2\.3[a-z]\{2\}\(-.*\)\{0,1\}$:1.2.3zh\1:g" -e "s:beta[0-9]:beta3:g" -e "s:pre[0-9]*:pre2:g" | sort | uniq
|
||||||
|
*/
|
||||||
|
public static function getOpenSslVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
// Generated
|
||||||
|
array('1.2.3', '1.2.3.0'),
|
||||||
|
array('1.2.3-beta3', '1.2.3.0-beta3'),
|
||||||
|
array('1.2.3-beta3-dev', '1.2.3.0-beta3-dev'),
|
||||||
|
array('1.2.3-beta3-fips', '1.2.3.0-beta3', true),
|
||||||
|
array('1.2.3-beta3-fips-dev', '1.2.3.0-beta3-dev', true),
|
||||||
|
array('1.2.3-dev', '1.2.3.0-dev'),
|
||||||
|
array('1.2.3-fips', '1.2.3.0', true),
|
||||||
|
array('1.2.3-fips-beta3', '1.2.3.0-beta3', true),
|
||||||
|
array('1.2.3-fips-beta3-dev', '1.2.3.0-beta3-dev', true),
|
||||||
|
array('1.2.3-fips-dev', '1.2.3.0-dev', true),
|
||||||
|
array('1.2.3-pre2', '1.2.3.0-alpha2'),
|
||||||
|
array('1.2.3-pre2-dev', '1.2.3.0-alpha2-dev'),
|
||||||
|
array('1.2.3-pre2-fips', '1.2.3.0-alpha2', true),
|
||||||
|
array('1.2.3-pre2-fips-dev', '1.2.3.0-alpha2-dev', true),
|
||||||
|
array('1.2.3a', '1.2.3.1'),
|
||||||
|
array('1.2.3a-beta3','1.2.3.1-beta3'),
|
||||||
|
array('1.2.3a-beta3-dev', '1.2.3.1-beta3-dev'),
|
||||||
|
array('1.2.3a-dev', '1.2.3.1-dev'),
|
||||||
|
array('1.2.3a-dev-fips', '1.2.3.1-dev', true),
|
||||||
|
array('1.2.3a-fips', '1.2.3.1', true),
|
||||||
|
array('1.2.3a-fips-beta3', '1.2.3.1-beta3', true),
|
||||||
|
array('1.2.3a-fips-dev', '1.2.3.1-dev', true),
|
||||||
|
array('1.2.3beta3', '1.2.3.0-beta3'),
|
||||||
|
array('1.2.3beta3-dev', '1.2.3.0-beta3-dev'),
|
||||||
|
array('1.2.3zh', '1.2.3.34'),
|
||||||
|
array('1.2.3zh-dev', '1.2.3.34-dev'),
|
||||||
|
array('1.2.3zh-fips', '1.2.3.34',true),
|
||||||
|
array('1.2.3zh-fips-dev', '1.2.3.34-dev', true),
|
||||||
|
// Additional cases
|
||||||
|
array('1.2.3zh-fips-rc3', '1.2.3.34-rc3', true, '1.2.3.34-RC3'),
|
||||||
|
array('1.2.3zh-alpha10-fips', '1.2.3.34-alpha10', true),
|
||||||
|
// Check that alphabetical patch levels overflow correctly
|
||||||
|
array('1.2.3', '1.2.3.0'),
|
||||||
|
array('1.2.3a', '1.2.3.1'),
|
||||||
|
array('1.2.3z', '1.2.3.26'),
|
||||||
|
array('1.2.3za', '1.2.3.27'),
|
||||||
|
array('1.2.3zy', '1.2.3.51'),
|
||||||
|
array('1.2.3zz', '1.2.3.52'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getOpenSslVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
* @param bool $fipsExpected
|
||||||
|
* @param string|null $normalizedVersion
|
||||||
|
*/
|
||||||
|
public function testParseOpensslVersions($input, $parsedVersion, $fipsExpected = false, $normalizedVersion = null)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseOpenssl($input, $isFips));
|
||||||
|
self::assertSame($fipsExpected, $isFips);
|
||||||
|
|
||||||
|
$normalizedVersion = $normalizedVersion ? $normalizedVersion : $parsedVersion;
|
||||||
|
self::assertSame($normalizedVersion, $this->getVersionParser()->normalize($parsedVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLibJpegVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('9', '9.0'),
|
||||||
|
array('9a', '9.1'),
|
||||||
|
array('9b', '9.2'),
|
||||||
|
// Never seen in the wild, just for overflow correctness
|
||||||
|
array('9za', '9.27'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getLibJpegVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
*/
|
||||||
|
public function testParseLibjpegVersion($input, $parsedVersion)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseLibjpeg($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZoneinfoVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('2019c', '2019.3'),
|
||||||
|
array('2020a', '2020.1'),
|
||||||
|
// Never happened so far but fixate overflow behavior
|
||||||
|
array('2020za', '2020.27'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getZoneinfoVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
*/
|
||||||
|
public function testParseZoneinfoVersion($input, $parsedVersion)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseZoneinfoVersion($input));
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ class ArtifactRepositoryTest extends TestCase
|
||||||
'vendor1/package2-4.3.2',
|
'vendor1/package2-4.3.2',
|
||||||
'vendor3/package1-5.4.3',
|
'vendor3/package1-5.4.3',
|
||||||
'test/jsonInRoot-1.0.0',
|
'test/jsonInRoot-1.0.0',
|
||||||
|
'test/jsonInRootTarFile-1.0.0',
|
||||||
'test/jsonInFirstLevel-1.0.0',
|
'test/jsonInFirstLevel-1.0.0',
|
||||||
//The files not-an-artifact.zip and jsonSecondLevel are not valid
|
//The files not-an-artifact.zip and jsonSecondLevel are not valid
|
||||||
//artifacts and do not get detected.
|
//artifacts and do not get detected.
|
||||||
|
@ -52,6 +53,13 @@ class ArtifactRepositoryTest extends TestCase
|
||||||
sort($foundPackages);
|
sort($foundPackages);
|
||||||
|
|
||||||
$this->assertSame($expectedPackages, $foundPackages);
|
$this->assertSame($expectedPackages, $foundPackages);
|
||||||
|
|
||||||
|
$tarPackage = array_filter($repo->getPackages(), function (BasePackage $package) {
|
||||||
|
return $package->getPrettyName() === 'test/jsonInRootTarFile';
|
||||||
|
});
|
||||||
|
$this->assertCount(1, $tarPackage);
|
||||||
|
$tarPackage = array_pop($tarPackage);
|
||||||
|
$this->assertSame('tar', $tarPackage->getDistType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAbsoluteRepoUrlCreatesAbsoluteUrlPackages()
|
public function testAbsoluteRepoUrlCreatesAbsoluteUrlPackages()
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Test\Util;
|
||||||
|
|
||||||
|
use Composer\Util\Tar;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wissem Riahi <wissemr@gmail.com>
|
||||||
|
*/
|
||||||
|
class TarTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testReturnsNullifTheTarIsNotFound()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/invalid.zip');
|
||||||
|
|
||||||
|
$this->assertNull($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsNullIfTheTarIsEmpty()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/empty.tar.gz');
|
||||||
|
$this->assertNull($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheTarHasNoComposerJson()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/nojson.tar.gz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheComposerJsonIsInASubSubfolder()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/subfolders.tar.gz');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsComposerJsonInTarRoot()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/root.tar.gz');
|
||||||
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsComposerJsonInFirstFolder()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/folder.tar.gz');
|
||||||
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testMultipleTopLevelDirsIsInvalid()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/multiple.tar.gz');
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ use Composer\Test\TestCase;
|
||||||
*/
|
*/
|
||||||
class ZipTest extends TestCase
|
class ZipTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testThrowsExceptionIfZipExcentionIsNotLoaded()
|
public function testThrowsExceptionIfZipExtensionIsNotLoaded()
|
||||||
{
|
{
|
||||||
if (extension_loaded('zip')) {
|
if (extension_loaded('zip')) {
|
||||||
$this->markTestSkipped('The PHP zip extension is loaded.');
|
$this->markTestSkipped('The PHP zip extension is loaded.');
|
||||||
|
@ -55,28 +55,30 @@ class ZipTest extends TestCase
|
||||||
$this->assertNull($result);
|
$this->assertNull($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReturnsNullIfTheZipHasNoComposerJson()
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheZipHasNoComposerJson()
|
||||||
{
|
{
|
||||||
if (!extension_loaded('zip')) {
|
if (!extension_loaded('zip')) {
|
||||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/nojson.zip');
|
Zip::getComposerJson(__DIR__.'/Fixtures/Zip/nojson.zip');
|
||||||
|
|
||||||
$this->assertNull($result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReturnsNullIfTheComposerJsonIsInASubSubfolder()
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheComposerJsonIsInASubSubfolder()
|
||||||
{
|
{
|
||||||
if (!extension_loaded('zip')) {
|
if (!extension_loaded('zip')) {
|
||||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/subfolder.zip');
|
Zip::getComposerJson(__DIR__.'/Fixtures/Zip/subfolders.zip');
|
||||||
|
|
||||||
$this->assertNull($result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReturnsComposerJsonInZipRoot()
|
public function testReturnsComposerJsonInZipRoot()
|
||||||
|
@ -99,19 +101,44 @@ class ZipTest extends TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/folder.zip');
|
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/folder.zip');
|
||||||
|
|
||||||
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReturnsRootComposerJsonAndSkipsSubfolders()
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testMultipleTopLevelDirsIsInvalid()
|
||||||
{
|
{
|
||||||
if (!extension_loaded('zip')) {
|
if (!extension_loaded('zip')) {
|
||||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/multiple.zip');
|
Zip::getComposerJson(__DIR__.'/Fixtures/Zip/multiple.zip');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsComposerJsonFromFirstSubfolder()
|
||||||
|
{
|
||||||
|
if (!extension_loaded('zip')) {
|
||||||
|
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/single-sub.zip');
|
||||||
|
|
||||||
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfMultipleComposerInSubFoldersWereFound()
|
||||||
|
{
|
||||||
|
if (!extension_loaded('zip')) {
|
||||||
|
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Zip::getComposerJson(__DIR__.'/Fixtures/Zip/multiple_subfolders.zip');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue