From fa195a7d60d298208d206b32465fcf05847ec259 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 30 Oct 2024 03:07:57 +0100 Subject: [PATCH] v3.1 --- README.md | 29 +++++++-- src/Request/UrlEncodedRequest.php | 81 +++++++++++++++++++++++++ tests/Request/UrlEncodedRequestTest.php | 54 +++++++++++++++++ 3 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 src/Request/UrlEncodedRequest.php create mode 100644 tests/Request/UrlEncodedRequestTest.php diff --git a/README.md b/README.md index 6ed7b71..5407ff7 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,7 @@ composer require sunrise/http-message ## Documentation Navigation - [Server Request from Global Environment](#server-request-from-global-environment) -- [JSON and HTML Messages](#json-and-html-messages) -- - [JSON Request](#json-request) -- - [JSON Response](#json-response) -- - [HTML Response](#html-response) +- [Typed Messages](#typed-messages) - [Streams](#streams) - - [File Stream](#file-stream) - - [PHP Input Stream](#php-input-stream) @@ -43,7 +40,7 @@ use Sunrise\Http\Message\ServerRequestFactory; $request = ServerRequestFactory::fromGlobals(); ``` -### JSON and HTML Messages +### Typed Messages #### JSON Request @@ -61,6 +58,28 @@ You can also specify [encoding flags](https://www.php.net/manual/en/json.constan $request = new JsonRequest('GET', '/', $data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE, 512); ``` +#### URL Encoded Request + +```php +use Sunrise\Http\Message\Request\UrlEncodedRequest; + +/** @var $data mixed */ + +$request = new UrlEncodedRequest('GET', '/', $data); +``` + +You can also specify [encoding type](https://www.php.net/manual/ru/url.constants.php#constant.php-query-rfc1738) like below: + +```php +use Sunrise\Http\Message\Request\UrlEncodedRequest; + +$encodingType = UrlEncodedRequest::ENCODING_TYPE_RFC1738; +// or +$encodingType = UrlEncodedRequest::ENCODING_TYPE_RFC3986; + +$request = new UrlEncodedRequest('GET', '/', $data, $encodingType); +``` + #### JSON Response ```php diff --git a/src/Request/UrlEncodedRequest.php b/src/Request/UrlEncodedRequest.php new file mode 100644 index 0000000..b54d8c1 --- /dev/null +++ b/src/Request/UrlEncodedRequest.php @@ -0,0 +1,81 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Request; + +use Psr\Http\Message\StreamInterface; +use Sunrise\Http\Message\Exception\InvalidArgumentException; +use Sunrise\Http\Message\Request; +use Sunrise\Http\Message\Stream\PhpTempStream; +use TypeError; + +use function gettype; +use function http_build_query; +use function is_array; +use function is_object; +use function sprintf; + +use const PHP_QUERY_RFC1738; +use const PHP_QUERY_RFC3986; + +/** + * @since 3.1.0 + */ +final class UrlEncodedRequest extends Request +{ + public const ENCODING_TYPE_RFC1738 = PHP_QUERY_RFC1738; + public const ENCODING_TYPE_RFC3986 = PHP_QUERY_RFC3986; + + /** + * @param mixed $uri + * @param array|object $data + * @param self::ENCODING_TYPE_* $encodingType + * + * @throws InvalidArgumentException + */ + public function __construct(string $method, $uri, $data, int $encodingType = self::ENCODING_TYPE_RFC1738) + { + /** + * @psalm-suppress DocblockTypeContradiction + * @phpstan-ignore-next-line + */ + if (!is_array($data) && !is_object($data)) { + throw new TypeError(sprintf( + 'Argument #3 ($data) must be of type string, %s given', + gettype($data), + )); + } + + parent::__construct($method, $uri); + + $this->setBody(self::createBody($data, $encodingType)); + $this->setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'); + } + + /** + * @param array|object $data + * @param self::ENCODING_TYPE_* $encodingType + */ + private static function createBody($data, int $encodingType): StreamInterface + { + if ($data instanceof StreamInterface) { + return $data; + } + + $encodedData = http_build_query($data, '', null, $encodingType); + + $stream = new PhpTempStream('r+b'); + $stream->write($encodedData); + $stream->rewind(); + + return $stream; + } +} diff --git a/tests/Request/UrlEncodedRequestTest.php b/tests/Request/UrlEncodedRequestTest.php new file mode 100644 index 0000000..50e6c54 --- /dev/null +++ b/tests/Request/UrlEncodedRequestTest.php @@ -0,0 +1,54 @@ + 'bar']); + + $this->assertSame('POST', $request->getMethod()); + $this->assertSame('/', (string) $request->getUri()); + $this->assertSame('application/x-www-form-urlencoded; charset=utf-8', $request->getHeaderLine('Content-Type')); + $this->assertStringStartsWith('php://temp', $request->getBody()->getMetadata('uri')); + $this->assertTrue($request->getBody()->isReadable()); + $this->assertTrue($request->getBody()->isWritable()); + $this->assertSame('foo=bar', $request->getBody()->__toString()); + } + + public function testConstructorWithObject(): void + { + $request = new UrlEncodedRequest('POST', '/', (object) ['foo' => 'bar']); + + $this->assertSame('foo=bar', $request->getBody()->__toString()); + } + + public function testConstructorWithDefaultEncodingType(): void + { + $request = new UrlEncodedRequest('POST', '/', ['foo' => 'bar baz']); + + $this->assertSame('foo=bar+baz', $request->getBody()->__toString()); + } + + public function testConstructorWithEncodingType(): void + { + $request = new UrlEncodedRequest('POST', '/', ['foo' => 'bar baz'], UrlEncodedRequest::ENCODING_TYPE_RFC3986); + + $this->assertSame('foo=bar%20baz', $request->getBody()->__toString()); + } + + public function testConstructorWithStream(): void + { + $body = $this->createMock(StreamInterface::class); + $request = new UrlEncodedRequest('POST', '/', $body); + + $this->assertSame($body, $request->getBody()); + } +}