Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. RemoteFilesystem.php

function RemoteFilesystem::get

Get file content or copy action.

Parameters

string $originUrl The origin URL:

non-empty-string $fileUrl The file URL:

mixed[] $additionalOptions context options:

string $fileName the local filename:

bool $progress Display the progression:

Return value

bool|string

Throws

TransportException|\Exception

TransportException When the file could not be downloaded

3 calls to RemoteFilesystem::get()
RemoteFilesystem::copy in vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php
Copy the remote file in local.
RemoteFilesystem::getContents in vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php
Get the content.
RemoteFilesystem::handleRedirect in vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php

File

vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php, line 216

Class

RemoteFilesystem
@internal @author François Pluchino <francois.pluchino@opendisplay.com> @author Jordi Boggiano <j.boggiano@seld.be> @author Nils Adermann <naderman@naderman.de>

Namespace

Composer\Util

Code

protected function get(string $originUrl, string $fileUrl, array $additionalOptions = [], ?string $fileName = null, bool $progress = true) {
    $this->scheme = parse_url(strtr($fileUrl, '\\', '/'), PHP_URL_SCHEME);
    $this->bytesMax = 0;
    $this->originUrl = $originUrl;
    $this->fileUrl = $fileUrl;
    $this->fileName = $fileName;
    $this->progress = $progress;
    $this->lastProgress = null;
    $retryAuthFailure = true;
    $this->lastHeaders = [];
    $this->redirects = 1;
    // The first request counts.
    $tempAdditionalOptions = $additionalOptions;
    if (isset($tempAdditionalOptions['retry-auth-failure'])) {
        $retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure'];
        unset($tempAdditionalOptions['retry-auth-failure']);
    }
    $isRedirect = false;
    if (isset($tempAdditionalOptions['redirects'])) {
        $this->redirects = $tempAdditionalOptions['redirects'];
        $isRedirect = true;
        unset($tempAdditionalOptions['redirects']);
    }
    $options = $this->getOptionsForUrl($originUrl, $tempAdditionalOptions);
    unset($tempAdditionalOptions);
    $origFileUrl = $fileUrl;
    if (isset($options['prevent_ip_access_callable'])) {
        throw new \RuntimeException("RemoteFilesystem doesn't support the 'prevent_ip_access_callable' config.");
    }
    if (isset($options['gitlab-token'])) {
        $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['gitlab-token'];
        unset($options['gitlab-token']);
    }
    if (isset($options['http'])) {
        $options['http']['ignore_errors'] = true;
    }
    if ($this->degradedMode && strpos($fileUrl, 'http://repo.packagist.org/') === 0) {
        // access packagist using the resolved IPv4 instead of the hostname to force IPv4 protocol
        $fileUrl = 'http://' . gethostbyname('repo.packagist.org') . substr($fileUrl, 20);
        $degradedPackagist = true;
    }
    $maxFileSize = null;
    if (isset($options['max_file_size'])) {
        $maxFileSize = $options['max_file_size'];
        unset($options['max_file_size']);
    }
    $ctx = StreamContextFactory::getContext($fileUrl, $options, [
        'notification' => [
            $this,
            'callbackGet',
        ],
    ]);
    $proxy = ProxyManager::getInstance()->getProxyForRequest($fileUrl);
    $usingProxy = $proxy->getStatus(' using proxy (%s)');
    $this->io
        ->writeError((strpos($origFileUrl, 'http') === 0 ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
    unset($origFileUrl, $proxy, $usingProxy);
    // Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
    if ((!Preg::isMatch('{^http://(repo\\.)?packagist\\.org/p/}', $fileUrl) || false === strpos($fileUrl, '$') && false === strpos($fileUrl, '%24')) && empty($degradedPackagist)) {
        $this->config
            ->prohibitUrlByConfig($fileUrl, $this->io);
    }
    if ($this->progress && !$isRedirect) {
        $this->io
            ->writeError("Downloading (<comment>connecting...</comment>)", false);
    }
    $errorMessage = '';
    $errorCode = 0;
    $result = false;
    set_error_handler(static function ($code, $msg) use (&$errorMessage) : bool {
        if ($errorMessage) {
            $errorMessage .= "\n";
        }
        $errorMessage .= Preg::replace('{^file_get_contents\\(.*?\\): }', '', $msg);
        return true;
    });
    $http_response_header = [];
    try {
        $result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header, $maxFileSize);
        if (!empty($http_response_header[0])) {
            $statusCode = self::findStatusCode($http_response_header);
            if ($statusCode >= 400 && Response::findHeaderValue($http_response_header, 'content-type') === 'application/json') {
                HttpDownloader::outputWarnings($this->io, $originUrl, json_decode($result, true));
            }
            if (in_array($statusCode, [
                401,
                403,
            ]) && $retryAuthFailure) {
                $this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), $http_response_header);
            }
        }
        $contentLength = !empty($http_response_header[0]) ? Response::findHeaderValue($http_response_header, 'content-length') : null;
        if ($contentLength && Platform::strlen($result) < $contentLength) {
            // alas, this is not possible via the stream callback because STREAM_NOTIFY_COMPLETED is documented, but not implemented anywhere in PHP
            $e = new TransportException('Content-Length mismatch, received ' . Platform::strlen($result) . ' bytes out of the expected ' . $contentLength);
            $e->setHeaders($http_response_header);
            $e->setStatusCode(self::findStatusCode($http_response_header));
            try {
                $e->setResponse($this->decodeResult($result, $http_response_header));
            } catch (\Exception $discarded) {
                $e->setResponse($this->normalizeResult($result));
            }
            $this->io
                ->writeError('Content-Length mismatch, received ' . Platform::strlen($result) . ' out of ' . $contentLength . ' bytes: (' . base64_encode($result) . ')', true, IOInterface::DEBUG);
            throw $e;
        }
    } catch (\Exception $e) {
        if ($e instanceof TransportException && !empty($http_response_header[0])) {
            $e->setHeaders($http_response_header);
            $e->setStatusCode(self::findStatusCode($http_response_header));
        }
        if ($e instanceof TransportException && $result !== false) {
            $e->setResponse($this->decodeResult($result, $http_response_header));
        }
        $result = false;
    }
    if ($errorMessage && !filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) {
        $errorMessage = 'allow_url_fopen must be enabled in php.ini (' . $errorMessage . ')';
    }
    restore_error_handler();
    if (isset($e) && !$this->retry) {
        if (!$this->degradedMode && false !== strpos($e->getMessage(), 'Operation timed out')) {
            $this->degradedMode = true;
            $this->io
                ->writeError('');
            $this->io
                ->writeError([
                '<error>' . $e->getMessage() . '</error>',
                '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>',
            ]);
            return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
        }
        throw $e;
    }
    $statusCode = null;
    $contentType = null;
    $locationHeader = null;
    if (!empty($http_response_header[0])) {
        $statusCode = self::findStatusCode($http_response_header);
        $contentType = Response::findHeaderValue($http_response_header, 'content-type');
        $locationHeader = Response::findHeaderValue($http_response_header, 'location');
    }
    // check for bitbucket login page asking to authenticate
    if ($originUrl === 'bitbucket.org' && !$this->authHelper
        ->isPublicBitBucketDownload($fileUrl) && substr($fileUrl, -4) === '.zip' && (!$locationHeader || substr(parse_url($locationHeader, PHP_URL_PATH), -4) !== '.zip') && $contentType && Preg::isMatch('{^text/html\\b}i', $contentType)) {
        $result = false;
        if ($retryAuthFailure) {
            $this->promptAuthAndRetry(401);
        }
    }
    // check for gitlab 404 when downloading archives
    if ($statusCode === 404 && in_array($originUrl, $this->config
        ->get('gitlab-domains'), true) && false !== strpos($fileUrl, 'archive.zip')) {
        $result = false;
        if ($retryAuthFailure) {
            $this->promptAuthAndRetry(401);
        }
    }
    // handle 3xx redirects, 304 Not Modified is excluded
    $hasFollowedRedirect = false;
    if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $this->redirects < $this->maxRedirects) {
        $hasFollowedRedirect = true;
        $result = $this->handleRedirect($http_response_header, $additionalOptions, $result);
    }
    // fail 4xx and 5xx responses and capture the response
    if ($statusCode && $statusCode >= 400 && $statusCode <= 599) {
        if (!$this->retry) {
            if ($this->progress && !$isRedirect) {
                $this->io
                    ->overwriteError("Downloading (<error>failed</error>)", false);
            }
            $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded (' . $http_response_header[0] . ')', $statusCode);
            $e->setHeaders($http_response_header);
            $e->setResponse($this->decodeResult($result, $http_response_header));
            $e->setStatusCode($statusCode);
            throw $e;
        }
        $result = false;
    }
    if ($this->progress && !$this->retry && !$isRedirect) {
        $this->io
            ->overwriteError("Downloading (" . ($result === false ? '<error>failed</error>' : '<comment>100%</comment>') . ")", false);
    }
    // decode gzip
    if ($result && extension_loaded('zlib') && strpos($fileUrl, 'http') === 0 && !$hasFollowedRedirect) {
        try {
            $result = $this->decodeResult($result, $http_response_header);
        } catch (\Exception $e) {
            if ($this->degradedMode) {
                throw $e;
            }
            $this->degradedMode = true;
            $this->io
                ->writeError([
                '',
                '<error>Failed to decode response: ' . $e->getMessage() . '</error>',
                '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>',
            ]);
            return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
        }
    }
    // handle copy command if download was successful
    if (false !== $result && null !== $fileName && !$isRedirect) {
        if ('' === $result) {
            throw new TransportException('"' . $this->fileUrl . '" appears broken, and returned an empty 200 response');
        }
        $errorMessage = '';
        set_error_handler(static function ($code, $msg) use (&$errorMessage) : bool {
            if ($errorMessage) {
                $errorMessage .= "\n";
            }
            $errorMessage .= Preg::replace('{^file_put_contents\\(.*?\\): }', '', $msg);
            return true;
        });
        $result = (bool) file_put_contents($fileName, $result);
        restore_error_handler();
        if (false === $result) {
            throw new TransportException('The "' . $this->fileUrl . '" file could not be written to ' . $fileName . ': ' . $errorMessage);
        }
    }
    if ($this->retry) {
        $this->retry = false;
        $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
        if ($this->storeAuth) {
            $this->authHelper
                ->storeAuth($this->originUrl, $this->storeAuth);
            $this->storeAuth = false;
        }
        return $result;
    }
    if (false === $result) {
        $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded: ' . $errorMessage, $errorCode);
        if (!empty($http_response_header[0])) {
            $e->setHeaders($http_response_header);
        }
        if (!$this->degradedMode && false !== strpos($e->getMessage(), 'Operation timed out')) {
            $this->degradedMode = true;
            $this->io
                ->writeError('');
            $this->io
                ->writeError([
                '<error>' . $e->getMessage() . '</error>',
                '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>',
            ]);
            return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
        }
        throw $e;
    }
    if (!empty($http_response_header[0])) {
        $this->lastHeaders = $http_response_header;
    }
    return $result;
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal