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\UtilCode
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;
}