/
home
/
obinna
/
html
/
restaurants
/
vendor
/
symfony
/
flex
/
src
/
Upload File
HOME
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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 ComposerCache; use Composer\Composer; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\Downloader\TransportException; use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Util\HttpDownloader; use Composer\Util\Loop; /** * @author Fabien Potencier <fabien@symfony.com> */ class Downloader { private static $DEFAULT_ENDPOINT = 'https://flex.symfony.com'; private static $MAX_LENGTH = 1000; private static $versions; private $io; private $sess; private $cache; private $rfs; private $degradedMode = false; private $endpoint; private $caFile; private $enabled = true; public function __construct(Composer $composer, IoInterface $io, $rfs) { if (getenv('SYMFONY_CAFILE')) { $this->caFile = getenv('SYMFONY_CAFILE'); } $this->endpoint = rtrim(getenv('SYMFONY_ENDPOINT') ?: ($composer->getPackage()->getExtra()['symfony']['endpoint'] ?? self::$DEFAULT_ENDPOINT), '/'); $this->io = $io; $config = $composer->getConfig(); $this->rfs = $rfs; $this->cache = new ComposerCache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->endpoint)); $this->sess = bin2hex(random_bytes(16)); } 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; } public function disable() { $this->enabled = false; } public function getVersions() { return self::$versions ?? self::$versions = $this->get('/versions.json')->getBody(); } /** * Downloads recipes. * * @param OperationInterface[] $operations */ public function getRecipes(array $operations): array { if ($this->enabled && self::$DEFAULT_ENDPOINT !== $this->endpoint) { $this->io->writeError('<warning>Using "'.$this->endpoint.'" as the Symfony endpoint</>'); } $paths = []; $chunk = ''; foreach ($operations as $i => $operation) { $o = 'i'; if ($operation instanceof UpdateOperation) { $package = $operation->getTargetPackage(); $o = 'u'; } else { $package = $operation->getPackage(); if ($operation instanceof UninstallOperation) { $o = 'r'; } } $version = $package->getPrettyVersion(); if (0 === strpos($version, 'dev-') && isset($package->getExtra()['branch-alias'])) { $branchAliases = $package->getExtra()['branch-alias']; if ( (isset($branchAliases[$version]) && $alias = $branchAliases[$version]) || (isset($branchAliases['dev-main']) && $alias = $branchAliases['dev-main']) || (isset($branchAliases['dev-trunk']) && $alias = $branchAliases['dev-trunk']) || (isset($branchAliases['dev-develop']) && $alias = $branchAliases['dev-develop']) || (isset($branchAliases['dev-default']) && $alias = $branchAliases['dev-default']) || (isset($branchAliases['dev-latest']) && $alias = $branchAliases['dev-latest']) || (isset($branchAliases['dev-next']) && $alias = $branchAliases['dev-next']) || (isset($branchAliases['dev-current']) && $alias = $branchAliases['dev-current']) || (isset($branchAliases['dev-support']) && $alias = $branchAliases['dev-support']) || (isset($branchAliases['dev-tip']) && $alias = $branchAliases['dev-tip']) || (isset($branchAliases['dev-master']) && $alias = $branchAliases['dev-master']) ) { $version = $alias; } } // FIXME: Multi name with getNames() $name = str_replace('/', ',', $package->getName()); $path = sprintf('%s,%s%s', $name, $o, $version); if ($date = $package->getReleaseDate()) { $path .= ','.$date->format('U'); } if (\strlen($chunk) + \strlen($path) > self::$MAX_LENGTH) { $paths[] = ['/p/'.$chunk]; $chunk = $path; } elseif ($chunk) { $chunk .= ';'.$path; } else { $chunk = $path; } } if ($chunk) { $paths[] = ['/p/'.$chunk]; } if ($this->rfs instanceof HttpDownloader) { $loop = new Loop($this->rfs); $bodies = []; $jobs = []; foreach ($paths as $path) { $jobs[] = $this->rfs->add($this->endpoint.$path[0])->then(static function ($response) use (&$bodies) { $bodies[] = json_decode($response->getBody(), true); }, function (\Exception $e) { $this->io->writeError('<warning>Failed to download recipe: '.$e->getMessage().'</>'); }); } $loop->wait($jobs); } else { $bodies = []; $this->rfs->download($paths, function ($path) use (&$bodies) { if ($body = $this->get($path, [], false)->getBody()) { $bodies[] = $body; } }); } $data = []; foreach ($bodies as $body) { foreach ($body['manifests'] as $name => $manifest) { $data['manifests'][$name] = $manifest; } foreach ($body['locks'] ?? [] as $name => $lock) { $data['locks'][$name] = $lock; } } return $data; } /** * Decodes a JSON HTTP response body. * * @param string $path The path to get on the server * @param array $headers An array of HTTP headers */ public function get(string $path, array $headers = [], $cache = true): Response { if (!$this->enabled && '/versions.json' !== $path) { return new Response([]); } $headers[] = 'Package-Session: '.$this->sess; $url = $this->endpoint.'/'.ltrim($path, '/'); $cacheKey = $cache ? ltrim($path, '/') : ''; if ($cacheKey && $contents = $this->cache->read($cacheKey)) { $cachedResponse = Response::fromJson(json_decode($contents, true)); if ($lastModified = $cachedResponse->getHeader('last-modified')) { $response = $this->fetchFileIfLastModified($url, $cacheKey, $lastModified, $headers); if (304 === $response->getStatusCode()) { $response = new Response($cachedResponse->getBody(), $response->getOrigHeaders(), 304); } return $response; } } return $this->fetchFile($url, $cacheKey, $headers); } private function fetchFile(string $url, string $cacheKey, array $headers): Response { $options = $this->getOptions($headers); $retries = 3; while ($retries--) { try { if ($this->rfs instanceof HttpDownloader) { $response = $this->rfs->get($url, $options); return $this->parseJson($response->getBody(), $url, $cacheKey, $response->getHeaders()); } $json = $this->rfs->getContents($this->endpoint, $url, false, $options); return $this->parseJson($json, $url, $cacheKey, $this->rfs->getLastHeaders()); } catch (\Exception $e) { if ($e instanceof TransportException && 404 === $e->getStatusCode()) { throw $e; } if ($retries) { usleep(100000); continue; } if ($cacheKey && $contents = $this->cache->read($cacheKey)) { $this->switchToDegradedMode($e, $url); return Response::fromJson(JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey)); } throw $e; } } } private function fetchFileIfLastModified(string $url, string $cacheKey, string $lastModifiedTime, array $headers): Response { $headers[] = 'If-Modified-Since: '.$lastModifiedTime; $options = $this->getOptions($headers); $retries = 3; while ($retries--) { try { if ($this->rfs instanceof HttpDownloader) { $response = $this->rfs->get($url, $options); if (304 === $response->getStatusCode()) { return new Response($response->getBody(), $response->getHeaders(), 304); } return $this->parseJson($response->getBody(), $url, $cacheKey, $response->getHeaders()); } $json = $this->rfs->getContents($this->endpoint, $url, false, $options); if (304 === $this->rfs->findStatusCode($this->rfs->getLastHeaders())) { return new Response('', $this->rfs->getLastHeaders(), 304); } return $this->parseJson($json, $url, $cacheKey, $this->rfs->getLastHeaders()); } catch (\Exception $e) { if ($e instanceof TransportException && 404 === $e->getStatusCode()) { throw $e; } if ($retries) { usleep(100000); continue; } $this->switchToDegradedMode($e, $url); return new Response('', [], 304); } } } private function parseJson(string $json, string $url, string $cacheKey, array $lastHeaders): Response { $data = JsonFile::parseJson($json, $url); if (!empty($data['warning'])) { $this->io->writeError('<warning>Warning from '.$url.': '.$data['warning'].'</>'); } if (!empty($data['info'])) { $this->io->writeError('<info>Info from '.$url.': '.$data['info'].'</>'); } $response = new Response($data, $lastHeaders); if ($response->getHeader('last-modified') && $cacheKey) { $this->cache->write($cacheKey, json_encode($response)); } return $response; } private function switchToDegradedMode(\Exception $e, string $url) { if (!$this->degradedMode) { $this->io->writeError('<warning>'.$e->getMessage().'</>'); $this->io->writeError('<warning>'.$url.' could not be fully loaded, package information was loaded from the local cache and may be out of date</>'); } $this->degradedMode = true; } private function getOptions(array $headers): array { $options = ['http' => ['header' => $headers]]; if (null !== $this->caFile) { $options['ssl']['cafile'] = $this->caFile; } return $options; } }