Skip to content

Commit 2d94491

Browse files
committed
Improve internal codebase
1 parent fd5e26f commit 2d94491

18 files changed

+391
-323
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
1111
### Fixed
1212

1313
- `Parameters::remove` also removes parameters per indexes
14+
- `Type::fromValue` throws an `InvalidArgument` exception.
15+
- `Type::fromValue` and `Type::tryFromValue` should only check the PHP variable type and not take into account the variable value.
1416

1517
### Deprecated
1618

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ use Bakame\Http\StructuredFields\Type;
145145

146146
echo Type::fromValue(42); // returns Type::Integer
147147
echo Type::fromValue(42.0)->name; // returns 'Decimal'
148-
echo Type::fromValue(new SplTempFileObject()); // throws TypeError
148+
echo Type::fromValue(new SplTempFileObject()); // throws InvalidArgument
149149
echo Type::tryFromValue(new SplTempFileObject()); // returns null
150150
```
151151

@@ -236,7 +236,7 @@ use Bakame\Http\StructuredFields\Parameters;
236236
$value = Parameters::fromHttpValue(';a=foobar']);
237237
$value->has('b'); // return false
238238
$value['a']->value(); // return 'foobar'
239-
$value['b']; // triggers a SyntaxError exception, the index does not exist
239+
$value['b']; // triggers a InvalidOffset exception, the index does not exist
240240
$value['a'] = 23 // triggers a ForbiddenOperation exception
241241
unset($value['a']); // triggers a ForbiddenOperation exception
242242
```
@@ -412,7 +412,7 @@ $value = Dictionary::new()
412412
Item::fromInteger(42),
413413
Item::fromDecimal(42)
414414
)],
415-
['c', true]
415+
['c', Item::true()]
416416
)
417417
->unshift(['b', Item::false()])
418418
->replace(2, ['c', Item::fromDateString('2022-12-23 13:00:23')])
@@ -433,7 +433,7 @@ If the submitted type is:
433433

434434
- a `StructuredField` implementing object, it will be passed as is
435435
- an iterable structure, it will be converted to an `InnerList` instance using `InnerList::new`
436-
- otherwise, it is converted into an `Item` using `Item::new` following the conversion rules explained in the table above.
436+
- otherwise, it is converted into an `Item` using the `Item::new` named constructor.
437437

438438
If no conversion is possible an `InvalidArgument` exception will be thrown.
439439

@@ -466,7 +466,7 @@ echo Dictionary::new()
466466
// both will return 'b=?0, a=(bar "42" 42 42.0), c=@1671800423
467467
```
468468

469-
Of course, it is possible to mix both notation as shown in the example.
469+
Of course, it is possible to mix both notations.
470470

471471
#### Lists
472472

@@ -564,7 +564,7 @@ $field->withoutParameters(string ...$keys): static;
564564
$field->withoutAnyParameter(): static;
565565
$field->withParameters(Parameters $parameters): static;
566566
```
567-
Since verrsion `1.1` it is also possible to use the index of each member to perform additional
567+
Since version `1.1` it is also possible to use the index of each member to perform additional
568568
modifications.
569569

570570
```php

src/Dictionary.php

Lines changed: 75 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use function count;
1515
use function implode;
1616
use function is_array;
17+
use function is_int;
1718
use function is_iterable;
1819
use function is_string;
1920

@@ -90,15 +91,14 @@ public static function fromAssociative(iterable $members): self
9091
*/
9192
public static function fromPairs(iterable $pairs): self
9293
{
93-
if ($pairs instanceof MemberOrderedMap) {
94-
return new self($pairs);
95-
}
96-
97-
return new self((function (iterable $pairs) {
98-
foreach ($pairs as [$key, $member]) {
99-
yield $key => $member;
100-
}
101-
})($pairs));
94+
return match (true) {
95+
$pairs instanceof MemberOrderedMap => new self($pairs),
96+
default => new self((function (iterable $pairs) {
97+
foreach ($pairs as [$key, $member]) {
98+
yield $key => $member;
99+
}
100+
})($pairs)),
101+
};
102102
}
103103

104104
/**
@@ -110,18 +110,15 @@ public static function fromPairs(iterable $pairs): self
110110
*/
111111
public static function fromHttpValue(Stringable|string $httpValue): self
112112
{
113-
return new self((function (iterable $members) {
114-
foreach ($members as $key => $member) {
115-
if (!is_array($member[0])) {
116-
yield $key => Item::fromAssociative(...$member);
117-
118-
continue;
119-
}
113+
$converter = fn (array $member): InnerList|Item => match (true) {
114+
is_array($member[0]) => InnerList::fromAssociative(
115+
array_map(fn (array $item) => Item::fromAssociative(...$item), $member[0]),
116+
$member[1]
117+
),
118+
default => Item::fromAssociative($member[0], $member[1]),
119+
};
120120

121-
$member[0] = array_map(fn (array $item) => Item::fromAssociative(...$item), $member[0]);
122-
yield $key => InnerList::fromAssociative(...$member);
123-
}
124-
})(Parser::parseDictionary($httpValue)));
121+
return new self(array_map($converter, Parser::parseDictionary($httpValue)));
125122
}
126123

127124
public function toHttpValue(): string
@@ -163,7 +160,7 @@ public function getIterator(): Iterator
163160
}
164161

165162
/**
166-
* @return Iterator<array{0:string, 1:SfMember}>
163+
* @return Iterator<int, array{0:string, 1:SfMember}>
167164
*/
168165
public function toPairs(): Iterator
169166
{
@@ -199,17 +196,14 @@ public function has(string|int ...$keys): bool
199196
*/
200197
public function get(string|int $key): StructuredField
201198
{
202-
if (!$this->has($key)) {
203-
throw InvalidOffset::dueToKeyNotFound($key);
204-
}
205-
206-
return $this->members[$key];
199+
return $this->members[$key] ?? throw InvalidOffset::dueToKeyNotFound($key);
207200
}
208201

209202
public function hasPair(int ...$indexes): bool
210203
{
204+
$max = count($this->members);
211205
foreach ($indexes as $index) {
212-
if (null === $this->filterIndex($index)) {
206+
if (null === $this->filterIndex($index, $max)) {
213207
return false;
214208
}
215209
}
@@ -220,12 +214,14 @@ public function hasPair(int ...$indexes): bool
220214
/**
221215
* Filters and format instance index.
222216
*/
223-
private function filterIndex(int $index): int|null
217+
private function filterIndex(int $index, int|null $max = null): int|null
224218
{
225-
$max = count($this->members);
219+
$max ??= count($this->members);
226220

227221
return match (true) {
228-
[] === $this->members, 0 > $max + $index, 0 > $max - $index - 1 => null,
222+
[] === $this->members,
223+
0 > $max + $index,
224+
0 > $max - $index - 1 => null,
229225
0 > $index => $max + $index,
230226
default => $index,
231227
};
@@ -238,10 +234,7 @@ private function filterIndex(int $index): int|null
238234
*/
239235
public function pair(int $index): array
240236
{
241-
$offset = $this->filterIndex($index);
242-
if (null === $offset) {
243-
throw InvalidOffset::dueToIndexNotFound($index);
244-
}
237+
$offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
245238

246239
return [...$this->toPairs()][$offset];
247240
}
@@ -262,29 +255,39 @@ public function add(string $key, iterable|StructuredField|Token|ByteSequence|Dat
262255
*/
263256
private function newInstance(array $members): self
264257
{
265-
if ($members == $this->members) {
266-
return $this;
267-
}
268-
269-
return new self($members);
258+
return match (true) {
259+
$members == $this->members => $this,
260+
default => new self($members),
261+
};
270262
}
271263

272264
public function remove(string|int ...$keys): static
273265
{
274-
/** @var array<array-key, true> $indexes */
275-
$indexes = array_fill_keys($keys, true);
276-
$pairs = [];
277-
foreach ($this->toPairs() as $index => $pair) {
278-
if (!isset($indexes[$index]) && !isset($indexes[$pair[0]])) {
279-
$pairs[] = $pair;
280-
}
281-
}
282-
283-
if (count($this->members) === count($pairs)) {
266+
if ([] === $this->members || [] === $keys) {
284267
return $this;
285268
}
286269

287-
return self::fromPairs($pairs);
270+
$offsets = array_keys($this->members);
271+
$max = count($offsets);
272+
$reducer = fn (array $carry, string|int $key): array => match (true) {
273+
is_string($key) && (false !== ($position = array_search($key, $offsets, true))),
274+
is_int($key) && (null !== ($position = $this->filterIndex($key, $max))) => [$position => true] + $carry,
275+
default => $carry,
276+
};
277+
278+
$indices = array_reduce($keys, $reducer, []);
279+
280+
return match (true) {
281+
[] === $indices => $this,
282+
$max === count($indices) => self::new(),
283+
default => self::fromPairs((function (array $offsets) {
284+
foreach ($this->toPairs() as $offset => $pair) {
285+
if (!array_key_exists($offset, $offsets)) {
286+
yield $pair;
287+
}
288+
}
289+
})($indices)),
290+
};
288291
}
289292

290293
/**
@@ -294,9 +297,8 @@ public function append(string $key, iterable|StructuredField|Token|ByteSequence|
294297
{
295298
$members = $this->members;
296299
unset($members[$key]);
297-
$members[MapKey::from($key)->value] = self::filterMember($member);
298300

299-
return $this->newInstance($members);
301+
return $this->newInstance([...$members, MapKey::from($key)->value => self::filterMember($member)]);
300302
}
301303

302304
/**
@@ -306,53 +308,46 @@ public function prepend(string $key, iterable|StructuredField|Token|ByteSequence
306308
{
307309
$members = $this->members;
308310
unset($members[$key]);
309-
$members = [MapKey::from($key)->value => self::filterMember($member), ...$members];
310311

311-
return $this->newInstance($members);
312+
return $this->newInstance([MapKey::from($key)->value => self::filterMember($member), ...$members]);
312313
}
313314

314315
/**
315316
* @param array{0:string, 1:SfMember|SfMemberInput} ...$pairs
316317
*/
317318
public function push(array ...$pairs): self
318319
{
319-
if ([] === $pairs) {
320-
return $this;
321-
}
322-
323-
$newPairs = iterator_to_array($this->toPairs());
324-
foreach ($pairs as $pair) {
325-
$newPairs[] = $pair;
326-
}
327-
328-
return self::fromPairs($newPairs);
320+
return match (true) {
321+
[] === $pairs => $this,
322+
default => self::fromPairs((function (iterable $pairs) {
323+
yield from $this->toPairs();
324+
yield from $pairs;
325+
})($pairs)),
326+
};
329327
}
330328

331329
/**
332330
* @param array{0:string, 1:SfMember|SfMemberInput} ...$pairs
333331
*/
334332
public function unshift(array ...$pairs): self
335333
{
336-
if ([] === $pairs) {
337-
return $this;
338-
}
339-
340-
foreach ($this->members as $key => $member) {
341-
$pairs[] = [$key, $member];
342-
}
343-
344-
return self::fromPairs($pairs);
334+
return match (true) {
335+
[] === $pairs => $this,
336+
default => self::fromPairs((function (iterable $pairs) {
337+
yield from $pairs;
338+
yield from $this->toPairs();
339+
})($pairs)),
340+
};
345341
}
346342

347343
/**
348344
* @param array{0:string, 1:SfMember|SfMemberInput} ...$members
349345
*/
350346
public function insert(int $index, array ...$members): static
351347
{
352-
$offset = $this->filterIndex($index);
348+
$offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
353349

354350
return match (true) {
355-
null === $offset => throw InvalidOffset::dueToIndexNotFound($index),
356351
[] === $members => $this,
357352
0 === $offset => $this->unshift(...$members),
358353
count($this->members) === $offset => $this->push(...$members),
@@ -370,18 +365,14 @@ public function insert(int $index, array ...$members): static
370365
*/
371366
public function replace(int $index, array $member): static
372367
{
373-
$offset = $this->filterIndex($index);
374-
if (null === $offset) {
375-
throw InvalidOffset::dueToIndexNotFound($index);
376-
}
377-
368+
$offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
378369
$member[1] = self::filterMember($member[1]);
379370
$pairs = iterator_to_array($this->toPairs());
380-
if ($pairs[$offset] == $member) {
381-
return $this;
382-
}
383371

384-
return self::fromPairs(array_replace($pairs, [$offset => $member]));
372+
return match (true) {
373+
$pairs[$offset] == $member => $this,
374+
default => self::fromPairs(array_replace($pairs, [$offset => $member])),
375+
};
385376
}
386377

387378
/**

src/DictionaryTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ public function it_can_delete_a_member_via_remove_method(): void
255255
self::assertSame($newInstance, $newInstance2);
256256

257257
self::assertFalse($newInstance->remove('foo')->hasMembers());
258+
self::assertSame($newInstance, $newInstance->remove('baz', 'bar', 'yolo-not-there', 325));
259+
260+
$instanceWithoutMember = Dictionary::new();
261+
self::assertSame($instanceWithoutMember->remove(), $instanceWithoutMember);
258262
}
259263

260264
#[Test]
@@ -375,4 +379,24 @@ public function it_returns_the_same_instance_if_nothing_is_replaced(): void
375379

376380
self::assertSame($field->replace(0, ['a', true]), $field);
377381
}
382+
383+
#[Test]
384+
public function it_can_create_a_new_instance_using_parameters_position_modifying_methods(): void
385+
{
386+
$instance1 = Dictionary::new();
387+
$instance2 = $instance1
388+
->push(['a', true], ['v', ByteSequence::fromDecoded('I will be removed')], ['c', 'true'])
389+
->unshift(['b', Item::false()])
390+
->replace(1, ['a', 'false'])
391+
->remove(-2, 'toto')
392+
->insert(1, ['d', Token::fromString('*/*')]);
393+
394+
self::assertTrue($instance1->hasNoMembers());
395+
self::assertTrue($instance2->hasMembers());
396+
self::assertCount(4, $instance2);
397+
self::assertEquals(['d', Item::fromToken('*/*')], $instance2->pair(1));
398+
self::assertEquals(['b', Item::false()], $instance2->pair(0));
399+
self::assertEquals(['c', Item::fromString('true')], $instance2->pair(-1));
400+
self::assertSame('b=?0, d=*/*, a="false", c="true"', $instance2->toHttpValue());
401+
}
378402
}

0 commit comments

Comments
 (0)