Merge branch 'master' into 2.0

* master: (48 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
  ...
main
Nils Adermann 5 years ago
commit d2fa1e1319

5
.gitattributes vendored

@ -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

@ -1,3 +1,17 @@
### [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
@ -737,6 +751,8 @@
* 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

61
composer.lock generated

@ -126,24 +126,23 @@
},
{
"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,7 +226,7 @@
"Xdebug",
"performance"
],
"time": "2019-01-28T20:25:53+00:00"
"time": "2019-05-27T17:52:04+00:00"
},
{
"name": "justinrainbow/json-schema",
@ -481,7 +480,7 @@
},
{
"name": "symfony/console",
"version": "v2.8.49",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
@ -542,7 +541,7 @@
},
{
"name": "symfony/debug",
"version": "v2.8.49",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
@ -599,7 +598,7 @@
},
{
"name": "symfony/filesystem",
"version": "v2.8.49",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
@ -649,7 +648,7 @@
},
{
"name": "symfony/finder",
"version": "v2.8.49",
"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.49",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
@ -1780,7 +1779,7 @@
},
{
"name": "symfony/yaml",
"version": "v2.8.49",
"version": "v2.8.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",

@ -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

@ -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

@ -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

@ -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

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

@ -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];
}
// canceling circular dependency
if (isset($computing[$name])) {
return 0;
}
$sortedPackages = PackageSorter::sortPackages($packages);
$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]);
}

@ -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

@ -184,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();

@ -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>
@ -160,15 +161,26 @@ EOT
if ($input->getOption('no-update')) {
return 0;
}
$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');
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');
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);

@ -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);
}
}

@ -81,8 +81,11 @@ class Problem
$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();
}
@ -90,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);
@ -103,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.';
}
@ -117,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.';
}
}
@ -202,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 = array();
}
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)

@ -477,6 +477,8 @@ class Installer
$solver = new Solver($policy, $pool, $installedRepo, $this->io);
try {
$operations = $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());
@ -493,7 +495,7 @@ class Installer
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations);
$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);
// execute operations
if (!$operations) {

@ -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* \} )

@ -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);
}
}

@ -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)) {

@ -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);
}

@ -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()) {

@ -803,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;

@ -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':

@ -219,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,

@ -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);

@ -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;
@ -68,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;
@ -133,7 +135,8 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
{
parent::initialize();
$verbose = $this->verbose;
$isVerbose = $this->isVerbose;
$isVeryVerbose = $this->isVeryVerbose;
$driver = $this->getDriver();
if (!$driver) {
@ -151,23 +154,23 @@ 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);
@ -179,7 +182,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
}
if (!$parsedTag = $this->validateTag($tag)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped tag '.$tag.', invalid tag name</warning>');
}
continue;
@ -187,7 +190,7 @@ 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;
@ -209,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;
@ -217,13 +220,13 @@ 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'].')');
}
@ -232,35 +235,35 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
if ($e instanceof TransportException && $e->getCode() === 404) {
$this->emptyReferences[] = $identifier;
}
if ($verbose) {
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;
@ -274,7 +277,7 @@ 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);
@ -287,7 +290,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
try {
if (!$data = $driver->getComposerInformation($identifier)) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file</warning>');
}
$this->emptyReferences[] = $identifier;
@ -298,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'].')');
}
@ -312,12 +315,12 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
if ($e->getCode() === 404) {
$this->emptyReferences[] = $identifier;
}
if ($verbose) {
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;
@ -328,7 +331,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
}
$driver->cleanup();
if (!$verbose) {
if (!$isVeryVerbose) {
$this->io->overwriteError('', false);
}
@ -373,7 +376,7 @@ 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;
@ -381,7 +384,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$cachedPackage = $this->versionCache->getVersionPackage($version, $identifier);
if ($cachedPackage === false) {
if ($verbose) {
if ($isVeryVerbose) {
$this->io->writeError('<warning>Skipped '.$version.', no composer file (cached from ref '.$identifier.')</warning>');
}
@ -390,14 +393,14 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
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;

@ -0,0 +1,92 @@
<?php
namespace Composer\Util;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
class PackageSorter
{
/**
* Sorts packages by dependency weight
*
* Packages of equal weight retain the original order
*
* @param array $packages
* @return array
*/
public static function sortPackages(array $packages) {
$usageList = array();
foreach ($packages as $package) { /** @var PackageInterface $package */
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { /** @var Link $link */
$target = $link->getTarget();
$usageList[$target][] = $package->getName();
}
}
$computing = array();
$computed = array();
$computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
// reusing computed importance
if (isset($computed[$name])) {
return $computed[$name];
}
// 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) {
$v = $v[3];
};
}
array_walk($array, $transform);
asort($array);
array_walk($array, $restore);
};
$stable_sort($weightList);
$sortedPackages = array();
foreach (array_keys($weightList) as $name) {
$sortedPackages[] = $packages[$name];
}
return $sortedPackages;
}
}

@ -51,6 +51,7 @@ class ProcessExecutor
return '://'.$m['user'].':***@';
}, $command);
$safeCommand = preg_replace("{--password (.*[^\\\\]\') }", '--password \'***\' ', $safeCommand);
$this->io->writeError('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand);
}

@ -486,6 +486,47 @@ class AutoloadGeneratorTest extends TestCase
$this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty.");
}
public function testPharAutoload()
{
$package = new Package('a', '1.0', '1.0');
$package->setRequires(array(
new Link('a', 'a/a'),
));
$package->setAutoload(array(
'psr-0' => array(
'Foo' => 'foo.phar',
'Bar' => 'dir/bar.phar/src',
),
'psr-4' => array(
'Baz\\' => 'baz.phar',
'Qux\\' => 'dir/qux.phar/src',
),
));
$vendorPackage = new Package('a/a', '1.0', '1.0');
$vendorPackage->setAutoload(array(
'psr-0' => array(
'Lorem' => 'lorem.phar',
'Ipsum' => 'dir/ipsum.phar/src',
),
'psr-4' => array(
'Dolor\\' => 'dolor.phar',
'Sit\\' => 'dir/sit.phar/src',
),
));
$this->repository->expects($this->once())
->method('getCanonicalPackages')
->will($this->returnValue(array($vendorPackage)));
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, 'Phar');
$this->assertAutoloadFiles('phar', $this->vendorDir . '/composer');
$this->assertAutoloadFiles('phar_psr4', $this->vendorDir . '/composer', 'psr4');
$this->assertAutoloadFiles('phar_static', $this->vendorDir . '/composer', 'static');
}
public function testPSRToClassMapIgnoresNonExistingDir()
{
$package = new Package('a', '1.0', '1.0');

@ -0,0 +1,13 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Lorem' => array('phar://' . $vendorDir . '/a/a/lorem.phar'),
'Ipsum' => array('phar://' . $vendorDir . '/a/a/dir/ipsum.phar/src'),
'Foo' => array('phar://' . $baseDir . '/foo.phar'),
'Bar' => array('phar://' . $baseDir . '/dir/bar.phar/src'),
);

@ -0,0 +1,13 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Sit\\' => array('phar://' . $vendorDir . '/a/a/dir/sit.phar/src'),
'Qux\\' => array('phar://' . $baseDir . '/dir/qux.phar/src'),
'Dolor\\' => array('phar://' . $vendorDir . '/a/a/dolor.phar'),
'Baz\\' => array('phar://' . $baseDir . '/baz.phar'),
);

@ -0,0 +1,87 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitPhar
{
public static $prefixLengthsPsr4 = array (
'S' =>
array (
'Sit\\' => 4,
),
'Q' =>
array (
'Qux\\' => 4,
),
'D' =>
array (
'Dolor\\' => 6,
),
'B' =>
array (
'Baz\\' => 4,
),
);
public static $prefixDirsPsr4 = array (
'Sit\\' =>
array (
0 => 'phar://' . __DIR__ . '/..' . '/a/a/dir/sit.phar/src',
),
'Qux\\' =>
array (
0 => 'phar://' . __DIR__ . '/../..' . '/dir/qux.phar/src',
),
'Dolor\\' =>
array (
0 => 'phar://' . __DIR__ . '/..' . '/a/a/dolor.phar',
),
'Baz\\' =>
array (
0 => 'phar://' . __DIR__ . '/../..' . '/baz.phar',
),
);
public static $prefixesPsr0 = array (
'L' =>
array (
'Lorem' =>
array (
0 => 'phar://' . __DIR__ . '/..' . '/a/a/lorem.phar',
),
),
'I' =>
array (
'Ipsum' =>
array (
0 => 'phar://' . __DIR__ . '/..' . '/a/a/dir/ipsum.phar/src',
),
),
'F' =>
array (
'Foo' =>
array (
0 => 'phar://' . __DIR__ . '/../..' . '/foo.phar',
),
),
'B' =>
array (
'Bar' =>
array (
0 => 'phar://' . __DIR__ . '/../..' . '/dir/bar.phar/src',
),
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitPhar::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitPhar::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInitPhar::$prefixesPsr0;
}, null, ClassLoader::class);
}
}

@ -7,42 +7,81 @@ namespace Foo;
*/
class StripNoise
{
public function test()
public function test_heredoc()
{
return <<<A
class Fail2
return <<<HEREDOC
class FailHeredocBasic
{
}
A
. <<< AB
class Fail3
HEREDOC . <<< WHITESPACE
class FailHeredocWhitespace
{
}
AB
. <<<'TEST'
class Fail4
WHITESPACE . <<<"DOUBLEQUOTES"
class FailHeredocDoubleQuotes
{
}
TEST
. <<< 'ANOTHER'
class Fail5
DOUBLEQUOTES . <<< "DOUBLEQUOTESTABBED"
class FailHeredocDoubleQuotesTabbed
{
}
DOUBLEQUOTESTABBED . <<<HEREDOCPHP73
class FailHeredocPHP73
{
}
HEREDOCPHP73;
}
public function test_nowdoc()
{
return <<<'NOWDOC'
class FailNowdocBasic
{
}
ANOTHER
. <<< 'ONEMORE'
class Fail6
NOWDOC . <<< 'WHITESPACE'
class FailNowdocWhitespace
{
}
WHITESPACE . <<< 'NOWDOCTABBED'
class FailNowdocTabbed
{
}
ONEMORE;
NOWDOCTABBED . <<<'NOWDOCPHP73'
class FailNowdocPHP73
{
}
NOWDOCPHP73;
}
public function test_followed_by_parentheses()
{
return array(<<<PARENTHESES
class FailParentheses
{
}
PARENTHESES);
}
public function test_followed_by_comma()
{
return array(1, 2, <<<COMMA
class FailComma
{
}
COMMA, 3, 4);
}
public function test_followed_by_period()
{
return <<<PERIOD
class FailPeriod
{
}
PERIOD.'?>';
}
public function test2()
public function test_simple_string()
{
$class = 'class Fail4 {}';
return 'class FailSimpleString {}';
}
}

@ -2374,6 +2374,26 @@ class JsonManipulatorTest extends TestCase
"package/a": "*"
}
}
', $manipulator->getContents());
}
public function testEscapedUnicodeDoesNotCauseBacktrackLimitErrorGithubIssue8131()
{
$manipulator = new JsonManipulator('{
"description": "Some U\u00F1icode",
"require": {
"foo/bar": "^1.0"
}
}');
$this->assertTrue($manipulator->addLink('require', 'foo/baz', '^1.0'));
$this->assertEquals('{
"description": "Some U\u00F1icode",
"require": {
"foo/bar": "^1.0",
"foo/baz": "^1.0"
}
}
', $manipulator->getContents());
}
}

@ -298,6 +298,30 @@ class ValidatingArrayLoaderTest extends TestCase
'homepage : invalid value (foo:bar), must be an http/https URL',
),
),
array(
array(
'name' => 'foo/bar.json',
),
array(
'Deprecation warning: Your package name foo/bar.json is invalid, package names can not end in .json, consider renaming it or perhaps using a -json suffix instead. Make sure you fix this as Composer 2.0 will error.',
),
),
array(
array(
'name' => 'com1/foo',
),
array(
'Deprecation warning: Your package name com1/foo is reserved, package and vendor names can not match any of: nul, con, prn, aux, com1, com2, com3, com4, com5, com6, com7, com8, com9, lpt1, lpt2, lpt3, lpt4, lpt5, lpt6, lpt7, lpt8, lpt9. Make sure you fix this as Composer 2.0 will error.',
),
),
array(
array(
'name' => 'Foo/Bar',
),
array(
'Deprecation warning: Your package name Foo/Bar is invalid, it should not contain uppercase characters. We suggest using foo/bar instead. Make sure you fix this as Composer 2.0 will error.',
),
),
array(
array(
'name' => 'foo/bar',
@ -337,6 +361,18 @@ class ValidatingArrayLoaderTest extends TestCase
),
false,
),
array(
array(
'name' => 'foo/bar',
'require' => array(
'Foo/Baz' => '^1.0',
),
),
array(
'Deprecation warning: require.Foo/Baz is invalid, it should not contain uppercase characters. Please use foo/baz instead. Make sure you fix this as Composer 2.0 will error.',
),
false,
),
array(
array(
'name' => 'foo/bar',

@ -214,4 +214,71 @@ class ComposerRepositoryTest extends TestCase
$repository->search('foo', RepositoryInterface::SEARCH_FULLTEXT, 'library')
);
}
/**
* @dataProvider canonicalizeUrlProvider
*
* @param string $expected
* @param string $url
* @param string $repositoryUrl
*/
public function testCanonicalizeUrl($expected, $url, $repositoryUrl)
{
$repository = new ComposerRepository(
array('url' => $repositoryUrl),
new NullIO(),
FactoryMock::createConfig()
);
$object = new \ReflectionObject($repository);
$method = $object->getMethod('canonicalizeUrl');
$method->setAccessible(true);
// ComposerRepository::__construct ensures that the repository URL has a
// protocol, so reset it here in order to test all cases.
$property = $object->getProperty('url');
$property->setAccessible(true);
$property->setValue($repository, $repositoryUrl);
$this->assertSame($expected, $method->invoke($repository, $url));
}
public function canonicalizeUrlProvider()
{
return array(
array(
'https://example.org/path/to/file',
'/path/to/file',
'https://example.org',
),
array(
'https://example.org/canonic_url',
'https://example.org/canonic_url',
'https://should-not-see-me.test',
),
array(
'file:///path/to/repository/file',
'/path/to/repository/file',
'file:///path/to/repository',
),
array(
// Assert that the repository URL is returned unchanged if it is
// not a URL.
// (Backward compatibility test)
'invalid_repo_url',
'/path/to/file',
'invalid_repo_url',
),
array(
// Assert that URLs can contain sequences resembling pattern
// references as understood by preg_replace() without messing up
// the result.
// (Regression test)
'https://example.org/path/to/unusual_$0_filename',
'/path/to/unusual_$0_filename',
'https://example.org',
),
);
}
}

@ -61,11 +61,25 @@ class ProcessExecutorTest extends TestCase
ProcessExecutor::setTimeout(60);
}
public function testHidePasswords()
/**
* @dataProvider hidePasswordProvider
*/
public function testHidePasswords($command, $expectedCommandOutput)
{
$process = new ProcessExecutor($buffer = new BufferIO('', StreamOutput::VERBOSITY_DEBUG));
$process->execute('echo https://foo:bar@example.org/ && echo http://foo@example.org && echo http://abcdef1234567890234578:x-oauth-token@github.com/', $output);
$this->assertEquals('Executing command (CWD): echo https://foo:***@example.org/ && echo http://foo@example.org && echo http://***:***@github.com/', trim($buffer->getOutput()));
$process->execute($command, $output);
$this->assertEquals('Executing command (CWD): ' . $expectedCommandOutput, trim($buffer->getOutput()));
}
public function hidePasswordProvider()
{
return array(
array('echo https://foo:bar@example.org/', 'echo https://foo:***@example.org/'),
array('echo http://foo@example.org', 'echo http://foo@example.org'),
array('echo http://abcdef1234567890234578:x-oauth-token@github.com/', 'echo http://***:***@github.com/'),
array("svn ls --verbose --non-interactive --username 'foo' --password 'bar' 'https://foo.example.org/svn/'", "svn ls --verbose --non-interactive --username 'foo' --password '***' 'https://foo.example.org/svn/'"),
array("svn ls --verbose --non-interactive --username 'foo' --password 'bar \'bar' 'https://foo.example.org/svn/'", "svn ls --verbose --non-interactive --username 'foo' --password '***' 'https://foo.example.org/svn/'"),
);
}
public function testDoesntHidePorts()

Loading…
Cancel
Save