Made FormContent iterable.

This commit is contained in:
flash 2025-03-15 00:00:53 +00:00
parent 3bb955d61e
commit cc351a2f05
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
5 changed files with 108 additions and 7 deletions

View file

@ -1 +1 @@
0.2503.142326
0.2503.142359

View file

@ -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.
*

View file

@ -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]);

View file

@ -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]);

View file

@ -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);
}
}