1
0
Fork 0

Merge branch '2.0' into solve-without-installed

* 2.0: (101 commits)
  SVN: hide passwords for debug output
  Free $solver asap
  fixes #8179
  [minor] Fixed a typo in the CHANGELOG.md.
  Update deps
  Update changelog
  Revert "Allow overriding self-update target file with envvar COMPOSER_SELF_UPDATE_TARGET" Revert "Add docs for COMPOSER_SELF_UPDATE_TARGET, refs #8151"
  Add docs for COMPOSER_SELF_UPDATE_TARGET, refs #8151
  Fix display of HHVM warning appearing when HHVM is not in use, fixes #8138
  Read classmap-authoritative and apcu-autoloader from project config when installing via create-project, fixes #8155
  Use possessive quantifiers
  Update xdebug-handler to 1.3.3
  fixes #8159
  Allow overriding self-update target file with envvar COMPOSER_SELF_UPDATE_TARGET
  flag should come before script name
  use full command name, not abbreviated/alias
  modify text
  Document the alternatives to disable the default script timeout
  Anchor pattern
  Fix URL resolution for Composer repositories
  ...
pull/7936/head
Nils Adermann 2019-06-27 14:35:36 +02:00
commit 9053d74282
130 changed files with 1451 additions and 543 deletions

5
.gitattributes vendored
View File

@ -10,3 +10,8 @@
# Exclude non-essential files from dist
/tests export-ignore
.github export-ignore
.php_cs export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
phpunit.xml.dist export-ignore

View File

@ -25,8 +25,10 @@ matrix:
- php: 7.1
- php: 7.2
- php: 7.3
env: PHPSTAN=1
- php: 7.3
env: deps=high
env:
- deps=high
- php: nightly
fast_finish: true
allow_failures:
@ -58,6 +60,11 @@ before_script:
script:
# run test suite directories in parallel using GNU parallel
- ls -d tests/Composer/Test/* | grep -v TestCase.php | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && exit 1);'
# Run PHPStan
- if [[ $PHPSTAN == "1" ]]; then
composer require --dev phpstan/phpstan-shim:^0.11 --ignore-platform-reqs &&
vendor/bin/phpstan.phar analyse src tests --configuration=phpstan/config.neon --autoload-file=phpstan/autoload.php;
fi
before_deploy:
- php -d phar.readonly=0 bin/compile

View File

@ -1,3 +1,25 @@
### [1.8.6] 2019-06-11
* Fixed handling of backslash-escapes handling in composer.json when using the require command
* Fixed create-project not following classmap-authoritative and apcu-autoloader config values
* Fixed HHVM version warning showing up in some cases when it was not in use
### [1.8.5] 2019-04-09
* HHVM 4.0 is no longer compatible with Composer. Please use PHP instead going forward.
* Added forward compatibility with upcoming 2.0 changes
* Fixed support for PHP 7.3-style heredoc/nowdoc syntax changes in autoload generation
* Fixed require command usage when combined with --ignore-platform-reqs
* Fixed and cleaned up various Windows junctions handling issues
### [1.8.4] 2019-02-11
* Fixed long standing solver bug leading to odd solving issues in edge cases, see #7946
* Fixed HHVM support for upcoming releases
* Fixed unix proxy for binaries to be POSIX compatible instead of breaking some shells
* Fixed invalid deprecation warning for composer-plugin-api
* Fixed edge case issues with Windows junctions when working with path repositories
### [1.8.3] 2019-01-30
* Fixed regression when executing partial updates
@ -729,6 +751,12 @@
* Initial release
[1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
[1.8.5]: https://github.com/composer/composer/compare/1.8.4...1.8.5
[1.8.4]: https://github.com/composer/composer/compare/1.8.3...1.8.4
[1.8.3]: https://github.com/composer/composer/compare/1.8.2...1.8.3
[1.8.2]: https://github.com/composer/composer/compare/1.8.1...1.8.2
[1.8.1]: https://github.com/composer/composer/compare/1.8.0...1.8.1
[1.8.0]: https://github.com/composer/composer/compare/1.7.3...1.8.0
[1.7.3]: https://github.com/composer/composer/compare/1.7.2...1.7.3
[1.7.2]: https://github.com/composer/composer/compare/1.7.1...1.7.2

View File

@ -3,7 +3,7 @@ clone_depth: 5
environment:
# This sets the PHP version (from Chocolatey)
PHPCI_CHOCO_VERSION: 7.2.9
PHPCI_CHOCO_VERSION: 7.3.1
PHPCI_CACHE: C:\tools\phpci
PHPCI_PHP: C:\tools\phpci\php
PHPCI_COMPOSER: C:\tools\phpci\composer

View File

@ -18,6 +18,11 @@ $xdebug = new XdebugHandler('Composer', '--ansi');
$xdebug->check();
unset($xdebug);
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '4.0', '>=')) {
echo 'HHVM 4.0 has dropped support for Composer, please use PHP instead. Aborting.'.PHP_EOL;
exit(1);
}
if (function_exists('ini_set')) {
@ini_set('display_errors', 1);

View File

@ -1,7 +1,7 @@
{
"name": "composer/composer",
"type": "library",
"description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
"description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
"keywords": [
"package",
"dependency",
@ -56,7 +56,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
"dev-master": "2.0-dev"
}
},
"autoload": {

85
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b078b12b2912d599e0c6904f64def484",
"content-hash": "280f5d5184039085b5f22236d267ae82",
"packages": [
{
"name": "composer/ca-bundle",
@ -64,16 +64,16 @@
},
{
"name": "composer/semver",
"version": "1.4.2",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
"reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
"reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
"url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
"reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
"shasum": ""
},
"require": {
@ -122,28 +122,27 @@
"validation",
"versioning"
],
"time": "2016-08-30T16:08:34+00:00"
"time": "2019-03-19T17:25:45+00:00"
},
{
"name": "composer/spdx-licenses",
"version": "1.5.0",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/composer/spdx-licenses.git",
"reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2"
"reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7a9556b22bd9d4df7cad89876b00af58ef20d3a2",
"reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2",
"url": "https://api.github.com/repos/composer/spdx-licenses/zipball/a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
"reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0"
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
"phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
"phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7"
},
"type": "library",
"extra": {
@ -183,20 +182,20 @@
"spdx",
"validator"
],
"time": "2018-11-01T09:45:54+00:00"
"time": "2019-03-26T10:23:26+00:00"
},
{
"name": "composer/xdebug-handler",
"version": "1.3.2",
"version": "1.3.3",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
"reference": "d17708133b6c276d6e42ef887a877866b909d892"
"reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/d17708133b6c276d6e42ef887a877866b909d892",
"reference": "d17708133b6c276d6e42ef887a877866b909d892",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
"reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
"shasum": ""
},
"require": {
@ -227,27 +226,27 @@
"Xdebug",
"performance"
],
"time": "2019-01-28T20:25:53+00:00"
"time": "2019-05-27T17:52:04+00:00"
},
{
"name": "justinrainbow/json-schema",
"version": "5.2.7",
"version": "5.2.8",
"source": {
"type": "git",
"url": "https://github.com/justinrainbow/json-schema.git",
"reference": "8560d4314577199ba51bf2032f02cd1315587c23"
"reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8560d4314577199ba51bf2032f02cd1315587c23",
"reference": "8560d4314577199ba51bf2032f02cd1315587c23",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
"reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.1",
"friendsofphp/php-cs-fixer": "~2.2.20",
"json-schema/json-schema-test-suite": "1.2.0",
"phpunit/phpunit": "^4.8.35"
},
@ -293,7 +292,7 @@
"json",
"schema"
],
"time": "2018-02-14T22:26:30+00:00"
"time": "2019-01-14T23:55:14+00:00"
},
{
"name": "psr/log",
@ -481,7 +480,7 @@
},
{
"name": "symfony/console",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
@ -542,7 +541,7 @@
},
{
"name": "symfony/debug",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
@ -599,7 +598,7 @@
},
{
"name": "symfony/filesystem",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
@ -649,7 +648,7 @@
},
{
"name": "symfony/finder",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
@ -698,16 +697,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.10.0",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
"reference": "82ebae02209c21113908c229e9883c419720738a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
"reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
"reference": "82ebae02209c21113908c229e9883c419720738a",
"shasum": ""
},
"require": {
@ -719,7 +718,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
"dev-master": "1.11-dev"
}
},
"autoload": {
@ -752,20 +751,20 @@
"polyfill",
"portable"
],
"time": "2018-08-06T14:22:27+00:00"
"time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.10.0",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494"
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494",
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"shasum": ""
},
"require": {
@ -777,7 +776,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
"dev-master": "1.11-dev"
}
},
"autoload": {
@ -811,11 +810,11 @@
"portable",
"shim"
],
"time": "2018-09-21T13:07:52+00:00"
"time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/process",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
@ -1780,7 +1779,7 @@
},
{
"name": "symfony/yaml",
"version": "v2.8.48",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",

View File

@ -47,7 +47,7 @@ Linux and macOS.
### Downloading the Composer Executable
Composer offers a convenient installer that you can execute directly from the
commandline. Feel free to [download this file](https://getcomposer.org/installer)
command line. Feel free to [download this file](https://getcomposer.org/installer)
or review it on [GitHub](https://github.com/composer/getcomposer.org/blob/master/web/installer)
if you wish to know more about the inner workings of the installer. The source
is plain PHP.
@ -82,7 +82,7 @@ Now run `php bin/composer` in order to run Composer.
#### Globally
You can place the Composer PHAR anywhere you wish. If you put it in a directory
that is part of your `PATH`, you can access it globally. On unixy systems you
that is part of your `PATH`, you can access it globally. On Unix systems you
can even make it executable and invoke it without directly using the `php`
interpreter.

View File

@ -920,6 +920,10 @@ If you use a proxy but it does not support the request_fulluri flag for HTTPS
requests, then you should set this env var to `false` or `0` to prevent Composer
from setting the request_fulluri option.
### COMPOSER_SELF_UPDATE_TARGET
If set, makes the self-update command write the new Composer phar file into that path instead of overwriting itself. Useful for updating Composer on read-only filesystem.
### no_proxy or NO_PROXY
If you are behind a proxy and would like to disable it for certain domains, you

View File

@ -666,6 +666,10 @@ Instead of default fallback strategy you can force to use symlink with
mirroring can be useful when deploying or generating package from a
monolithic repository.
> **Note:** On Windows, directory symlinks are implemented using NTFS junctions
> because they can be created by non-admin users. Mirroring will always be used
> on versions below Windows 7 or if `proc_open` has been disabled.
```json
{
"repositories": [

View File

@ -9,6 +9,20 @@ Defaults to `300`. The duration processes like git clones can run before
Composer assumes they died out. You may need to make this higher if you have a
slow connection or huge vendors.
To disable the process timeout on a custom command under `scripts`, a static
helper is available:
```json
{
"scripts": {
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
]
}
}
```
## use-include-path
Defaults to `false`. If `true`, the Composer autoloader will also look for classes

View File

@ -261,6 +261,11 @@ Now the `custom-plugin-command` is available alongside Composer commands.
> _Composer commands are based on the [Symfony Console Component][10]._
## Running plugins manually
Plugins for an event can be run manually by the `run-script` command. This works the same way as
[running scripts manually](scripts.md#running-scripts-manually).
## Using Plugins
Plugin packages are automatically loaded as soon as they are installed and will

View File

@ -189,7 +189,7 @@ composer run-script [--dev] [--no-dev] script
```
For example `composer run-script post-install-cmd` will run any
**post-install-cmd** scripts that have been defined.
**post-install-cmd** scripts and [plugins](plugins.md) that have been defined.
You can also give additional arguments to the script handler by appending `--`
followed by the handler arguments. e.g.
@ -221,6 +221,56 @@ to the `phpunit` script.
> are easily accessible. In this example no matter if the `phpunit` binary is
> actually in `vendor/bin/phpunit` or `bin/phpunit` it will be found and executed.
Although Composer is not intended to manage long-running processes and other
such aspects of PHP projects, it can sometimes be handy to disable the process
timeout on custom commands. This timeout defaults to 300 seconds and can be
overridden in a variety of ways depending on the desired effect:
- disable it for all commands using the config key `process-timeout`,
- disable it for the current or future invocations of composer using the
environment variable `COMPOSER_PROCESS_TIMEOUT`,
- for a specific invocation using the `--timeout` flag of the `run-script` command,
- using a static helper for specific scripts.
To disable the timeout for specific scripts with the static helper directly in
composer.json:
```json
{
"scripts": {
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
]
}
}
```
To disable the timeout for every script on a given project, you can use the
composer.json configuration:
```json
{
"config": {
"process-timeout": 0
}
}
```
It's also possible to set the global environment variable to disable the timeout
of all following scripts in the current terminal environment:
```
export COMPOSER_PROCESS_TIMEOUT=0
```
To disable the timeout of a single script call, you must use the `run-script` composer
command and specify the `--timeout` parameter:
```
composer run-script --timeout=0 test
```
## Referencing scripts
To enable script re-use and avoid duplicates, you can call a script from another

View File

@ -32,7 +32,7 @@ repository:*
v1
v2
my-feature
nother-feature
another-feature
~/my-library$ git tag
v1.0

5
phpstan/autoload.php Normal file
View File

@ -0,0 +1,5 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../src/bootstrap.php';

42
phpstan/config.neon Normal file
View File

@ -0,0 +1,42 @@
parameters:
level: 0
excludes_analyse:
- 'tests/Composer/Test/Fixtures'
- 'tests/Composer/Test/Autoload/Fixtures'
- 'tests/Composer/Test/Plugin/Fixtures'
ignoreErrors:
# unused parameters
- '~^Constructor of class Composer\\Repository\\VcsRepository has an unused parameter \$dispatcher\.$~'
- '~^Constructor of class Composer\\Repository\\PearRepository has an unused parameter \$dispatcher\.$~'
- '~^Constructor of class Composer\\Util\\Http\\CurlDownloader has an unused parameter \$disableTls\.$~'
- '~^Constructor of class Composer\\Util\\Http\\CurlDownloader has an unused parameter \$options\.$~'
- '~^Constructor of class Composer\\Repository\\PearRepository has an unused parameter \$config\.$~'
# unused uses
- '~^Anonymous function has an unused use \$io\.$~'
- '~^Anonymous function has an unused use \$cache\.$~'
- '~^Anonymous function has an unused use \$path\.$~'
- '~^Anonymous function has an unused use \$fileName\.$~'
# ion cube is not installed
- '~^Function ioncube_loader_\w+ not found\.$~'
# rar is not installed
- '~^Call to static method open\(\) on an unknown class RarArchive\.$~'
# imagick is not installed
- '~^Instantiated class Imagick not found\.$~'
# variables from global scope
- '~^Undefined variable: \$vendorDir$~'
- '~^Undefined variable: \$baseDir$~'
# variable defined in eval
- '~^Undefined variable: \$res$~'
# always checked whether the class exists
- '~^Instantiated class Symfony\\Component\\Console\\Terminal not found\.$~'
- '~^Class Symfony\\Component\\Console\\Input\\StreamableInputInterface not found\.$~'
- '~^Call to an undefined static method Symfony\\Component\\Process\\Process::fromShellCommandline\(\).$~'
# parent call in test mocks
- '~^Composer\\Test\\Mock\\HttpDownloaderMock::__construct\(\) does not call parent constructor from Composer\\Util\\HttpDownloader\.$~'
- '~^Composer\\Test\\Mock\\InstallationManagerMock::__construct\(\) does not call parent constructor from Composer\\Installer\\InstallationManager\.$~'

View File

@ -21,6 +21,7 @@ use Composer\Package\PackageInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Util\Filesystem;
use Composer\Script\ScriptEvents;
use Composer\Util\PackageSorter;
/**
* @author Igor Wiedler <igor@wiedler.ch>
@ -545,7 +546,7 @@ EOF;
}
}
if (preg_match('/\.phar.+$/', $path)) {
if (strpos($path, '.phar') !== false) {
$baseDir = "'phar://' . " . $baseDir;
}
@ -769,10 +770,14 @@ HEADER;
$filesystem = new Filesystem();
$vendorPathCode = ' => ' . $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true, true) . " . '/";
$vendorPharPathCode = ' => \'phar://\' . ' . $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true, true) . " . '/";
$appBaseDirCode = ' => ' . $filesystem->findShortestPathCode(realpath($targetDir), $basePath, true, true) . " . '/";
$appBaseDirPharCode = ' => \'phar://\' . ' . $filesystem->findShortestPathCode(realpath($targetDir), $basePath, true, true) . " . '/";
$absoluteVendorPathCode = ' => ' . substr(var_export(rtrim($vendorDir, '\\/') . '/', true), 0, -1);
$absoluteVendorPharPathCode = ' => ' . substr(var_export(rtrim('phar://' . $vendorDir, '\\/') . '/', true), 0, -1);
$absoluteAppBaseDirCode = ' => ' . substr(var_export(rtrim($baseDir, '\\/') . '/', true), 0, -1);
$absoluteAppBaseDirPharCode = ' => ' . substr(var_export(rtrim('phar://' . $baseDir, '\\/') . '/', true), 0, -1);
$initializer = '';
$prefix = "\0Composer\Autoload\ClassLoader\0";
@ -795,9 +800,15 @@ HEADER;
// See https://bugs.php.net/68057
$staticPhpVersion = 70000;
}
$value = var_export($value, true);
$value = str_replace($absoluteVendorPathCode, $vendorPathCode, $value);
$value = str_replace($absoluteAppBaseDirCode, $appBaseDirCode, $value);
$value = strtr(
var_export($value, true),
array(
$absoluteVendorPathCode => $vendorPathCode,
$absoluteVendorPharPathCode => $vendorPharPathCode,
$absoluteAppBaseDirCode => $appBaseDirCode,
$absoluteAppBaseDirPharCode => $appBaseDirPharCode,
)
);
$value = ltrim(preg_replace('/^ */m', ' $0$0', $value));
$file .= sprintf(" public static $%s = %s;\n\n", $prop, $value);
@ -963,80 +974,21 @@ INITIALIZER;
{
$packages = array();
$paths = array();
$usageList = array();
foreach ($packageMap as $item) {
list($package, $path) = $item;
$name = $package->getName();
$packages[$name] = $package;
$paths[$name] = $path;
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) {
$target = $link->getTarget();
$usageList[$target][] = $name;
}
}
$computing = array();
$computed = array();
$computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
// reusing computed importance
if (isset($computed[$name])) {
return $computed[$name];
}
$sortedPackages = PackageSorter::sortPackages($packages);
// canceling circular dependency
if (isset($computing[$name])) {
return 0;
}
$computing[$name] = true;
$weight = 0;
if (isset($usageList[$name])) {
foreach ($usageList[$name] as $user) {
$weight -= 1 - $computeImportance($user);
}
}
unset($computing[$name]);
$computed[$name] = $weight;
return $weight;
};
$weightList = array();
foreach ($packages as $name => $package) {
$weight = $computeImportance($name);
$weightList[$name] = $weight;
}
$stable_sort = function (&$array) {
static $transform, $restore;
$i = 0;
if (!$transform) {
$transform = function (&$v, $k) use (&$i) {
$v = array($v, ++$i, $k, $v);
};
$restore = function (&$v, $k) {
$v = $v[3];
};
}
array_walk($array, $transform);
asort($array);
array_walk($array, $restore);
};
$stable_sort($weightList);
$sortedPackageMap = array();
foreach (array_keys($weightList) as $name) {
foreach ($sortedPackages as $package) {
$name = $package->getName();
$sortedPackageMap[] = array($packages[$name], $paths[$name]);
}

View File

@ -162,7 +162,7 @@ class ClassMapGenerator
}
// strip heredocs/nowdocs
$contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
$contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents);
// strip strings
$contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
// strip leading non-php code if needed

View File

@ -189,7 +189,8 @@ class Cache
public function clear()
{
if ($this->enabled) {
return $this->filesystem->removeDirectory($this->root);
$this->filesystem->emptyDirectory($this->root);
return true;
}
return false;

View File

@ -57,6 +57,7 @@ package in the specified version and writes it to the specified directory.
<info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>
Read more at https://getcomposer.org/doc/03-cli.md#archive
EOT
)
;

View File

@ -27,6 +27,8 @@ use Symfony\Component\Console\Command\Command;
/**
* Base class for Composer commands
*
* @method Application getApplication()
*
* @author Ryan Weaver <ryan@knplabs.com>
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
@ -46,7 +48,7 @@ abstract class BaseCommand extends Command
* @param bool $required
* @param bool|null $disablePlugins
* @throws \RuntimeException
* @return Composer
* @return Composer|null
*/
public function getComposer($required = true, $disablePlugins = null)
{
@ -173,7 +175,7 @@ abstract class BaseCommand extends Command
if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'))) {
$preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'));
$preferDist = $input->getOption('prefer-dist');
$preferDist = (bool) $input->getOption('prefer-dist');
}
return array($preferSource, $preferDist);

View File

@ -32,6 +32,8 @@ class ClearCacheCommand extends BaseCommand
<<<EOT
The <info>clear-cache</info> deletes all cached packages from composer's
cache directory.
Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-
EOT
)
;

View File

@ -125,6 +125,8 @@ You can always pass more than one option. As an example, if you want to edit the
global config.json file.
<comment>%command.full_name% --editor --global</comment>
Read more at https://getcomposer.org/doc/03-cli.md#config
EOT
)
;
@ -226,7 +228,7 @@ EOT
}
$settingKey = $input->getArgument('setting-key');
if (!$settingKey) {
if (!$settingKey || !is_string($settingKey)) {
return 0;
}

View File

@ -105,6 +105,7 @@ controlled code by appending the <info>'--prefer-source'</info> flag.
To install a package from another repository than the default one you
can pass the <info>'--repository=https://myrepository.org'</info> flag.
Read more at https://getcomposer.org/doc/03-cli.md#create-project
EOT
)
;
@ -183,7 +184,9 @@ EOT
->setRunScripts(!$noScripts)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setSuggestedPackagesReporter($this->suggestedPackagesReporter)
->setOptimizeAutoloader($config->get('optimize-autoloader'));
->setOptimizeAutoloader($config->get('optimize-autoloader'))
->setClassMapAuthoritative($config->get('classmap-authoritative'))
->setApcuAutoloader($config->get('apcu-autoloader'));
if ($disablePlugins) {
$installer->disablePlugins();

View File

@ -37,6 +37,7 @@ Displays detailed information about where a package is referenced.
<info>php composer.phar depends composer/composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#depends-why-
EOT
)
;

View File

@ -55,6 +55,7 @@ The <info>diagnose</info> command checks common errors to help debugging problem
The process exit code will be 1 in case of warnings and 2 for errors.
Read more at https://getcomposer.org/doc/03-cli.md#diagnose
EOT
)
;
@ -602,20 +603,6 @@ EOT
$text .= "Install either of them or recompile php without --disable-iconv";
break;
case 'unicode':
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " detect_unicode = Off";
$displayIniMessage = true;
break;
case 'suhosin':
$text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL;
$text .= " suhosin.executor.include.whitelist = phar ".$current;
$displayIniMessage = true;
break;
case 'php':
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
break;
@ -713,7 +700,7 @@ EOT
/**
* Check if allow_url_fopen is ON
*
* @return bool|string
* @return true|string
*/
private function checkConnectivity()
{

View File

@ -39,6 +39,8 @@ class DumpAutoloadCommand extends BaseCommand
->setHelp(
<<<EOT
<info>php composer.phar dump-autoload</info>
Read more at https://getcomposer.org/doc/03-cli.md#dump-autoload-dumpautoload-
EOT
)
;

View File

@ -36,6 +36,13 @@ class ExecCommand extends BaseCommand
'Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments'
),
))
->setHelp(
<<<EOT
Executes a vendored binary/script.
Read more at https://getcomposer.org/doc/03-cli.md#exec
EOT
)
;
}

View File

@ -50,6 +50,7 @@ XDG_CONFIG_HOME or default to /home/<user>/.config/composer
Note: This path may vary depending on customizations to bin-dir in
composer.json or the environmental variable COMPOSER_BIN_DIR.
Read more at https://getcomposer.org/doc/03-cli.md#global
EOT
)
;

View File

@ -49,6 +49,8 @@ homepage in your default browser.
To open the homepage by default, use -H or --homepage.
To show instead of open the repository or homepage URL, use -s or --show.
Read more at https://getcomposer.org/doc/03-cli.md#browse-home
EOT
);
}

View File

@ -72,6 +72,7 @@ in the current directory.
<info>php composer.phar init</info>
Read more at https://getcomposer.org/doc/03-cli.md#init
EOT
)
;
@ -694,15 +695,22 @@ EOT
{
// find the latest version allowed in this repo set
$versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability));
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
$ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs');
// retry without phpVersion if platform requirements are ignored in case nothing was found
if ($input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs')) {
// ignore phpVersion if platform requirements are ignored
if ($ignorePlatformReqs) {
$phpVersion = null;
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
}
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
if (!$package) {
// platform packages can not be found in the pool in versions other than the local platform's has
// so if platform reqs are ignored we just take the user's word for it
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
return array($name, $requiredVersion ?: '*');
}
// Check whether the PHP version was the problem
if ($phpVersion && $versionSelector->findBestCandidate($name, $requiredVersion, null, $preferredStability)) {
throw new \InvalidArgumentException(sprintf(

View File

@ -61,6 +61,7 @@ exist it will look for composer.json and do the same.
<info>php composer.phar install</info>
Read more at https://getcomposer.org/doc/03-cli.md#install-i
EOT
)
;

View File

@ -41,6 +41,7 @@ class LicensesCommand extends BaseCommand
The license command displays detailed information about the licenses of
the installed dependencies.
Read more at https://getcomposer.org/doc/03-cli.md#licenses
EOT
)
;

View File

@ -50,7 +50,7 @@ The color coding (or signage if you have ANSI colors disabled) for dependency ve
may involve work.
- <highlight>red</highlight> (!): Dependency has a new version that is semver-compatible and you should upgrade it.
Read more at https://getcomposer.org/doc/03-cli.md#outdated
EOT
)
;

View File

@ -37,6 +37,7 @@ Displays detailed information about why a package cannot be installed.
<info>php composer.phar prohibits composer/composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#prohibits-why-not-
EOT
)
;

View File

@ -56,6 +56,7 @@ list of installed packages
<info>php composer.phar remove</info>
Read more at https://getcomposer.org/doc/03-cli.md#remove
EOT
)
;

View File

@ -25,6 +25,7 @@ use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\IO\IOInterface;
/**
* @author Jérémy Romey <jeremy@free-agent.fr>
@ -73,6 +74,7 @@ If you do not specify a version constraint, composer will choose a suitable one
If you do not want to install the new dependencies immediately you can call it with --no-update
Read more at https://getcomposer.org/doc/03-cli.md#require
EOT
)
;
@ -159,15 +161,26 @@ EOT
if ($input->getOption('no-update')) {
return 0;
}
try {
return $this->doUpdate($input, $output, $io, $requirements);
} catch (\Exception $e) {
$this->revertComposerFile(false);
throw $e;
}
}
private function doUpdate(InputInterface $input, OutputInterface $output, IOInterface $io, array $requirements)
{
// Update packages
$this->resetComposer();
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$updateDevMode = !$input->getOption('update-no-dev');
$optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
// Update packages
$this->resetComposer();
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);

View File

@ -63,6 +63,8 @@ class RunScriptCommand extends BaseCommand
The <info>run-script</info> command runs scripts defined in composer.json:
<info>php composer.phar run-script post-update-cmd</info>
Read more at https://getcomposer.org/doc/03-cli.md#run-script
EOT
)
;

View File

@ -48,6 +48,8 @@ class ScriptAliasCommand extends BaseCommand
The <info>run-script</info> command runs scripts defined in composer.json:
<info>php composer.phar run-script post-update-cmd</info>
Read more at https://getcomposer.org/doc/03-cli.md#run-script
EOT
)
;

View File

@ -49,6 +49,7 @@ class SearchCommand extends BaseCommand
The search command searches for packages by its name
<info>php composer.phar search symfony composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#search
EOT
)
;

View File

@ -60,6 +60,7 @@ versions of composer and if found, installs the latest.
<info>php composer.phar self-update</info>
Read more at https://getcomposer.org/doc/03-cli.md#self-update-selfupdate-
EOT
)
;

View File

@ -85,6 +85,7 @@ class ShowCommand extends BaseCommand
The show command displays detailed information about a package, or
lists all packages available.
Read more at https://getcomposer.org/doc/03-cli.md#show
EOT
)
;
@ -823,10 +824,10 @@ EOT
/**
* Display a package tree
*
* @param PackageInterface|string $package
* @param array $packagesInTree
* @param string $previousTreeBar
* @param int $level
* @param array|string $package
* @param array $packagesInTree
* @param string $previousTreeBar
* @param int $level
*/
protected function displayTree(
$package,
@ -835,7 +836,7 @@ EOT
$level = 1
) {
$previousTreeBar = str_replace('├', '│', $previousTreeBar);
if (isset($package['requires'])) {
if (is_array($package) && isset($package['requires'])) {
$requires = $package['requires'];
$treeBar = $previousTreeBar . ' ├';
$i = 0;
@ -968,7 +969,7 @@ EOT
* @param string $phpVersion
* @param bool $minorOnly
*
* @return PackageInterface|null
* @return PackageInterface|false
*/
private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false)
{

View File

@ -52,6 +52,7 @@ class StatusCommand extends BaseCommand
The status command displays a list of dependencies that have
been modified locally.
Read more at https://getcomposer.org/doc/03-cli.md#status
EOT
)
;

View File

@ -38,11 +38,15 @@ The <info>%command.name%</info> command shows a sorted list of suggested package
Enabling <info>-v</info> implies <info>--by-package --by-suggestion</info>, showing both lists.
Read more at https://getcomposer.org/doc/03-cli.md#suggests
EOT
)
;
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = $this->getComposer()->getLocker()->getLockData();
@ -117,7 +121,7 @@ EOT
$io->write(sprintf('<info>%s</info>', $suggestion));
}
return;
return null;
}
// Grouped by package

View File

@ -81,6 +81,7 @@ from a specific vendor:
To select packages names interactively with auto-completion use <info>-i</info>.
Read more at https://getcomposer.org/doc/03-cli.md#update-u
EOT
)
;

View File

@ -55,6 +55,7 @@ Exit codes in case of errors are:
2 validation error(s)
3 file unreadable or missing
Read more at https://getcomposer.org/doc/03-cli.md#validate
EOT
);
}

View File

@ -194,6 +194,7 @@ class Compiler
$content = str_replace('@package_version@', $this->version, $content);
$content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content);
$content = str_replace('@release_date@', $this->versionDate->format('Y-m-d H:i:s'), $content);
$content = preg_replace('{SOURCE_VERSION = \'[^\']+\';}', 'SOURCE_VERSION = \'\';', $content);
}
$phar->addFromString($path, $content);

View File

@ -29,10 +29,46 @@ use Composer\Package\Archiver\ArchiveManager;
*/
class Composer
{
/*
* Examples of the following constants in the various configurations they can be in
*
* releases (phar):
* const VERSION = '1.8.2';
* const BRANCH_ALIAS_VERSION = '';
* const RELEASE_DATE = '2019-01-29 15:00:53';
* const SOURCE_VERSION = '';
*
* snapshot builds (phar):
* const VERSION = 'd3873a05650e168251067d9648845c220c50e2d7';
* const BRANCH_ALIAS_VERSION = '1.9-dev';
* const RELEASE_DATE = '2019-02-20 07:43:56';
* const SOURCE_VERSION = '';
*
* source (git clone):
* const VERSION = '@package_version@';
* const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@';
* const RELEASE_DATE = '@release_date@';
* const SOURCE_VERSION = '1.8-dev+source';
*/
const VERSION = '@package_version@';
const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@';
const RELEASE_DATE = '@release_date@';
const SOURCE_VERSION = '2.0-source';
const SOURCE_VERSION = '2.0-dev+source';
public static function getVersion()
{
// no replacement done, this must be a source checkout
if (self::VERSION === '@package_version'.'@') {
return self::SOURCE_VERSION;
}
// we have a branch alias and version is a commit id, this must be a snapshot build
if (self::BRANCH_ALIAS_VERSION !== '' && preg_match('{^[a-f0-9]{40}$}', self::VERSION)) {
return self::BRANCH_ALIAS_VERSION.'+'.self::VERSION;
}
return self::VERSION;
}
/**
* @var Package\RootPackageInterface

View File

@ -16,6 +16,7 @@ use Composer\Config\ConfigSourceInterface;
use Composer\Downloader\TransportException;
use Composer\IO\IOInterface;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
@ -459,4 +460,20 @@ class Config
}
}
}
/**
* Used by long-running custom scripts in composer.json
*
* "scripts": {
* "watch": [
* "Composer\\Config::disableProcessTimeout",
* "vendor/bin/long-running-script --watch"
* ]
* }
*/
public static function disableProcessTimeout()
{
// Override global timeout set earlier by environment or config
ProcessExecutor::setTimeout(0);
}
}

View File

@ -39,7 +39,7 @@ interface ConfigSourceInterface
* Add a config setting
*
* @param string $name Name
* @param string $value Value
* @param string|array $value Value
*/
public function addConfigSetting($name, $value);

View File

@ -259,7 +259,7 @@ class JsonConfigSource implements ConfigSourceInterface
*
* @param array $array
* @param mixed $value
* @return array
* @return int
*/
private function arrayUnshiftRef(&$array, &$value)
{

View File

@ -91,7 +91,7 @@ class Application extends BaseApplication
$this->io = new NullIO();
parent::__construct('Composer', Composer::VERSION);
parent::__construct('Composer', Composer::getVersion());
}
/**
@ -190,7 +190,7 @@ class Application extends BaseApplication
if (!$isProxyCommand) {
$io->writeError(sprintf(
'Running %s (%s) with %s on %s',
Composer::VERSION,
Composer::getVersion(),
Composer::RELEASE_DATE,
defined('HHVM_VERSION') ? 'HHVM '.HHVM_VERSION : 'PHP '.PHP_VERSION,
function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknown OS'
@ -284,7 +284,7 @@ class Application extends BaseApplication
return $result;
} catch (ScriptExecutionException $e) {
return $e->getCode();
return (int) $e->getCode();
} catch (\Exception $e) {
$this->hintCommonErrors($e);
restore_error_handler();
@ -440,7 +440,7 @@ class Application extends BaseApplication
*/
public function getLongVersion()
{
if (Composer::BRANCH_ALIAS_VERSION) {
if (Composer::BRANCH_ALIAS_VERSION && Composer::BRANCH_ALIAS_VERSION !== '@package_branch_alias_version'.'@') {
return sprintf(
'<info>%s</info> version <comment>%s (%s)</comment> %s',
$this->getName(),

View File

@ -23,10 +23,10 @@ class GenericRule extends Rule
protected $literals;
/**
* @param array $literals
* @param int $reason A RULE_* constant describing the reason for generating this rule
* @param Link|PackageInterface $reasonData
* @param array $job The job this rule was created from
* @param array $literals
* @param int|null $reason A RULE_* constant describing the reason for generating this rule
* @param Link|PackageInterface|int|null $reasonData
* @param array $job The job this rule was created from
*/
public function __construct(array $literals, $reason, $reasonData, $job = null)
{

View File

@ -48,7 +48,7 @@ class PoolBuilder
public function buildPool(array $repositories, array $rootAliases, Request $request)
{
$this->pool = new Pool($this->filterRequires);
$pool = new Pool($this->filterRequires);
$this->rootAliases = $rootAliases;
// TODO do we really want the request here? kind of want a root requirements thingy instead
@ -137,13 +137,13 @@ class PoolBuilder
}
}
$this->pool->setPackages($this->packages, $this->priorities);
$pool->setPackages($this->packages, $this->priorities);
unset($this->aliasMap);
unset($this->loadedNames);
unset($this->nameConstraints);
return $this->pool;
return $pool;
}
private function loadPackage(PackageInterface $package, $repoIndex)
@ -180,12 +180,12 @@ class PoolBuilder
if (!isset($this->loadedNames[$require])) {
$loadNames[$require] = null;
}
if ($link->getConstraint()) {
if ($linkConstraint = $link->getConstraint()) {
if (!array_key_exists($require, $this->nameConstraints)) {
$this->nameConstraints[$require] = new MultiConstraint(array($link->getConstraint()), false);
$this->nameConstraints[$require] = new MultiConstraint(array($linkConstraint), false);
} elseif ($this->nameConstraints[$require]) {
// TODO addConstraint function?
$this->nameConstraints[$require] = new MultiConstraint(array_merge(array($link->getConstraint()), $this->nameConstraints[$require]->getConstraints()), false);
$this->nameConstraints[$require] = new MultiConstraint(array_merge(array($linkConstraint), $this->nameConstraints[$require]->getConstraints()), false);
}
} else {
$this->nameConstraints[$require] = null;

View File

@ -79,11 +79,13 @@ class Problem
reset($reasons);
$reason = current($reasons);
$rule = $reason['rule'];
$job = $reason['job'];
if (isset($job['constraint'])) {
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
$packageName = $job['packageName'];
$constraint = $job['constraint'];
if (isset($constraint)) {
$packages = $this->pool->whatProvides($packageName, $constraint);
} else {
$packages = array();
}
@ -91,9 +93,9 @@ class Problem
if ($job && $job['cmd'] === 'install' && empty($packages)) {
// handle php/hhvm
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
$version = phpversion();
$available = $this->pool->whatProvides($job['packageName']);
$available = $this->pool->whatProvides($packageName);
if (count($available)) {
$firstAvailable = reset($available);
@ -104,13 +106,13 @@ class Problem
}
}
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
$msg = "\n - This package requires ".$packageName.$this->constraintToText($constraint).' but ';
if (defined('HHVM_VERSION') || count($available)) {
if (defined('HHVM_VERSION') || (count($available) && $packageName === 'hhvm')) {
return $msg . 'your HHVM version does not satisfy that requirement.';
}
if ($job['packageName'] === 'hhvm') {
if ($packageName === 'hhvm') {
return $msg . 'you are running this with PHP and not HHVM.';
}
@ -118,43 +120,43 @@ class Problem
}
// handle php extensions
if (0 === stripos($job['packageName'], 'ext-')) {
if (false !== strpos($job['packageName'], ' ')) {
return "\n - The requested PHP extension ".$job['packageName'].' should be required as '.str_replace(' ', '-', $job['packageName']).'.';
if (0 === stripos($packageName, 'ext-')) {
if (false !== strpos($packageName, ' ')) {
return "\n - The requested PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.';
}
$ext = substr($job['packageName'], 4);
$ext = substr($packageName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
return "\n - The requested PHP extension ".$packageName.$this->constraintToText($constraint).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
}
// handle linked libs
if (0 === stripos($job['packageName'], 'lib-')) {
if (strtolower($job['packageName']) === 'lib-icu') {
if (0 === stripos($packageName, 'lib-')) {
if (strtolower($packageName) === 'lib-icu') {
$error = extension_loaded('intl') ? 'has the wrong version installed, try upgrading the intl extension.' : 'is missing from your system, make sure the intl extension is loaded.';
return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error;
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' '.$error;
}
return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
}
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $job['packageName'])) {
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $job['packageName']);
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName);
return "\n - The requested package ".$job['packageName'].' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
return "\n - The requested package ".$packageName.' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
}
if ($providers = $this->pool->whatProvides($job['packageName'], $job['constraint'], true, true)) {
return "\n - The requested package ".$job['packageName'].$this->constraintToText($job['constraint']).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.';
if ($providers = $this->pool->whatProvides($packageName, $constraint, true, true)) {
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.';
}
if ($providers = $this->pool->whatProvides($job['packageName'], null, true, true)) {
return "\n - The requested package ".$job['packageName'].$this->constraintToText($job['constraint']).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.';
if ($providers = $this->pool->whatProvides($packageName, null, true, true)) {
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.';
}
return "\n - The requested package ".$job['packageName'].' could not be found in any version, there may be a typo in the package name.';
return "\n - The requested package ".$packageName.' could not be found in any version, there may be a typo in the package name.';
}
}
@ -180,7 +182,7 @@ class Problem
* Store a reason descriptor but ignore duplicates
*
* @param string $id A canonical identifier for the reason
* @param string $reason The reason descriptor
* @param string|array $reason The reason descriptor
*/
protected function addReason($id, $reason)
{
@ -203,27 +205,29 @@ class Problem
*/
protected function jobToText($job)
{
$packageName = $job['packageName'];
$constraint = $job['constraint'];
switch ($job['cmd']) {
case 'install':
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
$packages = $this->pool->whatProvides($packageName, $constraint);
if (!$packages) {
return 'No package found to satisfy install request for '.$job['packageName'].$this->constraintToText($job['constraint']);
return 'No package found to satisfy install request for '.$packageName.$this->constraintToText($constraint);
}
return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($packages).'.';
return 'Installation request for '.$packageName.$this->constraintToText($constraint).' -> satisfiable by '.$this->getPackageList($packages).'.';
case 'update':
return 'Update request for '.$job['packageName'].$this->constraintToText($job['constraint']).'.';
return 'Update request for '.$packageName.$this->constraintToText($constraint).'.';
case 'remove':
return 'Removal request for '.$job['packageName'].$this->constraintToText($job['constraint']).'';
return 'Removal request for '.$packageName.$this->constraintToText($constraint).'';
}
if (isset($job['constraint'])) {
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
if (isset($constraint)) {
$packages = $this->pool->whatProvides($packageName, $constraint);
} else {
$packages = $this->pool->whatProvides($job['packageName'], null);
}
return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->getPackageList($packages).'])';
return 'Job(cmd='.$job['cmd'].', target='.$packageName.', packages=['.$this->getPackageList($packages).'])';
}
protected function getPackageList($packages)

View File

@ -13,6 +13,7 @@
namespace Composer\DependencyResolver;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositorySet;
@ -39,7 +40,7 @@ class Solver
protected $watchGraph;
/** @var Decisions */
protected $decisions;
/** @var Package[] */
/** @var PackageInterface[] */
protected $fixedMap;
/** @var int */
@ -659,7 +660,7 @@ class Solver
/**
* @todo this makes $disableRules always false; determine the rationale and possibly remove dead code?
*/
$disableRules = array();
$disableRules = false;
$level = 1;
$systemLevel = $level + 1;

View File

@ -294,9 +294,7 @@ class DownloadManager
// if downloader type changed, or update failed and user asks for reinstall,
// we wipe the dir and do a new install instead of updating it
if ($initialDownloader) {
$initialDownloader->remove($initial, $targetDir);
}
$initialDownloader->remove($initial, $targetDir);
$this->install($target, $targetDir);
}

View File

@ -362,7 +362,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
) {
$command = sprintf('git checkout '.$force.'-B %s %s -- && git reset --hard %2$s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference));
if (0 === $this->process->execute($command, $output, $path)) {
return;
return null;
}
}
@ -380,14 +380,14 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
) {
$command = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference));
if (0 === $this->process->execute($command, $output, $path)) {
return;
return null;
}
}
}
$command = sprintf($template, ProcessExecutor::escape($gitRef));
if (0 === $this->process->execute($command, $output, $path)) {
return;
return null;
}
// reference was not found (prints "fatal: reference is not a tree: $ref")

View File

@ -28,6 +28,7 @@ use Composer\IO\IOInterface;
*/
class GzipDownloader extends ArchiveDownloader
{
/** @var ProcessExecutor */
protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)

View File

@ -91,6 +91,12 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
$allowedStrategies = array(self::STRATEGY_MIRROR);
}
// Check we can use junctions safely if we are on Windows
if (Platform::isWindows() && self::STRATEGY_SYMLINK === $currentStrategy && !$this->safeJunctions()) {
$currentStrategy = self::STRATEGY_MIRROR;
$allowedStrategies = array(self::STRATEGY_MIRROR);
}
$fileSystem = new Filesystem();
$this->filesystem->removeDirectory($path);
@ -181,4 +187,25 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
return $packageVersion['commit'];
}
}
/**
* Returns true if junctions can be created and safely used on Windows
*
* A PHP bug makes junction detection fragile, leading to possible data loss
* when removing a package. See https://bugs.php.net/bug.php?id=77552
*
* For safety we require a minimum version of Windows 7, so we can call the
* system rmdir which will preserve target content if given a junction.
*
* The PHP bug was fixed in 7.2.16 and 7.3.3 (requires at least Windows 7).
*
* @return bool
*/
private function safeJunctions()
{
// We need to call mklink, and rmdir on Windows 7 (version 6.1)
return function_exists('proc_open') &&
(PHP_WINDOWS_VERSION_MAJOR > 6 ||
(PHP_WINDOWS_VERSION_MAJOR === 6 && PHP_WINDOWS_VERSION_MINOR >= 1));
}
}

View File

@ -78,7 +78,7 @@ class PerforceDownloader extends VcsDownloader
*/
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
{
$this->doDownload($target, $path, $url);
$this->doInstall($target, $path, $url);
}
/**
@ -88,7 +88,7 @@ class PerforceDownloader extends VcsDownloader
{
$this->io->writeError('Perforce driver does not check for local changes before overriding', true);
return;
return null;
}
/**

View File

@ -32,6 +32,7 @@ use RarArchive;
*/
class RarDownloader extends ArchiveDownloader
{
/** @var ProcessExecutor */
protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)

View File

@ -28,6 +28,7 @@ use Composer\IO\IOInterface;
*/
class XzDownloader extends ArchiveDownloader
{
/** @var ProcessExecutor */
protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)

View File

@ -33,7 +33,9 @@ class ZipDownloader extends ArchiveDownloader
private static $hasZipArchive;
private static $isWindows;
/** @var ProcessExecutor */
protected $process;
/** @var ZipArchive|null */
private $zipArchiveObject;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)

View File

@ -47,7 +47,7 @@ class EventDispatcher
protected $io;
protected $loader;
protected $process;
protected $listeners;
protected $listeners = array();
private $eventStack;
/**
@ -173,6 +173,9 @@ class EventDispatcher
throw new \RuntimeException('Subscriber '.$className.'::'.$callable[1].' for event '.$event->getName().' is not callable, make sure the function is defined and public');
}
if (is_array($callable) && (is_string($callable[0]) || is_object($callable[0])) && is_string($callable[1])) {
$this->io->writeError(sprintf('> %s: %s', $event->getName(), (is_object($callable[0]) ? get_class($callable[0]) : $callable[0]).'->'.$callable[1] ), true, IOInterface::VERBOSE);
}
$event = $this->checkListenerExpectedEvent($callable, $event);
$return = false === call_user_func($callable, $event) ? 1 : 0;
} elseif ($this->isComposerScript($callable)) {
@ -197,6 +200,7 @@ class EventDispatcher
}
try {
/** @var InstallerEvent $event */
$return = $this->dispatch($scriptName, new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags));
} catch (ScriptExecutionException $e) {
$this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET);
@ -365,6 +369,22 @@ class EventDispatcher
$this->listeners[$eventName][$priority][] = $listener;
}
/**
* @param callable|object $listener A callable or an object instance for which all listeners should be removed
*/
public function removeListener($listener)
{
foreach ($this->listeners as $eventName => $priorities) {
foreach ($priorities as $priority => $listeners) {
foreach ($listeners as $index => $candidate) {
if ($listener === $candidate || (is_array($candidate) && is_object($listener) && $candidate[0] === $listener)) {
unset($this->listeners[$eventName][$priority][$index]);
}
}
}
}
}
/**
* Adds object methods as listeners for the events in getSubscribedEvents
*
@ -481,7 +501,7 @@ class EventDispatcher
*
* @param Event $event
* @throws \RuntimeException
* @return number
* @return int
*/
protected function pushEvent(Event $event)
{

View File

@ -413,7 +413,7 @@ class Factory
/**
* @param IOInterface $io IO instance
* @param bool $disablePlugins Whether plugins should not be loaded
* @return Composer
* @return Composer|null
*/
public static function createGlobal(IOInterface $io, $disablePlugins = false)
{

View File

@ -14,10 +14,9 @@ namespace Composer\IO;
use Composer\Config;
use Composer\Util\ProcessExecutor;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
abstract class BaseIO implements IOInterface, LoggerInterface
abstract class BaseIO implements IOInterface
{
protected $authentications = array();

View File

@ -13,13 +13,14 @@
namespace Composer\IO;
use Composer\Config;
use Psr\Log\LoggerInterface;
/**
* The Input/Output helper interface.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
interface IOInterface
interface IOInterface extends LoggerInterface
{
const QUIET = 1;
const NORMAL = 2;
@ -107,7 +108,7 @@ interface IOInterface
* @param string $default The default answer if none is given by the user
*
* @throws \RuntimeException If there is no data to read in the input stream
* @return string The user answer
* @return string|null The user answer
*/
public function ask($question, $default = null);
@ -145,7 +146,7 @@ interface IOInterface
*
* @param string $question The question to ask
*
* @return string The answer
* @return string|null The answer
*/
public function askAndHideAnswer($question);
@ -160,7 +161,7 @@ interface IOInterface
* @param bool $multiselect Select more than one value separated by comma
*
* @throws \InvalidArgumentException
* @return int|string|array The selected value or values (the key of the choices array)
* @return int|string|array|bool The selected value or values (the key of the choices array)
*/
public function select($question, $choices, $default, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false);

View File

@ -399,6 +399,8 @@ class Installer
$solver = new Solver($policy, $pool, $this->io);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$ruleSetSize = $solver->getRuleSetSize();
$solver = null;
} catch (SolverProblemsException $e) {
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
$this->io->writeError($e->getMessage());
@ -413,7 +415,7 @@ class Installer
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $lockedRepository, $request, $lockTransaction);
$this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE);
$this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies", true, IOInterface::VERBOSE);
$this->io->writeError("Analyzed ".$ruleSetSize." rules to resolve dependencies", true, IOInterface::VERBOSE);
if (!$lockTransaction->getOperations()) {
$this->io->writeError('Nothing to modify in lock file');
@ -559,6 +561,7 @@ class Installer
$solver = new Solver($policy, $pool, $this->io);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$solver = null;
// installing the locked packages on this platfom resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system
if (0 !== count($lockTransaction->getOperations())) {

View File

@ -48,7 +48,7 @@ interface InstallerInterface
*
* @param PackageInterface $package package instance
* @param PackageInterface $prevPackage previous package instance in case of an update
* @return PromiseInterface
* @return PromiseInterface|null
*/
public function download(PackageInterface $package, PackageInterface $prevPackage = null);

View File

@ -43,7 +43,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
*
* @param IOInterface $io
* @param Composer $composer
* @param string $type
* @param string|null $type
* @param Filesystem $filesystem
* @param BinaryInstaller $binaryInstaller
*/

View File

@ -22,7 +22,7 @@ class JsonManipulator
private static $DEFINES = '(?(DEFINE)
(?<number> -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )
(?<boolean> true | false | null )
(?<string> " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
(?<string> " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " )
(?<array> \[ (?: (?&json) \s* (?: , (?&json) \s* )* )? \s* \] )
(?<pair> \s* (?&string) \s* : (?&json) \s* )
(?<object> \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} )

View File

@ -401,4 +401,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';
}
public function setDistUrl($url)
{
return $this->aliasOf->setDistUrl($url);
}
public function setDistType($type)
{
return $this->aliasOf->setDistType($type);
}
}

View File

@ -239,7 +239,7 @@ abstract class BasePackage implements PackageInterface
* Build a regexp from a package name, expanding * globs as required
*
* @param string $whiteListedPattern
* @param bool $wrap Wrap the cleaned string by the given string
* @param string $wrap Wrap the cleaned string by the given string
* @return string
*/
public static function packageNameToRegexp($whiteListedPattern, $wrap = '{^%s$}i')

View File

@ -62,39 +62,16 @@ class ArrayLoader implements LoaderInterface
public function loadPackages(array $versions, $class)
{
static $uniqKeys = array('version', 'version_normalized', 'source', 'dist', 'time');
$packages = array();
$linkCache = array();
foreach ($versions as $version) {
if (isset($version['versions'])) {
$baseVersion = $version;
foreach ($uniqKeys as $key) {
unset($baseVersion[$key.'s']);
}
$package = $this->createObject($version, $class);
foreach ($version['versions'] as $index => $dummy) {
$unpackedVersion = $baseVersion;
foreach ($uniqKeys as $key) {
$unpackedVersion[$key] = $version[$key.'s'][$index];
}
$this->configureCachedLinks($linkCache, $package, $version);
$package = $this->configureObject($package, $version);
$package = $this->createObject($unpackedVersion, $class);
$this->configureCachedLinks($linkCache, $package, $unpackedVersion);
$package = $this->configureObject($package, $unpackedVersion);
$packages[] = $package;
}
} else {
$package = $this->createObject($version, $class);
$this->configureCachedLinks($linkCache, $package, $version);
$package = $this->configureObject($package, $version);
$packages[] = $package;
}
$packages[] = $package;
}
return $packages;

View File

@ -49,6 +49,10 @@ class ValidatingArrayLoader implements LoaderInterface
$this->warnings = array();
$this->config = $config;
if ($err = self::hasPackageNamingError($config['name'])) {
$this->warnings[] = 'Deprecation warning: Your package name '.$err.' Make sure you fix this as Composer 2.0 will error.';
}
if ($this->strictName) {
$this->validateRegex('name', '[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*', true);
} else {
@ -195,7 +199,9 @@ class ValidatingArrayLoader implements LoaderInterface
foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) {
if ($this->validateArray($linkType) && isset($this->config[$linkType])) {
foreach ($this->config[$linkType] as $package => $constraint) {
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $package)) {
if ($err = self::hasPackageNamingError($package, true)) {
$this->warnings[] = 'Deprecation warning: '.$linkType.'.'.$err.' Make sure you fix this as Composer 2.0 will error.';
} elseif (!preg_match('{^[A-Za-z0-9_./-]+$}', $package)) {
$this->warnings[] = $linkType.'.'.$package.' : invalid key, package names must be strings containing only [A-Za-z0-9_./-]';
}
if (!is_string($constraint)) {

View File

@ -31,13 +31,21 @@ use Seld\JsonLint\ParsingException;
*/
class Locker
{
/** @var JsonFile */
private $lockFile;
/** @var RepositoryManager */
private $repositoryManager;
/** @var InstallationManager */
private $installationManager;
/** @var string */
private $hash;
/** @var string */
private $contentHash;
/** @var ArrayLoader */
private $loader;
/** @var ArrayDumper */
private $dumper;
/** @var ProcessExecutor */
private $process;
private $lockDataCache;
private $virtualFileWritten;

View File

@ -76,7 +76,7 @@ interface PackageInterface
/**
* Returns the package targetDir property
*
* @return string The package targetDir
* @return string|null The package targetDir
*/
public function getTargetDir();
@ -358,4 +358,32 @@ interface PackageInterface
* @return array
*/
public function getTransportOptions();
/**
* @param string $reference
*
* @return void
*/
public function setSourceReference($reference);
/**
* @param string $url
*
* @return void
*/
public function setDistUrl($url);
/**
* @param string $type
*
* @return void
*/
public function setDistType($type);
/**
* @param string $reference
*
* @return void
*/
public function setDistReference($reference);
}

View File

@ -17,6 +17,7 @@ use Composer\Repository\Vcs\HgDriver;
use Composer\IO\NullIO;
use Composer\Semver\VersionParser as SemverVersionParser;
use Composer\Util\Git as GitUtil;
use Composer\Util\HttpDownloader;
use Composer\Util\ProcessExecutor;
use Composer\Util\Svn as SvnUtil;

View File

@ -45,7 +45,7 @@ class VersionSelector
* @param string $targetPackageVersion
* @param string $targetPhpVersion
* @param string $preferredStability
* @return PackageInterface|bool
* @return PackageInterface|false
*/
public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable')
{

View File

@ -15,15 +15,16 @@ namespace Composer\Plugin;
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Package\CompletePackage;
use Composer\Package\Package;
use Composer\Package\Version\VersionParser;
use Composer\Repository\RepositoryInterface;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface;
use Composer\Package\Link;
use Composer\Repository\RepositorySet;
use Composer\Semver\Constraint\Constraint;
use Composer\Plugin\Capability\Capability;
use Composer\Util\PackageSorter;
/**
* Plugin manager
@ -253,8 +254,10 @@ class PluginManager
*/
private function loadRepository(RepositoryInterface $repo)
{
foreach ($repo->getPackages() as $package) { /** @var PackageInterface $package */
if ($package instanceof AliasPackage) {
$packages = $repo->getPackages();
$sortedPackages = array_reverse(PackageSorter::sortPackages($packages));
foreach ($sortedPackages as $package) {
if (!($package instanceof CompletePackage)) {
continue;
}
if ('composer-plugin' === $package->getType()) {

View File

@ -32,9 +32,11 @@ abstract class BaseRepository implements RepositoryInterface
$result = array();
foreach ($packages as $package) {
if (array_key_exists($package->getName(), $packageMap) &&
(!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion()))) &&
call_user_func($isPackageAcceptableCallable, $package->getNames(), $package->getStability())) {
if (
array_key_exists($package->getName(), $packageMap)
&& (!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion())))
&& call_user_func($isPackageAcceptableCallable, $package->getNames(), $package->getStability())
) {
$result[spl_object_hash($package)] = $package;
if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) {
$result[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();

View File

@ -19,6 +19,7 @@ use Composer\Package\Version\VersionParser;
use Composer\Json\JsonFile;
use Composer\Cache;
use Composer\Config;
use Composer\Composer;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\Util\HttpDownloader;
@ -103,7 +104,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
$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->versionParser = new VersionParser();
$this->loader = new ArrayLoader($this->versionParser);
$this->httpDownloader = $httpDownloader;
@ -139,9 +140,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return;
}
$packages = $this->loadAsyncPackages(array($name => $constraint), function ($name, $stability) {
return true;
});
$packages = $this->loadAsyncPackages(array($name => $constraint));
return reset($packages);
}
@ -181,9 +180,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return array();
}
return $this->loadAsyncPackages(array($name => $constraint ?: new EmptyConstraint()), function ($name, $stability) {
return true;
});
return $this->loadAsyncPackages(array($name => $constraint));
}
if ($hasProviders) {
@ -241,7 +238,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$packageMap[$name] = new EmptyConstraint();
}
return array_values($this->loadAsyncPackages($packageMap, function ($name, $stability) { return true; }));
return array_values($this->loadAsyncPackages($packageMap));
}
throw new \LogicException('Composer repositories that have lazy providers and no available-packages list can not load the complete list of packages, use getProviderNames instead.');
@ -313,9 +310,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
}
}
// add aliases of matched packages even if they did not match the constraint
foreach ($candidates as $candidate) {
if ($candidate instanceof AliasPackage) {
if (isset($result[spl_object_hash($candidate->getAliasOf())])) {
if (isset($matches[spl_object_hash($candidate->getAliasOf())])) {
$matches[spl_object_hash($candidate)] = $candidate;
}
}
@ -511,11 +510,13 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
if (!isset($versionsToLoad[$version['uid']])) {
if ($isPackageAcceptableCallable && !call_user_func($isPackageAcceptableCallable, $normalizedName, VersionParser::parseStability($version['version']))) {
continue;
if (!isset($version['version_normalized'])) {
$version['version_normalized'] = $this->versionParser->normalize($version['version']);
}
$versionsToLoad[$version['uid']] = $version;
if ($this->isVersionAcceptable($isPackageAcceptableCallable, null, $normalizedName, $version)) {
$versionsToLoad[$version['uid']] = $version;
}
}
}
}
@ -567,7 +568,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$this->configurePackageTransportOptions($package);
}
private function loadAsyncPackages(array $packageNames, $isPackageAcceptableCallable)
/**
* @param array $packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded
*/
private function loadAsyncPackages(array $packageNames, $isPackageAcceptableCallable = null)
{
$this->loadRootServerFile();
@ -579,16 +583,24 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
throw new \LogicException('loadAsyncPackages only supports v2 protocol composer repos with a metadata-url');
}
// load ~dev variants as well if present
// TODO ideally there should be a flag set from the repositoryset/poolbuilder to know which packages should have the dev packages loaded
// so we can optimize away some requests entirely
foreach ($packageNames as $name => $constraint) {
$packageNames[$name.'~dev'] = $constraint;
}
foreach ($packageNames as $name => $constraint) {
$name = strtolower($name);
$realName = preg_replace('{~dev$}', '', $name);
// skip platform packages, root package and composer-plugin-api
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name || 'composer-plugin-api' === $name) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $realName) || '__root__' === $realName || 'composer-plugin-api' === $realName) {
continue;
}
$url = str_replace('%package%', $name, $this->lazyProvidersUrl);
$cacheKey = 'provider-'.strtr($name, '/', '$').'.json';
$cacheKey = 'provider-'.strtr($name, '/', '~').'.json';
$lastModified = null;
if ($contents = $this->cache->read($cacheKey)) {
@ -597,16 +609,16 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
$promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified)
->then(function ($response) use (&$packages, $contents, $name, $constraint, $repo, $isPackageAcceptableCallable) {
->then(function ($response) use (&$packages, $contents, $realName, $constraint, $repo, $isPackageAcceptableCallable) {
if (true === $response) {
$response = $contents;
}
if (!isset($response['packages'][$name])) {
if (!isset($response['packages'][$realName])) {
return;
}
$versions = $response['packages'][$name];
$versions = $response['packages'][$realName];
if (isset($response['minified']) && $response['minified'] === 'composer/2.0') {
// TODO extract in other method
@ -635,37 +647,24 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
unset($expanded, $expandedVersion, $versionData);
}
static $uniqKeys = array('version', 'version_normalized', 'source', 'dist', 'time');
$versionsToLoad = array();
foreach ($versions as $version) {
if (isset($version['version_normalizeds'])) {
foreach ($version['version_normalizeds'] as $index => $normalizedVersion) {
if (!$repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $normalizedVersion)) {
foreach ($uniqKeys as $key) {
unset($version[$key.'s'][$index]);
}
}
}
if (count($version['version_normalizeds'])) {
$versionsToLoad[] = $version;
}
} else {
if (!isset($version['version_normalized'])) {
$version['version_normalized'] = $repo->versionParser->normalize($version['version']);
}
if (!isset($version['version_normalized'])) {
$version['version_normalized'] = $repo->versionParser->normalize($version['version']);
}
if ($repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $version['version_normalized'])) {
$versionsToLoad[] = $version;
}
if ($repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $realName, $version)) {
$versionsToLoad[] = $version;
}
}
$loadedPackages = $repo->createPackages($versionsToLoad, 'Composer\Package\CompletePackage');
foreach ($loadedPackages as $package) {
$package->setRepository($repo);
$packages[spl_object_hash($package)] = $package;
if ($package instanceof AliasPackage && !isset($packages[spl_object_hash($package->getAliasOf())])) {
$package->getAliasOf()->setRepository($repo);
$packages[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();
}
}
@ -681,19 +680,30 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
*
* @param string $name package name (must be lowercased already)
* @private
*/
public function isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $versionNormalized)
public function isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $versionData)
{
if (!call_user_func($isPackageAcceptableCallable, strtolower($name), VersionParser::parseStability($versionNormalized))) {
return false;
$versions = array($versionData['version_normalized']);
if ($alias = $this->loader->getBranchAlias($versionData)) {
$versions[] = $alias;
}
if ($constraint && !$constraint->matches(new Constraint('==', $versionNormalized))) {
return false;
foreach ($versions as $version) {
if ($isPackageAcceptableCallable && !call_user_func($isPackageAcceptableCallable, $name, VersionParser::parseStability($version))) {
continue;
}
if ($constraint && !$constraint->matches(new Constraint('==', $version))) {
continue;
}
return true;
}
return true;
return false;
}
protected function loadRootServerFile()
@ -793,7 +803,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
private function canonicalizeUrl($url)
{
if ('/' === $url[0]) {
return preg_replace('{(https?://[^/]+).*}i', '$1' . $url, $this->url);
if (preg_match('{^[^:]++://[^/]*+}', $this->url, $matches)) {
return $matches[0] . $url;
}
return $this->url;
}
return $url;
@ -952,12 +966,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
$data = $response->decodeJson();
if (!empty($data['warning'])) {
$this->io->writeError('<warning>Warning from '.$this->url.': '.$data['warning'].'</warning>');
}
if (!empty($data['info'])) {
$this->io->writeError('<info>Info from '.$this->url.': '.$data['info'].'</info>');
}
HttpDownloader::outputWarnings($this->io, $this->url, $data);
if ($cacheKey) {
if ($storeLastModifiedTime) {
@ -1031,12 +1040,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
$data = $response->decodeJson();
if (!empty($data['warning'])) {
$this->io->writeError('<warning>Warning from '.$this->url.': '.$data['warning'].'</warning>');
}
if (!empty($data['info'])) {
$this->io->writeError('<info>Info from '.$this->url.': '.$data['info'].'</info>');
}
HttpDownloader::outputWarnings($this->io, $this->url, $data);
$lastModifiedDate = $response->getHeader('last-modified');
$response->collect();
@ -1101,12 +1105,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
$data = $response->decodeJson();
if (!empty($data['warning'])) {
$io->writeError('<warning>Warning from '.$url.': '.$data['warning'].'</warning>');
}
if (!empty($data['info'])) {
$io->writeError('<info>Info from '.$url.': '.$data['info'].'</info>');
}
HttpDownloader::outputWarnings($io, $url, $data);
$lastModifiedDate = $response->getHeader('last-modified');
$response->collect();

View File

@ -51,6 +51,11 @@ class FilesystemRepository extends WritableArrayRepository
try {
$packages = $this->file->read();
// forward compatibility for composer v2 installed.json
if (isset($packages['packages'])) {
$packages = $packages['packages'];
}
if (!is_array($packages)) {
throw new \UnexpectedValueException('Could not parse package list from the repository');
}

View File

@ -47,7 +47,7 @@ abstract class BaseChannelReader
* @param string $origin server
* @param string $path relative path to content
* @throws \UnexpectedValueException
* @return \SimpleXMLElement
* @return string
*/
protected function requestContent($origin, $path)
{

View File

@ -150,7 +150,7 @@ class ChannelRest10Reader extends BaseChannelReader
* @param string $baseUrl
* @param string $packageName
* @param string $version
* @return DependencyInfo[]
* @return DependencyInfo
*/
private function readPackageReleaseDependencies($baseUrl, $packageName, $version)
{

View File

@ -97,7 +97,7 @@ class PearRepository extends ArrayRepository implements ConfigurableRepositoryIn
*
* @param ChannelInfo $channelInfo
* @param SemverVersionParser $versionParser
* @return CompletePackage
* @return CompletePackage[]
*/
private function buildComposerPackages(ChannelInfo $channelInfo, SemverVersionParser $versionParser)
{

View File

@ -166,8 +166,14 @@ class PlatformRepository extends ArrayRepository
case 'imagick':
$imagick = new \Imagick();
$imageMagickVersion = $imagick->getVersion();
preg_match('/^ImageMagick ([\d.]+)-(\d+)/', $imageMagickVersion['versionString'], $matches);
$prettyVersion = "{$matches[1]}.{$matches[2]}";
// 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
preg_match('/^ImageMagick ([\d.]+)(?:-(\d+))?/', $imageMagickVersion['versionString'], $matches);
if (isset($matches[2])) {
$prettyVersion = "{$matches[1]}.{$matches[2]}";
} else {
$prettyVersion = $matches[1];
}
break;
case 'libxml':

View File

@ -78,8 +78,9 @@ interface RepositoryInterface extends \Countable
*
* @param string $query search query
* @param int $mode a set of SEARCH_* constants to search on, implementations should do a best effort only
* @param string $type The type of package to search for. Defaults to all types of packages
*
* @return array[] an array of array('name' => '...', 'description' => '...')
*/
public function search($query, $mode = 0);
public function search($query, $mode = 0, $type = null);
}

View File

@ -125,13 +125,7 @@ class RepositoryManager
$class = $this->repositoryClasses[$type];
$reflMethod = new \ReflectionMethod($class, '__construct');
$params = $reflMethod->getParameters();
if (isset($params[3]) && $params[3]->getClass() && $params[3]->getClass()->getName() === 'Composer\Util\HttpDownloader') {
return new $class($config, $this->io, $this->config, $this->httpDownloader, $this->eventDispatcher);
}
return new $class($config, $this->io, $this->config, $this->eventDispatcher);
return new $class($config, $this->io, $this->config, $this->httpDownloader, $this->eventDispatcher);
}
/**

View File

@ -125,50 +125,52 @@ abstract class BitbucketDriver extends VcsDriver
$composer = $this->getBaseComposerInformation($identifier);
// specials for bitbucket
if (!isset($composer['support']['source'])) {
$label = array_search(
$identifier,
$this->getTags()
) ?: array_search(
$identifier,
$this->getBranches()
) ?: $identifier;
if ($composer) {
// specials for bitbucket
if (!isset($composer['support']['source'])) {
$label = array_search(
$identifier,
$this->getTags()
) ?: array_search(
$identifier,
$this->getBranches()
) ?: $identifier;
if (array_key_exists($label, $tags = $this->getTags())) {
$hash = $tags[$label];
} elseif (array_key_exists($label, $branches = $this->getBranches())) {
$hash = $branches[$label];
if (array_key_exists($label, $tags = $this->getTags())) {
$hash = $tags[$label];
} elseif (array_key_exists($label, $branches = $this->getBranches())) {
$hash = $branches[$label];
}
if (! isset($hash)) {
$composer['support']['source'] = sprintf(
'https://%s/%s/%s/src',
$this->originUrl,
$this->owner,
$this->repository
);
} else {
$composer['support']['source'] = sprintf(
'https://%s/%s/%s/src/%s/?at=%s',
$this->originUrl,
$this->owner,
$this->repository,
$hash,
$label
);
}
}
if (! isset($hash)) {
$composer['support']['source'] = sprintf(
'https://%s/%s/%s/src',
if (!isset($composer['support']['issues']) && $this->hasIssues) {
$composer['support']['issues'] = sprintf(
'https://%s/%s/%s/issues',
$this->originUrl,
$this->owner,
$this->repository
);
} else {
$composer['support']['source'] = sprintf(
'https://%s/%s/%s/src/%s/?at=%s',
$this->originUrl,
$this->owner,
$this->repository,
$hash,
$label
);
}
}
if (!isset($composer['support']['issues']) && $this->hasIssues) {
$composer['support']['issues'] = sprintf(
'https://%s/%s/%s/issues',
$this->originUrl,
$this->owner,
$this->repository
);
}
if (!isset($composer['homepage'])) {
$composer['homepage'] = empty($this->website) ? $this->homeUrl : $this->website;
if (!isset($composer['homepage'])) {
$composer['homepage'] = empty($this->website) ? $this->homeUrl : $this->website;
}
}
$this->infoCache[$identifier] = $composer;
@ -217,6 +219,13 @@ abstract class BitbucketDriver extends VcsDriver
return $this->fallbackDriver->getChangeDate($identifier);
}
if (strpos($identifier, '/') !== false) {
$branches = $this->getBranches();
if (isset($branches[$identifier])) {
$identifier = $branches[$identifier];
}
}
$resource = sprintf(
'https://api.bitbucket.org/2.0/repositories/%s/%s/commit/%s?fields=date',
$this->owner,

View File

@ -154,8 +154,8 @@ class GitHubDriver extends VcsDriver
}
$composer = $this->getBaseComposerInformation($identifier);
if ($composer) {
if ($composer) {
// specials for github
if (!isset($composer['support']['source'])) {
$label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;

View File

@ -66,8 +66,9 @@ class HgDriver extends VcsDriver
// clean up directory and do a fresh clone into it
$fs->removeDirectory($this->repoDir);
$command = function ($url) {
return sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($this->repoDir));
$repoDir = $this->repoDir;
$command = function ($url) use ($repoDir) {
return sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($repoDir));
};
$hgUtils->runCommand($command, $this->url, $this->repoDir);

View File

@ -38,7 +38,7 @@ interface VcsDriverInterface
*
* @param string $file
* @param string $identifier
* @return string
* @return string|null
*/
public function getFileContent($file, $identifier);
@ -46,7 +46,7 @@ interface VcsDriverInterface
* Get the changedate for $identifier.
*
* @param string $identifier
* @return \DateTime
* @return \DateTime|null
*/
public function getChangeDate($identifier);
@ -73,7 +73,7 @@ interface VcsDriverInterface
/**
* @param string $identifier Any identifier to a specific branch/tag/commit
* @return array With type, url reference and shasum keys.
* @return array|null With type, url reference and shasum keys.
*/
public function getDist($identifier);

View File

@ -32,7 +32,8 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
{
protected $url;
protected $packageName;
protected $verbose;
protected $isVerbose;
protected $isVeryVerbose;
protected $io;
protected $config;
protected $versionParser;
@ -47,6 +48,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
private $driver;
/** @var VersionCacheInterface */
private $versionCache;
private $emptyReferences = array();
public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $dispatcher = null, array $drivers = null, VersionCacheInterface $versionCache = null)
{
@ -67,7 +69,8 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$this->url = $repoConfig['url'];
$this->io = $io;
$this->type = isset($repoConfig['type']) ? $repoConfig['type'] : 'vcs';
$this->verbose = $io->isVeryVerbose();
$this->isVerbose = $io->isVerbose();
$this->isVeryVerbose = $io->isVeryVerbose();
$this->config = $config;
$this->repoConfig = $repoConfig;
$this->versionCache = $versionCache;
@ -123,11 +126,17 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
return $this->branchErrorOccurred;
}
public function getEmptyReferences()
{
return $this->emptyReferences;
}
protected function initialize()
{
parent::initialize();
$verbose = $this->verbose;
$isVerbose = $this->isVerbose;
$isVeryVerbose = $this->isVeryVerbose;
$driver = $this->getDriver();
if (!$driver) {
@ -145,31 +154,35 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$this->packageName = !empty($data['name']) ? $data['name'] : null;
}
} catch (\Exception $e) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<error>Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage().'</error>');
}
}
foreach ($driver->getTags() as $tag => $identifier) {
$msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $tag . '</comment>)';
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError($msg);
} else {
} elseif ($isVerbose) {
$this->io->overwriteError($msg, false);
}
// strip the release- prefix from tags if present
$tag = str_replace('release-', '', $tag);
$cachedPackage = $this->getCachedPackageVersion($tag, $identifier, $verbose);
$cachedPackage = $this->getCachedPackageVersion($tag, $identifier, $isVerbose, $isVeryVerbose);
if ($cachedPackage) {
$this->addPackage($cachedPackage);
continue;
} elseif ($cachedPackage === false) {
$this->emptyReferences[] = $identifier;
continue;
}
if (!$parsedTag = $this->validateTag($tag)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', invalid tag name</warning>');
}
continue;
@ -177,9 +190,10 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
try {
if (!$data = $driver->getComposerInformation($identifier)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', no composer file</warning>');
}
$this->emptyReferences[] = $identifier;
continue;
}
@ -198,7 +212,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
// broken package, version doesn't match tag
if ($data['version_normalized'] !== $parsedTag) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
}
continue;
@ -206,47 +220,50 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$tagPackageName = isset($data['name']) ? $data['name'] : $this->packageName;
if ($existingPackage = $this->findPackage($tagPackageName, $data['version_normalized'])) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$data['version_normalized'].' internally</warning>');
}
continue;
}
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('Importing tag '.$tag.' ('.$data['version_normalized'].')');
}
$this->addPackage($this->loader->load($this->preProcess($driver, $data, $identifier)));
} catch (\Exception $e) {
if ($verbose) {
if ($e instanceof TransportException && $e->getCode() === 404) {
$this->emptyReferences[] = $identifier;
}
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()).'</warning>');
}
continue;
}
}
if (!$verbose) {
if (!$isVeryVerbose) {
$this->io->overwriteError('', false);
}
$branches = $driver->getBranches();
foreach ($branches as $branch => $identifier) {
$msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)';
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError($msg);
} else {
} elseif ($isVerbose) {
$this->io->overwriteError($msg, false);
}
if ($branch === 'trunk' && isset($branches['master'])) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped branch '.$branch.', can not parse both master and trunk branches as they both resolve to 9999999-dev internally</warning>');
}
continue;
}
if (!$parsedBranch = $this->validateBranch($branch)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped branch '.$branch.', invalid name</warning>');
}
continue;
@ -260,18 +277,23 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$version = $prefix . preg_replace('{(\.9{7})+}', '.x', $parsedBranch);
}
$cachedPackage = $this->getCachedPackageVersion($version, $identifier, $verbose);
$cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose);
if ($cachedPackage) {
$this->addPackage($cachedPackage);
continue;
} elseif ($cachedPackage === false) {
$this->emptyReferences[] = $identifier;
continue;
}
try {
if (!$data = $driver->getComposerInformation($identifier)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file</warning>');
}
$this->emptyReferences[] = $identifier;
continue;
}
@ -279,7 +301,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$data['version'] = $version;
$data['version_normalized'] = $parsedBranch;
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')');
}
@ -290,12 +312,15 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
}
$this->addPackage($package);
} catch (TransportException $e) {
if ($verbose) {
if ($e->getCode() === 404) {
$this->emptyReferences[] = $identifier;
}
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file was found</warning>');
}
continue;
} catch (\Exception $e) {
if (!$verbose) {
if (!$isVeryVerbose) {
$this->io->writeError('');
}
$this->branchErrorOccurred = true;
@ -306,7 +331,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
}
$driver->cleanup();
if (!$verbose) {
if (!$isVeryVerbose) {
$this->io->overwriteError('', false);
}
@ -351,23 +376,31 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
return false;
}
private function getCachedPackageVersion($version, $identifier, $verbose)
private function getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose)
{
if (!$this->versionCache) {
return;
}
$cachedPackage = $this->versionCache->getVersionPackage($version, $identifier);
if ($cachedPackage === false) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped '.$version.', no composer file (cached from ref '.$identifier.')</warning>');
}
return false;
}
if ($cachedPackage) {
$msg = 'Found cached composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $version . '</comment>)';
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError($msg);
} else {
} elseif ($isVerbose) {
$this->io->overwriteError($msg, false);
}
if ($existingPackage = $this->findPackage($cachedPackage['name'], $cachedPackage['version_normalized'])) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally</warning>');
}
$cachedPackage = null;

View File

@ -17,7 +17,7 @@ interface VersionCacheInterface
/**
* @param string $version
* @param string $identifier
* @return array Package version data
* @return array|null|false Package version data if found, false to indicate the identifier is known but has no package, null for an unknown identifier
*/
public function getVersionPackage($version, $identifier);
}

View File

@ -71,12 +71,11 @@ class AuthHelper
* @param string $origin
* @param int $statusCode HTTP status code that triggered this call
* @param string|null $reason a message/description explaining why this was called
* @param string $warning an authentication warning returned by the server as {"warning": ".."}, if present
* @param string[] $headers
* @return array containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be
* @return array|null containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be
* retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json
*/
public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $warning = null, $headers = array())
public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $headers = array())
{
$storeAuth = false;
$retry = false;
@ -173,10 +172,6 @@ class AuthHelper
throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode);
}
$this->io->overwriteError('');
if ($warning) {
$this->io->writeError(' <warning>'.$warning.'</warning>');
}
$this->io->writeError(' Authentication required (<info>'.parse_url($url, PHP_URL_HOST).'</info>):');
$username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: ');

View File

@ -22,11 +22,17 @@ use Composer\Downloader\TransportException;
*/
class Bitbucket
{
/** @var IOInterface */
private $io;
/** @var Config */
private $config;
/** @var ProcessExecutor */
private $process;
/** @var HttpDownloader */
private $httpDownloader;
/** @var array */
private $token = array();
/** @var int|null */
private $time;
const OAUTH2_ACCESS_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token';

View File

@ -23,6 +23,7 @@ use Symfony\Component\Finder\Finder;
*/
class Filesystem
{
/** @var ProcessExecutor */
private $processExecutor;
public function __construct(ProcessExecutor $executor = null)
@ -199,9 +200,15 @@ class Filesystem
*/
public function unlink($path)
{
if (!@$this->unlinkImplementation($path)) {
$unlinked = @$this->unlinkImplementation($path);
if (!$unlinked) {
// retry after a bit on windows since it tends to be touchy with mass removals
if (!Platform::isWindows() || (usleep(350000) && !@$this->unlinkImplementation($path))) {
if (Platform::isWindows()) {
usleep(350000);
$unlinked = @$this->unlinkImplementation($path);
}
if (!$unlinked) {
$error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message'];
if (Platform::isWindows()) {
@ -224,9 +231,15 @@ class Filesystem
*/
public function rmdir($path)
{
if (!@rmdir($path)) {
$deleted = @rmdir($path);
if (!$deleted) {
// retry after a bit on windows since it tends to be touchy with mass removals
if (!Platform::isWindows() || (usleep(350000) && !@rmdir($path))) {
if (Platform::isWindows()) {
usleep(350000);
$deleted = @rmdir($path);
}
if (!$deleted) {
$error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message'];
if (Platform::isWindows()) {
@ -279,6 +292,7 @@ class Filesystem
$this->ensureDirectoryExists($target);
$result = true;
/** @var RecursiveDirectoryIterator $ri */
foreach ($ri as $file) {
$targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
if ($file->isDir()) {
@ -428,7 +442,7 @@ class Filesystem
*/
public function isAbsolutePath($path)
{
return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':';
return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':' || substr($path, 0, 2) === '\\\\';
}
/**
@ -525,6 +539,9 @@ class Filesystem
return $size;
}
/**
* @return ProcessExecutor
*/
protected function getProcess()
{
return $this->processExecutor;
@ -648,6 +665,20 @@ class Filesystem
/**
* Returns whether the target directory is a Windows NTFS Junction.
*
* We test if the path is a directory and not an ordinary link, then check
* that the mode value returned from lstat (which gives the status of the
* link itself) is not a directory, by replicating the POSIX S_ISDIR test.
*
* This logic works because PHP does not set the mode value for a junction,
* since there is no universal file type flag for it. Unfortunately an
* uninitialized variable in PHP prior to 7.2.16 and 7.3.3 may cause a
* random value to be returned. See https://bugs.php.net/bug.php?id=77552
*
* If this random value passes the S_ISDIR test, then a junction will not be
* detected and a recursive delete operation could lead to loss of data in
* the target directory. Note that Windows rmdir can handle this situation
* and will only delete the junction (from Windows 7 onwards).
*
* @param string $junction Path to check.
* @return bool
*/
@ -659,22 +690,13 @@ class Filesystem
if (!is_dir($junction) || is_link($junction)) {
return false;
}
/**
* According to MSDN at https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx we can detect a junction now
* using the 'mode' value from stat: "The _S_IFDIR bit is set if path specifies a directory; the _S_IFREG bit
* is set if path specifies an ordinary file or a device." We have just tested for a directory above, so if
* we have a directory that isn't one according to lstat(...) we must have a junction.
*
* #define _S_IFDIR 0x4000
* #define _S_IFREG 0x8000
*
* Stat cache should be cleared before to avoid accidentally reading wrong information from previous installs.
*/
// Important to clear all caches first
clearstatcache(true, $junction);
clearstatcache(false);
$stat = lstat($junction);
return !($stat['mode'] & 0xC000);
// S_ISDIR test (S_IFDIR is 0x4000, S_IFMT is 0xF000 bitmask)
return $stat ? 0x4000 !== ($stat['mode'] & 0xF000) : false;
}
/**
@ -692,9 +714,7 @@ class Filesystem
if (!$this->isJunction($junction)) {
throw new IOException(sprintf('%s is not a junction and thus cannot be removed as one', $junction));
}
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape($junction));
clearstatcache(true, $junction);
return ($this->getProcess()->execute($cmd, $output) === 0);
return $this->rmdir($junction);
}
}

View File

@ -22,9 +22,13 @@ use Composer\Downloader\TransportException;
*/
class GitHub
{
/** @var IOInterface */
protected $io;
/** @var Config */
protected $config;
/** @var ProcessExecutor */
protected $process;
/** @var HttpDownloader */
protected $httpDownloader;
/**

Some files were not shown because too many files have changed in this diff Show More