1
0
Fork 0

Add a bunch of type info to Util namespace

pull/10086/head
Jordi Boggiano 2021-08-29 20:07:50 +02:00
parent 8559279025
commit 024f0eda53
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
20 changed files with 357 additions and 75 deletions

View File

@ -35,6 +35,10 @@ parameters:
# Mock errors
- '~^Call to an undefined method (PHPUnit\\Framework\\MockObject\\MockObject|Prophecy\\Prophecy\\ObjectProphecy)::.*$~'
# Level 6 TODO
#- '~parameter .*? with no return typehint specified.$~'
#- '~has no return typehint specified.$~'
bootstrapFiles:
- ../tests/bootstrap.php

View File

@ -75,6 +75,7 @@ class AuthHelper
* @param string[] $headers
* @return array|null containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be
* retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json
* @phpstan-return ?array{retry: bool, storeAuth: string|bool}
*/
public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $headers = array())
{

View File

@ -26,11 +26,16 @@ use React\Promise\Promise;
* @internal
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Nicolas Grekas <p@tchwork.com>
* @phpstan-type Attributes array{retryAuthFailure: bool, redirects: int, storeAuth: bool}
* @phpstan-type Job array{url: string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: resource, filename: string|false, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable}
*/
class CurlDownloader
{
/** @var ?resource */
private $multiHandle;
/** @var ?resource */
private $shareHandle;
/** @var Job[] */
private $jobs = array();
/** @var IOInterface */
private $io;
@ -38,11 +43,15 @@ class CurlDownloader
private $config;
/** @var AuthHelper */
private $authHelper;
/** @var float */
private $selectTimeout = 5.0;
/** @var int */
private $maxRedirects = 20;
/** @var ProxyManager */
private $proxyManager;
/** @var bool */
private $supportsSecureProxy;
/** @var array<int, string[]> */
protected $multiErrors = array(
CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
@ -50,6 +59,7 @@ class CurlDownloader
CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!'),
);
/** @var mixed[] */
private static $options = array(
'http' => array(
'method' => CURLOPT_CUSTOMREQUEST,
@ -68,6 +78,7 @@ class CurlDownloader
),
);
/** @var array<string, true> */
private static $timeInfo = array(
'total_time' => true,
'namelookup_time' => true,
@ -77,6 +88,10 @@ class CurlDownloader
'redirect_time' => true,
);
/**
* @param mixed[] $options
* @param bool $disableTls
*/
public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
{
$this->io = $io;
@ -106,6 +121,13 @@ class CurlDownloader
}
/**
* @param callable $resolve
* @param callable $reject
* @param string $origin
* @param string $url
* @param mixed[] $options
* @param ?string $copyTo
*
* @return int internal job id
*/
public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
@ -120,6 +142,15 @@ class CurlDownloader
}
/**
* @param callable $resolve
* @param callable $reject
* @param string $origin
* @param string $url
* @param mixed[] $options
* @param ?string $copyTo
*
* @param array{retryAuthFailure?: bool, redirects?: int, storeAuth?: bool} $attributes
*
* @return int internal job id
*/
private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array())
@ -245,6 +276,10 @@ class CurlDownloader
return (int) $curlHandle;
}
/**
* @param int $id
* @return void
*/
public function abortRequest($id)
{
if (isset($this->jobs[$id], $this->jobs[$id]['handle'])) {
@ -264,6 +299,9 @@ class CurlDownloader
}
}
/**
* @return void
*/
public function tick()
{
if (!$this->jobs) {
@ -409,6 +447,10 @@ class CurlDownloader
}
}
/**
* @param Job $job
* @return string
*/
private function handleRedirect(array $job, Response $response)
{
if ($locationHeader = $response->getHeader('location')) {
@ -440,6 +482,10 @@ class CurlDownloader
throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')');
}
/**
* @param Job $job
* @return array{retry: bool, storeAuth: string|bool}
*/
private function isAuthenticatedRetryNeeded(array $job, Response $response)
{
if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) {
@ -487,6 +533,14 @@ class CurlDownloader
return array('retry' => false, 'storeAuth' => false);
}
/**
* @param Job $job
* @param string $url
*
* @param array{retryAuthFailure?: bool, redirects?: int, storeAuth?: bool} $attributes
*
* @return void
*/
private function restartJob(array $job, $url, array $attributes = array())
{
if ($job['filename']) {
@ -499,6 +553,11 @@ class CurlDownloader
$this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes);
}
/**
* @param Job $job
* @param string $errorMessage
* @return TransportException
*/
private function failResponse(array $job, Response $response, $errorMessage)
{
if ($job['filename']) {
@ -513,6 +572,10 @@ class CurlDownloader
return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode());
}
/**
* @param Job $job
* @return void
*/
private function rejectJob(array $job, \Exception $e)
{
if (is_resource($job['headerHandle'])) {
@ -527,6 +590,10 @@ class CurlDownloader
call_user_func($job['reject'], $e);
}
/**
* @param int $code
* @return void
*/
private function checkCurlResult($code)
{
if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {

View File

@ -12,10 +12,20 @@
namespace Composer\Util\Http;
/**
* @phpstan-type CurlInfo array{url: mixed, content_type: mixed, http_code: mixed, header_size: mixed, request_size: mixed, filetime: mixed, ssl_verify_result: mixed, redirect_count: mixed, total_time: mixed, namelookup_time: mixed, connect_time: mixed, pretransfer_time: mixed, size_upload: mixed, size_download: mixed, speed_download: mixed, speed_upload: mixed, download_content_length: mixed, upload_content_length: mixed, starttransfer_time: mixed, redirect_time: mixed, certinfo: mixed, primary_ip: mixed, primary_port: mixed, local_ip: mixed, local_port: mixed, redirect_url: mixed}
*/
class CurlResponse extends Response
{
/**
* @see https://www.php.net/curl_getinfo
* @var CurlInfo
*/
private $curlInfo;
/**
* @param CurlInfo $curlInfo
*/
public function __construct(array $request, $code, array $headers, $body, array $curlInfo)
{
parent::__construct($request, $code, $headers, $body);
@ -23,7 +33,7 @@ class CurlResponse extends Response
}
/**
* @return array
* @return CurlInfo
*/
public function getCurlInfo()
{

View File

@ -20,15 +20,19 @@ use Composer\Util\Url;
*/
class RequestProxy
{
/** @var mixed[] */
private $contextOptions;
/** @var bool */
private $isSecure;
/** @var string */
private $formattedUrl;
/** @var string */
private $url;
/**
* @param string $url
* @param array $contextOptions
* @param string $formattedUrl
* @param string $url
* @param mixed[] $contextOptions
* @param string $formattedUrl
*/
public function __construct($url, array $contextOptions, $formattedUrl)
{
@ -41,7 +45,7 @@ class RequestProxy
/**
* Returns an array of context options
*
* @return array
* @return mixed[]
*/
public function getContextOptions()
{

View File

@ -13,14 +13,28 @@
namespace Composer\Util\Http;
use Composer\Json\JsonFile;
use Composer\Util\HttpDownloader;
/**
* @phpstan-import-type Request from HttpDownloader
*/
class Response
{
/** @var Request */
private $request;
/** @var int */
private $code;
/** @var string[] */
private $headers;
/** @var ?string */
private $body;
/**
* @param Request $request
* @param int $code
* @param string[] $headers
* @param ?string $body
*/
public function __construct(array $request, $code, array $headers, $body)
{
if (!isset($request['url'])) {
@ -32,6 +46,9 @@ class Response
$this->body = $body;
}
/**
* @return int
*/
public function getStatusCode()
{
return $this->code;
@ -54,33 +71,51 @@ class Response
return $value;
}
/**
* @return string[]
*/
public function getHeaders()
{
return $this->headers;
}
/**
* @param string $name
* @return ?string
*/
public function getHeader($name)
{
return self::findHeaderValue($this->headers, $name);
}
/**
* @return ?string
*/
public function getBody()
{
return $this->body;
}
/**
* @return mixed
*/
public function decodeJson()
{
return JsonFile::parseJson($this->body, $this->request['url']);
}
/**
* @return void
* @phpstan-impure
*/
public function collect()
{
/** @phpstan-ignore-next-line */
$this->request = $this->code = $this->headers = $this->body = null;
}
/**
* @param array $headers array of returned headers like from getLastHeaders()
* @param string[] $headers array of returned headers like from getLastHeaders()
* @param string $name header name (case insensitive)
* @return string|null
*/

View File

@ -16,14 +16,18 @@ use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Downloader\TransportException;
use Composer\Util\Http\Response;
use Composer\Util\Http\CurlDownloader;
use Composer\Composer;
use Composer\Package\Version\VersionParser;
use Composer\Semver\Constraint\Constraint;
use Composer\Exception\IrrecoverableDownloadException;
use React\Promise\Promise;
use React\Promise\PromiseInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @phpstan-type Request array{url: string, options?: mixed[], copyTo?: ?string}
* @phpstan-type Job array{id: int, status: int, request: Request, sync: bool, origin: string, resolve?: callable, reject?: callable, curl_id?: int, response?: Response, exception?: TransportException}
*/
class HttpDownloader
{
@ -33,22 +37,33 @@ class HttpDownloader
const STATUS_FAILED = 4;
const STATUS_ABORTED = 5;
/** @var IOInterface */
private $io;
/** @var Config */
private $config;
/** @var array<Job> */
private $jobs = array();
/** @var mixed[] */
private $options = array();
/** @var int */
private $runningJobs = 0;
/** @var int */
private $maxJobs = 12;
/** @var ?CurlDownloader */
private $curl;
/** @var ?RemoteFilesystem */
private $rfs;
/** @var int */
private $idGen = 0;
/** @var bool */
private $disabled;
/** @var bool */
private $allowAsync = false;
/**
* @param IOInterface $io The IO instance
* @param Config $config The config
* @param array $options The options
* @param mixed[] $options The options
* @param bool $disableTls
*/
public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
@ -68,7 +83,7 @@ class HttpDownloader
$this->config = $config;
if (self::isCurlEnabled()) {
$this->curl = new Http\CurlDownloader($io, $config, $options, $disableTls);
$this->curl = new CurlDownloader($io, $config, $options, $disableTls);
}
$this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls);
@ -82,14 +97,14 @@ class HttpDownloader
* Download a file synchronously
*
* @param string $url URL to download
* @param array $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* although not all options are supported when using the default curl downloader
* @throws TransportException
* @return Response
*/
public function get($url, $options = array())
{
list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false), true);
list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null), true);
$this->wait($job['id']);
$response = $this->getResponse($job['id']);
@ -106,7 +121,7 @@ class HttpDownloader
$this->curl = null;
list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false), true);
list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null), true);
$this->wait($job['id']);
$response = $this->getResponse($job['id']);
@ -119,14 +134,14 @@ class HttpDownloader
* Create an async download operation
*
* @param string $url URL to download
* @param array $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* although not all options are supported when using the default curl downloader
* @throws TransportException
* @return Promise
* @return PromiseInterface
*/
public function add($url, $options = array())
{
list(, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false));
list(, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null));
return $promise;
}
@ -136,7 +151,7 @@ class HttpDownloader
*
* @param string $url URL to download
* @param string $to Path to copy to
* @param array $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* although not all options are supported when using the default curl downloader
* @throws TransportException
* @return Response
@ -154,10 +169,10 @@ class HttpDownloader
*
* @param string $url URL to download
* @param string $to Path to copy to
* @param array $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php
* although not all options are supported when using the default curl downloader
* @throws TransportException
* @return Promise
* @return PromiseInterface
*/
public function addCopy($url, $to, $options = array())
{
@ -169,7 +184,7 @@ class HttpDownloader
/**
* Retrieve the options set in the constructor
*
* @return array Options
* @return mixed[] Options
*/
public function getOptions()
{
@ -179,6 +194,7 @@ class HttpDownloader
/**
* Merges new options
*
* @param mixed[] $options
* @return void
*/
public function setOptions(array $options)
@ -186,10 +202,17 @@ class HttpDownloader
$this->options = array_replace_recursive($this->options, $options);
}
/**
* @param Request $request
* @param bool $sync
*
* @return array{Job, PromiseInterface}
*/
private function addJob($request, $sync = false)
{
$request['options'] = array_replace_recursive($this->options, $request['options']);
/** @var Job */
$job = array(
'id' => $this->idGen++,
'status' => self::STATUS_QUEUED,
@ -283,6 +306,10 @@ class HttpDownloader
return array($job, $promise);
}
/**
* @param int $id
* @return void
*/
private function startJob($id)
{
$job = &$this->jobs[$id];
@ -325,6 +352,7 @@ class HttpDownloader
/**
* @private
* @return void
*/
public function markJobDone()
{
@ -335,6 +363,8 @@ class HttpDownloader
* Wait for current async download jobs to complete
*
* @param int|null $index For internal use only, the job id
*
* @return void
*/
public function wait($index = null)
{
@ -345,6 +375,8 @@ class HttpDownloader
/**
* @internal
*
* @return void
*/
public function enableAsync()
{
@ -387,6 +419,10 @@ class HttpDownloader
return $active;
}
/**
* @param int $index Job id
* @return Response
*/
private function getResponse($index)
{
if (!isset($this->jobs[$index])) {
@ -410,6 +446,10 @@ class HttpDownloader
/**
* @internal
*
* @param string $url
* @param array{warning?: string, info?: string, warning-versions?: string, info-versions?: string} $data
* @return void
*/
public static function outputWarnings(IOInterface $io, $url, $data)
{
@ -433,11 +473,13 @@ class HttpDownloader
/**
* @internal
*
* @return ?string[]
*/
public static function getExceptionHints(\Exception $e)
{
if (!$e instanceof TransportException) {
return;
return null;
}
if (
@ -460,8 +502,14 @@ class HttpDownloader
'<error>The following exception probably indicates you are offline or have misconfigured DNS resolver(s)</error>',
);
}
return null;
}
/**
* @param Job $job
* @return bool
*/
private function canUseCurl(array $job)
{
if (!$this->curl) {

View File

@ -29,7 +29,7 @@ class IniHelper
* The equivalent of calling php_ini_loaded_file then php_ini_scanned_files.
* The loaded ini location is the first entry and may be empty.
*
* @return array
* @return string[]
*/
public static function getAll()
{

View File

@ -12,8 +12,9 @@
namespace Composer\Util;
use React\Promise\Promise;
use React\Promise\CancellablePromiseInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use React\Promise\PromiseInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
@ -24,7 +25,7 @@ class Loop
private $httpDownloader;
/** @var ProcessExecutor|null */
private $processExecutor;
/** @var Promise[][] */
/** @var PromiseInterface[][] */
private $currentPromises = array();
/** @var int */
private $waitIndex = 0;
@ -56,6 +57,11 @@ class Loop
return $this->processExecutor;
}
/**
* @param PromiseInterface[] $promises
* @param ?ProgressBar $progress
* @return void
*/
public function wait(array $promises, ProgressBar $progress = null)
{
/** @var \Exception|null */
@ -113,11 +119,16 @@ class Loop
}
}
/**
* @return void
*/
public function abortJobs()
{
foreach ($this->currentPromises as $promiseGroup) {
foreach ($promiseGroup as $promise) {
$promise->cancel();
if ($promise instanceof CancellablePromiseInterface) {
$promise->cancel();
}
}
}
}

View File

@ -420,6 +420,8 @@ class NoProxyPattern
* @param string $int
* @param int $min
* @param int $max
*
* @return bool
*/
private function validateInt($int, $min, $max)
{

View File

@ -142,6 +142,10 @@ class Platform
return \strlen($str);
}
/**
* @param ?resource $fd Open file descriptor or null to default to STDOUT
* @return bool
*/
public static function isTty($fd = null)
{
if ($fd === null) {
@ -170,6 +174,9 @@ class Platform
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
}
/**
* @return void
*/
public static function workaroundFilesystemIssues()
{
if (self::isVirtualBoxGuest()) {

View File

@ -31,6 +31,7 @@ class ProcessExecutor
const STATUS_FAILED = 4;
const STATUS_ABORTED = 5;
/** @var int */
protected static $timeout = 300;
/** @var bool */
@ -44,9 +45,13 @@ class ProcessExecutor
* @phpstan-var array<int, array<string, mixed>>
*/
private $jobs = array();
/** @var int */
private $runningJobs = 0;
/** @var int */
private $maxJobs = 10;
/** @var int */
private $idGen = 0;
/** @var bool */
private $allowAsync = false;
public function __construct(IOInterface $io = null)
@ -57,11 +62,11 @@ class ProcessExecutor
/**
* runs a process on the commandline
*
* @param string $command the command to execute
* @param mixed $output the output will be written into this var if passed by ref
* if a callable is passed it will be used as output handler
* @param string $cwd the working directory
* @return int statuscode
* @param string $command the command to execute
* @param mixed $output the output will be written into this var if passed by ref
* if a callable is passed it will be used as output handler
* @param ?string $cwd the working directory
* @return int statuscode
*/
public function execute($command, &$output = null, $cwd = null)
{
@ -75,9 +80,9 @@ class ProcessExecutor
/**
* runs a process on the commandline in TTY mode
*
* @param string $command the command to execute
* @param string $cwd the working directory
* @return int statuscode
* @param string $command the command to execute
* @param ?string $cwd the working directory
* @return int statuscode
*/
public function executeTty($command, $cwd = null)
{
@ -88,6 +93,13 @@ class ProcessExecutor
return $this->doExecute($command, $cwd, false);
}
/**
* @param string $command
* @param ?string $cwd
* @param bool $tty
* @param mixed $output
* @return int
*/
private function doExecute($command, $cwd, $tty, &$output = null)
{
if ($this->io && $this->io->isDebug()) {
@ -120,6 +132,7 @@ class ProcessExecutor
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$process = Process::fromShellCommandline($command, $cwd, null, null, static::getTimeout());
} else {
/** @phpstan-ignore-next-line */
$process = new Process($command, $cwd, null, null, static::getTimeout());
}
if (!Platform::isWindows() && $tty) {
@ -218,6 +231,10 @@ class ProcessExecutor
return $promise;
}
/**
* @param int $id
* @return void
*/
private function startJob($id)
{
$job = &$this->jobs[$id];
@ -286,6 +303,10 @@ class ProcessExecutor
}
}
/**
* @param ?int $index job id
* @return void
*/
public function wait($index = null)
{
while (true) {
@ -299,6 +320,8 @@ class ProcessExecutor
/**
* @internal
*
* @return void
*/
public function enableAsync()
{
@ -308,7 +331,8 @@ class ProcessExecutor
/**
* @internal
*
* @return int number of active (queued or started) jobs
* @param ?int $index job id
* @return int number of active (queued or started) jobs
*/
public function countActiveJobs($index = null)
{
@ -345,6 +369,8 @@ class ProcessExecutor
/**
* @private
*
* @return void
*/
public function markJobDone()
{
@ -352,6 +378,7 @@ class ProcessExecutor
}
/**
* @param ?string $output
* @return string[]
*/
public function splitLines($output)
@ -373,6 +400,11 @@ class ProcessExecutor
/**
* @private
*
* @param Process::ERR|Process::OUT $type
* @param string $buffer
*
* @return void
*/
public function outputHandler($type, $buffer)
{
@ -402,7 +434,8 @@ class ProcessExecutor
}
/**
* @param int $timeout the timeout in seconds
* @param int $timeout the timeout in seconds
* @return void
*/
public static function setTimeout($timeout)
{
@ -466,6 +499,11 @@ class ProcessExecutor
return "'".str_replace("'", "'\\''", $argument)."'";
}
/**
* @param string $arg
* @param string $char
* @return bool
*/
private static function isSurroundedBy($arg, $char)
{
return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];

View File

@ -28,25 +28,45 @@ use Composer\Util\Http\ProxyManager;
*/
class RemoteFilesystem
{
/** @var IOInterface */
private $io;
/** @var Config */
private $config;
/** @var string */
private $scheme;
/** @var int */
private $bytesMax;
/** @var string */
private $originUrl;
/** @var string */
private $fileUrl;
/** @var ?string */
private $fileName;
private $retry;
/** @var bool */
private $retry = false;
/** @var bool */
private $progress;
/** @var ?int */
private $lastProgress;
/** @var mixed[] */
private $options = array();
/** @var array<string, array{cn: string, fp: string}> */
private $peerCertificateMap = array();
/** @var bool */
private $disableTls = false;
/** @var string[] */
private $lastHeaders;
private $storeAuth;
/** @var bool */
private $storeAuth = false;
/** @var AuthHelper */
private $authHelper;
/** @var bool */
private $degradedMode = false;
/** @var int */
private $redirects;
/** @var int */
private $maxRedirects = 20;
/** @var ProxyManager */
private $proxyManager;
/**
@ -54,7 +74,7 @@ class RemoteFilesystem
*
* @param IOInterface $io The IO instance
* @param Config $config The config
* @param array $options The options
* @param mixed[] $options The options
* @param bool $disableTls
* @param AuthHelper $authHelper
*/
@ -80,11 +100,11 @@ class RemoteFilesystem
/**
* Copy the remote file in local.
*
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param string $fileName the local filename
* @param bool $progress Display the progression
* @param array $options Additional context options
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param string $fileName the local filename
* @param bool $progress Display the progression
* @param mixed[] $options Additional context options
*
* @return bool true
*/
@ -96,10 +116,10 @@ class RemoteFilesystem
/**
* Get the content.
*
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param bool $progress Display the progression
* @param array $options Additional context options
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param bool $progress Display the progression
* @param mixed[] $options Additional context options
*
* @return bool|string The content
*/
@ -111,7 +131,7 @@ class RemoteFilesystem
/**
* Retrieve the options set in the constructor
*
* @return array Options
* @return mixed[] Options
*/
public function getOptions()
{
@ -121,7 +141,8 @@ class RemoteFilesystem
/**
* Merges new options
*
* @param array $options
* @param mixed[] $options
* @return void
*/
public function setOptions(array $options)
{
@ -141,7 +162,7 @@ class RemoteFilesystem
/**
* Returns the headers of the last request
*
* @return array
* @return string[]
*/
public function getLastHeaders()
{
@ -149,7 +170,7 @@ class RemoteFilesystem
}
/**
* @param array $headers array of returned headers like from getLastHeaders()
* @param string[] $headers array of returned headers like from getLastHeaders()
* @return int|null
*/
public static function findStatusCode(array $headers)
@ -167,7 +188,7 @@ class RemoteFilesystem
}
/**
* @param array $headers array of returned headers like from getLastHeaders()
* @param string[] $headers array of returned headers like from getLastHeaders()
* @return string|null
*/
public function findStatusMessage(array $headers)
@ -187,11 +208,11 @@ class RemoteFilesystem
/**
* Get file content or copy action.
*
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param array $additionalOptions context options
* @param string $fileName the local filename
* @param bool $progress Display the progression
* @param string $originUrl The origin URL
* @param string $fileUrl The file URL
* @param mixed[] $additionalOptions context options
* @param string $fileName the local filename
* @param bool $progress Display the progression
*
* @throws TransportException|\Exception
* @throws TransportException When the file could not be downloaded
@ -260,7 +281,7 @@ class RemoteFilesystem
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_match('{^http://(repo\.)?packagist\.org/p/}', $fileUrl) || (false === strpos($fileUrl, '$') && false === strpos($fileUrl, '%24'))) && empty($degradedPackagist) && $this->config) {
if ((!preg_match('{^http://(repo\.)?packagist\.org/p/}', $fileUrl) || (false === strpos($fileUrl, '$') && false === strpos($fileUrl, '%24'))) && empty($degradedPackagist)) {
$this->config->prohibitUrlByConfig($fileUrl, $this->io);
}
@ -375,7 +396,7 @@ class RemoteFilesystem
// check for gitlab 404 when downloading archives
if ($statusCode === 404
&& $this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)
&& in_array($originUrl, $this->config->get('gitlab-domains'), true)
&& false !== strpos($fileUrl, 'archive.zip')
) {
$result = false;
@ -493,7 +514,7 @@ class RemoteFilesystem
$result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
if ($this->storeAuth && $this->config) {
if ($this->storeAuth) {
$this->authHelper->storeAuth($this->originUrl, $this->storeAuth);
$this->storeAuth = false;
}
@ -596,7 +617,7 @@ class RemoteFilesystem
case STREAM_NOTIFY_PROGRESS:
if ($this->bytesMax > 0 && $this->progress) {
$progression = min(100, round($bytesTransferred / $this->bytesMax * 100));
$progression = min(100, (int) round($bytesTransferred / $this->bytesMax * 100));
if ((0 === $progression % 5) && 100 !== $progression && $progression !== $this->lastProgress) {
$this->lastProgress = $progression;
@ -741,6 +762,8 @@ class RemoteFilesystem
* Fetch certificate common name and fingerprint for validation of SAN.
*
* @todo Remove when PHP 5.6 is minimum supported version.
*
* @return ?array{cn: string, fp: string}
*/
private function getCertificateCnAndFp($url, $options)
{
@ -761,7 +784,7 @@ class RemoteFilesystem
// Ideally this would just use stream_socket_client() to avoid sending a
// HTTP request but that does not capture the certificate.
if (false === $handle = @fopen($url, 'rb', false, $context)) {
return;
return null;
}
// Close non authenticated connection without reading any content.
@ -780,6 +803,8 @@ class RemoteFilesystem
);
}
}
return null;
}
private function getUrlAuthority($url)

View File

@ -44,6 +44,8 @@ class Silencer
/**
* Restores a single state.
*
* @return void
*/
public static function restore()
{

View File

@ -32,8 +32,8 @@ final class StreamContextFactory
*
* @param string $url URL the context is to be used for
* @phpstan-param array{http?: array{follow_location?: int, max_redirects?: int, header?: string|array<string>}} $defaultOptions
* @param array $defaultOptions Options to merge with the default
* @param array $defaultParams Parameters to specify on the context
* @param mixed[] $defaultOptions Options to merge with the default
* @param mixed[] $defaultParams Parameters to specify on the context
* @throws \RuntimeException if https proxy required and OpenSSL uninstalled
* @return resource Default context
*/
@ -57,9 +57,9 @@ final class StreamContextFactory
}
/**
* @param string $url
* @param array $options
* @param bool $forCurl When true, will not add proxy values as these are handled separately
* @param string $url
* @param mixed[] $options
* @param bool $forCurl When true, will not add proxy values as these are handled separately
* @phpstan-return array{http: array{header: string[], proxy?: string, request_fulluri: bool}, ssl: array}
* @return array formatted as a stream context array
*/
@ -130,9 +130,9 @@ final class StreamContextFactory
}
/**
* @param array $options
* @param mixed[] $options
*
* @return array
* @return mixed[]
*/
public static function getTlsDefaults(array $options, LoggerInterface $logger = null)
{
@ -239,8 +239,8 @@ final class StreamContextFactory
* This method fixes the array by moving the content-type header to the end
*
* @link https://bugs.php.net/bug.php?id=61548
* @param string|array $header
* @return array
* @param string|string[] $header
* @return string[]
*/
private static function fixHttpHeaderField($header)
{

View File

@ -24,7 +24,7 @@ class Svn
const MAX_QTY_AUTH_TRIES = 5;
/**
* @var array
* @var ?array{username: string, password: string}
*/
protected $credentials;
@ -82,6 +82,9 @@ class Svn
$this->process = $process ?: new ProcessExecutor($io);
}
/**
* @return void
*/
public static function cleanEnv()
{
// clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
@ -127,6 +130,15 @@ class Svn
return $this->executeWithAuthRetry($command, $cwd, '', $path, $verbose);
}
/**
* @param string $svnCommand
* @param string $cwd
* @param string $url
* @param string $path
* @param bool $verbose
*
* @return ?string
*/
private function executeWithAuthRetry($svnCommand, $cwd, $url, $path, $verbose)
{
// Regenerate the command at each try, to use the newly user-provided credentials
@ -136,10 +148,10 @@ class Svn
$io = $this->io;
$handler = function ($type, $buffer) use (&$output, $io, $verbose) {
if ($type !== 'out') {
return;
return null;
}
if (strpos($buffer, 'Redirecting to URL ') === 0) {
return;
return null;
}
$output .= $buffer;
if ($verbose) {
@ -178,7 +190,8 @@ class Svn
}
/**
* @param bool $cacheCredentials
* @param bool $cacheCredentials
* @return void
*/
public function setCacheCredentials($cacheCredentials)
{

View File

@ -28,6 +28,8 @@ class SyncHelper
* @param string $path the installation path for the package
* @param PackageInterface $package the package to install
* @param PackageInterface|null $prevPackage the previous package if this is an update and not an initial installation
*
* @return void
*/
public static function downloadAndInstallPackageSync(Loop $loop, DownloaderInterface $downloader, $path, PackageInterface $package, PackageInterface $prevPackage = null)
{
@ -56,6 +58,8 @@ class SyncHelper
*
* @param Loop $loop Loop instance which you can get from $composer->getLoop()
* @param PromiseInterface|null $promise
*
* @return void
*/
public static function await(Loop $loop, PromiseInterface $promise = null)
{

View File

@ -57,7 +57,7 @@ final class TlsHelper
*
* @param mixed $certificate X.509 certificate
*
* @return array|null
* @return array{cn: string, san: string[]}|null
*/
public static function getCertificateNames($certificate)
{
@ -130,6 +130,9 @@ final class TlsHelper
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @param string $certificate
* @return string
*/
public static function getCertificateFingerprint($certificate)
{

View File

@ -103,6 +103,10 @@ class Url
return $origin;
}
/**
* @param string $url
* @return string
*/
public static function sanitize($url)
{
// GitHub repository rename result in redirect locations containing the access_token as GET parameter

View File

@ -10,9 +10,13 @@
* file that was distributed with this source code.
*/
/**
* @param string $file
* @return ?\Composer\Autoload\ClassLoader
*/
function includeIfExists($file)
{
return file_exists($file) ? include $file : false;
return file_exists($file) ? include $file : null;
}
if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) {