Test for cookie strings and allow taking a $_SERVER variable in HttpRequest::fromRequest.
This commit is contained in:
parent
346b8a52e2
commit
d5b5efab46
4 changed files with 129 additions and 82 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.2503.80055
|
||||
0.2503.82012
|
||||
|
|
|
@ -31,7 +31,7 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
* @param string $method HTTP request method.
|
||||
* @param HttpUri $uri HTTP request URI.
|
||||
* @param array<string, list<?string>> $params HTTP request query parameters.
|
||||
* @param array<string, string[]> $cookies HTTP request cookies.
|
||||
* @param array<string, string> $cookies HTTP request cookies.
|
||||
* @param array<string, array<object|string>>|object|null $parsedBody Parsed body contents.
|
||||
* @param ?array<string, HttpUploadedFile[]> $uploadedFiles Parsed files.
|
||||
*/
|
||||
|
@ -110,7 +110,7 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
* @param ?string $method Value you'd otherwise pass to withMethod, null to leave unmodified.
|
||||
* @param ?UriInterface $uri Value you'd otherwise pass to withUri, null to leave unmodified.
|
||||
* @param ?array<string, list<?string>> $params Value you'd otherwise pass to withQueryParams, null to leave unmodified.
|
||||
* @param ?array<string, string[]> $cookies Value you'd otherwise pass to withCookiesParams, null to leave unmodified.
|
||||
* @param ?array<string, string> $cookies Value you'd otherwise pass to withCookiesParams, null to leave unmodified.
|
||||
* @param null|array<string, string[]|object[]>|object|false $parsedBody Value you'd otherwise pass to withParsedBody, false to leave unmodified.
|
||||
* @param array<string, HttpUploadedFile[]>|null|false $uploadedFiles Value you'd otherwise pass to withUploadedFiles, false to leave unmodified.
|
||||
* @throws InvalidArgumentException If any of the arguments are not acceptable.
|
||||
|
@ -210,14 +210,14 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string[]>
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getCookieParams(): array {
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string[]> $cookies Array of key/value pairs representing cookies.
|
||||
* @param array<string, string> $cookies Array of key/value pairs representing cookies.
|
||||
*/
|
||||
public function withCookieParams(array $cookies): HttpRequest {
|
||||
return $this->with(cookies: $cookies);
|
||||
|
@ -234,19 +234,18 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves an HTTP request cookie, or null if it is not present.
|
||||
* Retrieves an HTTP request cookie, or a default value if it is not present.
|
||||
*
|
||||
* @todo UPDATE FOR THE NEW SYNTAX THINGY WITH THE LIST<STRING>!!!!!!!!!!!
|
||||
* @param string $name Name of the request cookie.
|
||||
* @param int $filter A PHP filter extension filter constant.
|
||||
* @param array<string, mixed>|int $options Options for the PHP filter.
|
||||
* @return mixed Value of the cookie, null if not present.
|
||||
* @param mixed $default Default value.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCookie(string $name, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed {
|
||||
if(!isset($this->cookies[$name]))
|
||||
return null;
|
||||
|
||||
return filter_var($this->cookies[$name], $filter, $options);
|
||||
public function getCookie(string $name, int $filter = FILTER_DEFAULT, array|int $options = 0, mixed $default = null): mixed {
|
||||
return isset($this->cookies[$name])
|
||||
? (filter_var($this->cookies[$name], $filter, $options) ?? $default)
|
||||
: $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,16 +262,6 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
return $this->with(params: $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a query field is present.
|
||||
*
|
||||
* @param string $name Name of the query field.
|
||||
* @return bool true if the field is present, false if not.
|
||||
*/
|
||||
public function hasParam(string $name): bool {
|
||||
return isset($this->params[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all HTTP request query fields as a query string.
|
||||
*
|
||||
|
@ -285,24 +274,68 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
: http_build_query($this->params, '', '&', $spacesAsPlus ? PHP_QUERY_RFC1738 : PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a query field is present.
|
||||
*
|
||||
* @param string $name Name of the query field.
|
||||
* @return bool true if the field is present, false if not.
|
||||
*/
|
||||
public function hasParam(string $name): bool {
|
||||
return isset($this->params[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an HTTP request query field, or null if it is not present.
|
||||
*
|
||||
* @todo UPDATE FOR THE NEW SYNTAX THINGY WITH THE LIST<?STRING>!!!!!!!!!!!
|
||||
* @param string $name Name of the request query field.
|
||||
* @param int $filter A PHP filter extension filter constant.
|
||||
* @param mixed[]|int $options Options for the PHP filter.
|
||||
* @param ?scalar $default Default value to fall back on.
|
||||
* @return ?scalar Value of the query field, null if not present.
|
||||
* @param mixed $default Default value to fall back on.
|
||||
* @return mixed Value of the query field, null if not present.
|
||||
*/
|
||||
public function getParam(string $name, int $filter = FILTER_DEFAULT, array|int $options = 0, mixed $default = null): mixed {
|
||||
if(!isset($this->params[$name]))
|
||||
public function getParam(
|
||||
string $name,
|
||||
int $filter = FILTER_DEFAULT,
|
||||
array|int $options = 0,
|
||||
mixed $default = null,
|
||||
): mixed {
|
||||
return $this->getParamAt($name, 0, $filter, $options, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an HTTP request query field, or null if it is not present.
|
||||
*
|
||||
* @param string $name Name of the request query field.
|
||||
* @param int $index Index of the parameter value.
|
||||
* @param int $filter A PHP filter extension filter constant.
|
||||
* @param mixed[]|int $options Options for the PHP filter.
|
||||
* @param mixed $default Default value to fall back on.
|
||||
* @return mixed Value of the query field, null if not present.
|
||||
*/
|
||||
public function getParamAt(
|
||||
string $name,
|
||||
int $index,
|
||||
int $filter = FILTER_DEFAULT,
|
||||
array|int $options = 0,
|
||||
mixed $default = null,
|
||||
): mixed {
|
||||
if(!isset($this->params[$name]) && isset($this->params[$name][$index]))
|
||||
return $default;
|
||||
|
||||
$value = filter_var($this->params[$name], $filter, $options);
|
||||
$value = filter_var($this->params[$name][$index], $filter, $options);
|
||||
return is_scalar($value) ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves how many parameters of the same name appear in the HTTP request query field.
|
||||
*
|
||||
* @param string $name Name of the request query field.
|
||||
* @return int
|
||||
*/
|
||||
public function getParamCount(string $name): int {
|
||||
return isset($this->params[$name]) ? count($this->params[$name]) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, HttpUploadedFile[]> An array tree of UploadedFileInterface instances; an empty array MUST be returned if no data is present.
|
||||
*/
|
||||
|
@ -364,7 +397,7 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
* Parses a Cookie header string.
|
||||
*
|
||||
* @param string $cookies Cookie header string.
|
||||
* @return array<string, string[]>
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function parseCookieString(string $cookies): array {
|
||||
$params = [];
|
||||
|
@ -375,11 +408,8 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
$parts = explode('=', ltrim($paramPart, ' '), 2);
|
||||
if(count($parts) > 1) {
|
||||
$name = urldecode($parts[0]);
|
||||
$value = urldecode($parts[1]);
|
||||
if(array_key_exists($name, $params))
|
||||
$params[$name][] = $value;
|
||||
else
|
||||
$params[$name] = [$value];
|
||||
if(!array_key_exists($name, $params))
|
||||
$params[$name] = urldecode($parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -390,20 +420,23 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
/**
|
||||
* Creates an HttpRequest instance from the current request.
|
||||
*
|
||||
* @param ?array<string, mixed> $server Value of the $_SERVER variable, if null the current super-global $_SERVER is used.
|
||||
* @return HttpRequest An instance representing the current request.
|
||||
*/
|
||||
public static function fromRequest(): HttpRequest {
|
||||
public static function fromRequest(?array $server = null): HttpRequest {
|
||||
/** @var array<string, mixed> $server */
|
||||
$server ??= $_SERVER;
|
||||
$build = new HttpRequestBuilder;
|
||||
$build->remoteAddress = (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR');
|
||||
$build->secure = filter_has_var(INPUT_SERVER, 'HTTPS');
|
||||
$build->protocolVersion = (string)filter_input(INPUT_SERVER, 'SERVER_PROTOCOL');
|
||||
$build->method = (string)filter_input(INPUT_SERVER, 'REQUEST_METHOD');
|
||||
$build->remoteAddress = (string)filter_var($server['REMOTE_ADDR'] ?? '::');
|
||||
$build->secure = !empty($server['HTTPS']);
|
||||
$build->protocolVersion = (string)filter_var($server['SERVER_PROTOCOL'] ?? '1.1');
|
||||
$build->method = (string)filter_var($server['REQUEST_METHOD'] ?? 'GET');
|
||||
|
||||
if(filter_has_var(INPUT_SERVER, 'COUNTRY_CODE'))
|
||||
$build->countryCode = (string)filter_input(INPUT_SERVER, 'COUNTRY_CODE');
|
||||
if(!empty($server['COUNTRY_CODE']))
|
||||
$build->countryCode = (string)filter_var($server['COUNTRY_CODE']);
|
||||
|
||||
// this currently doesn't "properly" support the scenario where a full url is specified in the http request
|
||||
$path = (string)filter_input(INPUT_SERVER, 'REQUEST_URI');
|
||||
$path = (string)filter_var($server['REQUEST_URI'] ?? '/');
|
||||
$pathQueryOffset = strpos($path, '?');
|
||||
if($pathQueryOffset !== false)
|
||||
$path = substr($path, 0, $pathQueryOffset);
|
||||
|
@ -417,12 +450,12 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
$path = '/' . $path;
|
||||
|
||||
$build->path = $path;
|
||||
$build->params = HttpUri::parseQueryString((string)filter_input(INPUT_SERVER, 'QUERY_STRING'));
|
||||
$build->params = HttpUri::parseQueryString((string)filter_var($server['QUERY_STRING'] ?? ''));
|
||||
|
||||
$contentType = null;
|
||||
$contentLength = 0;
|
||||
|
||||
$headers = self::getRawRequestHeaders();
|
||||
$headers = self::getRawRequestHeaders($server);
|
||||
foreach($headers as $name => $value) {
|
||||
if($name === 'content-type')
|
||||
try {
|
||||
|
@ -438,13 +471,16 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
$build->setHeader($name, $value);
|
||||
}
|
||||
|
||||
$build->body = null;
|
||||
$build->body = HttpStream::createStreamFromFile('php://input', 'rb');
|
||||
|
||||
return $build->toRequest();
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private static function getRawRequestHeaders(): array {
|
||||
/**
|
||||
* @param ?array<string, mixed> $server
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private static function getRawRequestHeaders(?array $server = null): array {
|
||||
if(function_exists('getallheaders')) {
|
||||
$raw = getallheaders();
|
||||
$headers = [];
|
||||
|
@ -455,9 +491,10 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
return $headers;
|
||||
}
|
||||
|
||||
$server ??= $_SERVER;
|
||||
$headers = [];
|
||||
|
||||
foreach($_SERVER as $key => $value) {
|
||||
foreach($server as $key => $value) {
|
||||
if(!is_string($key) || !is_scalar($value))
|
||||
continue;
|
||||
|
||||
|
@ -472,16 +509,16 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
}
|
||||
|
||||
if(!isset($headers['authorization'])) {
|
||||
if(filter_has_var(INPUT_SERVER, 'REDIRECT_HTTP_AUTHORIZATION')) {
|
||||
$headers['authorization'] = (string)filter_input(INPUT_SERVER, 'REDIRECT_HTTP_AUTHORIZATION');
|
||||
} elseif(filter_has_var(INPUT_SERVER, 'PHP_AUTH_USER')) {
|
||||
if(!empty($server['REDIRECT_HTTP_AUTHORIZATION'])) {
|
||||
$headers['authorization'] = (string)filter_var($server['REDIRECT_HTTP_AUTHORIZATION']);
|
||||
} elseif(!empty($server['PHP_AUTH_USER'])) {
|
||||
$headers['authorization'] = sprintf('Basic %s', base64_encode(sprintf(
|
||||
'%s:%s',
|
||||
(string)filter_input(INPUT_SERVER, 'PHP_AUTH_USER'),
|
||||
(string)filter_input(INPUT_SERVER, 'PHP_AUTH_PW')
|
||||
(string)filter_var($server['PHP_AUTH_USER']),
|
||||
(string)filter_var($server['PHP_AUTH_PW'])
|
||||
)));
|
||||
} elseif(filter_has_var(INPUT_SERVER, 'PHP_AUTH_DIGEST')) {
|
||||
$headers['authorization'] = (string)filter_input(INPUT_SERVER, 'PHP_AUTH_DIGEST');
|
||||
} elseif(!empty($server['PHP_AUTH_DIGEST'])) {
|
||||
$headers['authorization'] = (string)filter_var($server['PHP_AUTH_DIGEST']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ final class HttpRequestBuilder extends HttpMessageBuilder {
|
|||
/**
|
||||
* HTTP request cookies.
|
||||
*
|
||||
* @var array<string, string[]>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $cookies = [];
|
||||
|
||||
|
@ -104,30 +104,7 @@ final class HttpRequestBuilder extends HttpMessageBuilder {
|
|||
* @param string $value Value of the cookie.
|
||||
*/
|
||||
public function setCookie(string $name, string $value): void {
|
||||
$this->cookies[$name] = [$value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value for a HTTP request cookie.
|
||||
*
|
||||
* @param string $name Name of the cookie.
|
||||
* @param string $value Value of the cookie.
|
||||
*/
|
||||
public function addCookie(string $name, string $value): void {
|
||||
if(array_key_exists($name, $this->cookies))
|
||||
$this->cookies[$name][] = $value;
|
||||
else
|
||||
$this->cookies[$name] = [$value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets values for a HTTP request cookie.
|
||||
*
|
||||
* @param string $name Name of the cookie.
|
||||
* @param string[] $values Values of the cookie.
|
||||
*/
|
||||
public function setCookieValues(string $name, array $values): void {
|
||||
$this->cookies[$name] = $values;
|
||||
$this->cookies[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Index\Http\HttpUri;
|
||||
use Index\Http\{HttpRequest,HttpUri};
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\{CoversClass,DataProvider};
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
// based on https://github.com/bakame-php/psr7-uri-interface-tests/blob/5a556fdfe668a6c6a14772efeba6134c0b7dae34/tests/AbstractUriTestCase.php
|
||||
#[CoversClass(HttpRequest::class)]
|
||||
#[CoversClass(HttpUri::class)]
|
||||
final class HttpUriTest extends TestCase {
|
||||
private const string URI = 'http://username:pwd@secure.example.com:443/meow/soap.php?soup=beans#mewow';
|
||||
|
@ -502,4 +503,36 @@ final class HttpUriTest extends TestCase {
|
|||
public function testBuildQueryString(array $queryParams, string $expected): void {
|
||||
$this->assertEquals(HttpUri::buildQueryString($queryParams), $expected);
|
||||
}
|
||||
|
||||
/** @return array<array{0: string, 1: array<string, string>}> */
|
||||
public static function cookieStringProvider(): array {
|
||||
return [
|
||||
[
|
||||
'',
|
||||
[]
|
||||
],
|
||||
[
|
||||
'soup=meow',
|
||||
['soup' => 'meow']
|
||||
],
|
||||
[
|
||||
'soup=meow;the=the1; the2=the3; the4=the5;',
|
||||
['soup' => 'meow', 'the' => 'the1', 'the2' => 'the3', 'the4' => 'the5']
|
||||
],
|
||||
[
|
||||
'test%5B%5D=%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82',
|
||||
['test[]' => 'あああああああ']
|
||||
],
|
||||
[
|
||||
' =empty',
|
||||
['' => 'empty']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @param array<string, string> $expected */
|
||||
#[DataProvider('cookieStringProvider')]
|
||||
public function testParseCookieString(string $cookieString, array $expected): void {
|
||||
$this->assertEquals(HttpRequest::parseCookieString($cookieString), $expected);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue