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..fc229bac3 100644 --- a/composer.json +++ b/composer.json @@ -11,25 +11,28 @@ ], "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": { "Symfony\\Flex\\": "src" } }, + "replace": { + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*" + }, "extra": { - "branch-alias": { - "dev-main": "1.17-dev" - }, "class": "Symfony\\Flex\\Flex" } } diff --git a/src/Cache.php b/src/Cache.php deleted file mode 100644 index d1c00248d..000000000 --- a/src/Cache.php +++ /dev/null @@ -1,158 +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\Cache as BaseCache; -use Composer\IO\IOInterface; -use Composer\Package\RootPackageInterface; -use Composer\Semver\Constraint\Constraint; -use Composer\Semver\VersionParser; - -/** - * @author Nicolas Grekas - */ -class Cache extends BaseCache -{ - private $versions; - private $versionParser; - private $symfonyRequire; - private $rootConstraints = []; - private $symfonyConstraints; - private $downloader; - private $io; - - public function setSymfonyRequire(string $symfonyRequire, RootPackageInterface $rootPackage, Downloader $downloader, IOInterface $io = null) - { - $this->versionParser = new VersionParser(); - $this->symfonyRequire = $symfonyRequire; - $this->symfonyConstraints = $this->versionParser->parseConstraints($symfonyRequire); - $this->downloader = $downloader; - $this->io = $io; - - foreach ($rootPackage->getRequires() + $rootPackage->getDevRequires() as $name => $link) { - $this->rootConstraints[$name] = $link->getConstraint(); - } - } - - public function read($file) - { - $content = parent::read($file); - - if (0 === strpos($file, 'provider-symfony$') && \is_array($data = json_decode($content, true))) { - $content = json_encode($this->removeLegacyTags($data)); - } - - return $content; - } - - public function removeLegacyTags(array $data): array - { - if (!$this->symfonyConstraints || !isset($data['packages'])) { - return $data; - } - - foreach ($data['packages'] as $name => $versions) { - if (!isset($this->getVersions()['splits'][$name])) { - continue; - } - - $rootConstraint = $this->rootConstraints[$name] ?? null; - $rootVersions = []; - - foreach ($versions as $version => $composerJson) { - if (null !== $alias = $composerJson['extra']['branch-alias'][$version] ?? null) { - $normalizedVersion = $this->versionParser->normalize($alias); - } elseif (null === $normalizedVersion = $composerJson['version_normalized'] ?? null) { - continue; - } - - $constraint = new Constraint('==', $normalizedVersion); - - if ($rootConstraint && $rootConstraint->matches($constraint)) { - $rootVersions[$version] = $composerJson; - } - - if (!$this->symfonyConstraints->matches($constraint)) { - if (null !== $this->io) { - $this->io->writeError(sprintf('Restricting packages listed in "symfony/symfony" to "%s"', $this->symfonyRequire)); - $this->io = null; - } - unset($versions[$version]); - } - } - - if ($rootConstraint && !array_intersect_key($rootVersions, $versions)) { - $versions = $rootVersions; - } - - $data['packages'][$name] = $versions; - } - - if (null === $symfonySymfony = $data['packages']['symfony/symfony'] ?? null) { - return $data; - } - - foreach ($symfonySymfony as $version => $composerJson) { - if (null !== $alias = $composerJson['extra']['branch-alias'][$version] ?? null) { - $normalizedVersion = $this->versionParser->normalize($alias); - } elseif (null === $normalizedVersion = $composerJson['version_normalized'] ?? null) { - continue; - } - - if (!$this->symfonyConstraints->matches(new Constraint('==', $normalizedVersion))) { - unset($symfonySymfony[$version]); - } - } - - if ($symfonySymfony) { - $data['packages']['symfony/symfony'] = $symfonySymfony; - } - - return $data; - } - - private function getVersions(): array - { - if (null !== $this->versions) { - return $this->versions; - } - - $versions = $this->downloader->getVersions(); - $this->downloader = null; - $okVersions = []; - - foreach ($versions['splits'] as $name => $vers) { - foreach ($vers as $i => $v) { - if (!isset($okVersions[$v])) { - $okVersions[$v] = false; - - for ($j = 0; $j < 60; ++$j) { - if ($this->symfonyConstraints->matches(new Constraint('==', $v.'.'.$j.'.0'))) { - $okVersions[$v] = true; - break; - } - } - } - - if (!$okVersions[$v]) { - unset($vers[$i]); - } - } - - if (!$vers || $vers === $versions['splits'][$name]) { - unset($versions['splits'][$name]); - } - } - - return $this->versions = $versions; - } -} 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 1d65648c7..2820d7730 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; @@ -102,7 +102,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $write = []; $hasOutdatedRecipes = false; - /** @var Recipe $recipe */ foreach ($recipes as $name => $recipe) { $lockRef = $this->symfonyLock->get($name)['recipe']['ref'] ?? null; @@ -375,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 deleted file mode 100644 index 3f62108e3..000000000 --- a/src/Command/UnpackCommand.php +++ /dev/null @@ -1,128 +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 Composer\Command\BaseCommand; -use Composer\Factory; -use Composer\Installer; -use Composer\Package\Version\VersionParser; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Flex\PackageResolver; -use Symfony\Flex\Unpack\Operation; -use Symfony\Flex\Unpacker; - -/** - * @deprecated since Flex 1.4 - */ -class UnpackCommand extends BaseCommand -{ - private $resolver; - - public function __construct(PackageResolver $resolver) - { - $this->resolver = $resolver; - - parent::__construct(); - } - - protected function configure() - { - $this->setName('symfony:unpack') - ->setAliases(['unpack']) - ->setDescription('[DEPRECATED] Unpacks a Symfony pack.') - ->setDefinition([ - new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Installed packages to unpack.'), - new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages'), - ]) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $composer = $this->getComposer(); - $packages = $this->resolver->resolve($input->getArgument('packages'), true); - $io = $this->getIO(); - $lockData = $composer->getLocker()->getLockData(); - $installedRepo = $composer->getRepositoryManager()->getLocalRepository(); - $versionParser = new VersionParser(); - $dryRun = $input->hasOption('dry-run') && $input->getOption('dry-run'); - - $io->writeError('Command "symfony:unpack" is deprecated, Symfony packs are always unpacked now.'); - - $op = new Operation(true, $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages')); - foreach ($versionParser->parseNameVersionPairs($packages) as $package) { - if (null === $pkg = $installedRepo->findPackage($package['name'], '*')) { - $io->writeError(sprintf('Package %s is not installed', $package['name'])); - - return 1; - } - - $dev = false; - foreach ($lockData['packages-dev'] as $p) { - if ($package['name'] === $p['name']) { - $dev = true; - - break; - } - } - - $op->addPackage($pkg->getName(), $pkg->getVersion(), $dev); - } - - $unpacker = new Unpacker($composer, $this->resolver, $dryRun); - $result = $unpacker->unpack($op); - - // remove the packages themselves - if (!$result->getUnpacked()) { - $io->writeError('Nothing to unpack'); - - return 0; - } - - $io->writeError('Unpacking Symfony packs'); - foreach ($result->getUnpacked() as $pkg) { - $io->writeError(sprintf(' - Unpacked %s', $pkg->getName())); - } - - $unpacker->updateLock($result, $io); - - if ($input->hasOption('no-install') && $input->getOption('no-install')) { - return 0; - } - - $composer = Factory::create($io, null, true); - $installer = Installer::create($io, $composer); - $installer - ->setDryRun($dryRun) - ->setDevMode(true) - ->setDumpAutoloader(false) - ->setIgnorePlatformRequirements(true) - ->setUpdate(true) - ->setUpdateAllowList(['php']) - ; - - if (method_exists($composer->getEventDispatcher(), 'setRunScripts')) { - $composer->getEventDispatcher()->setRunScripts(false); - } else { - $installer->setRunScripts(false); - } - - if (method_exists($installer, 'setSkipSuggest')) { - $installer->setSkipSuggest(true); - } - - 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/ComposerRepository.php b/src/ComposerRepository.php deleted file mode 100644 index 26bbb16be..000000000 --- a/src/ComposerRepository.php +++ /dev/null @@ -1,58 +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\Repository\ComposerRepository as BaseComposerRepository; - -/** - * @author Nicolas Grekas - */ -class ComposerRepository extends BaseComposerRepository -{ - private $providerFiles; - - protected function loadProviderListings($data) - { - if (null !== $this->providerFiles) { - parent::loadProviderListings($data); - - return; - } - - $data = [$data]; - - while ($data) { - $this->providerFiles = []; - foreach ($data as $data) { - $this->loadProviderListings($data); - } - - $loadingFiles = $this->providerFiles; - $this->providerFiles = null; - $data = []; - $this->rfs->download($loadingFiles, function (...$args) use (&$data) { - $data[] = $this->fetchFile(...$args); - }); - } - } - - protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $storeLastModifiedTime = false) - { - if (null !== $this->providerFiles) { - $this->providerFiles[] = [$filename, $cacheKey, $sha256, $storeLastModifiedTime]; - - return []; - } - - return parent::fetchFile($filename, $cacheKey, $sha256, $storeLastModifiedTime); - } -} 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 919d68bb2..a6aeb0167 100644 --- a/src/Downloader.php +++ b/src/Downloader.php @@ -11,7 +11,7 @@ namespace Symfony\Flex; -use Composer\Cache as ComposerCache; +use Composer\Cache; use Composer\Composer; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; @@ -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'); @@ -89,7 +88,7 @@ public function __construct(Composer $composer, IoInterface $io, $rfs) $this->io = $io; $config = $composer->getConfig(); $this->rfs = $rfs; - $this->cache = new ComposerCache($io, $config->get('cache-repo-dir').'/flex'); + $this->cache = new Cache($io, $config->get('cache-repo-dir').'/flex'); $this->sess = bin2hex(random_bytes(16)); } @@ -98,11 +97,6 @@ public function getSessionId(): string return $this->sess; } - public function setFlexId(string $id = null) - { - // No-op to support downgrading to v1.12.x - } - public function isEnabled() { return $this->enabled; @@ -294,37 +288,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 = self::generateCacheKey($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 = self::generateCacheKey($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 0d6913916..7789463c8 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -17,13 +17,9 @@ use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; -use Composer\DependencyResolver\Pool; -use Composer\Downloader\FileDownloader; use Composer\EventDispatcher\EventSubscriberInterface; 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,16 +28,10 @@ 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; use Composer\Plugin\PluginInterface; -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; @@ -78,19 +68,9 @@ class Flex implements PluginInterface, EventSubscriberInterface private $postInstallOutput = ['']; private $operations = []; private $lock; - private $cacheDirPopulated = false; private $displayThanksReminder = 0; - private $rfs; - private $progress = true; private $dryRun = false; private static $activated = true; - private static $repoReadingCommands = [ - 'create-project' => true, - 'outdated' => true, - 'require' => true, - 'update' => true, - 'install' => true, - ]; private static $aliasResolveCommands = [ 'require' => true, 'update' => false, @@ -123,40 +103,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); - - if ($symfonyRequire) { - $this->filter = new PackageFilter($io, $symfonyRequire, $this->downloader); - } + $this->downloader = $downloader = new Downloader($composer, $io, $rfs); - $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); @@ -174,21 +126,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) { @@ -207,12 +144,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; @@ -220,22 +151,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])) { @@ -243,20 +165,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)) { @@ -266,19 +174,11 @@ 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; @@ -549,7 +449,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; } } @@ -570,124 +470,6 @@ public function executeAutoScripts(Event $event) $this->postInstallOutput = []; } - public function populateProvidersCacheDir(InstallerEvent $event) - { - $listed = []; - $packages = []; - $pool = $event->getPool(); - $pool = \Closure::bind(function () { - foreach ($this->providerRepos as $k => $repo) { - $this->providerRepos[$k] = new class($repo) extends BaseComposerRepository { - private $repo; - - public function __construct($repo) - { - $this->repo = $repo; - } - - public function whatProvides(Pool $pool, $name, $bypassFilters = false) - { - $packages = []; - foreach ($this->repo->whatProvides($pool, $name, $bypassFilters) as $k => $p) { - $packages[$k] = clone $p; - } - - return $packages; - } - }; - } - - return $this; - }, clone $pool, $pool)(); - - foreach ($event->getRequest()->getJobs() as $job) { - if ('install' !== $job['cmd'] || false === strpos($job['packageName'], '/')) { - continue; - } - - $listed[$job['packageName']] = true; - $packages[] = [$job['packageName'], $job['constraint']]; - } - - $loadExtraRepos = !(new \ReflectionMethod(Pool::class, 'match'))->isPublic(); // Detect Composer < 1.7.3 - $this->rfs->download($packages, function ($packageName, $constraint) use (&$listed, &$packages, $pool, $loadExtraRepos) { - foreach ($pool->whatProvides($packageName, $constraint, true) as $package) { - $links = $loadExtraRepos ? array_merge($package->getRequires(), $package->getConflicts(), $package->getReplaces()) : $package->getRequires(); - foreach ($links as $link) { - if (isset($listed[$link->getTarget()]) || false === strpos($link->getTarget(), '/')) { - continue; - } - $listed[$link->getTarget()] = true; - $packages[] = [$link->getTarget(), $link->getConstraint()]; - } - } - }); - } - - public function populateFilesCacheDir(InstallerEvent $event) - { - if ($this->cacheDirPopulated || $this->dryRun) { - return; - } - $this->cacheDirPopulated = true; - - $downloads = []; - $cacheDir = rtrim($this->config->get('cache-files-dir'), '\/').\DIRECTORY_SEPARATOR; - $getCacheKey = function (PackageInterface $package, $processedUrl) { - return $this->getCacheKey($package, $processedUrl); - }; - $getCacheKey = \Closure::bind($getCacheKey, new FileDownloader($this->io, $this->config), FileDownloader::class); - - foreach ($event->getOperations() as $op) { - if ('install' === $op->getJobType()) { - $package = $op->getPackage(); - } elseif ('update' === $op->getJobType()) { - $package = $op->getTargetPackage(); - } else { - continue; - } - - if (!$fileUrl = $package->getDistUrl()) { - continue; - } - - if ($package->getDistMirrors()) { - $fileUrl = current($package->getDistUrls()); - } - - if (!preg_match('/^https?:/', $fileUrl) || !$originUrl = parse_url($fileUrl, \PHP_URL_HOST)) { - continue; - } - - if (file_exists($file = $cacheDir.$getCacheKey($package, $fileUrl))) { - continue; - } - - @mkdir(\dirname($file), 0775, true); - - if (!is_dir(\dirname($file))) { - continue; - } - - if (preg_match('#^https://github\.com/#', $package->getSourceUrl()) && preg_match('#^https://api\.github\.com/repos(/[^/]++/[^/]++/)zipball(.++)$#', $fileUrl, $m)) { - $fileUrl = sprintf('https://codeload.github.com%slegacy.zip%s', $m[1], $m[2]); - } - - $downloads[] = [$originUrl, $fileUrl, [], $file, false]; - } - - if (1 < \count($downloads)) { - $this->rfs->download($downloads, [$this->rfs, 'get'], false, $this->progress); - } - } - - public function onFileDownload(PreFileDownloadEvent $event) - { - if ($event->getRemoteFilesystem() !== $this->rfs) { - $event->setRemoteFilesystem($this->rfs->setNextOptions($event->getRemoteFilesystem()->getOptions())); - } - } - /** * @return Recipe[] */ @@ -838,40 +620,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); @@ -929,10 +683,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); @@ -949,6 +699,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', @@ -956,18 +707,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/TruncatedComposerRepository.php b/src/TruncatedComposerRepository.php deleted file mode 100644 index 82485aeaf..000000000 --- a/src/TruncatedComposerRepository.php +++ /dev/null @@ -1,44 +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\EventDispatcher\EventDispatcher; -use Composer\IO\IOInterface; -use Composer\Package\RootPackageInterface; -use Composer\Repository\ComposerRepository as BaseComposerRepository; -use Composer\Util\RemoteFilesystem; - -/** - * @author Nicolas Grekas - */ -class TruncatedComposerRepository extends BaseComposerRepository -{ - public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) - { - parent::__construct($repoConfig, $io, $config, $eventDispatcher, $rfs); - - $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$'); - } - - public function setSymfonyRequire(string $symfonyRequire, RootPackageInterface $rootPackage, Downloader $downloader, IOInterface $io) - { - $this->cache->setSymfonyRequire($symfonyRequire, $rootPackage, $downloader, $io); - } - - protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $storeLastModifiedTime = false) - { - $data = parent::fetchFile($filename, $cacheKey, $sha256, $storeLastModifiedTime); - - return \is_array($data) ? $this->cache->removeLegacyTags($data) : $data; - } -} 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); } } diff --git a/tests/CacheTest.php b/tests/CacheTest.php deleted file mode 100644 index 7c25a9ba8..000000000 --- a/tests/CacheTest.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex\Tests; - -use Composer\Package\Link; -use Composer\Package\RootPackage; -use Composer\Semver\Constraint\Constraint; -use PHPUnit\Framework\TestCase; -use Symfony\Flex\Cache; - -class CacheTest extends TestCase -{ - /** - * @dataProvider provideRemoveLegacyTags - */ - public function testRemoveLegacyTags(array $expected, array $packages, string $symfonyRequire, array $versions) - { - $downloader = $this->getMockBuilder('Symfony\Flex\Downloader')->disableOriginalConstructor()->getMock(); - $downloader->expects($this->once()) - ->method('getVersions') - ->willReturn($versions); - - $rootPackage = new RootPackage('test/test', '1.0.0.0', '1.0'); - $rootPackage->setRequires([ - 'symfony/bar' => new Link('__root__', 'symfony/bar', new Constraint('>=', '3.0.0.0')), - ]); - - $cache = (new \ReflectionClass(Cache::class))->newInstanceWithoutConstructor(); - $cache->setSymfonyRequire($symfonyRequire, $rootPackage, $downloader); - - $this->assertSame(['packages' => $expected], $cache->removeLegacyTags(['packages' => $packages])); - } - - public function provideRemoveLegacyTags() - { - yield 'no-symfony/symfony' => [[123], [123], '~1', ['splits' => []]]; - - $branchAlias = function ($versionAlias) { - return [ - 'extra' => [ - 'branch-alias' => [ - 'dev-main' => $versionAlias.'-dev', - ], - ], - ]; - }; - - $packages = [ - 'foo/unrelated' => [ - '1.0.0' => [], - ], - 'symfony/symfony' => [ - '3.3.0' => ['version_normalized' => '3.3.0.0'], - '3.4.0' => ['version_normalized' => '3.4.0.0'], - 'dev-main' => $branchAlias('3.5'), - ], - 'symfony/foo' => [ - '3.3.0' => ['version_normalized' => '3.3.0.0'], - '3.4.0' => ['version_normalized' => '3.4.0.0'], - 'dev-main' => $branchAlias('3.5'), - ], - ]; - - yield 'empty-intersection-ignores-2' => [$packages, $packages, '~2.0', ['splits' => [ - 'symfony/foo' => ['3.3', '3.4', '3.5'], - ]]]; - yield 'empty-intersection-ignores-4' => [$packages, $packages, '~4.0', ['splits' => [ - 'symfony/foo' => ['3.3', '3.4', '3.5'], - ]]]; - - $expected = $packages; - unset($expected['symfony/symfony']['3.3.0']); - unset($expected['symfony/foo']['3.3.0']); - - yield 'non-empty-intersection-filters' => [$expected, $packages, '~3.4', ['splits' => [ - 'symfony/foo' => ['3.3', '3.4', '3.5'], - ]]]; - - unset($expected['symfony/symfony']['3.4.0']); - unset($expected['symfony/foo']['3.4.0']); - - yield 'main-only' => [$expected, $packages, '~3.5', ['splits' => [ - 'symfony/foo' => ['3.4', '3.5'], - ]]]; - - $packages = [ - 'symfony/symfony' => [ - '2.8.0' => ['version_normalized' => '2.8.0.0'], - ], - 'symfony/legacy' => [ - '2.8.0' => ['version_normalized' => '2.8.0.0'], - 'dev-main' => $branchAlias('2.8'), - ], - ]; - - yield 'legacy-are-not-filtered' => [$packages, $packages, '~3.0', ['splits' => [ - 'symfony/legacy' => ['2.8'], - 'symfony/foo' => ['2.8'], - ]]]; - - $packages = [ - 'symfony/symfony' => [ - '2.8.0' => ['version_normalized' => '2.8.0.0'], - 'dev-main' => $branchAlias('3.0'), - ], - 'symfony/foo' => [ - '2.8.0' => ['version_normalized' => '2.8.0.0'], - 'dev-main' => $branchAlias('3.0'), - ], - 'symfony/new' => [ - 'dev-main' => $branchAlias('3.0'), - ], - ]; - - $expected = $packages; - unset($expected['symfony/symfony']['dev-main']); - unset($expected['symfony/foo']['dev-main']); - - yield 'main-is-filtered-only-when-in-range' => [$expected, $packages, '~2.8', ['splits' => [ - 'symfony/foo' => ['2.8', '3.0'], - 'symfony/new' => ['3.0'], - ]]]; - - $packages = [ - 'symfony/symfony' => [ - '3.0.0' => ['version_normalized' => '3.0.0.0'], - ], - 'symfony/bar' => [ - '3.0.0' => ['version_normalized' => '3.0.0.0'], - ], - ]; - - yield 'root-constraints-are-preserved' => [$packages, $packages, '~2.8', ['splits' => [ - 'symfony/bar' => ['2.8', '3.0'], - ]]]; - } -}