*/ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInterface { /** @var string */ protected $url; /** @var ?string */ protected $packageName; /** @var bool */ protected $isVerbose; /** @var bool */ protected $isVeryVerbose; /** @var IOInterface */ protected $io; /** @var Config */ protected $config; /** @var VersionParser */ protected $versionParser; /** @var string */ protected $type; /** @var ?LoaderInterface */ protected $loader; /** @var array */ protected $repoConfig; /** @var HttpDownloader */ protected $httpDownloader; /** @var ProcessExecutor */ protected $processExecutor; /** @var bool */ protected $branchErrorOccurred = false; /** @var array> */ private $drivers; /** @var ?VcsDriverInterface */ private $driver; /** @var ?VersionCacheInterface */ private $versionCache; /** @var string[] */ private $emptyReferences = []; /** @var array<'tags'|'branches', array> */ private $versionTransportExceptions = []; /** * @param array{url: string, type?: string}&array $repoConfig * @param array>|null $drivers */ public function __construct( array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, ?EventDispatcher $dispatcher = null, ?ProcessExecutor $process = null, ?array $drivers = null, ?VersionCacheInterface $versionCache = null, ) { parent::__construct(); $this->drivers = $drivers ?: [ 'github' => 'Composer\Repository\Vcs\GitHubDriver', 'gitlab' => 'Composer\Repository\Vcs\GitLabDriver', 'bitbucket' => 'Composer\Repository\Vcs\GitBitbucketDriver', 'git-bitbucket' => 'Composer\Repository\Vcs\GitBitbucketDriver', 'git' => 'Composer\Repository\Vcs\GitDriver', 'hg' => 'Composer\Repository\Vcs\HgDriver', 'perforce' => 'Composer\Repository\Vcs\PerforceDriver', 'fossil' => 'Composer\Repository\Vcs\FossilDriver', // svn must be last because identifying a subversion server for sure is practically impossible 'svn' => 'Composer\Repository\Vcs\SvnDriver', ]; $this->url = $repoConfig['url']; $this->io = $io; $this->type = $repoConfig['type'] ?? 'vcs'; $this->isVerbose = $io->isVerbose(); $this->isVeryVerbose = $io->isVeryVerbose(); $this->config = $config; $this->repoConfig = $repoConfig; $this->versionCache = $versionCache; $this->httpDownloader = $httpDownloader; $this->processExecutor = $process ?? new ProcessExecutor($io); } public function getRepoName() { $driverClass = get_class($this->getDriver()); $driverType = array_search($driverClass, $this->drivers); if (!$driverType) { $driverType = $driverClass; } return 'vcs repo ('.$driverType.' '.Url::sanitize($this->url).')'; } public function getRepoConfig() { return $this->repoConfig; } public function setLoader(LoaderInterface $loader): void { $this->loader = $loader; } public function getDriver(): ?VcsDriverInterface { if ($this->driver) { return $this->driver; } if (isset($this->drivers[$this->type])) { $class = $this->drivers[$this->type]; $this->driver = new $class($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; } foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->config, $this->url)) { $this->driver = new $driver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; } } foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->config, $this->url, true)) { $this->driver = new $driver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; } } return null; } public function hadInvalidBranches(): bool { return $this->branchErrorOccurred; } /** * @return string[] */ public function getEmptyReferences(): array { return $this->emptyReferences; } /** * @return array<'tags'|'branches', array> */ public function getVersionTransportExceptions(): array { return $this->versionTransportExceptions; } protected function initialize() { parent::initialize(); $isVerbose = $this->isVerbose; $isVeryVerbose = $this->isVeryVerbose; $driver = $this->getDriver(); if (!$driver) { throw new \InvalidArgumentException('No driver found to handle VCS repository '.$this->url); } $this->versionParser = new VersionParser; if (!$this->loader) { $this->loader = new ArrayLoader($this->versionParser); } $hasRootIdentifierComposerJson = false; try { $hasRootIdentifierComposerJson = $driver->hasComposerFile($driver->getRootIdentifier()); if ($hasRootIdentifierComposerJson) { $data = $driver->getComposerInformation($driver->getRootIdentifier()); $this->packageName = !empty($data['name']) ? $data['name'] : null; } } catch (\Exception $e) { if ($e instanceof TransportException && $this->shouldRethrowTransportException($e)) { throw $e; } if ($isVeryVerbose) { $this->io->writeError('Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage().''); } } foreach ($driver->getTags() as $tag => $identifier) { $tag = (string) $tag; $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $tag . ')'; if ($isVeryVerbose) { $this->io->writeError($msg); } elseif ($isVerbose) { $this->io->overwriteError($msg, false); } // strip the release- prefix from tags if present $tag = str_replace('release-', '', $tag); $cachedPackage = $this->getCachedPackageVersion($tag, $identifier, $isVerbose, $isVeryVerbose); if ($cachedPackage) { $this->addPackage($cachedPackage); continue; } if ($cachedPackage === false) { $this->emptyReferences[] = $identifier; continue; } if (!$parsedTag = $this->validateTag($tag)) { if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', invalid tag name'); } continue; } try { $data = $driver->getComposerInformation($identifier); if (null === $data) { if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', no composer file'); } $this->emptyReferences[] = $identifier; continue; } // manually versioned package if (isset($data['version'])) { $data['version_normalized'] = $this->versionParser->normalize($data['version']); } else { // auto-versioned package, read value from tag $data['version'] = $tag; $data['version_normalized'] = $parsedTag; } // make sure tag packages have no -dev flag $data['version'] = Preg::replace('{[.-]?dev$}i', '', $data['version']); $data['version_normalized'] = Preg::replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); // make sure tag do not contain the default-branch marker unset($data['default-branch']); // broken package, version doesn't match tag if ($data['version_normalized'] !== $parsedTag) { if ($isVeryVerbose) { if (Preg::isMatch('{(^dev-|[.-]?dev$)}i', $parsedTag)) { $this->io->writeError('Skipped tag '.$tag.', invalid tag name, tags can not use dev prefixes or suffixes'); } else { $this->io->writeError('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); } } continue; } $tagPackageName = $this->packageName ?: ($data['name'] ?? ''); if ($existingPackage = $this->findPackage($tagPackageName, $data['version_normalized'])) { if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$data['version_normalized'].' internally'); } continue; } if ($isVeryVerbose) { $this->io->writeError('Importing tag '.$tag.' ('.$data['version_normalized'].')'); } $this->addPackage($this->loader->load($this->preProcess($driver, $data, $identifier))); } catch (\Exception $e) { if ($e instanceof TransportException) { $this->versionTransportExceptions['tags'][$tag] = $e; if ($e->getCode() === 404) { $this->emptyReferences[] = $identifier; } if ($this->shouldRethrowTransportException($e)) { throw $e; } } if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found (' . $e->getCode() . ' HTTP status code)' : $e->getMessage()).''); } continue; } } if (!$isVeryVerbose) { $this->io->overwriteError('', false); } $branches = $driver->getBranches(); // make sure the root identifier branch gets loaded first if ($hasRootIdentifierComposerJson && isset($branches[$driver->getRootIdentifier()])) { $branches = [$driver->getRootIdentifier() => $branches[$driver->getRootIdentifier()]] + $branches; } foreach ($branches as $branch => $identifier) { $branch = (string) $branch; $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $branch . ')'; if ($isVeryVerbose) { $this->io->writeError($msg); } elseif ($isVerbose) { $this->io->overwriteError($msg, false); } if (!$parsedBranch = $this->validateBranch($branch)) { if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', invalid name'); } continue; } // make sure branch packages have a dev flag if (strpos($parsedBranch, 'dev-') === 0 || VersionParser::DEFAULT_BRANCH_ALIAS === $parsedBranch) { $version = 'dev-' . $branch; } else { $prefix = strpos($branch, 'v') === 0 ? 'v' : ''; $version = $prefix . Preg::replace('{(\.9{7})+}', '.x', $parsedBranch); } $cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose, $driver->getRootIdentifier() === $branch); if ($cachedPackage) { $this->addPackage($cachedPackage); continue; } if ($cachedPackage === false) { $this->emptyReferences[] = $identifier; continue; } try { $data = $driver->getComposerInformation($identifier); if (null === $data) { if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', no composer file'); } $this->emptyReferences[] = $identifier; continue; } // branches are always auto-versioned, read value from branch name $data['version'] = $version; $data['version_normalized'] = $parsedBranch; unset($data['default-branch']); if ($driver->getRootIdentifier() === $branch) { $data['default-branch'] = true; } if ($isVeryVerbose) { $this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')'); } $packageData = $this->preProcess($driver, $data, $identifier); $package = $this->loader->load($packageData); if ($this->loader instanceof ValidatingArrayLoader && \count($this->loader->getWarnings()) > 0) { throw new InvalidPackageException($this->loader->getErrors(), $this->loader->getWarnings(), $packageData); } $this->addPackage($package); } catch (TransportException $e) { $this->versionTransportExceptions['branches'][$branch] = $e; if ($e->getCode() === 404) { $this->emptyReferences[] = $identifier; } if ($this->shouldRethrowTransportException($e)) { throw $e; } if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', no composer file was found (' . $e->getCode() . ' HTTP status code)'); } continue; } catch (\Exception $e) { if (!$isVeryVerbose) { $this->io->writeError(''); } $this->branchErrorOccurred = true; $this->io->writeError('Skipped branch '.$branch.', '.$e->getMessage().''); $this->io->writeError(''); continue; } } $driver->cleanup(); if (!$isVeryVerbose) { $this->io->overwriteError('', false); } if (!$this->getPackages()) { throw new InvalidRepositoryException('No valid composer.json was found in any branch or tag of '.$this->url.', could not load a package from it.'); } } /** * @param array{name?: string, dist?: array{type: string, url: string, reference: string, shasum: string}, source?: array{type: string, url: string, reference: string}} $data * * @return array{name: string|null, dist: array{type: string, url: string, reference: string, shasum: string}|null, source: array{type: string, url: string, reference: string}} */ protected function preProcess(VcsDriverInterface $driver, array $data, string $identifier): array { $dataPackageName = $data['name'] ?? null; $data['name'] = $this->packageName ?: $dataPackageName; if (!isset($data['dist'])) { $data['dist'] = $driver->getDist($identifier); } if (!isset($data['source'])) { $data['source'] = $driver->getSource($identifier); } return $data; } /** * @return string|false */ private function validateBranch(string $branch) { try { $normalizedBranch = $this->versionParser->normalizeBranch($branch); // validate that the branch name has no weird characters conflicting with constraints $this->versionParser->parseConstraints($normalizedBranch); return $normalizedBranch; } catch (\Exception $e) { } return false; } /** * @return string|false */ private function validateTag(string $version) { try { return $this->versionParser->normalize($version); } catch (\Exception $e) { } return false; } /** * @return \Composer\Package\CompletePackage|\Composer\Package\CompleteAliasPackage|null|false null if no cache present, false if the absence of a version was cached */ private function getCachedPackageVersion( string $version, string $identifier, bool $isVerbose, bool $isVeryVerbose, bool $isDefaultBranch = false, ) { if (!$this->versionCache) { return null; } $cachedPackage = $this->versionCache->getVersionPackage($version, $identifier); if ($cachedPackage === false) { if ($isVeryVerbose) { $this->io->writeError('Skipped '.$version.', no composer file (cached from ref '.$identifier.')'); } return false; } if ($cachedPackage) { $msg = 'Found cached composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $version . ')'; if ($isVeryVerbose) { $this->io->writeError($msg); } elseif ($isVerbose) { $this->io->overwriteError($msg, false); } unset($cachedPackage['default-branch']); if ($isDefaultBranch) { $cachedPackage['default-branch'] = true; } if ($existingPackage = $this->findPackage($cachedPackage['name'], new Constraint('=', $cachedPackage['version_normalized']))) { if ($isVeryVerbose) { $this->io->writeError('Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally'); } $cachedPackage = null; } } if ($cachedPackage) { return $this->loader->load($cachedPackage); } return null; } private function shouldRethrowTransportException(TransportException $e): bool { return in_array($e->getCode(), [401, 403, 429], true) || $e->getCode() >= 500; } } __halt_compiler();----SIGNATURE:----1ljZ29zC9aGYSRyk46xj7/fxdoMhedvevqQv0aS7/KAs5VW9mJ5wtPdRUdOv98S8aM0zsu9KVTRj7+gS4wRLP6Cg6aEHhlrBkjzEm6s1wNF+JZKLH1TS1rE3PYJOB0cU3PZRmPLq0yCb1GSkvAJeWpEzOTWE10AqtT4oxqD16vjZWs1pJ5Xhvv9FfjYD6mS7aYoDvywpg7x11+Aabg3+7Vg3edieTShFnxGVwazlRz0tbmrwp8GaJSFYQPWNNIxvOHv6HfOE89W8izK5o11UqLXmFXPeORIj504JsbxsAUGN2RM3gn1/+1Fqd5p8Dc40/3hTXctEjjQT1pMVu8MltlziXP7L5qf3vyd6vcTz5PLry4Iq4weu2EzUnzKmVupzWTGGwdAMf2hdCII6Kp/HesQT0oV3LCqPrQCSm4ZeHM5XGq8aTRI8FLnHfcmTm6aDWr0KNVJnLtYz2B1DYmZsCGT6SCU3Wu4bsWN2hHlrXwnndkXqAm6EPT6TnuN8YvzeVj4wqAstrvH/3UTbdh//JoJ8KLmvlL5dbucFbbNktbYvi3a3yOdUN46uM0Xkf0IUD99xTmO0Nvs3QJNBuJjpoM+QS1pyPrcLSNcZKaQMHC17DyPmpHfOThs/3f6ACPUYAzjBuym8KkQZmWG9FnvLE1H59X1FLJ4wY40HDnuoSYk=----ATTACHMENT:----MjY1NDQ1NjQwNjM0NTg4MSA4OTAwOTk2ODYwMzgyMDE4IDU2MDkxNzA4NDczOTQzNDE=