Skip to content

Commit 0f4ac7d

Browse files
committed
Allow OWS at the start of the InnerList parsing
1 parent 87ee36b commit 0f4ac7d

File tree

6 files changed

+101
-57
lines changed

6 files changed

+101
-57
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
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+
- `InnerList::fromHttpValue` accepts Optional White Spaces like all the other named constructors at the start of its textual representation.
14+
15+
### Deprecated
16+
17+
- None
18+
19+
### Removed
20+
21+
- None
22+
523
## [0.4.0] - 2022-03-27
624

725
### Added

src/Dictionary.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public function hasPair(int $index): bool
184184
}
185185

186186
/**
187-
* Validate and Format the submitted index position.
187+
* Validates and Format the submitted index position.
188188
*/
189189
private function filterIndex(int $index): int
190190
{
@@ -221,7 +221,9 @@ public function pair(int $index): array
221221
}
222222

223223
/**
224-
* Add a member at the end of the instance if the key is new otherwise update the value associated with the key.
224+
* Adds a member at the end of the instance otherwise updates the value associated with the key if already present.
225+
*
226+
* @throws SyntaxError If the string key is not a valid
225227
*/
226228
public function set(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): self
227229
{
@@ -231,7 +233,7 @@ public function set(string $key, InnerList|Item|ByteSequence|Token|bool|int|floa
231233
}
232234

233235
/**
234-
* Validate the instance key against RFC8941 rules.
236+
* Validates the instance key against RFC8941 rules.
235237
*/
236238
private static function filterKey(string $key): string
237239
{
@@ -251,7 +253,7 @@ private static function filterMember(InnerList|Item|ByteSequence|Token|bool|int|
251253
}
252254

253255
/**
254-
* Delete members associated with the list of submitted keys.
256+
* Deletes members associated with the list of submitted keys.
255257
*/
256258
public function delete(string ...$keys): self
257259
{
@@ -263,7 +265,7 @@ public function delete(string ...$keys): self
263265
}
264266

265267
/**
266-
* Remove all members from the instance.
268+
* Removes all members from the instance.
267269
*/
268270
public function clear(): self
269271
{
@@ -273,7 +275,9 @@ public function clear(): self
273275
}
274276

275277
/**
276-
* Add a member at the end of the instance if the key is new delete any previous reference to the key.
278+
* Adds a member at the end of the instance and deletes any previous reference to the key if present.
279+
*
280+
* @throws SyntaxError If the string key is not a valid
277281
*/
278282
public function append(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): self
279283
{
@@ -285,7 +289,9 @@ public function append(string $key, InnerList|Item|ByteSequence|Token|bool|int|f
285289
}
286290

287291
/**
288-
* Add a member at the beginning of the instance if the key is new delete any previous reference to the key.
292+
* Adds a member at the beginning of the instance and deletes any previous reference to the key if present.
293+
*
294+
* @throws SyntaxError If the string key is not a valid
289295
*/
290296
public function prepend(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): self
291297
{
@@ -297,7 +303,7 @@ public function prepend(string $key, InnerList|Item|ByteSequence|Token|bool|int|
297303
}
298304

299305
/**
300-
* Merge multiple instances.
306+
* Merges multiple instances using iterable associative structures.
301307
*
302308
* @param Dictionary|iterable<string, InnerList|Item|ByteSequence|Token|bool|int|float|string> ...$others
303309
*/
@@ -311,7 +317,7 @@ public function mergeAssociative(Dictionary|iterable ...$others): self
311317
}
312318

313319
/**
314-
* Merge multiple instances using iterable pairs.
320+
* Merges multiple instances using iterable pairs.
315321
*
316322
* @param Dictionary|iterable<array{0:string, 1:InnerList|Item|ByteSequence|Token|bool|int|float|string}> ...$others
317323
*/

src/InnerListTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,15 @@ public function it_successfully_parse_a_http_field(): void
162162
self::assertSame(42.0, $instance->get(2)->value);
163163
self::assertInstanceOf(Token::class, $instance->get(2)->parameters->value('john'));
164164
}
165+
166+
/**
167+
* @test
168+
*/
169+
public function it_successfully_parse_a_http_field_with_optional_white_spaces_in_front(): void
170+
{
171+
self::assertEquals(
172+
InnerList::fromHttpValue('("hello)world" 42 42.0;john=doe);foo="bar("'),
173+
InnerList::fromHttpValue(' ("hello)world" 42 42.0;john=doe);foo="bar("')
174+
);
175+
}
165176
}

src/OrderedList.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function get(int $index): Item|InnerList
126126
}
127127

128128
/**
129-
* Insert members at the beginning of the list.
129+
* Inserts members at the beginning of the list.
130130
*/
131131
public function unshift(InnerList|Item|ByteSequence|Token|bool|int|float|string ...$members): self
132132
{
@@ -136,7 +136,7 @@ public function unshift(InnerList|Item|ByteSequence|Token|bool|int|float|string
136136
}
137137

138138
/**
139-
* Insert members at the end of the list.
139+
* Inserts members at the end of the list.
140140
*/
141141
public function push(InnerList|Item|ByteSequence|Token|bool|int|float|string ...$members): self
142142
{
@@ -146,7 +146,7 @@ public function push(InnerList|Item|ByteSequence|Token|bool|int|float|string ...
146146
}
147147

148148
/**
149-
* Insert members starting at the given index.
149+
* Inserts members starting at the given index.
150150
*
151151
* @throws InvalidOffset If the index does not exist
152152
*/
@@ -166,7 +166,7 @@ public function insert(
166166
}
167167

168168
/**
169-
* Replace the member associated with the index.
169+
* Replaces the member associated with the index.
170170
*
171171
* @throws InvalidOffset If the index does not exist
172172
*/
@@ -182,7 +182,7 @@ public function replace(int $index, InnerList|Item|ByteSequence|Token|bool|int|f
182182
}
183183

184184
/**
185-
* Delete members associated with the list of instance indexes.
185+
* Deletes members associated with the list of instance indexes.
186186
*/
187187
public function remove(int ...$indexes): self
188188
{
@@ -203,7 +203,7 @@ public function remove(int ...$indexes): self
203203
}
204204

205205
/**
206-
* Remove all members from the instance.
206+
* Removes all members from the instance.
207207
*/
208208
public function clear(): self
209209
{

src/Parameters.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public function hasPair(int $index): bool
277277
}
278278

279279
/**
280-
* Filter and format instance index.
280+
* Filters and format instance index.
281281
*/
282282
private function filterIndex(int $index): int
283283
{
@@ -316,7 +316,7 @@ public function pair(int $index): array
316316
}
317317

318318
/**
319-
* Add a member at the end of the instance if the key is new otherwise update the value associated with the key.
319+
* Adds a member at the end of the instance otherwise updates the value associated with the key if already present.
320320
*
321321
* @throws SyntaxError If the string key is not a valid
322322
* @throws ForbiddenStateError if the found item is in invalid state
@@ -329,7 +329,7 @@ public function set(string $key, Item|ByteSequence|Token|bool|int|float|string $
329329
}
330330

331331
/**
332-
* Delete members associated with the list of submitted keys.
332+
* Deletes members associated with the list of submitted keys.
333333
*/
334334
public function delete(string ...$keys): self
335335
{
@@ -341,7 +341,7 @@ public function delete(string ...$keys): self
341341
}
342342

343343
/**
344-
* Remove all members from the instance.
344+
* Removes all members from the instance.
345345
*/
346346
public function clear(): self
347347
{
@@ -351,7 +351,7 @@ public function clear(): self
351351
}
352352

353353
/**
354-
* Add a member at the end of the instance if the key is new delete any previous reference to the key.
354+
* Adds a member at the end of the instance and deletes any previous reference to the key if present.
355355
*
356356
* @throws SyntaxError If the string key is not a valid
357357
* @throws ForbiddenStateError if the found item is in invalid state
@@ -366,7 +366,7 @@ public function append(string $key, Item|ByteSequence|Token|bool|int|float|strin
366366
}
367367

368368
/**
369-
* Add a member at the beginning of the instance if the key is new delete any previous reference to the key.
369+
* Adds a member at the beginning of the instance and deletes any previous reference to the key if present.
370370
*
371371
* @throws SyntaxError If the string key is not a valid
372372
* @throws ForbiddenStateError if the found item is in invalid state
@@ -381,7 +381,7 @@ public function prepend(string $key, Item|ByteSequence|Token|bool|int|float|stri
381381
}
382382

383383
/**
384-
* Merge multiple instances using associative pairs.
384+
* Merges multiple instances using iterable associative structures.
385385
*
386386
* @param Parameters|iterable<array-key, Item|Token|ByteSequence|float|int|bool|string> ...$others
387387
* @throws ForbiddenStateError if the found item is in invalid state

src/Parser.php

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,7 @@ public static function parseList(string $httpValue): array
4040
$remainder = ltrim($httpValue, ' ');
4141
while ('' !== $remainder) {
4242
[$members[], $offset] = self::parseItemOrInnerList($remainder);
43-
$remainder = ltrim(substr($remainder, $offset), " \t");
44-
if ('' === $remainder) {
45-
return $members;
46-
}
47-
48-
if (1 !== preg_match('/^(?<space>,[ \t]*)/', $remainder, $found)) {
49-
throw new SyntaxError("The HTTP textual representation `$httpValue` for a list is missing an excepted comma.");
50-
}
51-
52-
$remainder = substr($remainder, strlen($found['space']));
53-
if ('' === $remainder) {
54-
throw new SyntaxError("Unexpected end of line for The HTTP textual representation `$httpValue` of a list.");
55-
}
43+
$remainder = self::removeCommaSeparatedWhiteSpaces($remainder, $offset);
5644
}
5745

5846
return $members;
@@ -75,27 +63,12 @@ public static function parseDictionary(string $httpValue): array
7563
while ('' !== $remainder) {
7664
[$key, $offset] = self::parseKey($remainder);
7765
$remainder = substr($remainder, $offset);
78-
if ('' !== $remainder && $remainder[0] === '=') {
79-
$remainder = substr($remainder, 1);
80-
[$members[$key], $offset] = self::parseItemOrInnerList($remainder);
81-
} else {
82-
[$parameters, $offset] = self::parseParameters($remainder);
83-
$members[$key] = Item::from(true, $parameters);
84-
}
85-
86-
$remainder = ltrim(substr($remainder, $offset), " \t");
87-
if ('' === $remainder) {
88-
return $members;
89-
}
90-
91-
if (1 !== preg_match('/^(?<space>,[ \t]*)/', $remainder, $found)) {
92-
throw new SyntaxError("The HTTP textual representation `$httpValue` for a dictionary is missing an excepted comma.");
66+
if ('' === $remainder || '=' !== $remainder[0]) {
67+
$remainder = '=?1'.$remainder;
9368
}
9469

95-
$remainder = substr($remainder, strlen($found['space']));
96-
if ('' === $remainder) {
97-
throw new SyntaxError("Unexpected end of line for The HTTP textual representation `$httpValue` for a dictionary.");
98-
}
70+
[$members[$key], $offset] = self::parseItemOrInnerList(substr($remainder, 1));
71+
$remainder = self::removeCommaSeparatedWhiteSpaces($remainder, ++$offset);
9972
}
10073

10174
return $members;
@@ -113,19 +86,55 @@ public static function parseDictionary(string $httpValue): array
11386
*/
11487
public static function parseInnerList(string $httpValue): array
11588
{
116-
if ('(' !== $httpValue[0]) {
89+
$remainder = ltrim($httpValue, ' ');
90+
if ('(' !== $remainder[0]) {
11791
throw new SyntaxError("The HTTP textual representation `$httpValue` for a inner list is missing a parenthesis.");
11892
}
11993

120-
[$members, $offset] = self::parseInnerListValue($httpValue);
121-
$remainder = ltrim(substr($httpValue, $offset), " \t");
94+
[$members, $offset] = self::parseInnerListValue($remainder);
95+
$remainder = self::removeOptionalWhiteSpaces(substr($remainder, $offset));
96+
12297
if ('' !== $remainder) {
12398
throw new SyntaxError("The HTTP textual representation `$httpValue` for a inner list contains invalid data.");
12499
}
125100

126101
return $members;
127102
}
128103

104+
/**
105+
* Filter optional white spaces before and after comma.
106+
*
107+
* @see https://tools.ietf.org/html/rfc7230#section-3.2.3
108+
*/
109+
private static function removeCommaSeparatedWhiteSpaces(string $remainder, int $offset): string
110+
{
111+
$remainder = self::removeOptionalWhiteSpaces(substr($remainder, $offset));
112+
if ('' === $remainder) {
113+
return $remainder;
114+
}
115+
116+
if (1 !== preg_match('/^(?<space>,[ \t]*)/', $remainder, $found)) {
117+
throw new SyntaxError('The HTTP textual representation is missing an excepted comma.');
118+
}
119+
120+
$remainder = substr($remainder, strlen($found['space']));
121+
if ('' === $remainder) {
122+
throw new SyntaxError('Unexpected end of line for The HTTP textual representation.');
123+
}
124+
125+
return $remainder;
126+
}
127+
128+
/**
129+
* Remove optional white spaces before field value.
130+
*
131+
* @see https://tools.ietf.org/html/rfc7230#section-3.2.3
132+
*/
133+
private static function removeOptionalWhiteSpaces(string $value): string
134+
{
135+
return ltrim($value, " \t");
136+
}
137+
129138
/**
130139
* Returns an Item or an InnerList value object from an HTTP textual representation.
131140
*

0 commit comments

Comments
 (0)