From 783961b16b451a20d5ec8ba05a14e59f78e43ac8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 22 Nov 2021 17:57:37 +0100 Subject: [PATCH] Bump flex to v2, composer >= 2.1, php >= 8.0 --- .github/workflows/ci.yaml | 23 +-- composer.json | 16 +- src/Command/GenerateIdCommand.php | 39 ---- src/Command/RecipesCommand.php | 12 +- src/Command/RequireCommand.php | 5 - src/Command/UnpackCommand.php | 10 +- src/Command/UpdateCommand.php | 5 - src/CurlDownloader.php | 216 ---------------------- src/Downloader.php | 41 ++--- src/Flex.php | 144 +-------------- src/PackageJsonSynchronizer.php | 10 +- src/ParallelDownloader.php | 287 ------------------------------ src/ScriptExecutor.php | 3 +- src/Unpacker.php | 11 +- 14 files changed, 43 insertions(+), 779 deletions(-) delete mode 100644 src/Command/GenerateIdCommand.php delete mode 100644 src/CurlDownloader.php delete mode 100644 src/ParallelDownloader.php diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eecf859d2..50da29b7c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,13 +19,8 @@ jobs: fail-fast: false matrix: include: - - php: '7.1' - - php: '7.2' - - php: '7.3' - - php: '7.4' - php: '8.0' - php: '8.1' - - php: '7.4' mode: low-deps steps: @@ -42,9 +37,8 @@ jobs: - name: "Validate composer.json" run: "composer validate --strict --no-check-lock" - - run: | - composer require --no-update composer/composer:^1.0.2 --ansi - + - name: "Install dependencies" + run: | if [[ "${{ matrix.mode }}" = low-deps ]]; then composer u --prefer-lowest --prefer-stable --ansi else @@ -56,17 +50,6 @@ jobs: - run: vendor/bin/simple-phpunit - - run: | - composer require --no-update composer/composer:^2 --ansi - - if [[ "${{ matrix.mode }}" = low-deps ]]; then - composer u --prefer-lowest --prefer-stable --ansi - else - composer u --ansi - fi - - - run: vendor/bin/simple-phpunit - - - if: matrix.php == '7.1' + - if: matrix.php == '8.0' name: "Lint PHP files" run: find src/ -name '*.php' | xargs -n1 php -l diff --git a/composer.json b/composer.json index 0e1e7c3b2..027531b15 100644 --- a/composer.json +++ b/composer.json @@ -11,15 +11,15 @@ ], "minimum-stability": "dev", "require": { - "php": ">=7.1", - "composer-plugin-api": "^1.0|^2.0" + "php": ">=8.0", + "composer-plugin-api": "^2.1" }, "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "symfony/dotenv": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/phpunit-bridge": "^4.4.12|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" + "composer/composer": "^2.1", + "symfony/dotenv": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" }, "autoload": { "psr-4": { @@ -28,7 +28,7 @@ }, "extra": { "branch-alias": { - "dev-main": "1.17-dev" + "dev-main": "2.0-dev" }, "class": "Symfony\\Flex\\Flex" } diff --git a/src/Command/GenerateIdCommand.php b/src/Command/GenerateIdCommand.php deleted file mode 100644 index f26261587..000000000 --- a/src/Command/GenerateIdCommand.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex\Command; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; - -class GenerateIdCommand extends Command -{ - public function __construct() - { - // No-op to support downgrading to v1.12.x - parent::__construct(); - } - - protected function configure() - { - $this->setName('symfony:generate-id'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $ui = new SymfonyStyle($input, $output); - $ui->error('This command is a noop and should not be used anymore.'); - - return 1; - } -} diff --git a/src/Command/RecipesCommand.php b/src/Command/RecipesCommand.php index 89fab8763..d8e85fd39 100644 --- a/src/Command/RecipesCommand.php +++ b/src/Command/RecipesCommand.php @@ -30,10 +30,10 @@ class RecipesCommand extends BaseCommand /** @var \Symfony\Flex\Flex */ private $flex; - private $symfonyLock; - private $downloader; + private Lock $symfonyLock; + private HttpDownloader $downloader; - public function __construct(/* cannot be type-hinted */ $flex, Lock $symfonyLock, $downloader) + public function __construct(/* cannot be type-hinted */ $flex, Lock $symfonyLock, HttpDownloader $downloader) { $this->flex = $flex; $this->symfonyLock = $symfonyLock; @@ -374,11 +374,7 @@ private function findRecipeCommitDataFromTreeRef(string $package, string $repo, private function requestGitHubApi(string $path) { - if ($this->downloader instanceof HttpDownloader) { - $contents = $this->downloader->get($path)->getBody(); - } else { - $contents = $this->downloader->getContents('api.github.com', $path, false); - } + $contents = $this->downloader->get($path)->getBody(); return json_decode($contents, true); } diff --git a/src/Command/RequireCommand.php b/src/Command/RequireCommand.php index a92eef093..faa76c5b4 100644 --- a/src/Command/RequireCommand.php +++ b/src/Command/RequireCommand.php @@ -15,7 +15,6 @@ use Composer\Factory; use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; -use Composer\Plugin\PluginInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -56,10 +55,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $input->setArgument('packages', $this->resolver->resolve($input->getArgument('packages'), true)); } - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>') && $input->hasOption('no-suggest')) { - $input->setOption('no-suggest', true); - } - $file = Factory::getComposerFile(); $contents = file_get_contents($file); $json = JsonFile::parseJson($contents); diff --git a/src/Command/UnpackCommand.php b/src/Command/UnpackCommand.php index 3f62108e3..9d3a27de8 100644 --- a/src/Command/UnpackCommand.php +++ b/src/Command/UnpackCommand.php @@ -113,15 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ->setUpdateAllowList(['php']) ; - if (method_exists($composer->getEventDispatcher(), 'setRunScripts')) { - $composer->getEventDispatcher()->setRunScripts(false); - } else { - $installer->setRunScripts(false); - } - - if (method_exists($installer, 'setSkipSuggest')) { - $installer->setSkipSuggest(true); - } + $composer->getEventDispatcher()->setRunScripts(false); return $installer->run(); } diff --git a/src/Command/UpdateCommand.php b/src/Command/UpdateCommand.php index 82329f4df..364b772eb 100644 --- a/src/Command/UpdateCommand.php +++ b/src/Command/UpdateCommand.php @@ -12,7 +12,6 @@ namespace Symfony\Flex\Command; use Composer\Command\UpdateCommand as BaseUpdateCommand; -use Composer\Plugin\PluginInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Flex\PackageResolver; @@ -32,10 +31,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { $input->setArgument('packages', $this->resolver->resolve($input->getArgument('packages'))); - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>') && $input->hasOption('no-suggest')) { - $input->setOption('no-suggest', true); - } - return parent::execute($input, $output); } } diff --git a/src/CurlDownloader.php b/src/CurlDownloader.php deleted file mode 100644 index 9da133311..000000000 --- a/src/CurlDownloader.php +++ /dev/null @@ -1,216 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex; - -use Composer\Downloader\TransportException; - -/** - * @author Nicolas Grekas - */ -class CurlDownloader -{ - private $multiHandle; - private $shareHandle; - private $jobs = []; - private $exceptions = []; - - private static $options = [ - 'http' => [ - 'method' => \CURLOPT_CUSTOMREQUEST, - 'content' => \CURLOPT_POSTFIELDS, - ], - 'ssl' => [ - 'cafile' => \CURLOPT_CAINFO, - 'capath' => \CURLOPT_CAPATH, - ], - ]; - - private static $timeInfo = [ - 'total_time' => true, - 'namelookup_time' => true, - 'connect_time' => true, - 'pretransfer_time' => true, - 'starttransfer_time' => true, - 'redirect_time' => true, - ]; - - public function __construct() - { - $this->multiHandle = $mh = curl_multi_init(); - curl_multi_setopt($mh, \CURLMOPT_PIPELINING, /*CURLPIPE_MULTIPLEX*/ 2); - if (\defined('CURLMOPT_MAX_HOST_CONNECTIONS')) { - curl_multi_setopt($mh, \CURLMOPT_MAX_HOST_CONNECTIONS, 8); - } - - $this->shareHandle = $sh = curl_share_init(); - curl_share_setopt($sh, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_COOKIE); - curl_share_setopt($sh, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS); - curl_share_setopt($sh, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION); - } - - public function get($origin, $url, $context, $file) - { - $params = stream_context_get_params($context); - - $ch = curl_init(); - $hd = fopen('php://temp/maxmemory:32768', 'w+b'); - if ($file && !$fd = @fopen($file.'~', 'w+b')) { - $file = null; - } - if (!$file) { - $fd = @fopen('php://temp/maxmemory:524288', 'w+b'); - } - $headers = array_diff($params['options']['http']['header'], ['Connection: close']); - - if (!isset($params['options']['http']['protocol_version'])) { - curl_setopt($ch, \CURLOPT_HTTP_VERSION, \CURL_HTTP_VERSION_1_0); - } else { - $headers[] = 'Connection: keep-alive'; - if (0 === strpos($url, 'https://') && \defined('CURL_VERSION_HTTP2') && \defined('CURL_HTTP_VERSION_2_0') && (\CURL_VERSION_HTTP2 & curl_version()['features'])) { - curl_setopt($ch, \CURLOPT_HTTP_VERSION, \CURL_HTTP_VERSION_2_0); - } - } - - curl_setopt($ch, \CURLOPT_URL, $url); - curl_setopt($ch, \CURLOPT_HTTPHEADER, $headers); - curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, \CURLOPT_DNS_USE_GLOBAL_CACHE, false); - curl_setopt($ch, \CURLOPT_WRITEHEADER, $hd); - curl_setopt($ch, \CURLOPT_FILE, $fd); - curl_setopt($ch, \CURLOPT_SHARE, $this->shareHandle); - - foreach (self::$options as $type => $options) { - foreach ($options as $name => $curlopt) { - if (isset($params['options'][$type][$name])) { - curl_setopt($ch, $curlopt, $params['options'][$type][$name]); - } - } - } - - $progress = array_diff_key(curl_getinfo($ch), self::$timeInfo); - $this->jobs[(int) $ch] = [ - 'progress' => $progress, - 'ch' => $ch, - 'callback' => $params['notification'], - 'file' => $file, - 'fd' => $fd, - ]; - - curl_multi_add_handle($this->multiHandle, $ch); - $params['notification'](\STREAM_NOTIFY_RESOLVE, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false); - $active = true; - - try { - while ($active && isset($this->jobs[(int) $ch])) { - curl_multi_exec($this->multiHandle, $active); - curl_multi_select($this->multiHandle); - - while ($progress = curl_multi_info_read($this->multiHandle)) { - if (!isset($this->jobs[$i = (int) $h = $progress['handle']])) { - continue; - } - $progress = array_diff_key(curl_getinfo($h), self::$timeInfo); - $job = $this->jobs[$i]; - unset($this->jobs[$i]); - curl_multi_remove_handle($this->multiHandle, $h); - try { - $this->onProgress($h, $job['callback'], $progress, $job['progress']); - - if ('' !== curl_error($h)) { - throw new TransportException(curl_error($h)); - } - if ($job['file'] && \CURLE_OK === curl_errno($h) && !isset($this->exceptions[$i])) { - fclose($job['fd']); - rename($job['file'].'~', $job['file']); - } - } catch (TransportException $e) { - $this->exceptions[$i] = $e; - } - } - - foreach ($this->jobs as $i => $h) { - if (!isset($this->jobs[$i])) { - continue; - } - $h = $this->jobs[$i]['ch']; - $progress = array_diff_key(curl_getinfo($h), self::$timeInfo); - - if ($this->jobs[$i]['progress'] !== $progress) { - $previousProgress = $this->jobs[$i]['progress']; - $this->jobs[$i]['progress'] = $progress; - try { - $this->onProgress($h, $this->jobs[$i]['callback'], $progress, $previousProgress); - } catch (TransportException $e) { - unset($this->jobs[$i]); - curl_multi_remove_handle($this->multiHandle, $h); - $this->exceptions[$i] = $e; - } - } - } - } - - if ('' !== curl_error($ch) || \CURLE_OK !== curl_errno($ch)) { - $this->exceptions[(int) $ch] = new TransportException(curl_error($ch), curl_getinfo($ch, \CURLINFO_HTTP_CODE) ?: 0); - } - if (isset($this->exceptions[(int) $ch])) { - throw $this->exceptions[(int) $ch]; - } - } finally { - if ($file && !isset($this->exceptions[(int) $ch])) { - $fd = fopen($file, 'rb'); - } - $progress = array_diff_key(curl_getinfo($ch), self::$timeInfo); - $this->finishProgress($ch, $params['notification'], $progress); - unset($this->jobs[(int) $ch], $this->exceptions[(int) $ch]); - curl_multi_remove_handle($this->multiHandle, $ch); - curl_close($ch); - - rewind($hd); - $headers = explode("\r\n", rtrim(stream_get_contents($hd))); - fclose($hd); - - rewind($fd); - $contents = stream_get_contents($fd); - fclose($fd); - } - - return [$headers, $contents]; - } - - private function onProgress($ch, callable $notify, array $progress, array $previousProgress) - { - if (300 <= $progress['http_code'] && $progress['http_code'] < 400 || 0 > $progress['download_content_length']) { - return; - } - - if (!$previousProgress['http_code'] && $progress['http_code'] && $progress['http_code'] < 200 || 400 <= $progress['http_code']) { - $code = 403 === $progress['http_code'] ? \STREAM_NOTIFY_AUTH_RESULT : \STREAM_NOTIFY_FAILURE; - $notify($code, \STREAM_NOTIFY_SEVERITY_ERR, curl_error($ch), $progress['http_code'], 0, 0, false); - } - - if ($previousProgress['download_content_length'] < $progress['download_content_length']) { - $notify(\STREAM_NOTIFY_FILE_SIZE_IS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, (int) $progress['download_content_length'], false); - } - - if ($previousProgress['size_download'] < $progress['size_download']) { - $notify(\STREAM_NOTIFY_PROGRESS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, (int) $progress['size_download'], (int) $progress['download_content_length'], false); - } - } - - private function finishProgress($ch, callable $notify, array $progress) - { - if ($progress['download_content_length'] < 0) { - $notify(\STREAM_NOTIFY_FILE_SIZE_IS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, (int) $progress['size_download'], false); - $notify(\STREAM_NOTIFY_PROGRESS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, (int) $progress['size_download'], (int) $progress['size_download'], false); - } - } -} diff --git a/src/Downloader.php b/src/Downloader.php index 341a08260..93c51265e 100644 --- a/src/Downloader.php +++ b/src/Downloader.php @@ -41,8 +41,7 @@ class Downloader private $sess; private $cache; - /** @var HttpDownloader|ParallelDownloader */ - private $rfs; + private HttpDownloader $rfs; private $degradedMode = false; private $endpoints; private $index; @@ -50,7 +49,7 @@ class Downloader private $caFile; private $enabled = true; - public function __construct(Composer $composer, IoInterface $io, $rfs) + public function __construct(Composer $composer, IoInterface $io, HttpDownloader $rfs) { if (getenv('SYMFONY_CAFILE')) { $this->caFile = getenv('SYMFONY_CAFILE'); @@ -294,37 +293,19 @@ private function get(array $urls, bool $isRecipe = false, int $try = 3): array $options[$url] = $this->getOptions($headers); } - if ($this->rfs instanceof HttpDownloader) { - $loop = new Loop($this->rfs); - $jobs = []; - foreach ($urls as $url) { - $jobs[] = $this->rfs->add($url, $options[$url])->then(function (ComposerResponse $response) use ($url, &$responses) { - if (200 === $response->getStatusCode()) { - $cacheKey = preg_replace('{[^a-z0-9.]}i', '-', $url); - $responses[$url] = $this->parseJson($response->getBody(), $url, $cacheKey, $response->getHeaders())->getBody(); - } - }, function (\Exception $e) use ($url, &$retries) { - $retries[] = [$url, $e]; - }); - } - $loop->wait($jobs); - } else { - foreach ($urls as $i => $url) { - $urls[$i] = [$url]; - } - $this->rfs->download($urls, function ($url) use ($options, &$responses, &$retries, &$error) { - try { + $loop = new Loop($this->rfs); + $jobs = []; + foreach ($urls as $url) { + $jobs[] = $this->rfs->add($url, $options[$url])->then(function (ComposerResponse $response) use ($url, &$responses) { + if (200 === $response->getStatusCode()) { $cacheKey = preg_replace('{[^a-z0-9.]}i', '-', $url); - $origin = method_exists($this->rfs, 'getOrigin') ? $this->rfs::getOrigin($url) : parse_url($url, \PHP_URL_HOST); - $json = $this->rfs->getContents($origin, $url, false, $options[$url]); - if (200 === $this->rfs->findStatusCode($this->rfs->getLastHeaders())) { - $responses[$url] = $this->parseJson($json, $url, $cacheKey, $this->rfs->getLastHeaders())->getBody(); - } - } catch (\Exception $e) { - $retries[] = [$url, $e]; + $responses[$url] = $this->parseJson($response->getBody(), $url, $cacheKey, $response->getHeaders())->getBody(); } + }, function (\Exception $e) use ($url, &$retries) { + $retries[] = [$url, $e]; }); } + $loop->wait($jobs); if (!$retries) { return $responses; diff --git a/src/Flex.php b/src/Flex.php index c97e8f242..5257e1e7b 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -23,7 +23,6 @@ use Composer\Factory; use Composer\Installer; use Composer\Installer\InstallerEvent; -use Composer\Installer\InstallerEvents; use Composer\Installer\PackageEvent; use Composer\Installer\PackageEvents; use Composer\Installer\SuggestedPackagesReporter; @@ -32,7 +31,6 @@ use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; use Composer\Package\BasePackage; -use Composer\Package\Comparer\Comparer; use Composer\Package\Locker; use Composer\Package\PackageInterface; use Composer\Plugin\PluginEvents; @@ -40,8 +38,6 @@ use Composer\Plugin\PreFileDownloadEvent; use Composer\Plugin\PrePoolCreateEvent; use Composer\Repository\ComposerRepository as BaseComposerRepository; -use Composer\Repository\RepositoryFactory; -use Composer\Repository\RepositoryManager; use Composer\Script\Event; use Composer\Script\ScriptEvents; use Symfony\Component\Console\Input\ArgvInput; @@ -124,40 +120,12 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) $symfonyRequire = preg_replace('/\.x$/', '.x-dev', getenv('SYMFONY_REQUIRE') ?: ($composer->getPackage()->getExtra()['symfony']['require'] ?? '')); - if ($composer2 = version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '<=')) { - $rfs = Factory::createHttpDownloader($this->io, $this->config); + $rfs = Factory::createHttpDownloader($this->io, $this->config); - $this->downloader = $downloader = new Downloader($composer, $io, $rfs); + $this->downloader = $downloader = new Downloader($composer, $io, $rfs); - if ($symfonyRequire) { - $this->filter = new PackageFilter($io, $symfonyRequire, $this->downloader); - } - - $setRepositories = null; - } else { - $rfs = Factory::createRemoteFilesystem($this->io, $this->config); - $this->rfs = $rfs = new ParallelDownloader($this->io, $this->config, $rfs->getOptions(), $rfs->isTlsDisabled()); - - $this->downloader = $downloader = new Downloader($composer, $io, $this->rfs); - - $rootPackage = $composer->getPackage(); - $manager = RepositoryFactory::manager($this->io, $this->config, $composer->getEventDispatcher(), $this->rfs); - $setRepositories = \Closure::bind(function (RepositoryManager $manager) use (&$symfonyRequire, $rootPackage, $downloader) { - $manager->repositoryClasses = $this->repositoryClasses; - $manager->setRepositoryClass('composer', TruncatedComposerRepository::class); - $manager->repositories = $this->repositories; - $i = 0; - foreach (RepositoryFactory::defaultRepos(null, $this->config, $manager) as $repo) { - $manager->repositories[$i++] = $repo; - if ($repo instanceof TruncatedComposerRepository && $symfonyRequire) { - $repo->setSymfonyRequire($symfonyRequire, $rootPackage, $downloader, $this->io); - } - } - $manager->setLocalRepository($this->getLocalRepository()); - }, $composer->getRepositoryManager(), RepositoryManager::class); - - $setRepositories($manager); - $composer->setRepositoryManager($manager); + if ($symfonyRequire) { + $this->filter = new PackageFilter($io, $symfonyRequire, $this->downloader); } $this->configurator = new Configurator($composer, $io, $this->options); @@ -175,21 +143,6 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) $downloader->disable(); } - $populateRepoCacheDir = !$composer2 && __CLASS__ === self::class; - if (!$composer2 && $composer->getPluginManager()) { - foreach ($composer->getPluginManager()->getPlugins() as $plugin) { - if (0 === strpos(\get_class($plugin), 'Hirak\Prestissimo\Plugin')) { - if (method_exists($rfs, 'getRemoteContents')) { - $plugin->disable(); - } else { - $this->cacheDirPopulated = true; - } - $populateRepoCacheDir = false; - break; - } - } - } - $backtrace = $this->configureInstaller(); foreach ($backtrace as $trace) { @@ -208,12 +161,6 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) $resolver = new PackageResolver($this->downloader); - if (version_compare('1.1.0', PluginInterface::PLUGIN_API_VERSION, '>')) { - $note = $app->has('self-update') ? sprintf('`php %s self-update`', $_SERVER['argv'][0]) : 'https://getcomposer.org/'; - $io->writeError('Some Symfony Flex features may not work as expected: your version of Composer is too old'); - $io->writeError(sprintf('Please upgrade using %s', $note)); - } - try { $command = $input->getFirstArgument(); $command = $command ? $app->find($command)->getName() : null; @@ -221,22 +168,13 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) } if ('create-project' === $command) { - // detect Composer >=1.7 (using the Composer::VERSION constant doesn't work with snapshot builds) - if (class_exists(Comparer::class)) { - if ($input->hasOption('remove-vcs')) { - $input->setOption('remove-vcs', true); - } - } else { - $input->setInteractive(false); + if ($input->hasOption('remove-vcs')) { + $input->setOption('remove-vcs', true); } - $populateRepoCacheDir = $populateRepoCacheDir && !$input->hasOption('remove-vcs'); } elseif ('update' === $command) { $this->displayThanksReminder = 1; } elseif ('outdated' === $command) { $symfonyRequire = null; - if ($setRepositories) { - $setRepositories($manager); - } } if (isset(self::$aliasResolveCommands[$command])) { @@ -244,20 +182,6 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) if ($input->hasArgument('packages')) { $input->setArgument('packages', $resolver->resolve($input->getArgument('packages'), self::$aliasResolveCommands[$command])); } - - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>') && $input->hasOption('no-suggest')) { - $input->setOption('no-suggest', true); - } - } - - if (!$composer2) { - if ($input->hasParameterOption('--no-progress', true)) { - $this->progress = false; - } - - if ($input->hasParameterOption('--dry-run', true)) { - $this->dryRun = true; - } } if ($input->hasParameterOption('--prefer-lowest', true)) { @@ -267,19 +191,12 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) BasePackage::$stabilities['dev'] = 1 + BasePackage::STABILITY_STABLE; } - if ($populateRepoCacheDir && isset(self::$repoReadingCommands[$command]) && ('install' !== $command || (file_exists($composerFile = Factory::getComposerFile()) && !file_exists(substr($composerFile, 0, -4).'lock')))) { - $this->populateRepoCacheDir(); - } - $app->add(new Command\RequireCommand($resolver, \Closure::fromCallable([$this, 'updateComposerLock']))); $app->add(new Command\UpdateCommand($resolver)); $app->add(new Command\RemoveCommand($resolver)); $app->add(new Command\UnpackCommand($resolver)); $app->add(new Command\RecipesCommand($this, $this->lock, $rfs)); $app->add(new Command\InstallRecipesCommand($this, $this->options->get('root-dir'))); - if (class_exists(Command\GenerateIdCommand::class)) { - $app->add(new Command\GenerateIdCommand(null)); - } $app->add(new Command\DumpEnvCommand($this->config, $this->options)); break; @@ -550,7 +467,7 @@ public function uninstall(Composer $composer, IOInterface $io) public function enableThanksReminder() { if (1 === $this->displayThanksReminder) { - $this->displayThanksReminder = !class_exists(Thanks::class, false) && version_compare('1.1.0', PluginInterface::PLUGIN_API_VERSION, '<=') ? 2 : 0; + $this->displayThanksReminder = !class_exists(Thanks::class, false) ? 2 : 0; } } @@ -849,40 +766,12 @@ private function shouldRecordOperation(PackageEvent $event): bool return false; } - private function populateRepoCacheDir() - { - $repos = []; - - foreach ($this->composer->getPackage()->getRepositories() as $name => $repo) { - if (!isset($repo['type']) || 'composer' !== $repo['type'] || !empty($repo['force-lazy-providers'])) { - continue; - } - - if (!preg_match('#^http(s\??)?://#', $repo['url'])) { - continue; - } - - $repo = new ComposerRepository($repo, $this->io, $this->config, null, $this->rfs); - - $repos[] = [$repo]; - } - - $this->rfs->download($repos, function ($repo) { - ParallelDownloader::$cacheNext = true; - $repo->getProviderNames(); - }); - } - private function updateComposerLock() { $lock = substr(Factory::getComposerFile(), 0, -4).'lock'; $composerJson = file_get_contents(Factory::getComposerFile()); $lockFile = new JsonFile($lock, null, $this->io); - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>')) { - $locker = new Locker($this->io, $lockFile, $this->composer->getRepositoryManager(), $this->composer->getInstallationManager(), $composerJson); - } else { - $locker = new Locker($this->io, $lockFile, $this->composer->getInstallationManager(), $composerJson); - } + $locker = new Locker($this->io, $lockFile, $this->composer->getInstallationManager(), $composerJson); $lockData = $locker->getLockData(); $lockData['content-hash'] = Locker::getContentHash($composerJson); $lockFile->write($lockData); @@ -940,10 +829,6 @@ private function reinstall(Event $event, bool $update) $installer->setUpdateAllowList(['php']); } - if (method_exists($installer, 'setSkipSuggest')) { - $installer->setSkipSuggest(true); - } - $installer->run(); $this->io->write($this->postInstallOutput); @@ -960,6 +845,7 @@ public static function getSubscribedEvents(): array PackageEvents::POST_PACKAGE_INSTALL => 'record', PackageEvents::POST_PACKAGE_UPDATE => [['record'], ['enableThanksReminder']], PackageEvents::POST_PACKAGE_UNINSTALL => 'record', + PluginEvents::PRE_POOL_CREATE => 'truncatePackages', ScriptEvents::POST_CREATE_PROJECT_CMD => 'configureProject', ScriptEvents::POST_INSTALL_CMD => 'install', ScriptEvents::PRE_UPDATE_CMD => 'configureInstaller', @@ -967,18 +853,6 @@ public static function getSubscribedEvents(): array 'auto-scripts' => 'executeAutoScripts', ]; - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>')) { - $events += [ - InstallerEvents::PRE_DEPENDENCIES_SOLVING => [['populateProvidersCacheDir', \PHP_INT_MAX]], - InstallerEvents::POST_DEPENDENCIES_SOLVING => [['populateFilesCacheDir', \PHP_INT_MAX]], - PackageEvents::PRE_PACKAGE_INSTALL => [['populateFilesCacheDir', ~\PHP_INT_MAX]], - PackageEvents::PRE_PACKAGE_UPDATE => [['populateFilesCacheDir', ~\PHP_INT_MAX]], - PluginEvents::PRE_FILE_DOWNLOAD => 'onFileDownload', - ]; - } else { - $events += [PluginEvents::PRE_POOL_CREATE => 'truncatePackages']; - } - return $events; } } diff --git a/src/PackageJsonSynchronizer.php b/src/PackageJsonSynchronizer.php index 8d1b2c0ee..282aa8fec 100644 --- a/src/PackageJsonSynchronizer.php +++ b/src/PackageJsonSynchronizer.php @@ -228,12 +228,10 @@ private function resolvePackageJson(array $phpPackage): ?JsonFile */ private function compactConstraints(array $constraints): string { - if (method_exists(Intervals::class, 'isSubsetOf')) { - foreach ($constraints as $k1 => $constraint1) { - foreach ($constraints as $k2 => $constraint2) { - if ($k1 !== $k2 && Intervals::isSubsetOf($constraint1, $constraint2)) { - unset($constraints[$k2]); - } + foreach ($constraints as $k1 => $constraint1) { + foreach ($constraints as $k2 => $constraint2) { + if ($k1 !== $k2 && Intervals::isSubsetOf($constraint1, $constraint2)) { + unset($constraints[$k2]); } } } diff --git a/src/ParallelDownloader.php b/src/ParallelDownloader.php deleted file mode 100644 index 3acbc3b9b..000000000 --- a/src/ParallelDownloader.php +++ /dev/null @@ -1,287 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex; - -use Composer\Config; -use Composer\Downloader\TransportException; -use Composer\IO\IOInterface; -use Composer\Util\RemoteFilesystem; - -/** - * Speedup Composer by downloading packages in parallel. - * - * @author Nicolas Grekas - */ -class ParallelDownloader extends RemoteFilesystem -{ - private $io; - private $downloader; - private $quiet = true; - private $progress = true; - private $nextCallback; - private $downloadCount; - private $nextOptions = []; - private $sharedState; - private $fileName; - private $lastHeaders; - - public static $cacheNext = false; - protected static $cache = []; - - public function __construct(IOInterface $io, Config $config, array $options = [], $disableTls = false) - { - $this->io = $io; - if (!method_exists(parent::class, 'getRemoteContents')) { - $this->io->writeError('Composer >=1.7 not found, downloads will happen in sequence', true, IOInterface::DEBUG); - } elseif (!\extension_loaded('curl')) { - $this->io->writeError('ext-curl not found, downloads will happen in sequence', true, IOInterface::DEBUG); - } else { - $this->downloader = new CurlDownloader(); - } - parent::__construct($io, $config, $options, $disableTls); - } - - public function download(array &$nextArgs, callable $nextCallback, bool $quiet = true, bool $progress = true) - { - $previousState = [$this->quiet, $this->progress, $this->downloadCount, $this->nextCallback, $this->sharedState]; - $this->quiet = $quiet; - $this->progress = $progress; - $this->downloadCount = \count($nextArgs); - $this->nextCallback = $nextCallback; - $this->sharedState = (object) [ - 'bytesMaxCount' => 0, - 'bytesMax' => 0, - 'bytesTransferred' => 0, - 'nextArgs' => &$nextArgs, - 'nestingLevel' => 0, - 'maxNestingReached' => false, - 'lastProgress' => 0, - 'lastUpdate' => microtime(true), - ]; - - if (!$this->quiet) { - if (!$this->downloader && method_exists(parent::class, 'getRemoteContents')) { - $this->io->writeError('Enable the "cURL" PHP extension for faster downloads'); - } - - $note = ''; - if ($this->io->isDecorated()) { - $note = '\\' === \DIRECTORY_SEPARATOR ? '' : (false !== stripos(\PHP_OS, 'darwin') ? '🎵' : '🎶'); - $note .= $this->downloader ? ('\\' !== \DIRECTORY_SEPARATOR ? ' 💨' : '') : ''; - } - - $this->io->writeError(''); - $this->io->writeError(sprintf('Prefetching %d packages %s', $this->downloadCount, $note)); - $this->io->writeError(' - Downloading', false); - if ($this->progress) { - $this->io->writeError(' (0%)', false); - } - } - try { - $this->getNext(); - if ($this->quiet) { - // no-op - } elseif ($this->progress) { - $this->io->overwriteError(' (100%)'); - } else { - $this->io->writeError(' (100%)'); - } - } finally { - if (!$this->quiet) { - $this->io->writeError(''); - } - list($this->quiet, $this->progress, $this->downloadCount, $this->nextCallback, $this->sharedState) = $previousState; - } - } - - public function getOptions() - { - $options = array_replace_recursive(parent::getOptions(), $this->nextOptions); - $this->nextOptions = []; - - return $options; - } - - public function setNextOptions(array $options) - { - $this->nextOptions = parent::getOptions() !== $options ? $options : []; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getLastHeaders() - { - return $this->lastHeaders ?? parent::getLastHeaders(); - } - - /** - * {@inheritdoc} - */ - public function copy($originUrl, $fileUrl, $fileName, $progress = true, $options = []) - { - $options = array_replace_recursive($this->nextOptions, $options); - $this->nextOptions = []; - $rfs = clone $this; - $rfs->fileName = $fileName; - $rfs->progress = $this->progress && $progress; - - try { - return $rfs->get($originUrl, $fileUrl, $options, $fileName, $rfs->progress); - } finally { - $rfs->lastHeaders = null; - $this->lastHeaders = $rfs->getLastHeaders(); - } - } - - /** - * {@inheritdoc} - */ - public function getContents($originUrl, $fileUrl, $progress = true, $options = []) - { - return $this->copy($originUrl, $fileUrl, null, $progress, $options); - } - - /** - * @internal - */ - public function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax, $nativeDownload = true) - { - if (!$nativeDownload && \STREAM_NOTIFY_SEVERITY_ERR === $severity) { - throw new TransportException($message, $messageCode); - } - - parent::callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax); - - if (!$state = $this->sharedState) { - return; - } - - if (\STREAM_NOTIFY_FILE_SIZE_IS === $notificationCode) { - ++$state->bytesMaxCount; - $state->bytesMax += $bytesMax; - } - - if (!$bytesMax || \STREAM_NOTIFY_PROGRESS !== $notificationCode) { - if ($state->nextArgs && !$nativeDownload) { - $this->getNext(); - } - - return; - } - - if (0 < $state->bytesMax) { - $progress = $state->bytesMaxCount / $this->downloadCount; - $progress *= 100 * ($state->bytesTransferred + $bytesTransferred) / $state->bytesMax; - } else { - $progress = 0; - } - - if ($bytesTransferred === $bytesMax) { - $state->bytesTransferred += $bytesMax; - } - - if (null !== $state->nextArgs && !$this->quiet && $this->progress && 1 <= $progress - $state->lastProgress) { - $progressTime = microtime(true); - - if (5 <= $progress - $state->lastProgress || 1 <= $progressTime - $state->lastUpdate) { - $state->lastProgress = $progress; - $this->io->overwriteError(sprintf(' (%d%%)', $progress), false); - $state->lastUpdate = microtime(true); - } - } - - if (!$nativeDownload || !$state->nextArgs || $bytesTransferred === $bytesMax || $state->maxNestingReached) { - return; - } - - if (5 < $state->nestingLevel) { - $state->maxNestingReached = true; - } else { - $this->getNext(); - } - } - - /** - * {@inheritdoc} - */ - protected function getRemoteContents($originUrl, $fileUrl, $context, array &$responseHeaders = null, $maxFileSize = null) - { - if (isset(self::$cache[$fileUrl])) { - self::$cacheNext = false; - - $result = self::$cache[$fileUrl]; - - if (3 < \func_num_args()) { - list($responseHeaders, $result) = $result; - } - - return $result; - } - - if (self::$cacheNext) { - self::$cacheNext = false; - - if (3 < \func_num_args()) { - $result = $this->getRemoteContents($originUrl, $fileUrl, $context, $responseHeaders, $maxFileSize); - self::$cache[$fileUrl] = [$responseHeaders, $result]; - } else { - $result = $this->getRemoteContents($originUrl, $fileUrl, $context); - self::$cache[$fileUrl] = $result; - } - - return $result; - } - - if (!$this->downloader || !preg_match('/^https?:/', $fileUrl)) { - return parent::getRemoteContents($originUrl, $fileUrl, $context, $responseHeaders, $maxFileSize); - } - - try { - $result = $this->downloader->get($originUrl, $fileUrl, $context, $this->fileName); - - if (3 < \func_num_args()) { - list($responseHeaders, $result) = $result; - } - - return $result; - } catch (TransportException $e) { - $this->io->writeError('Retrying download: '.$e->getMessage(), true, IOInterface::DEBUG); - - return parent::getRemoteContents($originUrl, $fileUrl, $context, $responseHeaders, $maxFileSize); - } catch (\Throwable $e) { - $responseHeaders = []; - throw $e; - } - } - - private function getNext() - { - $state = $this->sharedState; - ++$state->nestingLevel; - - try { - while ($state->nextArgs && (!$state->maxNestingReached || 1 === $state->nestingLevel)) { - try { - $state->maxNestingReached = false; - ($this->nextCallback)(...array_shift($state->nextArgs)); - } catch (TransportException $e) { - $this->io->writeError('Skipping download: '.$e->getMessage(), true, IOInterface::DEBUG); - } - } - } finally { - --$state->nestingLevel; - } - } -} diff --git a/src/ScriptExecutor.php b/src/ScriptExecutor.php index 967aba97c..69842b3f0 100644 --- a/src/ScriptExecutor.php +++ b/src/ScriptExecutor.php @@ -14,7 +14,6 @@ use Composer\Composer; use Composer\EventDispatcher\ScriptExecutionException; use Composer\IO\IOInterface; -use Composer\Semver\Constraint\EmptyConstraint; use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Output\OutputInterface; @@ -94,7 +93,7 @@ private function expandCmd(string $type, string $cmd) private function expandSymfonyCmd(string $cmd) { $repo = $this->composer->getRepositoryManager()->getLocalRepository(); - if (!$repo->findPackage('symfony/console', class_exists(MatchAllConstraint::class) ? new MatchAllConstraint() : new EmptyConstraint())) { + if (!$repo->findPackage('symfony/console', new MatchAllConstraint())) { $this->io->writeError(sprintf('Skipping "%s" (needs symfony/console to run).', $cmd)); return null; diff --git a/src/Unpacker.php b/src/Unpacker.php index 97482929a..e186e0323 100644 --- a/src/Unpacker.php +++ b/src/Unpacker.php @@ -13,14 +13,12 @@ use Composer\Composer; use Composer\Config\JsonConfigSource; -use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; use Composer\Package\Locker; use Composer\Package\Version\VersionSelector; -use Composer\Plugin\PluginInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositorySet; use Composer\Semver\VersionParser; @@ -105,8 +103,7 @@ public function unpack(Operation $op, Result $result = null, &$links = [], bool if ('*' === $constraint) { if (null === $versionSelector) { - $pool = class_exists(RepositorySet::class) ? RepositorySet::class : Pool::class; - $pool = new $pool($this->composer->getPackage()->getMinimumStability(), $this->composer->getPackage()->getStabilityFlags()); + $pool = new RepositorySet($this->composer->getPackage()->getMinimumStability(), $this->composer->getPackage()->getStabilityFlags()); $pool->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); $versionSelector = new VersionSelector($pool); } @@ -205,11 +202,7 @@ public function updateLock(Result $result, IOInterface $io): void } // force removal of files under vendor/ - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>')) { - $locker = new Locker($io, $lockFile, $this->composer->getRepositoryManager(), $this->composer->getInstallationManager(), $jsonContent); - } else { - $locker = new Locker($io, $lockFile, $this->composer->getInstallationManager(), $jsonContent); - } + $locker = new Locker($io, $lockFile, $this->composer->getInstallationManager(), $jsonContent); $this->composer->setLocker($locker); } }