Merge branch 'master' into 2.0

main
Jordi Boggiano 4 years ago
commit d63eb8179e
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -1,3 +1,11 @@
### [1.10.0] 2020-03-10
* Added `bearer` auth config to authenticate using `Authorization: Bearer <token>` headers
* Added `plugin-api-version` in composer.lock so future Composer versions know if they are running a lock file which was not built by the correct version
* Fixed composer fund command and funding info parsing to be more useful
* Fixed issue where --no-dev autoload generation was excluding some packages which should not have been excluded
* Fixed 1.10-RC regression in create project's handling of absolute paths
### [1.10.0-RC] 2020-02-14 ### [1.10.0-RC] 2020-02-14
* Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory. * Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory.
@ -811,6 +819,7 @@
* Initial release * Initial release
[1.10.0]: https://github.com/composer/composer/compare/1.10.0-RC...1.10.0
[1.10.0-RC]: https://github.com/composer/composer/compare/1.9.3...1.10.0-RC [1.10.0-RC]: https://github.com/composer/composer/compare/1.9.3...1.10.0-RC
[1.9.3]: https://github.com/composer/composer/compare/1.9.2...1.9.3 [1.9.3]: https://github.com/composer/composer/compare/1.9.2...1.9.3
[1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2 [1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2

70
composer.lock generated

@ -185,16 +185,16 @@
}, },
{ {
"name": "composer/xdebug-handler", "name": "composer/xdebug-handler",
"version": "1.4.0", "version": "1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/xdebug-handler.git", "url": "https://github.com/composer/xdebug-handler.git",
"reference": "cbe23383749496fe0f373345208b79568e4bc248" "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7",
"reference": "cbe23383749496fe0f373345208b79568e4bc248", "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -225,12 +225,13 @@
"Xdebug", "Xdebug",
"performance" "performance"
], ],
"support": { "funding": [
"irc": "irc://irc.freenode.org/composer", {
"issues": "https://github.com/composer/xdebug-handler/issues", "url": "https://packagist.com",
"source": "https://github.com/composer/xdebug-handler/tree/1.4.0" "type": "custom"
}, }
"time": "2019-11-06T16:40:04+00:00" ],
"time": "2020-03-01T12:26:26+00:00"
}, },
{ {
"name": "justinrainbow/json-schema", "name": "justinrainbow/json-schema",
@ -935,6 +936,10 @@
"constructor", "constructor",
"instantiate" "instantiate"
], ],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/master"
},
"time": "2015-06-14T21:17:01+00:00" "time": "2015-06-14T21:17:01+00:00"
}, },
{ {
@ -978,26 +983,20 @@
"license": [ "license": [
"MIT" "MIT"
], ],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"time": "2016-01-25T08:17:30+00:00" "time": "2016-01-25T08:17:30+00:00"
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
"version": "v1.10.2", "version": "v1.10.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/prophecy.git", "url": "https://github.com/phpspec/prophecy.git",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" "reference": "451c3cd1418cf640de218914901e51b064abb093"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", "reference": "451c3cd1418cf640de218914901e51b064abb093",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1047,7 +1046,7 @@
"spy", "spy",
"stub" "stub"
], ],
"time": "2020-01-20T15:57:02+00:00" "time": "2020-03-05T15:02:03+00:00"
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
@ -1287,23 +1286,23 @@
}, },
{ {
"name": "symfony/phpunit-bridge", "name": "symfony/phpunit-bridge",
"version": "v3.4.37", "version": "v3.4.38",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git", "url": "https://github.com/symfony/phpunit-bridge.git",
"reference": "ebfd1b428ffc14306e843092763f228bfba168d0" "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/ebfd1b428ffc14306e843092763f228bfba168d0", "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c02893ae43532b46a4f0e0f207d088b939f278d9",
"reference": "ebfd1b428ffc14306e843092763f228bfba168d0", "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"conflict": { "conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0"
}, },
"suggest": { "suggest": {
"symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
@ -1348,7 +1347,21 @@
], ],
"description": "Symfony PHPUnit Bridge", "description": "Symfony PHPUnit Bridge",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2020-01-14T14:27:59+00:00" "funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-21T08:01:47+00:00"
} }
], ],
"aliases": [], "aliases": [],
@ -1362,5 +1375,6 @@
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {
"php": "5.3.9" "php": "5.3.9"
} },
"plugin-api-version": "1.1.0"
} }

@ -79,16 +79,16 @@ an OAuth token for GitHub.
A list of domain names and oauth keys. For example using `{"gitlab.com": A list of domain names and oauth keys. For example using `{"gitlab.com":
"oauthtoken"}` as the value of this option will use `oauthtoken` to access "oauthtoken"}` as the value of this option will use `oauthtoken` to access
private repositories on gitlab. Please note: If the package is not hosted at private repositories on gitlab. Please note: If the package is not hosted at
gitlab.com the domain names must be also specified with the gitlab.com the domain names must be also specified with the
[`gitlab-domains`](06-config.md#gitlab-domains) option. [`gitlab-domains`](06-config.md#gitlab-domains) option.
## gitlab-token ## gitlab-token
A list of domain names and private tokens. For example using `{"gitlab.com": A list of domain names and private tokens. For example using `{"gitlab.com":
"privatetoken"}` as the value of this option will use `privatetoken` to access "privatetoken"}` as the value of this option will use `privatetoken` to access
private repositories on gitlab. Please note: If the package is not hosted at private repositories on gitlab. Please note: If the package is not hosted at
gitlab.com the domain names must be also specified with the gitlab.com the domain names must be also specified with the
[`gitlab-domains`](06-config.md#gitlab-domains) option. [`gitlab-domains`](06-config.md#gitlab-domains) option.
## disable-tls ## disable-tls
@ -129,11 +129,17 @@ A list of domain names and username/passwords to authenticate against them. For
example using `{"example.org": {"username": "alice", "password": "foo"}}` as the example using `{"example.org": {"username": "alice", "password": "foo"}}` as the
value of this option will let Composer authenticate against example.org. value of this option will let Composer authenticate against example.org.
> **Note:** Authentication-related config options like `http-basic` and > **Note:** Authentication-related config options like `http-basic`, `bearer` and
> `github-oauth` can also be specified inside a `auth.json` file that goes > `github-oauth` can also be specified inside a `auth.json` file that goes
> besides your `composer.json`. That way you can gitignore it and every > besides your `composer.json`. That way you can gitignore it and every
> developer can place their own credentials in there. > developer can place their own credentials in there.
## bearer
A list of domain names and tokens to authenticate against them. For example using
`{"example.org": "foo"}` as the value of this option will let Composer authenticate
against example.org using an `Authorization: Bearer foo` header.
## platform ## platform
Lets you fake platform packages (PHP and extensions) so that you can emulate a Lets you fake platform packages (PHP and extensions) so that you can emulate a
@ -298,7 +304,7 @@ in the composer home, cache, and data directories.
## lock ## lock
Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` Defaults to `true`. If set to `false`, Composer will not create a `composer.lock`
file. file.
&larr; [Repositories](05-repositories.md) | [Community](07-community.md) &rarr; &larr; [Repositories](05-repositories.md) | [Community](07-community.md) &rarr;

@ -54,7 +54,7 @@ v2.0.2
Normally, Composer deals with tags (as opposed to branches -- if you don't Normally, Composer deals with tags (as opposed to branches -- if you don't
know what this means, read up on know what this means, read up on
[version control systems](https://en.wikipedia.org/wiki/Version_control#Common_vocabulary)). [version control systems](https://en.wikipedia.org/wiki/Version_control#Common_terminology)).
When you write a version constraint, it may reference a specific tag (e.g., When you write a version constraint, it may reference a specific tag (e.g.,
`1.1`) or it may reference a valid range of tags (e.g., `>=1.1 <2.0`, or `1.1`) or it may reference a valid range of tags (e.g., `>=1.1 <2.0`, or
`~4.0`). To resolve these constraints, Composer first asks the VCS to list `~4.0`). To resolve these constraints, Composer first asks the VCS to list

@ -140,7 +140,16 @@
"gitlab-token": { "gitlab-token": {
"type": "object", "type": "object",
"description": "A hash of domain name => gitlab private tokens, typically {\"gitlab.com\":\"<token>\"}.", "description": "A hash of domain name => gitlab private tokens, typically {\"gitlab.com\":\"<token>\"}.",
"additionalProperties": true "additionalProperties": {
"type": "string"
}
},
"bearer": {
"type": "object",
"description": "A hash of domain name => bearer authentication token, for example {\"example.com\":\"<token>\"}.",
"additionalProperties": {
"type": "string"
}
}, },
"disable-tls": { "disable-tls": {
"type": "boolean", "type": "boolean",

@ -236,6 +236,7 @@ EOF;
// flatten array // flatten array
$classMap = array(); $classMap = array();
$ambiguousClasses = array();
if ($scanPsr0Packages) { if ($scanPsr0Packages) {
$namespacesToScan = array(); $namespacesToScan = array();
@ -256,14 +257,23 @@ EOF;
continue; continue;
} }
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap); $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap, $ambiguousClasses);
} }
} }
} }
} }
foreach ($autoloads['classmap'] as $dir) { foreach ($autoloads['classmap'] as $dir) {
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap); $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap, $ambiguousClasses);
}
foreach ($ambiguousClasses as $className => $ambigiousPaths) {
$cleanPath = str_replace(array('$vendorDir . \'', '$baseDir . \'', "',\n"), array($vendorPath, $basePath, ''), $classMap[$className]);
$this->io->writeError(
'<warning>Warning: Ambiguous class resolution, "'.$className.'"'.
' was found '. (count($ambigiousPaths) + 1) .'x: in "'.$cleanPath.'" and "'. implode('", "', $ambigiousPaths) .'", the first will be used.</warning>'
);
} }
ksort($classMap); ksort($classMap);
@ -326,17 +336,14 @@ EOF;
return 0; return 0;
} }
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array()) private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap, array &$ambiguousClasses)
{ {
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) { foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) {
$pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n"; $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
if (!isset($classMap[$class])) { if (!isset($classMap[$class])) {
$classMap[$class] = $pathCode; $classMap[$class] = $pathCode;
} elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) { } elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) {
$this->io->writeError( $ambiguousClasses[$class][] = $path;
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
' was found in both "'.str_replace(array('$vendorDir . \'', "',\n"), array($vendorPath, ''), $classMap[$class]).'" and "'.$path.'", the first will be used.</warning>'
);
} }
} }
@ -393,8 +400,8 @@ EOF;
/** /**
* Compiles an ordered list of namespace => path mappings * Compiles an ordered list of namespace => path mappings
* *
* @param array $packageMap array of array(package, installDir-relative-to-composer.json) * @param array $packageMap array of array(package, installDir-relative-to-composer.json)
* @param PackageInterface $mainPackage root package instance * @param PackageInterface $mainPackage root package instance
* @param bool $filterOutRequireDevPackages whether to filter out require-dev packages * @param bool $filterOutRequireDevPackages whether to filter out require-dev packages
* @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) * @return array array('psr-0' => array('Ns\\Foo' => array('installDir')))
*/ */
@ -939,16 +946,23 @@ INITIALIZER;
{ {
$packages = array(); $packages = array();
$include = array(); $include = array();
$replacedBy = array();
foreach ($packageMap as $item) { foreach ($packageMap as $item) {
$package = $item[0]; $package = $item[0];
$name = $package->getName(); $name = $package->getName();
$packages[$name] = $package; $packages[$name] = $package;
foreach ($package->getReplaces() as $replace) {
$replacedBy[$replace->getTarget()] = $name;
}
} }
$add = function (PackageInterface $package) use (&$add, $packages, &$include) { $add = function (PackageInterface $package) use (&$add, $packages, &$include, $replacedBy) {
foreach ($package->getRequires() as $link) { foreach ($package->getRequires() as $link) {
$target = $link->getTarget(); $target = $link->getTarget();
if (isset($replacedBy[$target])) {
$target = $replacedBy[$target];
}
if (!isset($include[$target])) { if (!isset($include[$target])) {
$include[$target] = true; $include[$target] = true;
if (isset($packages[$target])) { if (isset($packages[$target])) {

@ -187,7 +187,7 @@ EOT
} }
if ($input->getOption('global') && !$this->authConfigFile->exists()) { if ($input->getOption('global') && !$this->authConfigFile->exists()) {
touch($this->authConfigFile->getPath()); touch($this->authConfigFile->getPath());
$this->authConfigFile->write(array('bitbucket-oauth' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject, 'gitlab-token' => new \ArrayObject, 'http-basic' => new \ArrayObject)); $this->authConfigFile->write(array('bitbucket-oauth' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject, 'gitlab-token' => new \ArrayObject, 'http-basic' => new \ArrayObject, 'bearer' => new \ArrayObject));
Silencer::call('chmod', $this->authConfigFile->getPath(), 0600); Silencer::call('chmod', $this->authConfigFile->getPath(), 0600);
} }
@ -667,7 +667,7 @@ EOT
} }
// handle auth // handle auth
if (preg_match('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic)\.(.+)/', $settingKey, $matches)) { if (preg_match('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|bearer)\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) { if ($input->getOption('unset')) {
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
@ -681,7 +681,7 @@ EOT
} }
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('consumer-key' => $values[0], 'consumer-secret' => $values[1])); $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('consumer-key' => $values[0], 'consumer-secret' => $values[1]));
} elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token'), true)) { } elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token', 'bearer'), true)) {
if (1 !== count($values)) { if (1 !== count($values)) {
throw new \RuntimeException('Too many arguments, expected only one token'); throw new \RuntimeException('Too many arguments, expected only one token');
} }

@ -303,13 +303,16 @@ EOT
// if no directory was specified, use the 2nd part of the package name // if no directory was specified, use the 2nd part of the package name
if (null === $directory) { if (null === $directory) {
$parts = explode("/", $name, 2); $parts = explode("/", $name, 2);
$directory = array_pop($parts); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
} }
$directory = getcwd() . DIRECTORY_SEPARATOR . $directory;
$io->writeError('<info>Creating a "' . $packageName . '" project at "' . $directory . '"</info>');
$fs = new Filesystem(); $fs = new Filesystem();
if (!$fs->isAbsolutePath($directory)) {
$directory = getcwd() . DIRECTORY_SEPARATOR . $directory;
}
$io->writeError('<info>Creating a "' . $packageName . '" project at "' . $fs->findShortestPath(getcwd(), $directory, true) . '"</info>');
if (file_exists($directory)) { if (file_exists($directory)) {
if (!is_dir($directory)) { if (!is_dir($directory)) {
throw new \InvalidArgumentException('Cannot create project directory at "'.$directory.'", it exists as a file.'); throw new \InvalidArgumentException('Cannot create project directory at "'.$directory.'", it exists as a file.');

@ -14,6 +14,7 @@ namespace Composer\Command;
use Composer\Package\CompletePackageInterface; use Composer\Package\CompletePackageInterface;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
use Composer\Repository\CompositeRepository;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
@ -36,20 +37,19 @@ class FundCommand extends BaseCommand
$composer = $this->getComposer(); $composer = $this->getComposer();
$repo = $composer->getRepositoryManager()->getLocalRepository(); $repo = $composer->getRepositoryManager()->getLocalRepository();
$remoteRepos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
$fundings = array(); $fundings = array();
foreach ($repo->getPackages() as $package) { foreach ($repo->getPackages() as $package) {
if ($package instanceof AliasPackage) { if ($package instanceof AliasPackage) {
continue; continue;
} }
if ($package instanceof CompletePackageInterface && $funding = $package->getFunding()) { $latest = $remoteRepos->findPackage($package->getName(), 'dev-master');
foreach ($funding as $fundingOption) { if ($latest instanceof CompletePackageInterface && $latest->getFunding()) {
list($vendor, $packageName) = explode('/', $package->getPrettyName()); $fundings = $this->insertFundingData($fundings, $latest);
$url = $fundingOption['url']; continue;
if (!empty($fundingOption['type']) && $fundingOption['type'] === 'github' && preg_match('{^https://github.com/([^/]+)$}', $url, $match)) { }
$url = 'https://github.com/sponsors/'.$match[1]; if ($package instanceof CompletePackageInterface && $package->getFunding()) {
} $fundings = $this->insertFundingData($fundings, $package);
$fundings[$vendor][$url][] = $packageName;
}
} }
} }
@ -86,4 +86,18 @@ class FundCommand extends BaseCommand
return 0; return 0;
} }
private function insertFundingData(array $fundings, CompletePackageInterface $package)
{
foreach ($package->getFunding() as $fundingOption) {
list($vendor, $packageName) = explode('/', $package->getPrettyName());
$url = $fundingOption['url'];
if (!empty($fundingOption['type']) && $fundingOption['type'] === 'github' && preg_match('{^https://github.com/([^/]+)$}', $url, $match)) {
$url = 'https://github.com/sponsors/'.$match[1];
}
$fundings[$vendor][$url][] = $packageName;
}
return $fundings;
}
} }

@ -495,7 +495,7 @@ EOT
} }
$io->write(''); $io->write('');
if (isset($package['warning'])) { if (isset($package['warning'])) {
$io->writeError('<warning>' . $package['warning'] . '</warning>'); $io->write('<warning>' . $package['warning'] . '</warning>');
} }
} }

@ -70,6 +70,7 @@ class Config
// gitlab-oauth // gitlab-oauth
// gitlab-token // gitlab-token
// http-basic // http-basic
// bearer
); );
public static $defaultRepositories = array( public static $defaultRepositories = array(
@ -133,7 +134,7 @@ class Config
// override defaults with given config // override defaults with given config
if (!empty($config['config']) && is_array($config['config'])) { if (!empty($config['config']) && is_array($config['config'])) {
foreach ($config['config'] as $key => $val) { foreach ($config['config'] as $key => $val) {
if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic')) && isset($this->config[$key])) { if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic', 'bearer')) && isset($this->config[$key])) {
$this->config[$key] = array_merge($this->config[$key], $val); $this->config[$key] = array_merge($this->config[$key], $val);
} elseif ('preferred-install' === $key && isset($this->config[$key])) { } elseif ('preferred-install' === $key && isset($this->config[$key])) {
if (is_array($val) || is_array($this->config[$key])) { if (is_array($val) || is_array($this->config[$key])) {

@ -96,7 +96,7 @@ class JsonConfigSource implements ConfigSourceInterface
{ {
$authConfig = $this->authConfig; $authConfig = $this->authConfig;
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) { $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) {
if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) { if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2); list($key, $host) = explode('.', $key, 2);
if ($authConfig) { if ($authConfig) {
$config[$key][$host] = $val; $config[$key][$host] = $val;
@ -116,7 +116,7 @@ class JsonConfigSource implements ConfigSourceInterface
{ {
$authConfig = $this->authConfig; $authConfig = $this->authConfig;
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) { $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) {
if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) { if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2); list($key, $host) = explode('.', $key, 2);
if ($authConfig) { if ($authConfig) {
unset($config[$key][$host]); unset($config[$key][$host]);

@ -131,7 +131,8 @@ class Application extends BaseApplication
if ($input->hasParameterOption('--no-cache')) { if ($input->hasParameterOption('--no-cache')) {
$io->writeError('Disabling cache usage', true, IOInterface::DEBUG); $io->writeError('Disabling cache usage', true, IOInterface::DEBUG);
putenv('COMPOSER_CACHE_DIR='.(Platform::isWindows() ? 'nul' : '/dev/null')); $_SERVER['COMPOSER_CACHE_DIR'] = Platform::isWindows() ? 'nul' : '/dev/null';
putenv('COMPOSER_CACHE_DIR='.$_SERVER['COMPOSER_CACHE_DIR']);
} }
// switch working dir // switch working dir

@ -243,7 +243,8 @@ class EventDispatcher
$finder = new PhpExecutableFinder(); $finder = new PhpExecutableFinder();
$phpPath = $finder->find(false); $phpPath = $finder->find(false);
if ($phpPath) { if ($phpPath) {
putenv('PHP_BINARY=' . $phpPath); $_SERVER['PHP_BINARY'] = $phpPath;
putenv('PHP_BINARY=' . $_SERVER['PHP_BINARY']);
} }
} }

@ -115,6 +115,7 @@ abstract class BaseIO implements IOInterface
$gitlabOauth = $config->get('gitlab-oauth') ?: array(); $gitlabOauth = $config->get('gitlab-oauth') ?: array();
$gitlabToken = $config->get('gitlab-token') ?: array(); $gitlabToken = $config->get('gitlab-token') ?: array();
$httpBasic = $config->get('http-basic') ?: array(); $httpBasic = $config->get('http-basic') ?: array();
$bearerToken = $config->get('bearer') ?: array();
// reload oauth tokens from config if available // reload oauth tokens from config if available
@ -142,6 +143,10 @@ abstract class BaseIO implements IOInterface
$this->checkAndSetAuthentication($domain, $cred['username'], $cred['password']); $this->checkAndSetAuthentication($domain, $cred['username'], $cred['password']);
} }
foreach ($bearerToken as $domain => $token) {
$this->checkAndSetAuthentication($domain, $token, 'bearer');
}
// setup process timeout // setup process timeout
ProcessExecutor::setTimeout((int) $config->get('process-timeout')); ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
} }

@ -219,8 +219,8 @@ class Installer
} }
if ($this->runScripts) { if ($this->runScripts) {
$devMode = (int) $this->devMode; $_SERVER['COMPOSER_DEV_MODE'] = (int) $this->devMode;
putenv("COMPOSER_DEV_MODE=$devMode"); putenv('COMPOSER_DEV_MODE='.$_SERVER['COMPOSER_DEV_MODE']);
// dispatch pre event // dispatch pre event
// should we treat this more strictly as running an update and then running an install, triggering events multiple times? // should we treat this more strictly as running an update and then running an install, triggering events multiple times?

@ -23,6 +23,7 @@ use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\Util\Url; use Composer\Util\Url;
use Composer\Semver\Constraint\Constraint;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Config; use Composer\Config;
@ -381,7 +382,12 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
private function validateBranch($branch) private function validateBranch($branch)
{ {
try { try {
return $this->versionParser->normalizeBranch($branch); $normalizedBranch = $this->versionParser->normalizeBranch($branch);
// validate that the branch name has no weird characters conflicting with constraints
$this->versionParser->parseConstraints($normalizedBranch);
return $normalizedBranch;
} catch (\Exception $e) { } catch (\Exception $e) {
} }
@ -421,7 +427,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$this->io->overwriteError($msg, false); $this->io->overwriteError($msg, false);
} }
if ($existingPackage = $this->findPackage($cachedPackage['name'], $cachedPackage['version_normalized'])) { if ($existingPackage = $this->findPackage($cachedPackage['name'], new Constraint('=', $cachedPackage['version_normalized']))) {
if ($isVeryVerbose) { 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>'); $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>');
} }

@ -117,7 +117,7 @@ class AuthHelper
$message = "\n".'Could not fetch '.$url.', enter your ' . $origin . ' credentials ' .($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit'); $message = "\n".'Could not fetch '.$url.', enter your ' . $origin . ' credentials ' .($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit');
$gitLabUtil = new GitLab($this->io, $this->config, null); $gitLabUtil = new GitLab($this->io, $this->config, null);
if ($this->io->hasAuthentication($origin) && ($auth = $this->io->getAuthentication($origin)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token'), true)) { if ($this->io->hasAuthentication($origin) && ($auth = $this->io->getAuthentication($origin)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token', 'oauth2'), true)) {
throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode);
} }
@ -196,7 +196,9 @@ class AuthHelper
if ($this->io->hasAuthentication($origin)) { if ($this->io->hasAuthentication($origin)) {
$authenticationDisplayMessage = null; $authenticationDisplayMessage = null;
$auth = $this->io->getAuthentication($origin); $auth = $this->io->getAuthentication($origin);
if ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) { if ($auth['password'] === 'bearer') {
$headers[] = 'Authorization: Bearer '.$auth['username'];
} elseif ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) {
$headers[] = 'Authorization: token '.$auth['username']; $headers[] = 'Authorization: token '.$auth['username'];
$authenticationDisplayMessage = 'Using GitHub token authentication'; $authenticationDisplayMessage = 'Using GitHub token authentication';
} elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) { } elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) {

@ -207,7 +207,7 @@ class Filesystem
usleep(350000); usleep(350000);
$unlinked = @$this->unlinkImplementation($path); $unlinked = @$this->unlinkImplementation($path);
} }
if (!$unlinked) { if (!$unlinked) {
$error = error_get_last(); $error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message']; $message = 'Could not delete '.$path.': ' . @$error['message'];
@ -238,7 +238,7 @@ class Filesystem
usleep(350000); usleep(350000);
$deleted = @rmdir($path); $deleted = @rmdir($path);
} }
if (!$deleted) { if (!$deleted) {
$error = error_get_last(); $error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message']; $message = 'Could not delete '.$path.': ' . @$error['message'];
@ -312,7 +312,9 @@ class Filesystem
} }
if (!function_exists('proc_open')) { if (!function_exists('proc_open')) {
return $this->copyThenRemove($source, $target); $this->copyThenRemove($source, $target);
return;
} }
if (Platform::isWindows()) { if (Platform::isWindows()) {
@ -342,7 +344,7 @@ class Filesystem
} }
} }
return $this->copyThenRemove($source, $target); $this->copyThenRemove($source, $target);
} }
/** /**

@ -486,6 +486,58 @@ class AutoloadGeneratorTest extends TestCase
$this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty."); $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty.");
} }
public function testNonDevAutoloadReplacesNestedRequirements()
{
$package = new Package('a', '1.0', '1.0');
$package->setRequires(array(
new Link('a', 'a/a')
));
$packages = array();
$packages[] = $a = new Package('a/a', '1.0', '1.0');
$packages[] = $b = new Package('b/b', '1.0', '1.0');
$packages[] = $c = new Package('c/c', '1.0', '1.0');
$packages[] = $d = new Package('d/d', '1.0', '1.0');
$packages[] = $e = new Package('e/e', '1.0', '1.0');
$a->setAutoload(array('classmap' => array('src/A.php')));
$a->setRequires(array(
new Link('a/a', 'b/b')
));
$b->setAutoload(array('classmap' => array('src/B.php')));
$b->setRequires(array(
new Link('b/b', 'e/e')
));
$c->setAutoload(array('classmap' => array('src/C.php')));
$c->setReplaces(array(
new Link('c/c', 'b/b')
));
$c->setRequires(array(
new Link('c/c', 'd/d')
));
$d->setAutoload(array('classmap' => array('src/D.php')));
$e->setAutoload(array('classmap' => array('src/E.php')));
$this->repository->expects($this->once())
->method('getCanonicalPackages')
->will($this->returnValue($packages));
$this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/c/c/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/d/d/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/e/e/src');
file_put_contents($this->vendorDir.'/a/a/src/A.php', '<?php class A {}');
file_put_contents($this->vendorDir.'/b/b/src/B.php', '<?php class B {}');
file_put_contents($this->vendorDir.'/c/c/src/C.php', '<?php class C {}');
file_put_contents($this->vendorDir.'/d/d/src/D.php', '<?php class D {}');
file_put_contents($this->vendorDir.'/e/e/src/E.php', '<?php class E {}');
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_5');
$this->assertAutoloadFiles('classmap9', $this->vendorDir.'/composer', 'classmap');
}
public function testPharAutoload() public function testPharAutoload()
{ {
$package = new Package('a', '1.0', '1.0'); $package = new Package('a', '1.0', '1.0');

@ -0,0 +1,12 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'A' => $vendorDir . '/a/a/src/A.php',
'C' => $vendorDir . '/c/c/src/C.php',
'D' => $vendorDir . '/d/d/src/D.php',
);
Loading…
Cancel
Save