Skip to content

Commit 64aa702

Browse files
committed
Internal parser no longer depends on Item
1 parent f57b6b5 commit 64aa702

File tree

8 files changed

+75
-49
lines changed

8 files changed

+75
-49
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
All Notable changes to `bakame/http-strucured-fields` will be documented in this file.
44

5+
## [Next] - TBD
6+
7+
### Added
8+
9+
- None
10+
11+
### Fixed
12+
13+
- `Parser` no longer returns the `Item` instance
14+
- `Parser` improve internal Date generation
15+
- `Value` improve float serialization
16+
- `OuterList::fromHttpValue`, `InnerList::fromHttpValue`, `Dictionnary::fromHttpValue` rewritten to improve decoupling from `Parser`
17+
18+
### Deprecated
19+
20+
- None
21+
22+
### Removed
23+
24+
- None
25+
526
## [1.0.0] - 2023-04-16
627

728
### Added

src/Dictionary.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,14 @@ public static function fromHttpValue(Stringable|string $httpValue): self
113113
{
114114
return new self((function (iterable $members) {
115115
foreach ($members as $key => $member) {
116-
yield $key => is_array($member) ? InnerList::fromAssociative($member[0], $member[1]) : $member;
116+
if (!is_array($member[0])) {
117+
yield $key => Item::fromAssociative(...$member);
118+
119+
continue;
120+
}
121+
122+
$member[0] = array_map(fn (array $item) => Item::fromAssociative(...$item), $member[0]);
123+
yield $key => InnerList::fromAssociative(...$member);
117124
}
118125
})(Parser::parseDictionary($httpValue)));
119126
}

src/InnerList.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ private static function filterMember(mixed $member): object
6060
*/
6161
public static function fromHttpValue(Stringable|string $httpValue): self
6262
{
63-
return self::fromAssociative(...Parser::parseInnerList($httpValue));
63+
[$membersList, $parameters] = Parser::parseInnerList($httpValue);
64+
65+
return self::fromAssociative(
66+
array_map(fn (array $member): Item => Item::fromAssociative(...$member), $membersList),
67+
$parameters
68+
);
6469
}
6570

6671
/**

src/OuterList.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,18 @@ private static function filterMember(mixed $member): object
6161
*/
6262
public static function fromHttpValue(Stringable|string $httpValue): self
6363
{
64-
return self::new(...array_map(
65-
fn (mixed $value) => is_array($value) ? InnerList::fromAssociative($value[0], $value[1]) : $value,
66-
Parser::parseList($httpValue)
67-
));
64+
return self::new(...(function (iterable $members) {
65+
foreach ($members as $member) {
66+
if (!is_array($member[0])) {
67+
yield Item::fromAssociative(...$member);
68+
69+
continue;
70+
}
71+
72+
$member[0] = array_map(fn (array $item) => Item::fromAssociative(...$item), $member[0]);
73+
yield InnerList::fromAssociative(...$member);
74+
}
75+
})(Parser::parseList($httpValue)));
6876
}
6977

7078
/**

src/Parser.php

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Bakame\Http\StructuredFields;
66

77
use DateTimeImmutable;
8-
use DateTimeZone;
98
use Stringable;
109
use function in_array;
1110
use function ltrim;
@@ -25,7 +24,7 @@
2524
* @internal Use Dictionary::fromHttpValue(), Parameters::fromHttpValue(),
2625
* OuterList::fromHttpValue(), InnerList::fromHttpValue() or Item::fromHttpValue() instead
2726
*
28-
* @phpstan-import-type SfTypeInput from StructuredField
27+
* @phpstan-import-type SfType from StructuredField
2928
* @phpstan-import-type SfItem from StructuredField
3029
* @phpstan-import-type SfItemInput from StructuredField
3130
*/
@@ -36,7 +35,7 @@ final class Parser
3635
*
3736
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.1
3837
*
39-
* @return array<SfItemInput|array{0:array<SfItemInput>, 1:array<string, SfItemInput>}>
38+
* @return array<array{0:SfType|array<array{0:SfType, 1:array<string, SfType>}>, 1:array<string, SfType>}>
4039
*/
4140
public static function parseList(Stringable|string $httpValue): array
4241
{
@@ -55,7 +54,7 @@ public static function parseList(Stringable|string $httpValue): array
5554
*
5655
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.2
5756
*
58-
* @return array<string, SfItemInput|array{0:array<SfItemInput>, 1:array<string,SfItemInput>}>
57+
* @return array<string, array{0:SfType|array<array{0:SfType, 1:array<string, SfType>}>, 1:array<string, SfType>}>
5958
*/
6059
public static function parseDictionary(Stringable|string $httpValue): array
6160
{
@@ -80,7 +79,7 @@ public static function parseDictionary(Stringable|string $httpValue): array
8079
*
8180
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.1.2
8281
*
83-
* @return array{0:array<SfItemInput>, 1:array<string, SfItemInput>}
82+
* @return array{0:array<array{0:SfType, 1:array<string, SfType>}>, 1:array<string, SfType>}
8483
*/
8584
public static function parseInnerList(Stringable|string $httpValue): array
8685
{
@@ -137,7 +136,7 @@ private static function removeOptionalWhiteSpaces(string $httpValue): string
137136
*
138137
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.1.1
139138
*
140-
* @return array{0:array{0:array<SfItemInput>, 1:array<string,SfItemInput>}|SfItem, 1:int}
139+
* @return array{0:array{0:SfType|array<array{0:SfType, 1:array<string, SfType>}>, 1:array<string, SfType>}, 1:int}
141140
*/
142141
private static function parseItemOrInnerList(string $httpValue): array
143142
{
@@ -155,7 +154,7 @@ private static function parseItemOrInnerList(string $httpValue): array
155154
*
156155
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.1.2
157156
*
158-
* @return array{0:array{0:array<SfItemInput>, 1:array<string, SfItemInput>}, 1:int}
157+
* @return array{0:array{0:array<array{0:SfType, 1:array<string, SfType>}>, 1:array<string, SfType>}, 1:int}
159158
*/
160159
private static function parseInnerListValue(string $httpValue): array
161160
{
@@ -182,12 +181,24 @@ private static function parseInnerListValue(string $httpValue): array
182181
throw new SyntaxError("The HTTP textual representation \"$remainder\" for a inner list has an unexpected end of line.");
183182
}
184183

184+
/**
185+
* @return array{0:array{0:SfType, 1:array<string, SfType>}, 1:string}
186+
*/
187+
private static function parseItem(string $remainder): array
188+
{
189+
[$value, $offset] = self::parseBareItem($remainder);
190+
$remainder = substr($remainder, $offset);
191+
[$parameters, $offset] = self::parseParameters($remainder);
192+
193+
return [[$value, $parameters], substr($remainder, $offset)];
194+
}
195+
185196
/**
186197
* Returns an Item value from an HTTP textual representation and the consumed offset in a tuple.
187198
*
188199
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.3.1
189200
*
190-
* @return array{0:ByteSequence|Token|DateTimeImmutable|string|int|float|bool, 1:int}
201+
* @return array{0:SfType, 1:int}
191202
*/
192203
public static function parseBareItem(string $httpValue): array
193204
{
@@ -207,7 +218,7 @@ public static function parseBareItem(string $httpValue): array
207218
*
208219
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.3.2
209220
*
210-
* @return array{0:array<string, SfTypeInput>, 1:int}
221+
* @return array{0:array<string, SfType>, 1:int}
211222
*/
212223
public static function parseParameters(string $httpValue): array
213224
{
@@ -280,10 +291,7 @@ private static function parseDate(string $httpValue): array
280291
throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Date contains invalid characters.");
281292
}
282293

283-
return [
284-
(new DateTimeImmutable('NOW', new DateTimeZone('UTC')))->setTimestamp((int) $found['date']),
285-
strlen($found['date']) + 1,
286-
];
294+
return [new DateTimeImmutable('@'.$found['date']), strlen($found['date']) + 1];
287295
}
288296

289297
/**
@@ -362,16 +370,4 @@ private static function parseByteSequence(string $httpValue): array
362370

363371
return [ByteSequence::fromEncoded($matches['byte']), strlen($matches['sequence'])];
364372
}
365-
366-
/**
367-
* @return array{0:SfItem, 1:string}
368-
*/
369-
private static function parseItem(string $remainder): array
370-
{
371-
[$value, $offset] = self::parseBareItem($remainder);
372-
$remainder = substr($remainder, $offset);
373-
[$parameters, $offset] = self::parseParameters($remainder);
374-
375-
return [Item::fromAssociative($value, $parameters), substr($remainder, $offset)];
376-
}
377373
}

src/ParserTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public function it_parse_a_date_item(): void
4545
{
4646
$field = Parser::parseDictionary('a=@12345678;key=1');
4747

48-
self::assertInstanceOf(Item::class, $field['a']);
49-
self::assertInstanceOf(DateTimeImmutable::class, $field['a']->value());
48+
self::assertInstanceOf(DateTimeImmutable::class, $field['a'][0]);
49+
self::assertSame(1, $field['a'][1]['key']);
5050
}
5151

5252
#[Test]

src/StructuredField.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
namespace Bakame\Http\StructuredFields;
66

7+
use DateTimeInterface;
78
use Stringable;
89

910
/**
10-
* @phpstan-type SfTypeInput StructuredField|ByteSequence|Token|\DateTimeInterface|string|int|float|bool
11+
* @phpstan-type SfType ByteSequence|Token|DateTimeInterface|string|int|float|bool
12+
* @phpstan-type SfTypeInput StructuredField|SfType
1113
* @phpstan-type SfItem ValueAccess&ParameterAccess
1214
* @phpstan-type SfItemInput SfItem|SfTypeInput
1315
* @phpstan-type SfMember (MemberList<int, SfItem>|ValueAccess)&ParameterAccess

src/Value.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use function preg_match;
2020
use function preg_replace;
2121
use function round;
22-
use function str_contains;
22+
use const JSON_PRESERVE_ZERO_FRACTION;
2323
use const PHP_ROUND_HALF_EVEN;
2424

2525
/**
@@ -218,25 +218,12 @@ public function serialize(): string
218218
$this->value instanceof Token => $this->value->toString(),
219219
$this->value instanceof ByteSequence => ':'.$this->value->encoded().':',
220220
is_int($this->value) => (string) $this->value,
221-
is_float($this->value) => self::serializeDecimal($this->value),
221+
is_float($this->value) => (string) json_encode(round($this->value, 3, PHP_ROUND_HALF_EVEN), JSON_PRESERVE_ZERO_FRACTION),
222222
is_bool($this->value) => '?'.($this->value ? '1' : '0'),
223223
default => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"',
224224
};
225225
}
226226

227-
/**
228-
* Serialize the Item decimal value according to RFC8941.
229-
*
230-
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.1.5
231-
*/
232-
private static function serializeDecimal(float $value): string
233-
{
234-
/** @var string $result */
235-
$result = json_encode(round($value, 3, PHP_ROUND_HALF_EVEN));
236-
237-
return str_contains($result, '.') ? $result : $result.'.0';
238-
}
239-
240227
public function equals(mixed $value): bool
241228
{
242229
if ($value instanceof self) {

0 commit comments

Comments
 (0)