Skip to content

Commit c207d51

Browse files
committed
Improve internal codebase
1 parent 520c1b5 commit c207d51

13 files changed

+154
-132
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
2121

2222
- Improve annotation using `@phpstan-type`
2323
- `Value` internal class to improve Item public API;
24-
- **[BC Break]** `::fromAssociative` and `::fromPair` the `$parameters` argument is now required;
24+
- **[BC Break]** `::fromAssociative` and `::fromPair` the `$parameters` argument is now required;
25+
- **[BC Break]** `MemberOrderedMap` instances can no longer be added to `Dictionary` or `OuterList` instances.
2526

2627
### Deprecated
2728

README.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
[![Sponsor development of this project](https://img.shields.io/badge/sponsor%20this%20package-%E2%9D%A4-ff69b4.svg?style=flat-square)](https://github.com/sponsors/nyamsprod)
99

1010
`bakame/http-structured-fields` is a framework-agnostic PHP library that allows you to parse, serialize
11-
and build HTTP Structured Fields in PHP according to the [RFC8941](https://www.rfc-editor.org/rfc/rfc8941.html).
11+
build and update HTTP Structured Fields in PHP according to the [RFC8941](https://www.rfc-editor.org/rfc/rfc8941.html).
1212

1313
## System Requirements
1414

@@ -30,7 +30,6 @@ Once the library is installed parsing the header value is done via the normalize
3030
constructor attached to library's structured fields representation as shown below:
3131

3232
```php
33-
3433
declare(strict_types=1);
3534

3635
require 'vendor/autoload.php';
@@ -58,8 +57,8 @@ the `toHttpValue` method.
5857
````php
5958
use Bakame\Http\StructuredFields\Item;
6059

61-
$bar = Item::fromToken('bar')->addParameter('baz', 42);
62-
echo $bar->toHttpValue(); // return 'bar;baz=42'
60+
$bar = Item::fromHttpValue('bar; baz=42; secure=?1');
61+
echo $bar->toHttpValue(); // return 'bar;baz=42;secure' on serialization the field has been normalized
6362

6463
// the HTTP response object is build by your application
6564
// via your framework, a package or a native PHP function.
@@ -103,7 +102,7 @@ The table below summarizes the item value type.
103102

104103
As shown in the table, the RFC define two (2) specific data types that can not be represented by
105104
PHP default type system, for them, we have defined two classes `Token` and `ByteSequence` to help
106-
with represention.
105+
with representation.
107106

108107
```php
109108
use Bakame\Http\StructuredFields\Token;
@@ -147,11 +146,9 @@ are accessible using the following methods:
147146
use Bakame\Http\StructuredFields\Item;
148147
use Bakame\Http\StructuredFields\Type;
149148

150-
//@type SfItemInput ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool
151-
// the Item::value() can return one of those type
152-
$item = Item::fromDate(CarbonImmutable::parse('today'));
149+
$item = Item::fromHttpValue('@1234567890');
153150
$item->type(); // return Type::Date;
154-
$item->value() // return a CarbonImmutable instance because it extends DateTimeImmutable
151+
$item->value() // return the equivalent to DateTimeImmutable('2009-02-13T23:31:30.000+00:00');
155152
// you can also do
156153
Type::Date->equals($item); // returns true
157154
```
@@ -175,7 +172,7 @@ if you try to use them on any container object:
175172
```php
176173
use Bakame\Http\StructuredFields\Parameters;
177174

178-
$value = Parameters::fromAssociative(['a' => 'foobar']);
175+
$value = Parameters::fromHttpValue(';a=foobar']);
179176
$value->has('b'); // return false
180177
$value['a']->value(); // return 'foobar'
181178
$value['b']; // triggers a SyntaxError exception, the index does not exist
@@ -212,7 +209,7 @@ Every value object can be used as a builder to create an HTTP field value.
212209

213210
#### Items value
214211

215-
The `Item` value object exposes lots of named constructors to construct
212+
The `Item` value object exposes a number of named constructors to construct
216213
bare items (ie: item without parameters attached to them).
217214

218215
```php
@@ -223,10 +220,10 @@ Item::fromDecodedByteSequence(Stringable|string $value): self;
223220
Item::fromEncodedByteSequence(Stringable|string $value): self;
224221
Item::fromToken(Stringable|string $value): self;
225222
Item::fromString(Stringable|string $value): self;
226-
Item::fromTimestamp(int $value): self;
223+
Item::fromDate(DateTimeInterface $datetime): self;
227224
Item::fromDateFormat(string $format, string $datetime): self;
228225
Item::fromDateString(string $datetime, DateTimeZone|string|null $timezone = null): self;
229-
Item::fromDate(DateTimeInterface $datetime): self;
226+
Item::fromTimestamp(int $value): self;
230227
Item::fromDecimal(int|float $value): self;
231228
Item::fromInteger(int|float $value): self;
232229
Item::true(): self;
@@ -238,7 +235,7 @@ To update an `Item` object value, the `Item::withValue` method should be use:
238235
```php
239236
use Bakame\Http\StructuredFields\Item;
240237

241-
Item::withValue(SfItemInput $value): static
238+
Item::withValue(mixed $value): static
242239
```
243240

244241
#### Dictionaries
@@ -265,7 +262,7 @@ use Bakame\Http\StructuredFields\Parameters;
265262

266263
$value = Parameters::fromPairs([
267264
['b', false],
268-
['a', Item::fromPair([Token::fromString('bar')])],
265+
['a', Item::fromToken('bar')],
269266
['c', new DateTime('2022-12-23 13:00:23')]
270267
]);
271268

@@ -327,14 +324,15 @@ echo $list; //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
327324
Once again, builder methods exist on both classes to ease container construction.
328325

329326
```php
327+
use Bakame\Http\StructuredFields\ByteSequence;
330328
use Bakame\Http\StructuredFields\InnerList;
331329
use Bakame\Http\StructuredFields\Item;
332330

333331
$list = InnerList::new()
334332
->unshift('42')
335333
->push(42)
336334
->insert(1, 42.0)
337-
->replace(0, Item::fromDecodedByteSequence('Hello World'));
335+
->replace(0, Item::new(ByteSequence::fromDecoded('Hello World')));
338336

339337
echo $list->toHttpValue(); //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
340338
echo $list; //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
@@ -357,10 +355,12 @@ public API is added. It is also possible to instantiate an `InnerList` or an `It
357355
instance with included parameters using one of these named constructors:
358356

359357
```php
358+
use Bakame\Http\StructuredFields\ByteSequence;
360359
use Bakame\Http\StructuredFields\InnerList;
361360
use Bakame\Http\StructuredFields\Item;
361+
use Bakame\Http\StructuredFields\Token;
362362

363-
//@type SfItemInput ByteSequence|Token|DateTimeInterface|Stringable|string|int|float|bool
363+
//@type SfItemInput ByteSequence|Token|DateTimeInterface|string|int|float|bool
364364

365365
Item::fromAssociative(SfItemInput $value, Parameters|iterable<string, SfItemInput> $parameters): self;
366366
Item::fromPair(array{0:SfItemInput, 1:Parameters|iterable<array{0:string, 1:SfItemInput}>} $pair): self;

src/Dictionary.php

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use function is_array;
1717
use function is_iterable;
1818
use function is_string;
19+
use const ARRAY_FILTER_USE_KEY;
1920

2021
/**
2122
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.2
@@ -51,8 +52,8 @@ private function __construct(iterable $members = [])
5152
private static function filterMember(mixed $member): object
5253
{
5354
return match (true) {
54-
$member instanceof ParameterAccess &&
55-
($member instanceof MemberList || $member instanceof ValueAccess) => $member,
55+
$member instanceof ParameterAccess && ($member instanceof MemberList || $member instanceof ValueAccess) => $member,
56+
$member instanceof MemberOrderedMap => throw new InvalidArgument('Only structured fields as list are supported.'),
5657
is_iterable($member) => InnerList::new(...$member),
5758
default => Item::new($member),
5859
};
@@ -234,44 +235,50 @@ public function pair(int $index): array
234235
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
235236
{
236237
$members = $this->members;
237-
$members[$key] = $member;
238+
$members[$key] = self::filterMember($member);
238239

239-
return new self($members);
240+
return $this->newInstance($members);
240241
}
241242

242-
public function remove(string|int ...$keys): static
243+
/**
244+
* @param array<string, SfMember> $members
245+
*/
246+
private function newInstance(array $members): static
243247
{
244-
$members = $this->members;
245-
foreach (array_filter($keys, static fn (string|int $key): bool => !is_int($key)) as $key) {
246-
unset($members[$key]);
247-
}
248-
249-
if ($members === $this->members) {
248+
if ($members == $this->members) {
250249
return $this;
251250
}
252251

253252
return new self($members);
254253
}
255254

255+
public function remove(string|int ...$keys): static
256+
{
257+
$members = array_filter(
258+
$this->members,
259+
fn (string|int $key): bool => !in_array($key, $keys, true),
260+
ARRAY_FILTER_USE_KEY
261+
);
262+
263+
return $this->newInstance($members);
264+
}
265+
256266
public function append(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
257267
{
258268
$members = $this->members;
259269
unset($members[$key]);
260-
$members[$key] = $member;
270+
$members[$key] = self::filterMember($member);
261271

262-
if ($members == $this->members) {
263-
return $this;
264-
}
265-
266-
return new self($members);
272+
return $this->newInstance($members);
267273
}
268274

269275
public function prepend(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
270276
{
271277
$members = $this->members;
272278
unset($members[$key]);
279+
$members = [$key => self::filterMember($member), ...$members];
273280

274-
return new self([$key => $member, ...$members]);
281+
return $this->newInstance($members);
275282
}
276283

277284
/**

src/DictionaryTest.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ public function it_fails_to_add_an_item_with_wrong_key(): void
122122
Dictionary::fromAssociative(['bébé'=> Item::false()]);
123123
}
124124

125+
#[Test]
126+
public function it_fails_to_insert_somethine_other_than_a_inner_list_or_an_item(): void
127+
{
128+
$this->expectException(InvalidArgument::class);
129+
130+
Dictionary::new()->add('foo', Parameters::fromAssociative(['foo' => 'bar']));
131+
}
132+
125133
#[Test]
126134
public function it_can_prepend_a_new_member(): void
127135
{
@@ -132,6 +140,15 @@ public function it_can_prepend_a_new_member(): void
132140
self::assertSame('b, a=?0', (string) $instance);
133141
}
134142

143+
#[Test]
144+
public function it_can_prepend_a_new_member_without_changing(): void
145+
{
146+
$instance = Dictionary::new()->append('b', Item::true());
147+
$instance2 = $instance->prepend('b', Item::true());
148+
149+
self::assertSame($instance2, $instance);
150+
}
151+
135152
#[Test]
136153
public function it_can_returns_the_container_member_keys(): void
137154
{
@@ -226,12 +243,17 @@ public function it_can_handle_string_with_comma(): void
226243
}
227244

228245
#[Test]
229-
public function it_can_delete_a_member_via_array_access(): void
246+
public function it_can_delete_a_member_via_remove_method(): void
230247
{
231-
$structuredField = Dictionary::new();
232-
$newInstance = $structuredField->add('foo', 'bar');
248+
$newInstance = Dictionary::new()->add('foo', 'bar');
233249

234250
self::assertTrue($newInstance->hasMembers());
251+
self::assertCount(1, $newInstance);
252+
253+
$newInstance2 = $newInstance->add('foo', 'bar');
254+
self::assertCount(1, $newInstance2);
255+
self::assertSame($newInstance, $newInstance2);
256+
235257
self::assertFalse($newInstance->remove('foo')->hasMembers());
236258
}
237259

0 commit comments

Comments
 (0)