Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
->in(__DIR__)
->append([
__FILE__,
'rector.php',
]);
$config->setRules([
'@PSR1' => true,
Expand Down
10 changes: 5 additions & 5 deletions examples/asyncclient.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,26 @@
$client = new Client();

for ($i = 0; $i < 1000; ++$i) {
echo "$i sending\n";
echo "{$i} sending\n";
$client->sendAsync(
$request,

// This is the 'success' callback
function ($response) use ($i): void {
echo "$i -> ".$response->getStatus()."\n";
echo "{$i} -> ".$response->getStatus()."\n";
},

// This is the 'error' callback. It is called for general connection
// problems (such as not being able to connect to a host, dns errors,
// etc.) and also cases where a response was returned, but it had a
// status code of 400 or higher.
function ($error) use ($i): void {
function (array $error) use ($i): void {
if (Client::STATUS_CURLERROR === $error['status']) {
// Curl errors
echo "$i -> curl error: ".$error['curl_errmsg']."\n";
echo "{$i} -> curl error: ".$error['curl_errmsg']."\n";
} else {
// HTTP errors
echo "$i -> ".$error['response']->getStatus()."\n";
echo "{$i} -> ".$error['response']->getStatus()."\n";
}
}
);
Expand Down
2 changes: 2 additions & 0 deletions examples/basicauth.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* This example shows how to do Basic authentication.
* *.
Expand Down
2 changes: 2 additions & 0 deletions examples/client.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* This example shows how to make an HTTP request with the Request and Response
* objects.
Expand Down
2 changes: 2 additions & 0 deletions examples/digestauth.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* This example shows how to do Digest authentication.
* *.
Expand Down
2 changes: 2 additions & 0 deletions examples/reverseproxy.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

// The url we're proxying to.
$remoteUrl = 'http://example.org/';

Expand Down
10 changes: 8 additions & 2 deletions lib/Auth/AWS.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ class AWS extends AbstractAuth
public int $errorCode = 0;

public const ERR_NOAWSHEADER = 1;

public const ERR_MD5CHECKSUMWRONG = 2;

public const ERR_INVALIDDATEFORMAT = 3;

public const ERR_REQUESTTIMESKEWED = 4;

public const ERR_INVALIDSIGNATURE = 5;

/**
Expand All @@ -54,6 +58,7 @@ public function init(): bool

return false;
}

$authHeader = explode(' ', $authHeader);

if ('AWS' !== $authHeader[0] || !isset($authHeader[1])) {
Expand Down Expand Up @@ -183,6 +188,7 @@ protected function getAmzHeaders(): string
$amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0])."\n";
}
}

ksort($amzHeaders);

$headerStr = '';
Expand All @@ -206,11 +212,11 @@ private function hmacsha1(string $key, string $message): string
if (strlen($key) > $blocksize) {
$key = pack('H*', sha1($key));
}

$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5C), $blocksize);
$hmac = pack('H*', sha1(($key ^ $opad).pack('H*', sha1(($key ^ $ipad).$message))));

return $hmac;
return pack('H*', sha1(($key ^ $opad).pack('H*', sha1(($key ^ $ipad).$message))));
}
}
10 changes: 8 additions & 2 deletions lib/Auth/Digest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,20 @@ class Digest extends AbstractAuth
* These constants are used in setQOP();.
*/
public const QOP_AUTH = 1;

public const QOP_AUTHINT = 2;

protected string $nonce;

protected string $opaque;

/**
* @var array<int|string, string>|bool
*/
protected $digestParts;

protected string $A1;

protected int $qop = self::QOP_AUTH;

/**
Expand Down Expand Up @@ -133,6 +138,7 @@ protected function validate(): bool
if (0 === ($this->qop & self::QOP_AUTHINT)) {
return false;
}

// We need to add an MD5 of the entire request body to the A2 part of the hash
$body = $this->request->getBody();
$this->request->setBody($body);
Expand Down Expand Up @@ -191,7 +197,7 @@ public function getDigest(): ?string
*
* @return false|array<int|string, mixed>
*/
protected function parseDigest(string $digest)
protected function parseDigest(string $digest): array|false
{
// protect against missing data
$needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1];
Expand All @@ -204,6 +210,6 @@ protected function parseDigest(string $digest)
unset($needed_parts[$m[1]]);
}

return (count($needed_parts) > 0) ? false : $data;
return ([] !== $needed_parts) ? false : $data;
}
}
34 changes: 16 additions & 18 deletions lib/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,7 @@ public function send(RequestInterface $request): ResponseInterface
// If retry was still set to false, it means no event handler
// dealt with the problem. In this case we just re-throw the
// exception.
if (!$retry) {
throw $e;
}
throw $e;
}

if ($retry) {
Expand Down Expand Up @@ -194,7 +192,7 @@ public function sendAsync(RequestInterface $request, ?callable $success = null,
public function poll(): bool
{
// nothing to do?
if (0 === count($this->curlMultiMap)) {
if ([] === $this->curlMultiMap) {
return false;
}

Expand Down Expand Up @@ -264,7 +262,7 @@ public function poll(): bool
}
} while ($messagesInQueue > 0);

return count($this->curlMultiMap) > 0;
return [] !== $this->curlMultiMap;
}

/**
Expand Down Expand Up @@ -335,7 +333,7 @@ protected function doRequest(RequestInterface $request): ResponseInterface
*
* @var resource|null
*/
private $curlHandle;
private \CurlHandle|bool|null $curlHandle = null;

/**
* Handler for curl_multi requests.
Expand All @@ -344,7 +342,7 @@ protected function doRequest(RequestInterface $request): ResponseInterface
*
* @var resource|null
*/
private $curlMultiHandle;
private ?\CurlMultiHandle $curlMultiHandle = null;

/**
* Has a list of curl handles, as well as their associated success and
Expand Down Expand Up @@ -391,6 +389,7 @@ protected function createCurlSettingsArray(RequestInterface $request): array
// post local files.
$settings[CURLOPT_POSTFIELDS] = (string) $body;
}

$settings[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
break;
}
Expand All @@ -405,6 +404,7 @@ protected function createCurlSettingsArray(RequestInterface $request): array
if ([] !== $nHeaders) {
$settings[CURLOPT_HTTPHEADER] = $nHeaders;
}

$settings[CURLOPT_URL] = $request->getUrl();
// Prefer string-based protocol constants (PHP 8.3+), fall back to
// bitmask constants for older PHP versions. When PHP eventually
Expand All @@ -422,7 +422,9 @@ protected function createCurlSettingsArray(RequestInterface $request): array
}

public const STATUS_SUCCESS = 0;

public const STATUS_CURLERROR = 1;

public const STATUS_HTTPERROR = 2;

/**
Expand All @@ -437,17 +439,12 @@ private function parseResponse(string $response, $curlHandle): array

if ($separatedHeaders) {
$resourceId = (int) $curlHandle;
if (isset($this->headerLinesMap[$resourceId])) {
$headers = $this->headerLinesMap[$resourceId];
} else {
$headers = [];
}
$response = $this->parseCurlResponse($headers, $response, $curlHandle);
} else {
$response = $this->parseCurlResult($response, $curlHandle);
$headers = $this->headerLinesMap[$resourceId] ?? [];

return $this->parseCurlResponse($headers, $response, $curlHandle);
}

return $response;
return $this->parseCurlResult($response, $curlHandle);
}

/**
Expand Down Expand Up @@ -576,9 +573,10 @@ protected function parseCurlResult(string $response, $curlHandle): array
*/
protected function sendAsyncInternal(RequestInterface $request, callable $success, callable $error, int $retryCount = 0): void
{
if (null === $this->curlMultiHandle) {
if (!$this->curlMultiHandle instanceof \CurlMultiHandle) {
$this->curlMultiHandle = curl_multi_init();
}

$curl = curl_init();
curl_setopt_array(
$curl,
Expand Down Expand Up @@ -611,7 +609,7 @@ protected function curlExec($curlHandle): string

$result = curl_exec($curlHandle);
if (false === $result) {
$result = '';
return '';
}

return $result;
Expand Down
5 changes: 5 additions & 0 deletions lib/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function getBodyAsStream()
if (is_callable($this->body)) {
$body = $this->getBodyAsString();
}

if (is_string($body) || null === $body) {
$stream = fopen('php://temp', 'r+');
fwrite($stream, (string) $body);
Expand All @@ -73,15 +74,18 @@ public function getBodyAsString(): string
if (is_string($body)) {
return $body;
}

if (null === $body) {
return '';
}

if (is_callable($body)) {
ob_start();
$body();

return ob_get_clean();
}

$contentLength = $this->getHeader('Content-Length');
if (null !== $contentLength && ctype_digit($contentLength)) {
return stream_get_contents($body, (int) $contentLength);
Expand Down Expand Up @@ -265,6 +269,7 @@ public function removeHeader(string $name): bool
if (!isset($this->headers[$name])) {
return false;
}

unset($this->headers[$name]);

return true;
Expand Down
9 changes: 6 additions & 3 deletions lib/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function getAbsoluteUrl(): string
?? parse_url($url, PHP_URL_HOST)
?? 'localhost';
// Guessing we're a http endpoint.
$this->absoluteUrl = "http://$host$url";
$this->absoluteUrl = "http://{$host}{$url}";
}
}

Expand Down Expand Up @@ -169,6 +169,7 @@ public function getPath(): string
$uri = str_replace('//', '/', $this->getUrl());

$uri = Uri\normalize($uri);

$baseUri = Uri\normalize($this->getBaseUrl());

if (str_starts_with($uri, $baseUri)) {
Expand All @@ -181,6 +182,7 @@ public function getPath(): string
if ($uri.'/' === $baseUri) {
return '';
}

// A special case, if the baseUri was accessed without a trailing
// slash, we'll accept it as well.

Expand Down Expand Up @@ -263,12 +265,13 @@ public function __toString(): string
[$v] = explode(' ', (string) $v, 2);
$v .= ' REDACTED';
}

$out .= $key.': '.$v."\r\n";
}
}

$out .= "\r\n";
$out .= $this->getBodyAsString();

return $out;
return $out.$this->getBodyAsString();
}
}
9 changes: 6 additions & 3 deletions lib/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Response extends Message implements ResponseInterface
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a teapot', // RFC 2324
418 => "I'm a teapot", // RFC 2324
421 => 'Misdirected Request', // RFC7540 (HTTP/2)
422 => 'Unprocessable Entity', // RFC 4918
423 => 'Locked', // RFC 4918
Expand Down Expand Up @@ -106,9 +106,11 @@ public function __construct($status = 500, ?array $headers = null, $body = null)
if (null !== $status) {
$this->setStatus($status);
}

if (null !== $headers) {
$this->setHeaders($headers);
}

if (null !== $body) {
$this->setBody($body);
}
Expand Down Expand Up @@ -156,6 +158,7 @@ public function setStatus($status): void
$statusText,
) = explode(' ', $status, 2);
}

$statusCode = (int) $statusCode;
if ($statusCode < 100 || $statusCode > 999) {
throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits');
Expand All @@ -178,9 +181,9 @@ public function __toString(): string
$str .= $key.': '.$v."\r\n";
}
}

$str .= "\r\n";
$str .= $this->getBodyAsString();

return $str;
return $str.$this->getBodyAsString();
}
}
Loading
Loading