diff --git a/VERSION b/VERSION index bdd4fdb..99f6ee8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2503.142326 +0.2503.142359 diff --git a/src/Http/Content/FormContent.php b/src/Http/Content/FormContent.php index 787ef11..32fb08c 100644 --- a/src/Http/Content/FormContent.php +++ b/src/Http/Content/FormContent.php @@ -1,14 +1,19 @@ <?php // FormContent.php // Created: 2025-03-12 -// Updated: 2025-03-12 +// Updated: 2025-03-14 namespace Index\Http\Content; +use Iterator; + /** * Provides a common interface for application/x-www-form-urlencoded and multipart/form-data. + * + * @template TValue of string|\Stringable + * @extends Iterator<string, ?list<TValue>> */ -interface FormContent extends Content { +interface FormContent extends Content, Iterator { /** * Checks if a form field is present. * diff --git a/src/Http/Content/MultipartFormContent.php b/src/Http/Content/MultipartFormContent.php index 6b43a7c..8888373 100644 --- a/src/Http/Content/MultipartFormContent.php +++ b/src/Http/Content/MultipartFormContent.php @@ -1,7 +1,7 @@ <?php // MultipartFormContent.php // Created: 2025-03-12 -// Updated: 2025-03-12 +// Updated: 2025-03-14 namespace Index\Http\Content; @@ -12,8 +12,16 @@ use Psr\Http\Message\StreamInterface; /** * Implements multipart/form-data. + * + * @implements FormContent<MultipartFormData> */ class MultipartFormContent implements FormContent { + /** @var string[] */ + private array $keys; + + /** @var int<0, max> */ + private int $position = 0; + /** * @param StreamInterface $stream Raw request body stream. * @param array<string, list<MultipartFormData>> $params Form parameters. @@ -23,7 +31,33 @@ class MultipartFormContent implements FormContent { public private(set) StreamInterface $stream, public private(set) array $params, public private(set) array $files, - ) {} + ) { + $this->keys = array_keys($params); + } + + public function rewind(): void { + $this->position = 0; + } + + public function valid(): bool { + return $this->position < count($this->keys); + } + + /** @return ?list<?MultipartFormData> */ + #[\ReturnTypeWillChange] + public function current() { + return $this->valid() ? $this->params[$this->keys[$this->position]] : null; + } + + /** @return ?string */ + #[\ReturnTypeWillChange] + public function key() { + return $this->valid() ? $this->keys[$this->position] : null; + } + + public function next(): void { + ++$this->position; + } public function hasParam(string $name): bool { return isset($this->params[$name]); diff --git a/src/Http/Content/UrlEncodedFormContent.php b/src/Http/Content/UrlEncodedFormContent.php index b6e841e..0bf2525 100644 --- a/src/Http/Content/UrlEncodedFormContent.php +++ b/src/Http/Content/UrlEncodedFormContent.php @@ -1,7 +1,7 @@ <?php // UrlEncodedFormContent.php // Created: 2025-03-12 -// Updated: 2025-03-12 +// Updated: 2025-03-14 namespace Index\Http\Content; @@ -10,8 +10,16 @@ use Psr\Http\Message\StreamInterface; /** * Implements application/x-www-form-urlencoded. + * + * @implements FormContent<string> */ class UrlEncodedFormContent implements FormContent { + /** @var string[] */ + private array $keys; + + /** @var int<0, max> */ + private int $position = 0; + /** * @param StreamInterface $stream Raw request body stream. * @param array<string, list<?string>> $params Form parameters. @@ -19,7 +27,33 @@ class UrlEncodedFormContent implements FormContent { public function __construct( public private(set) StreamInterface $stream, public private(set) array $params, - ) {} + ) { + $this->keys = array_keys($params); + } + + public function rewind(): void { + $this->position = 0; + } + + public function valid(): bool { + return $this->position < count($this->keys); + } + + /** @return ?list<?string> */ + #[\ReturnTypeWillChange] + public function current() { + return $this->valid() ? $this->params[$this->keys[$this->position]] : null; + } + + /** @return ?string */ + #[\ReturnTypeWillChange] + public function key() { + return $this->valid() ? $this->keys[$this->position] : null; + } + + public function next(): void { + ++$this->position; + } public function hasParam(string $name): bool { return isset($this->params[$name]); diff --git a/tests/HttpFormContentTest.php b/tests/HttpFormContentTest.php index 808b9ce..870b004 100644 --- a/tests/HttpFormContentTest.php +++ b/tests/HttpFormContentTest.php @@ -75,6 +75,9 @@ final class HttpFormContentTest extends TestCase { for($i = 0; $i < $count; ++$i) $this->assertEquals($form->getParamAt($key, $i), $values[$i]); } + + $extracted = iterator_to_array($form); + $this->assertEquals($expected, $extracted); } public function testMultipartForm(): void { @@ -136,5 +139,30 @@ final class HttpFormContentTest extends TestCase { $this->assertIsString($the2Path); $the2->moveTo($the2Path); $this->assertEquals($the2Hash, hash_file('sha256', $the2Path)); + + $expected = [ + 'meow' => ['sfdsfs'], + 'mewow' => ['https://railgun.sh/sockchat'], + '"the' => [ + 'value!', + base64_decode( + 'iVBORw0KGgoAAAANSUhEUgAAABkAAAAdCAYAAABfeMd1AAAABHNCSVQICAgIfAhkiAAAArVJREFUSEu1' + . 'Vj9o00EUfr8gdNFBKtVEsUvjUgfpFBQKKrR1tRlUEEwFB8U4OHXroHSqaAanSgRRI0ZRpCUGFAwKpUJF' + . 'cEqqECiJLRgHC6XT2e/0Xe/3ctekBg9+9N37833vvb67C1GLFd9JqoVLZ2YQrNQXFf7+NzIAT6bTaqFU' + . '6ogs8NVqZz42lqaL42njeize5wyrrJITz6kEwYfKohOIlUuVmhZf5/M0pfJa3p39o5NkTSQgeDJbMgQH' + . '4jEjS8FFtD6kaN/ZOtlEIRIQoDVy2a2StnaInJW0apWPaOjpGW2S1UTsgHb+F5IAe27p9SCpzV3FgL4/' + . 'jhIPjyHxEaAd3BIXga0bTiaJiWx9qBLbwOCYHs703mTGuEC2yV0DwtVEUIF9JlyZuypB1kjAZev7eFC3' + . 'jJeuZO7BafpcukF2ptADhBeDcRLInImMkyWAaHn/FY0dwTwnzj+nteoCvZyfN8AguHR8icYzGRoYHNSf' + . 'JIYO9p5o8w3QaDQ0JrBNTWhZ9+EEwZgaGaFsoUDlcpmUUnRib1YTzBRqdOf+ism3mDui5bfLKQqCgDbu' + . 'OR3Hqz71Sx/MHazQJ/TLnApih7Rjbnqaen/cNgEsXLvQY3TF3KZZEjSGV40xNF0gUrUyvX832wTeSoHW' + . 'Ig7JoRP2MpX4QNY+3dxok9uK9mE1Xoy6Hf5qtyT59nWG+nsHDMCb0bshsJPPLnvBcbXwajqMaBneCx5f' + . 'TAg+37Jt1Yf9oavfS2KDnZtYJ1SDD2BHbyWMGbJNAJ9T1W5tt6vAvukWZhSMNKri2+DRRJfNH5J94HDC' + . 'CHtJJKJ99Ujbz9TmwyarCJ0TGSj38km17fFsTdlEMrbtSmSg3KNSJuJq5DMsY/5pD6I9V2Nq16to6Gbf' + . '8pxsl0kPClpH/h8f28X0+ss3yuvYiUFO4m81NF/DgbLLlAAAAABJRU5ErkJggg==' + ), + ], + ]; + $extracted = iterator_to_array($form); + $this->assertEquals($expected, $extracted); } }