From be08638e40f4c8583b025229e65754f44974ff56 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 16:08:31 +0100 Subject: [PATCH 01/14] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade8d99cd..b737db151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### [1.9.2] 2020-01-14 + + * Fixed minor git driver bugs + * Fixed schema validation for version field to allow dev-* versions too + * Fixed external processes' output being formatted even though it should not + * Fixed issue with path repositories when trying to install feature branches + ### [1.9.1] 2019-11-01 * Fixed various credential handling issues with gitlab and github @@ -776,6 +783,7 @@ * Initial release +[1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2 [1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1 [1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0 [1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6 From 8429a48dac648dd77cbb5883995c0175e63dd02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Wed, 15 Jan 2020 17:09:44 +0500 Subject: [PATCH 02/14] Change only modified files --- src/Composer/Autoload/AutoloadGenerator.php | 72 ++++++++++++++++----- src/Composer/Installer.php | 7 -- src/Composer/Json/JsonFile.php | 15 ++++- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 5e068ccff..bb0c94c49 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -285,24 +285,24 @@ EOF; } } - file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile); - file_put_contents($targetDir.'/autoload_psr4.php', $psr4File); - file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile); + $this->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile); + $this->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File); + $this->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile); $includePathFilePath = $targetDir.'/include_paths.php'; if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - file_put_contents($includePathFilePath, $includePathFileContents); + $this->filePutContentsIfModified($includePathFilePath, $includePathFileContents); } elseif (file_exists($includePathFilePath)) { unlink($includePathFilePath); } $includeFilesFilePath = $targetDir.'/autoload_files.php'; if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - file_put_contents($includeFilesFilePath, $includeFilesFileContents); + $this->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents); } elseif (file_exists($includeFilesFilePath)) { unlink($includeFilesFilePath); } - file_put_contents($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); - file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion)); + $this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); + $this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); + $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion)); $this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); $this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); @@ -316,6 +316,16 @@ EOF; return count($classMap); } + private function filePutContentsIfModified($path, $content) + { + $currentContent = file_get_contents($path); + if (!$currentContent || ($currentContent != $content)) { + return file_put_contents($path, $content); + } + + return 0; + } + private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array()) { foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) { @@ -383,8 +393,8 @@ EOF; /** * Compiles an ordered list of namespace => path mappings * - * @param array $packageMap array of array(package, installDir-relative-to-composer.json) - * @param PackageInterface $mainPackage root package instance + * @param array $packageMap array of array(package, installDir-relative-to-composer.json) + * @param PackageInterface $mainPackage root package instance * @param bool $filterOutRequireDevPackages whether to filter out require-dev packages * @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) */ @@ -986,7 +996,6 @@ INITIALIZER; $sortedPackages = PackageSorter::sortPackages($packages); - $sortedPackageMap = array(); foreach ($sortedPackages as $package) { @@ -1005,11 +1014,42 @@ INITIALIZER; */ protected function safeCopy($source, $target) { - $source = fopen($source, 'r'); - $target = fopen($target, 'w+'); + if (!$this->filesAreEqual($source, $target)) { + $source = fopen($source, 'r'); + $target = fopen($target, 'w+'); + + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + } + } + + /** + * compare 2 files + * https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files + */ + private function filesAreEqual($a, $b) + { + // Check if filesize is different + if (filesize($a) !== filesize($b)) { + return false; + } + + // Check if content is different + $ah = fopen($a, 'rb'); + $bh = fopen($b, 'rb'); + + $result = true; + while (!feof($ah)) { + if (fread($ah, 8192) != fread($bh, 8192)) { + $result = false; + break; + } + } + + fclose($ah); + fclose($bh); - stream_copy_to_stream($source, $target); - fclose($source); - fclose($target); + return $result; } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index c5ec5cc5d..bf0523356 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -311,13 +311,6 @@ class Installer foreach ($localRepo->getPackages() as $package) { $this->installationManager->ensureBinariesPresence($package); } - - $vendorDir = $this->config->get('vendor-dir'); - if (is_dir($vendorDir)) { - // suppress errors as this fails sometimes on OSX for no apparent reason - // see https://github.com/composer/composer/issues/4070#issuecomment-129792748 - @touch($vendorDir); - } } if ($this->runScripts) { diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index f2d950004..3b9b74082 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -129,7 +129,7 @@ class JsonFile $retries = 3; while ($retries--) { try { - file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); + $this->filePutContentsIfModified($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); break; } catch (\Exception $e) { if ($retries) { @@ -142,6 +142,19 @@ class JsonFile } } + /** + * modify file properties only if content modified + */ + private function filePutContentsIfModified($path, $content) + { + $currentContent = file_get_contents($path); + if (!$currentContent || ($currentContent != $content)) { + return file_put_contents($path, $content); + } + + return 0; + } + /** * Validates the schema of the current json file according to composer-schema.json rules * From a4dc076dc8fd997c054325ed21bab30e30f68fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Wed, 15 Jan 2020 18:09:42 +0500 Subject: [PATCH 03/14] fix file load errors --- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- src/Composer/Json/JsonFile.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index bb0c94c49..6ca4b3329 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -318,7 +318,7 @@ EOF; private function filePutContentsIfModified($path, $content) { - $currentContent = file_get_contents($path); + $currentContent = @file_get_contents($path); if (!$currentContent || ($currentContent != $content)) { return file_put_contents($path, $content); } @@ -1014,7 +1014,7 @@ INITIALIZER; */ protected function safeCopy($source, $target) { - if (!$this->filesAreEqual($source, $target)) { + if (!file_exists($target) OR !file_exists($source) OR !$this->filesAreEqual($source, $target)) { $source = fopen($source, 'r'); $target = fopen($target, 'w+'); diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 3b9b74082..89524df39 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -147,7 +147,7 @@ class JsonFile */ private function filePutContentsIfModified($path, $content) { - $currentContent = file_get_contents($path); + $currentContent = @file_get_contents($path); if (!$currentContent || ($currentContent != $content)) { return file_put_contents($path, $content); } From f02989ceba7448546873f327feaf344387c04478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Wed, 15 Jan 2020 18:27:12 +0500 Subject: [PATCH 04/14] revert formatting --- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 6ca4b3329..6432c8065 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -393,8 +393,8 @@ EOF; /** * Compiles an ordered list of namespace => path mappings * - * @param array $packageMap array of array(package, installDir-relative-to-composer.json) - * @param PackageInterface $mainPackage root package instance + * @param array $packageMap array of array(package, installDir-relative-to-composer.json) + * @param PackageInterface $mainPackage root package instance * @param bool $filterOutRequireDevPackages whether to filter out require-dev packages * @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) */ From 33db6ec71bd2efc86b73c987878ddbd309d5c627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Fri, 17 Jan 2020 10:58:52 +0500 Subject: [PATCH 05/14] touch only if update --- src/Composer/Installer.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index bf0523356..270c75544 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -313,6 +313,15 @@ class Installer } } + if ($this->update) { + $vendorDir = $this->config->get('vendor-dir'); + if (is_dir($vendorDir)) { + // suppress errors as this fails sometimes on OSX for no apparent reason + // see https://github.com/composer/composer/issues/4070#issuecomment-129792748 + @touch($vendorDir); + } + } + if ($this->runScripts) { // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; From 1f261f1f7a2d08f72312c716ea4acc538259d67f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jan 2020 11:44:17 +0100 Subject: [PATCH 06/14] Update changelog --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 091ba77fc..07ee13703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +### [1.10.0] 2020-01-XX + + * Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory. + * Warning: Added a warning when class names are being loaded by a PSR-4 or PSR-0 rule only due to classmap optimization, but would not otherwise be autoloadable. The next minor version will stop autoloading these classes so make sure you fix your autoload configs. + * Added support for configuring suggestions using config command, e.g. `composer config suggest.foo/bar some text` + * Added support for configuring fine-grained preferred-install using config command, e.g. `composer config preferred-install.foo/* dist` + * Added `@putenv` script handler to set environment variables from composer.json for following scripts + * Added `lock` option that can be set to false, in which case no composer.lock file will be generated + * Added support for IPv6 addresses in NO_PROXY + * Added package homepage display in the show command + * Added debug info about HTTP authentications + * Added Symfony 5 compatibility + * Added --fixed flag to require command to make it use a fixed constraint instead of a ^x.y constraint when adding the requirement + * Fixed GitHub deprecation of access_token query parameter, now using Authorization header + * Fixed archive command to persist file permissions inside the zip files + * Fixed init/require command to avoid suggesting packages which are already selected in the search results + * Fixed create-project UX issues + ### [1.9.2] 2020-01-14 * Fixed minor git driver bugs @@ -783,6 +801,7 @@ * Initial release +[1.10.0]: https://github.com/composer/composer/compare/1.9.2...1.10.0 [1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2 [1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1 [1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0 From b9d00153d9431d75496b048a6a4dcd4d278b1eaf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jan 2020 14:52:07 +0100 Subject: [PATCH 07/14] Suggest using -p when a platform package can not be found in show command --- src/Composer/Command/ShowCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 441fe6818..78a418a33 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -201,6 +201,9 @@ EOT if (empty($package)) { $options = $input->getOptions(); if (!isset($options['working-dir']) || !file_exists('composer.json')) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $input->getArgument('package')) && !$input->getOption('platform')) { + throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found, try using --platform (-p) to show platform packages.'); + } throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found'); } From 7d7e3d594befc601797d4a986c4a88e600868092 Mon Sep 17 00:00:00 2001 From: Patrick Weyck Date: Tue, 14 Jan 2020 15:39:11 +0100 Subject: [PATCH 08/14] Normalize minimum-stability `rc` to `RC` in `InitCommand` A `minimum-stability` of `rc` is valid according to `composer-schema.json` and works fine with install and update and generally in version comparisons, because it's normalized to `RC`. This commit makes it work in `InitCommand` and `RequireCommand` too. --- src/Composer/Command/InitCommand.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 440187ae8..d234a8cba 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -16,6 +16,7 @@ use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\Json\JsonFile; use Composer\Package\BasePackage; +use Composer\Package\Package; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; @@ -702,13 +703,13 @@ EOT private function getMinimumStability(InputInterface $input) { if ($input->hasOption('stability')) { - return $input->getOption('stability') ?: 'stable'; + return VersionParser::normalizeStability($input->getOption('stability') ?: 'stable'); } $file = Factory::getComposerFile(); if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (!empty($composer['minimum-stability'])) { - return $composer['minimum-stability']; + return VersionParser::normalizeStability($composer['minimum-stability']); } } From 1e927806000e46b73fed225446151af8825719e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Fri, 17 Jan 2020 19:37:39 +0500 Subject: [PATCH 09/14] operations case touch --- src/Composer/Installer.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 270c75544..a0e365ded 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -313,15 +313,6 @@ class Installer } } - if ($this->update) { - $vendorDir = $this->config->get('vendor-dir'); - if (is_dir($vendorDir)) { - // suppress errors as this fails sometimes on OSX for no apparent reason - // see https://github.com/composer/composer/issues/4070#issuecomment-129792748 - @touch($vendorDir); - } - } - if ($this->runScripts) { // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; @@ -633,6 +624,15 @@ class Installer $localRepo->write(); } + if ($operations) { + $vendorDir = $this->config->get('vendor-dir'); + if (is_dir($vendorDir)) { + // suppress errors as this fails sometimes on OSX for no apparent reason + // see https://github.com/composer/composer/issues/4070#issuecomment-129792748 + @touch($vendorDir); + } + } + return array(0, $devPackages); } From acc040f745e3cfa5398f411e89a46e830dd054c4 Mon Sep 17 00:00:00 2001 From: Adriano Ferreira Date: Sun, 19 Jan 2020 22:12:11 -0200 Subject: [PATCH 10/14] Append the bin dir on each listener iteration The "composer install" can create the vendor/dir folders and be used as a script item on composer.json. Having another script running after it that relies on vendor/bir binaries (such as phpunit) will cause it to not find the binary. This fix addresses the issue by trying to append the path on each script iteration. --- .../EventDispatcher/EventDispatcher.php | 36 +++++++------ .../EventDispatcher/EventDispatcherTest.php | 54 +++++++++++++++++++ 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index c623ada42..45288825d 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -145,27 +145,15 @@ class EventDispatcher */ protected function doDispatch(Event $event) { - $pathStr = 'PATH'; - if (!isset($_SERVER[$pathStr]) && isset($_SERVER['Path'])) { - $pathStr = 'Path'; - } - - // add the bin dir to the PATH to make local binaries of deps usable in scripts - $binDir = $this->composer->getConfig()->get('bin-dir'); - if (is_dir($binDir)) { - $binDir = realpath($binDir); - if (isset($_SERVER[$pathStr]) && !preg_match('{(^|'.PATH_SEPARATOR.')'.preg_quote($binDir).'($|'.PATH_SEPARATOR.')}', $_SERVER[$pathStr])) { - $_SERVER[$pathStr] = $binDir.PATH_SEPARATOR.getenv($pathStr); - putenv($pathStr.'='.$_SERVER[$pathStr]); - } - } - $listeners = $this->getListeners($event); $this->pushEvent($event); $return = 0; foreach ($listeners as $callable) { + + $this->ensureBinDirIsInPath(); + if (!is_string($callable)) { if (!is_callable($callable)) { $className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0]; @@ -545,4 +533,22 @@ class EventDispatcher { return array_pop($this->eventStack); } + + private function ensureBinDirIsInPath() + { + $pathStr = 'PATH'; + if (!isset($_SERVER[$pathStr]) && isset($_SERVER['Path'])) { + $pathStr = 'Path'; + } + + // add the bin dir to the PATH to make local binaries of deps usable in scripts + $binDir = $this->composer->getConfig()->get('bin-dir'); + if (is_dir($binDir)) { + $binDir = realpath($binDir); + if (isset($_SERVER[$pathStr]) && !preg_match('{(^|'.PATH_SEPARATOR.')'.preg_quote($binDir).'($|'.PATH_SEPARATOR.')}', $_SERVER[$pathStr])) { + $_SERVER[$pathStr] = $binDir.PATH_SEPARATOR.getenv($pathStr); + putenv($pathStr.'='.$_SERVER[$pathStr]); + } + } + } } diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index 3d5e0c712..04ae56c20 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -260,6 +260,60 @@ class EventDispatcherTest extends TestCase $this->assertEquals($expected, $io->getOutput()); } + public function testDispatcherAppendsDirBinOnPathForEveryListener() + { + $currentDirectoryBkp = getcwd(); + $composerBinDirBkp = getenv('COMPOSER_BIN_DIR'); + chdir(__DIR__); + putenv('COMPOSER_BIN_DIR=' . __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR)); + + $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock(); + $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->setConstructorArgs(array( + $this->createComposerInstance(), + $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE), + $process, + ))->setMethods(array( + 'getListeners', + ))->getMock(); + + $listeners = array( + 'Composer\\Test\\EventDispatcher\\EventDispatcherTest::createsVendorBinFolderChecksEnvDoesNotContainsBin', + 'Composer\\Test\\EventDispatcher\\EventDispatcherTest::createsVendorBinFolderChecksEnvContainsBin', + ); + + $dispatcher->expects($this->atLeastOnce())->method('getListeners')->will($this->returnValue($listeners)); + + $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false); + rmdir(__DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR)); + rmdir(__DIR__ . sprintf('%svendor', DIRECTORY_SEPARATOR)); + + chdir($currentDirectoryBkp); + putenv('COMPOSER_BIN_DIR=' . $composerBinDirBkp); + } + + static public function createsVendorBinFolderChecksEnvDoesNotContainsBin() + { + mkdir(__DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), 0700, true); + $val = getenv('PATH'); + + if ( ! $val ) { + $val = getenv('Path'); + } + + self::assertFalse(strpos($val, __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR))); + } + + static public function createsVendorBinFolderChecksEnvContainsBin() + { + $val = getenv('PATH'); + + if ( ! $val ) { + $val = getenv('Path'); + } + + self::assertNotFalse(strpos($val, __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR))); + } + static public function getTestEnv() { $val = getenv('ABC'); if ($val !== '123') { From 48c7442b63fd74bea6416d80f5095a1723374b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Mon, 20 Jan 2020 16:25:38 +0500 Subject: [PATCH 11/14] should be || not OR --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 6432c8065..940ab3f18 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -1014,7 +1014,7 @@ INITIALIZER; */ protected function safeCopy($source, $target) { - if (!file_exists($target) OR !file_exists($source) OR !$this->filesAreEqual($source, $target)) { + if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) { $source = fopen($source, 'r'); $target = fopen($target, 'w+'); From ccd8be382ba7c95d79178b67af61eec48d0f8481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=96=D0=B0=D0=BA=D0=BE=D0=B2=20=D0=92=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9?= Date: Tue, 21 Jan 2020 11:10:18 +0500 Subject: [PATCH 12/14] comments for #2764 --- src/Composer/Installer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index a0e365ded..71fa13d9b 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -624,6 +624,7 @@ class Installer $localRepo->write(); } + // see https://github.com/composer/composer/issues/2764 if ($operations) { $vendorDir = $this->config->get('vendor-dir'); if (is_dir($vendorDir)) { From 933fe27cd5ecb9da49e0ccb1401ef6c3802dc493 Mon Sep 17 00:00:00 2001 From: Julian van den Berkmortel Date: Tue, 21 Jan 2020 22:38:35 +0100 Subject: [PATCH 13/14] Add support for "composer show --format=json " #8544 --- src/Composer/Command/ShowCommand.php | 191 +++++++++++++++++++++++++-- 1 file changed, 180 insertions(+), 11 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 78a418a33..cac98e97d 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -238,18 +238,12 @@ EOT return $exitCode; } - $this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null); - $this->printLinks($package, 'requires'); - $this->printLinks($package, 'devRequires', 'requires (dev)'); - if ($package->getSuggests()) { - $io->write("\nsuggests"); - foreach ($package->getSuggests() as $suggested => $reason) { - $io->write($suggested . ' ' . $reason . ''); - } + + if ('json' === $format) { + $this->printPackageInfoAsJson($package, $versions, $installedRepo, $latestPackage ?: null); + } else { + $this->printPackageInfo($package, $versions, $installedRepo, $latestPackage ?: null); } - $this->printLinks($package, 'provides'); - $this->printLinks($package, 'conflicts'); - $this->printLinks($package, 'replaces'); } return $exitCode; @@ -577,12 +571,41 @@ EOT return array($matchedPackage, $versions); } + /** + * Prints package info. + * + * @param CompletePackageInterface $package + * @param array $versions + * @param RepositoryInterface $installedRepo + * @param PackageInterface|null $latestPackage + */ + protected function printPackageInfo(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) + { + $io = $this->getIO(); + + $this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null); + $this->printLinks($package, 'requires'); + $this->printLinks($package, 'devRequires', 'requires (dev)'); + + if ($package->getSuggests()) { + $io->write("\nsuggests"); + foreach ($package->getSuggests() as $suggested => $reason) { + $io->write($suggested . ' ' . $reason . ''); + } + } + + $this->printLinks($package, 'provides'); + $this->printLinks($package, 'conflicts'); + $this->printLinks($package, 'replaces'); + } + /** * Prints package metadata. * * @param CompletePackageInterface $package * @param array $versions * @param RepositoryInterface $installedRepo + * @param PackageInterface|null $latestPackage */ protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) { @@ -724,6 +747,152 @@ EOT } } + /** + * Prints package info in JSON format. + * + * @param CompletePackageInterface $package + * @param array $versions + * @param RepositoryInterface $installedRepo + * @param PackageInterface|null $latestPackage + */ + protected function printPackageInfoAsJson(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) + { + $json = array( + 'name' => $package->getPrettyName(), + 'description' => $package->getDescription(), + 'keywords' => $package->getKeywords() ?: array(), + 'type' => $package->getType(), + 'homepage' => $package->getHomepage(), + 'names' => $package->getNames() + ); + + $this->appendVersions($json, $versions); + $this->appendLicenses($json, $package); + + if ($latestPackage) { + $json['latest'] = $latestPackage->getPrettyVersion(); + } else { + $latestPackage = $package; + } + + if ($package->getSourceType()) { + $json['source'] = array( + 'type' => $package->getSourceType(), + 'url' => $package->getSourceUrl(), + 'reference' => $package->getSourceReference() + ); + } + + if ($package->getDistType()) { + $json['dist'] = array( + 'type' => $package->getDistType(), + 'url' => $package->getDistUrl(), + 'reference' => $package->getDistReference() + ); + } + + if ($installedRepo->hasPackage($package)) { + $json['path'] = realpath($this->getComposer()->getInstallationManager()->getInstallPath($package)); + } + + if ($latestPackage->isAbandoned()) { + $json['replacement'] = $latestPackage->getReplacementPackage(); + } + + if ($package->getSuggests()) { + $json['suggests'] = $package->getSuggests(); + } + + if ($package->getSupport()) { + $json['support'] = $package->getSupport(); + } + + $this->appendAutoload($json, $package); + + if ($package->getIncludePaths()) { + $json['include_path'] = $package->getIncludePaths(); + } + + $this->appendLinks($json, $package); + + $this->getIO()->write(JsonFile::encode($json)); + } + + protected function appendVersions(&$json, array $versions) + { + uasort($versions, 'version_compare'); + $versions = array_keys(array_reverse($versions)); + $json['versions'] = $versions; + } + + protected function appendLicenses(&$json, CompletePackageInterface $package) + { + if ($licenses = $package->getLicense()) { + $spdxLicenses = new SpdxLicenses(); + + $json['licenses'] = array_map(function ($licenseId) use ($spdxLicenses) { + $license = $spdxLicenses->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url + + if (!$license) { + return $licenseId; + } + + return array( + 'name' => $license[0], + 'osi' => $licenseId, + 'url' => $license[2] + ); + }, $licenses); + } + } + + protected function appendAutoload(&$json, CompletePackageInterface $package) + { + if ($package->getAutoload()) { + $autoload = array(); + + foreach ($package->getAutoload() as $type => $autoloads) { + if ($type === 'psr-0' || $type === 'psr-4') { + $psr = array(); + + foreach ($autoloads as $name => $path) { + if (!$path) { + $path = '.'; + } + + $psr[$name ?: '*'] = $path; + } + + $autoload[$type] = $psr; + } elseif ($type === 'classmap') { + $autoload['classmap'] = $autoloads; + } + } + + $json['autoload'] = $autoload; + } + } + + protected function appendLinks(&$json, CompletePackageInterface $package) + { + foreach (array('requires', 'devRequires', 'provides', 'conflicts', 'replaces') as $linkType) { + $this->appendLink($json, $package, $linkType); + } + } + + protected function appendLink(&$json, CompletePackageInterface $package, $linkType) + { + $links = $package->{'get' . ucfirst($linkType)}(); + + if ($links) { + $json[$linkType] = array(); + + foreach ($links as $link) { + $json[$linkType][$link->getTarget()] = $link->getPrettyConstraint(); + } + } + } + /** * Init styles for tree * From 94dce374242222d236eb6b891fafc7d055164690 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 28 Jan 2020 13:52:22 +0100 Subject: [PATCH 14/14] Avoid use of refs and avoid a false path when showing the root package, refs #8545 --- src/Composer/Command/ShowCommand.php | 33 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index cac98e97d..83f58350f 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -766,8 +766,8 @@ EOT 'names' => $package->getNames() ); - $this->appendVersions($json, $versions); - $this->appendLicenses($json, $package); + $json = $this->appendVersions($json, $versions); + $json = $this->appendLicenses($json, $package); if ($latestPackage) { $json['latest'] = $latestPackage->getPrettyVersion(); @@ -793,6 +793,9 @@ EOT if ($installedRepo->hasPackage($package)) { $json['path'] = realpath($this->getComposer()->getInstallationManager()->getInstallPath($package)); + if ($json['path'] === false) { + unset($json['path']); + } } if ($latestPackage->isAbandoned()) { @@ -807,25 +810,27 @@ EOT $json['support'] = $package->getSupport(); } - $this->appendAutoload($json, $package); + $json = $this->appendAutoload($json, $package); if ($package->getIncludePaths()) { $json['include_path'] = $package->getIncludePaths(); } - $this->appendLinks($json, $package); + $json = $this->appendLinks($json, $package); $this->getIO()->write(JsonFile::encode($json)); } - protected function appendVersions(&$json, array $versions) + private function appendVersions($json, array $versions) { uasort($versions, 'version_compare'); $versions = array_keys(array_reverse($versions)); $json['versions'] = $versions; + + return $json; } - protected function appendLicenses(&$json, CompletePackageInterface $package) + private function appendLicenses($json, CompletePackageInterface $package) { if ($licenses = $package->getLicense()) { $spdxLicenses = new SpdxLicenses(); @@ -844,9 +849,11 @@ EOT ); }, $licenses); } + + return $json; } - protected function appendAutoload(&$json, CompletePackageInterface $package) + private function appendAutoload($json, CompletePackageInterface $package) { if ($package->getAutoload()) { $autoload = array(); @@ -871,16 +878,20 @@ EOT $json['autoload'] = $autoload; } + + return $json; } - protected function appendLinks(&$json, CompletePackageInterface $package) + private function appendLinks($json, CompletePackageInterface $package) { foreach (array('requires', 'devRequires', 'provides', 'conflicts', 'replaces') as $linkType) { - $this->appendLink($json, $package, $linkType); + $json = $this->appendLink($json, $package, $linkType); } + + return $json; } - protected function appendLink(&$json, CompletePackageInterface $package, $linkType) + private function appendLink($json, CompletePackageInterface $package, $linkType) { $links = $package->{'get' . ucfirst($linkType)}(); @@ -891,6 +902,8 @@ EOT $json[$linkType][$link->getTarget()] = $link->getPrettyConstraint(); } } + + return $json; } /**