diff --git a/composer.lock b/composer.lock index 320e1d0c0..db4aacb10 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" + "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8afa52cd417f4ec417b4bfe86b68106538a87660", + "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660", "shasum": "" }, "require": { @@ -60,7 +60,7 @@ "ssl", "tls" ], - "time": "2018-08-08T08:57:40+00:00" + "time": "2018-10-18T06:09:13+00:00" }, { "name": "composer/semver", diff --git a/doc/03-cli.md b/doc/03-cli.md index 372bea85a..74374ec6b 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -856,9 +856,9 @@ is a hidden, global (per-user on the machine) directory that is shared between all projects. By default it points to `C:\Users\\AppData\Roaming\Composer` on Windows -and `/Users//.composer` on macOS. On *nix systems that follow the [XDG Base +and `/Users//.composer` on macOS. On \*nix systems that follow the [XDG Base Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), -it points to `$XDG_CONFIG_HOME/composer`. On other *nix systems, it points to +it points to `$XDG_CONFIG_HOME/composer`. On other \*nix systems, it points to `/home//.composer`. #### COMPOSER_HOME/config.json diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 95f7e0978..fce8549f0 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -279,7 +279,7 @@ class ClassLoader */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index f4d642f49..10340a56b 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -522,7 +522,7 @@ EOT $errors['iconv_mbstring'] = true; } - if (!ini_get('allow_url_fopen')) { + if (!filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) { $errors['allow_url_fopen'] = true; } @@ -546,7 +546,7 @@ EOT $warnings['openssl_version'] = true; } - if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) { + if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) { $warnings['apc_cli'] = true; } @@ -569,7 +569,7 @@ EOT } } - if (ini_get('xdebug.profiler_enabled')) { + if (filter_var(ini_get('xdebug.profiler_enabled'), FILTER_VALIDATE_BOOLEAN)) { $warnings['xdebug_profile'] = true; } elseif (extension_loaded('xdebug')) { $warnings['xdebug_loaded'] = true; diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 2641a922b..243755963 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -220,7 +220,7 @@ TAGSPUBKEY $pubkeyid = openssl_pkey_get_public($sigFile); $algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384'; - if (!in_array('SHA384', openssl_get_md_methods())) { + if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) { throw new \RuntimeException('SHA384 is not supported by your openssl extension, could not verify the phar file integrity'); } $signature = json_decode($signature, true); diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 0299f1458..e73d6ead9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -222,6 +222,12 @@ EOT if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) { $exitCode = 1; } + if ($input->getOption('path')) { + $io->write($package->getName(), false); + $io->write(' ' . strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n")); + + return $exitCode; + } $this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null); $this->printLinks($package, 'requires'); $this->printLinks($package, 'devRequires', 'requires (dev)'); @@ -585,6 +591,9 @@ EOT $this->printLicenses($package); $io->write('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $io->write('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); + if ($installedRepo->hasPackage($package)) { + $io->write('path : ' . sprintf('%s', realpath($this->getComposer()->getInstallationManager()->getInstallPath($package)))); + } $io->write('names : ' . implode(', ', $package->getNames())); if ($latestPackage->isAbandoned()) { diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 4064b20b5..27b1f4816 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -255,7 +255,7 @@ class Compiler */ // Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264 -if (extension_loaded('apc') && ini_get('apc.enable_cli') && ini_get('apc.cache_by_default')) { +if (extension_loaded('apc') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.cache_by_default'), FILTER_VALIDATE_BOOLEAN)) { if (version_compare(phpversion('apc'), '3.0.12', '>=')) { ini_set('apc.cache_by_default', 0); } else { diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 5ca7a2dab..6534db3d8 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -69,7 +69,8 @@ class ZipDownloader extends ArchiveDownloader if (!self::$isWindows && !self::$hasSystemUnzip) { $this->io->writeError("As there is no 'unzip' command installed zip files are being unpacked using the PHP zip extension."); - $this->io->writeError("This may cause invalid reports of corrupted archives. Installing 'unzip' may remediate them."); + $this->io->writeError("This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost."); + $this->io->writeError("Installing 'unzip' may remediate them."); } } diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 2383b2dd3..0184cb4d5 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -126,7 +126,9 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito private function getComposerInformation(\SplFileInfo $file) { $zip = new \ZipArchive(); - $zip->open($file->getPathname()); + if ($zip->open($file->getPathname()) !== true) { + return false; + } if (0 == $zip->numFiles) { $zip->close(); diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 02d552424..0a2e79f35 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -236,7 +236,12 @@ class PlatformRepository extends ArrayRepository // Skip if overridden if (isset($this->overrides[$package->getName()])) { $overrider = $this->findPackage($package->getName(), '*'); - $overrider->setDescription($overrider->getDescription().' (actual: '.$package->getPrettyVersion().')'); + if ($package->getVersion() === $overrider->getVersion()) { + $actualText = 'same as actual'; + } else { + $actualText = 'actual: '.$package->getPrettyVersion(); + } + $overrider->setDescription($overrider->getDescription().' ('.$actualText.')'); return; } @@ -244,7 +249,12 @@ class PlatformRepository extends ArrayRepository // Skip if PHP is overridden and we are adding a php-* package if (isset($this->overrides['php']) && 0 === strpos($package->getName(), 'php-')) { $overrider = $this->addOverriddenPackage($this->overrides['php'], $package->getPrettyName()); - $overrider->setDescription($overrider->getDescription().' (actual: '.$package->getPrettyVersion().')'); + if ($package->getVersion() === $overrider->getVersion()) { + $actualText = 'same as actual'; + } else { + $actualText = 'actual: '.$package->getPrettyVersion(); + } + $overrider->setDescription($overrider->getDescription().' ('.$actualText.')'); return; } diff --git a/src/Composer/Repository/Vcs/BitbucketDriver.php b/src/Composer/Repository/Vcs/BitbucketDriver.php index 64a954d43..24a4af4dd 100644 --- a/src/Composer/Repository/Vcs/BitbucketDriver.php +++ b/src/Composer/Repository/Vcs/BitbucketDriver.php @@ -197,7 +197,7 @@ abstract class BitbucketDriver extends VcsDriver } $resource = sprintf( - 'https://api.bitbucket.org/1.0/repositories/%s/%s/raw/%s/%s', + 'https://api.bitbucket.org/2.0/repositories/%s/%s/src/%s/%s', $this->owner, $this->repository, $identifier, @@ -428,11 +428,16 @@ abstract class BitbucketDriver extends VcsDriver protected function getMainBranchData() { $resource = sprintf( - 'https://api.bitbucket.org/1.0/repositories/%s/%s/main-branch', + 'https://api.bitbucket.org/2.0/repositories/%s/%s?fields=mainbranch', $this->owner, $this->repository ); - return JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + $data = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + if (isset($data['mainbranch'])) { + return $data['mainbranch']; + } + + return null; } } diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 57639cdea..d6fb1bbee 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -41,8 +41,10 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt private $drivers; /** @var VcsDriverInterface */ private $driver; + /** @var VersionCacheInterface */ + private $versionCache; - public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, array $drivers = null) + public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, array $drivers = null, VersionCacheInterface $versionCache = null) { parent::__construct(); $this->drivers = $drivers ?: array( @@ -64,6 +66,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt $this->verbose = $io->isVeryVerbose(); $this->config = $config; $this->repoConfig = $repoConfig; + $this->versionCache = $versionCache; } public function getRepoConfig() @@ -152,6 +155,13 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt // strip the release- prefix from tags if present $tag = str_replace('release-', '', $tag); + $cachedPackage = $this->getCachedPackageVersion($tag, $identifier, $verbose); + if ($cachedPackage) { + $this->addPackage($cachedPackage); + + continue; + } + if (!$parsedTag = $this->validateTag($tag)) { if ($verbose) { $this->io->writeError('Skipped tag '.$tag.', invalid tag name'); @@ -188,7 +198,8 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt continue; } - if ($existingPackage = $this->findPackage($data['name'], $data['version_normalized'])) { + $tagPackageName = isset($data['name']) ? $data['name'] : $this->packageName; + if ($existingPackage = $this->findPackage($tagPackageName, $data['version_normalized'])) { if ($verbose) { $this->io->writeError('Skipped tag '.$tag.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$data['version_normalized'].' internally'); } @@ -235,6 +246,21 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt continue; } + // make sure branch packages have a dev flag + if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { + $version = 'dev-' . $branch; + } else { + $prefix = substr($branch, 0, 1) === 'v' ? 'v' : ''; + $version = $prefix . preg_replace('{(\.9{7})+}', '.x', $parsedBranch); + } + + $cachedPackage = $this->getCachedPackageVersion($version, $identifier, $verbose); + if ($cachedPackage) { + $this->addPackage($cachedPackage); + + continue; + } + try { if (!$data = $driver->getComposerInformation($identifier)) { if ($verbose) { @@ -244,17 +270,9 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt } // branches are always auto-versioned, read value from branch name - $data['version'] = $branch; + $data['version'] = $version; $data['version_normalized'] = $parsedBranch; - // make sure branch packages have a dev flag - if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { - $data['version'] = 'dev-' . $data['version']; - } else { - $prefix = substr($branch, 0, 1) === 'v' ? 'v' : ''; - $data['version'] = $prefix . preg_replace('{(\.9{7})+}', '.x', $parsedBranch); - } - if ($verbose) { $this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')'); } @@ -294,7 +312,8 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt protected function preProcess(VcsDriverInterface $driver, array $data, $identifier) { // keep the name of the main identifier for all packages - $data['name'] = $this->packageName ?: $data['name']; + $dataPackageName = isset($data['name']) ? $data['name'] : null; + $data['name'] = $this->packageName ?: $dataPackageName; if (!isset($data['dist'])) { $data['dist'] = $driver->getDist($identifier); @@ -325,4 +344,34 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt return false; } + + private function getCachedPackageVersion($version, $identifier, $verbose) + { + if (!$this->versionCache) { + return; + } + + $cachedPackage = $this->versionCache->getVersionPackage($version, $identifier); + if ($cachedPackage) { + $msg = 'Found cached composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $version . ')'; + if ($verbose) { + $this->io->writeError($msg); + } else { + $this->io->overwriteError($msg, false); + } + + if ($existingPackage = $this->findPackage($cachedPackage['name'], $cachedPackage['version_normalized'])) { + if ($verbose) { + $this->io->writeError('Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally'); + } + $cachedPackage = null; + } + } + + if ($cachedPackage) { + return $this->loader->load($cachedPackage); + } + + return null; + } } diff --git a/src/Composer/Repository/VersionCacheInterface.php b/src/Composer/Repository/VersionCacheInterface.php new file mode 100644 index 000000000..db5934b59 --- /dev/null +++ b/src/Composer/Repository/VersionCacheInterface.php @@ -0,0 +1,23 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +interface VersionCacheInterface +{ + /** + * @param string $version + * @param string $identifier + * @return array Package version data + */ + public function getVersionPackage($version, $identifier); +} diff --git a/src/Composer/Util/ErrorHandler.php b/src/Composer/Util/ErrorHandler.php index 925cab74c..83e6b5ede 100644 --- a/src/Composer/Util/ErrorHandler.php +++ b/src/Composer/Util/ErrorHandler.php @@ -41,7 +41,7 @@ class ErrorHandler return; } - if (ini_get('xdebug.scream')) { + if (filter_var(ini_get('xdebug.scream'), FILTER_VALIDATE_BOOLEAN)) { $message .= "\n\nWarning: You have xdebug.scream enabled, the warning above may be". "\na legitimately suppressed error that you were not supposed to see."; } diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index d2efc5377..2a0742778 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -131,15 +131,11 @@ class ProcessExecutor */ public static function escape($argument) { - if (method_exists('Symfony\Component\Process\ProcessUtils', 'escapeArgument')) { - return ProcessUtils::escapeArgument($argument); - } - return self::escapeArgument($argument); } /** - * Copy of ProcessUtils::escapeArgument() that is removed in Symfony 4. + * Copy of ProcessUtils::escapeArgument() that is deprecated in Symfony 3.3 and removed in Symfony 4. * * @param string $argument * diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 6e16520fd..ea18a9e30 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -369,7 +369,7 @@ class RemoteFilesystem } $result = false; } - if ($errorMessage && !ini_get('allow_url_fopen')) { + if ($errorMessage && !filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) { $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')'; } restore_error_handler(); @@ -390,15 +390,18 @@ class RemoteFilesystem $statusCode = null; $contentType = null; + $locationHeader = null; if (!empty($http_response_header[0])) { $statusCode = $this->findStatusCode($http_response_header); $contentType = $this->findHeaderValue($http_response_header, 'content-type'); + $locationHeader = $this->findHeaderValue($http_response_header, 'location'); } // check for bitbucket login page asking to authenticate if ($originUrl === 'bitbucket.org' && !$this->isPublicBitBucketDownload($fileUrl) && substr($fileUrl, -4) === '.zip' + && (!$locationHeader || substr($locationHeader, -4) !== '.zip') && $contentType && preg_match('{^text/html\b}i', $contentType) ) { $result = false; diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/not-a-zip-with-zip-extension.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/not-a-zip-with-zip-extension.zip new file mode 100644 index 000000000..562f032d4 Binary files /dev/null and b/tests/Composer/Test/Repository/Fixtures/artifacts/not-a-zip-with-zip-extension.zip differ diff --git a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php index b35bb8867..7547855bf 100644 --- a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php @@ -113,7 +113,7 @@ class GitBitbucketDriverTest extends TestCase ), array( $this->originUrl, - 'https://api.bitbucket.org/1.0/repositories/user/repo/main-branch', + 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=mainbranch', false, ), array( @@ -128,7 +128,7 @@ class GitBitbucketDriverTest extends TestCase ), array( $this->originUrl, - 'https://api.bitbucket.org/1.0/repositories/user/repo/raw/master/composer.json', + 'https://api.bitbucket.org/2.0/repositories/user/repo/src/master/composer.json', false, ), array( @@ -139,7 +139,7 @@ class GitBitbucketDriverTest extends TestCase ) ->willReturnOnConsecutiveCalls( '{"scm":"git","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo.git","name":"https"},{"href":"ssh:\/\/git@bitbucket.org\/user\/repo.git","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}', - '{"name": "master"}', + '{"mainbranch": {"name": "master"}}', '{"values":[{"name":"1.0.1","target":{"hash":"9b78a3932143497c519e49b8241083838c8ff8a1"}},{"name":"1.0.0","target":{"hash":"d3393d514318a9267d2f8ebbf463a9aaa389f8eb"}}]}', '{"values":[{"name":"master","target":{"hash":"937992d19d72b5116c3e8c4a04f960e5fa270b22"}}]}', '{"name": "user/repo","description": "test repo","license": "GPL","authors": [{"name": "Name","email": "local@domain.tld"}],"require": {"creator/package": "^1.0"},"require-dev": {"phpunit/phpunit": "~4.8"}}',