Skip to content

Commit f44228f

Browse files
authored
Merge pull request #2 from bakame-php/feature/remove-regexp
[WIP] Retrofit add and adapt Parser implementation from gapple
2 parents 9678d25 + e32800e commit f44228f

15 files changed

+574
-377
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ use Bakame\Http\StructuredFields\InnerList;
224224
use Bakame\Http\StructuredFields\OrderedList;
225225
use Bakame\Http\StructuredFields\Token;
226226

227-
$innerList = InnerList::fromElements([42, 42.0, "42"], ["a" => true]);
227+
$innerList = InnerList::fromMembers([42, 42.0, "42"], ["a" => true]);
228228
$innerList->has(2); //return true
229229
$innerList->has(42); //return false
230230
$innerList->push(new Token('forty-two'));

src/Dictionary.php

Lines changed: 47 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final class Dictionary implements Countable, IteratorAggregate, StructuredField
1515
{
1616
private function __construct(
1717
/** @var array<string, Item|InnerList> */
18-
private array $elements = []
18+
private array $members = []
1919
) {
2020
}
2121

@@ -25,13 +25,13 @@ private function __construct(
2525
* its keys represent the dictionary entry key
2626
* its values represent the dictionary entry value
2727
*
28-
* @param iterable<string, InnerList|Item|ByteSequence|Token|bool|int|float|string> $elements
28+
* @param iterable<string, InnerList|Item|ByteSequence|Token|bool|int|float|string> $members
2929
*/
30-
public static function fromAssociative(iterable $elements = []): self
30+
public static function fromAssociative(iterable $members = []): self
3131
{
3232
$instance = new self();
33-
foreach ($elements as $index => $element) {
34-
$instance->set($index, $element);
33+
foreach ($members as $index => $member) {
34+
$instance->set($index, $member);
3535
}
3636

3737
return $instance;
@@ -40,17 +40,17 @@ public static function fromAssociative(iterable $elements = []): self
4040
/**
4141
* Returns a new instance from a pair iterable construct.
4242
*
43-
* Each element is composed of an array with two elements
44-
* the first element represents the instance entry key
45-
* the second element represents the instance entry value
43+
* Each member is composed of an array with two elements
44+
* the first member represents the instance entry key
45+
* the second member represents the instance entry value
4646
*
4747
* @param iterable<array{0:string, 1:InnerList|Item|ByteSequence|Token|bool|int|float|string}> $pairs
4848
*/
4949
public static function fromPairs(iterable $pairs = []): self
5050
{
5151
$instance = new self();
52-
foreach ($pairs as [$key, $element]) {
53-
$instance->set($key, $element);
52+
foreach ($pairs as [$key, $member]) {
53+
$instance->set($key, $member);
5454
}
5555

5656
return $instance;
@@ -63,67 +63,16 @@ public static function fromPairs(iterable $pairs = []): self
6363
*/
6464
public static function fromHttpValue(string $httpValue): self
6565
{
66-
$instance = new self();
67-
$httpValue = trim($httpValue, ' ');
68-
if ('' === $httpValue) {
69-
return $instance;
70-
}
71-
72-
if (1 === preg_match("/[^\x20-\x7E\t]/", $httpValue) || str_starts_with($httpValue, "\t")) {
73-
throw new SyntaxError("The HTTP textual representation `$httpValue` for dictionary contains invalid characters.");
74-
}
75-
76-
$parser = fn (string $element): Item|InnerList => str_starts_with($element, '(')
77-
? InnerList::fromHttpValue($element)
78-
: Item::fromHttpValue($element);
79-
80-
return array_reduce(explode(',', $httpValue), function (self $instance, string $element) use ($parser): self {
81-
[$key, $value] = self::extractPair($element);
82-
83-
$instance->set($key, $parser($value));
84-
85-
return $instance;
86-
}, $instance);
87-
}
88-
89-
/**
90-
* Extracts a dictionary pair from an HTTP textual representation.
91-
*
92-
* @throws SyntaxError
93-
*
94-
* @return array{0:string, 1:string}
95-
*/
96-
private static function extractPair(string $pair): array
97-
{
98-
$pair = trim($pair);
99-
100-
if ('' === $pair) {
101-
throw new SyntaxError('The HTTP textual representation for a dictionary pair can not be empty.');
102-
}
103-
104-
if (1 !== preg_match('/^(?<key>[a-z*][a-z0-9.*_-]*)(=)?(?<value>.*)/', $pair, $found)) {
105-
throw new SyntaxError("The HTTP textual representation `$pair` for a dictionary pair contains invalid characters.");
106-
}
107-
108-
if (rtrim($found['key']) !== $found['key'] || ltrim($found['value']) !== $found['value']) {
109-
throw new SyntaxError("The HTTP textual representation `$pair` for a dictionary pair contains invalid characters.");
110-
}
111-
112-
$found['value'] = trim($found['value']);
113-
if ('' === $found['value'] || str_starts_with($found['value'], ';')) {
114-
$found['value'] = '?1'.$found['value'];
115-
}
116-
117-
return [$found['key'], $found['value']];
66+
return Parser::parseDictionary($httpValue);
11867
}
11968

12069
public function toHttpValue(): string
12170
{
12271
$returnValue = [];
123-
foreach ($this->elements as $key => $element) {
72+
foreach ($this->members as $key => $member) {
12473
$returnValue[] = match (true) {
125-
$element instanceof Item && true === $element->value() => $key.$element->parameters()->toHttpValue(),
126-
default => $key.'='.$element->toHttpValue(),
74+
$member instanceof Item && true === $member->value() => $key.$member->parameters()->toHttpValue(),
75+
default => $key.'='.$member->toHttpValue(),
12776
};
12877
}
12978

@@ -132,21 +81,21 @@ public function toHttpValue(): string
13281

13382
public function count(): int
13483
{
135-
return count($this->elements);
84+
return count($this->members);
13685
}
13786

13887
public function isEmpty(): bool
13988
{
140-
return [] === $this->elements;
89+
return [] === $this->members;
14190
}
14291

14392
/**
14493
* @return Iterator<string, Item|InnerList>
14594
*/
14695
public function getIterator(): Iterator
14796
{
148-
foreach ($this->elements as $index => $element) {
149-
yield $index => $element;
97+
foreach ($this->members as $index => $member) {
98+
yield $index => $member;
15099
}
151100
}
152101

@@ -157,8 +106,8 @@ public function getIterator(): Iterator
157106
*/
158107
public function toPairs(): Iterator
159108
{
160-
foreach ($this->elements as $index => $element) {
161-
yield [$index, $element];
109+
foreach ($this->members as $index => $member) {
110+
yield [$index, $member];
162111
}
163112
}
164113

@@ -167,21 +116,21 @@ public function toPairs(): Iterator
167116
*/
168117
public function keys(): array
169118
{
170-
return array_keys($this->elements);
119+
return array_keys($this->members);
171120
}
172121

173122
public function has(string $key): bool
174123
{
175-
return array_key_exists($key, $this->elements);
124+
return array_key_exists($key, $this->members);
176125
}
177126

178127
public function get(string $key): Item|InnerList
179128
{
180-
if (!array_key_exists($key, $this->elements)) {
129+
if (!array_key_exists($key, $this->members)) {
181130
throw InvalidOffset::dueToKeyNotFound($key);
182131
}
183132

184-
return $this->elements[$key];
133+
return $this->members[$key];
185134
}
186135

187136
public function hasPair(int $index): bool
@@ -191,10 +140,10 @@ public function hasPair(int $index): bool
191140

192141
private function filterIndex(int $index): int|null
193142
{
194-
$max = count($this->elements);
143+
$max = count($this->members);
195144

196145
return match (true) {
197-
[] === $this->elements, 0 > $max + $index, 0 > $max - $index - 1 => null,
146+
[] === $this->members, 0 > $max + $index, 0 > $max - $index - 1 => null,
198147
0 > $index => $max + $index,
199148
default => $index,
200149
};
@@ -211,19 +160,19 @@ public function pair(int $index): array
211160
}
212161

213162
return [
214-
array_keys($this->elements)[$offset],
215-
array_values($this->elements)[$offset],
163+
array_keys($this->members)[$offset],
164+
array_values($this->members)[$offset],
216165
];
217166
}
218167

219168
/**
220-
* Add an element at the end of the instance if the key is new otherwise update the value associated with the key.
169+
* Add a member at the end of the instance if the key is new otherwise update the value associated with the key.
221170
*/
222-
public function set(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $element): void
171+
public function set(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): void
223172
{
224173
self::validateKey($key);
225174

226-
$this->elements[$key] = self::filterElement($element);
175+
$this->members[$key] = self::filterMember($member);
227176
}
228177

229178
/**
@@ -236,54 +185,54 @@ private static function validateKey(string $key): void
236185
}
237186
}
238187

239-
private static function filterElement(InnerList|Item|ByteSequence|Token|bool|int|float|string $element): InnerList|Item
188+
private static function filterMember(InnerList|Item|ByteSequence|Token|bool|int|float|string $member): InnerList|Item
240189
{
241190
return match (true) {
242-
$element instanceof InnerList, $element instanceof Item => $element,
243-
default => Item::from($element),
191+
$member instanceof InnerList, $member instanceof Item => $member,
192+
default => Item::from($member),
244193
};
245194
}
246195

247196
/**
248-
* Delete elements associated with the list of submitted keys.
197+
* Delete members associated with the list of submitted keys.
249198
*/
250199
public function delete(string ...$keys): void
251200
{
252201
foreach ($keys as $key) {
253-
unset($this->elements[$key]);
202+
unset($this->members[$key]);
254203
}
255204
}
256205

257206
/**
258-
* Remove all elements from the instance.
207+
* Remove all members from the instance.
259208
*/
260209
public function clear(): void
261210
{
262-
$this->elements = [];
211+
$this->members = [];
263212
}
264213

265214
/**
266-
* Add an element at the end of the instance if the key is new delete any previous reference to the key.
215+
* Add a member at the end of the instance if the key is new delete any previous reference to the key.
267216
*/
268-
public function append(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $element): void
217+
public function append(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): void
269218
{
270219
self::validateKey($key);
271220

272-
unset($this->elements[$key]);
221+
unset($this->members[$key]);
273222

274-
$this->elements[$key] = self::filterElement($element);
223+
$this->members[$key] = self::filterMember($member);
275224
}
276225

277226
/**
278-
* Add an element at the beginning of the instance if the key is new delete any previous reference to the key.
227+
* Add a member at the beginning of the instance if the key is new delete any previous reference to the key.
279228
*/
280-
public function prepend(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $element): void
229+
public function prepend(string $key, InnerList|Item|ByteSequence|Token|bool|int|float|string $member): void
281230
{
282231
self::validateKey($key);
283232

284-
unset($this->elements[$key]);
233+
unset($this->members[$key]);
285234

286-
$this->elements = [...[$key => self::filterElement($element)], ...$this->elements];
235+
$this->members = [...[$key => self::filterMember($member)], ...$this->members];
287236
}
288237

289238
/**
@@ -292,7 +241,7 @@ public function prepend(string $key, InnerList|Item|ByteSequence|Token|bool|int|
292241
public function merge(self ...$others): void
293242
{
294243
foreach ($others as $other) {
295-
$this->elements = [...$this->elements, ...$other->elements];
244+
$this->members = [...$this->members, ...$other->members];
296245
}
297246
}
298247
}

src/DictionaryTest.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function test_it_can_be_instantiated_with_key_value_pairs(): void
5656
/**
5757
* @test
5858
*/
59-
public function it_can_add_or_remove_elements(): void
59+
public function it_can_add_or_remove_members(): void
6060
{
6161
$stringItem = Item::from('helloWorld');
6262
$booleanItem = Item::from(true);
@@ -122,7 +122,7 @@ public function it_fails_to_add_an_item_with_wrong_key(): void
122122
/**
123123
* @test
124124
*/
125-
public function it_can_prepend_an_element(): void
125+
public function it_can_prepend_a_new_member(): void
126126
{
127127
$instance = Dictionary::fromAssociative();
128128
$instance->append('a', Item::from(false));
@@ -134,7 +134,7 @@ public function it_can_prepend_an_element(): void
134134
/**
135135
* @test
136136
*/
137-
public function it_can_returns_the_container_element_keys(): void
137+
public function it_can_returns_the_container_member_keys(): void
138138
{
139139
$instance = Dictionary::fromAssociative();
140140
self::assertSame([], $instance->keys());
@@ -185,4 +185,16 @@ public function it_can_merge_without_argument_and_not_throw(): void
185185
$instance->merge();
186186
self::assertCount(1, $instance);
187187
}
188+
189+
/**
190+
* @test
191+
*/
192+
public function it_can_handle_string_with_comma(): void
193+
{
194+
$expected = 'a=foobar;test="bar, baz", b=toto';
195+
$instance = Dictionary::fromHttpValue($expected);
196+
197+
self::assertSame($expected, $instance->toHttpValue());
198+
self::assertCount(2, $instance);
199+
}
188200
}

0 commit comments

Comments
 (0)