Skip to content

Commit b22377c

Browse files
committed
Normalize building and updating parameters for Item and InnerList
1 parent af83a0e commit b22377c

14 files changed

+175
-180
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
1212
- `Item` implements the `ValueAccess` interface;
1313
- `Item::toPair` to complement `Item::fromPair`;
1414
- `Item::fromDate` to improve and complete the Item Date public API;
15+
- `Item::fromAssociative` to improve Item public API;
1516
- `Value` internal class to improve Item public API;
1617
- `Token::toString` to return the string representation of the token.
1718

@@ -29,6 +30,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
2930
- **[BC Break]** `InnerList::fromAssociativeParameters` use `InnerList::fromAssociative` instead.
3031
- **[BC Break]** `Value` interface use a combination of `ValueAccess` **and** `ParameterAccess` instead.
3132
- **[BC Break]** `Token::value` is no longer public use `Token::toString` instead.
33+
- **[BC Break]** `Item::from` is removed use `Item::fromAssociative` instead.
3234

3335
## [0.8.0] - 2023-03-12
3436

README.md

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ use Bakame\Http\StructuredFields\Type;
149149

150150
//@type SfItemInput ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool
151151
// the Item::value() can return one of those type
152-
$item = Item::from(CarbonImmutable::parse('today'));
152+
$item = Item::fromDate(CarbonImmutable::parse('today'));
153153
$item->type(); // return Type::Date;
154154
$item->value() // return a CarbonImmutable instance because it extends DateTimeImmutable
155155
// you can also do
@@ -200,10 +200,12 @@ using the following methods:
200200
```php
201201
use Bakame\Http\StructuredFields\Parameters;
202202

203-
$innerList->parameter($key): ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool|null;
204-
$innerList->parameters(): Parameters;
203+
$field->parameter(string $key): ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool|null;
204+
$field->parameters(): Parameters;
205205
```
206206

207+
**Of note: the `parameter` method will return `null` if no value is found for the given index.**
208+
207209
### Building and Updating Structured Fields Values
208210

209211
Every value object can be used as a builder to create an HTTP field value.
@@ -246,12 +248,12 @@ use Bakame\Http\StructuredFields\Dictionary;
246248

247249
$value = Dictionary::fromAssociative([
248250
'b' => false,
249-
'a' => Item::fromToken('bar')->addParameter('baz', 42),
251+
'a' => Item::fromToken('bar'),
250252
'c' => new DateTimeImmutable('2022-12-23 13:00:23'),
251253
]);
252254

253-
echo $value->toHttpValue(); //"b=?0, a=bar;baz=42, c=@1671800423"
254-
echo $value; //"b=?0, a=bar;baz=42, c=@1671800423"
255+
echo $value->toHttpValue(); //"b=?0, a=bar, c=@1671800423"
256+
echo $value; //"b=?0, a=bar, c=@1671800423"
255257
```
256258

257259
Or with an iterable structure of pairs as per defined in the RFC:
@@ -269,23 +271,23 @@ echo $value->toHttpValue(); //;b=?0;a=bar;c=@1671800423
269271
echo $value; //;b=?0;a=bar;c=@1671800423
270272
```
271273

272-
If the preference is to use the builder pattern, the same result can be achieved with the following steps:
274+
If the preference is to use the builder pattern, the same result can be achieved with the following steps.
275+
We start building a `Parameters` or a `Dictionary` instance using the `create` named constructor which
276+
returns a new instance with no members.
273277

274278
```php
275279
use Bakame\Http\StructuredFields\Dictionary;
276280
use Bakame\Http\StructuredFields\Item;
277281
use Bakame\Http\StructuredFields\Token;
278282

279-
$bar = Item::fromToken('bar')
280-
->addParameter('baz', Item::from(42));
281283
$value = Dictionary::create()
282-
->add('a', $bar)
283-
->prepend('b', Item::from(false))
284-
->append('c', Item::from(new DateTimeImmutable('2022-12-23 13:00:23')))
284+
->add('a', Item::fromToken('bar'))
285+
->prepend('b', Item::false())
286+
->append('c', Item::fromDateString('2022-12-23 13:00:23'))
285287
;
286288

287-
echo $value->toHttpValue(); //"b=?0, a=bar;baz=42, c=@1671800423"
288-
echo $value; //"b=?0, a=bar;baz=42, c=@1671800423"
289+
echo $value->toHttpValue(); //"b=?0, a=bar, c=@1671800423"
290+
echo $value; //"b=?0, a=bar, c=@1671800423"
289291
```
290292

291293
Because we are using immutable value objects any change to the value object will return a new instance with
@@ -304,7 +306,7 @@ $map->remove(...$key): static;
304306

305307
#### Lists
306308

307-
To Create `OuterList` and `InnerList` instances you can use the `from` name constructor:
309+
To Create `OuterList` and `InnerList` instances you can use the `from` named constructor:
308310

309311
```php
310312
use Bakame\Http\StructuredFields\InnerList;
@@ -341,27 +343,28 @@ echo $list; //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
341343
```php
342344
$list->unshift(...$members): static;
343345
$list->push(...$members): static;
344-
$list->insert($key, ...$members): static;
345-
$list->replace($key, $member): static;
346-
$list->remove(...$key): static;
346+
$list->insert(int $key, ...$members): static;
347+
$list->replace(int $key, $member): static;
348+
$list->remove(int ...$key): static;
347349
```
348350

349351
#### Adding and updating parameters
350352

351-
It is also possible to instantiate an `InnerList` or an `Item` instance with included parameters
352-
using one of these named constructors:
353+
To ease working with instance that have a `Parameters` object attached to the following
354+
public API is added. It is also possible to instantiate an `InnerList` or an `Item`
355+
instance with included parameters using one of these named constructors:
353356

354357
```php
355358
use Bakame\Http\StructuredFields\InnerList;
356359
use Bakame\Http\StructuredFields\Item;
357360

358361
//@type SfItemInput ByteSequence|Token|DateTimeInterface|Stringable|string|int|float|bool
359362

360-
Item::from(SfItemInput $value, iterable<string, SfItemInput> $parameters = []): self;
361-
Item::fromPair(array{0:SfItemInput, 1:iterable<array{0:string, 1:SfItemInput}>} $pair): self;
363+
Item::fromAssociative(SfItemInput $value, iterable<string, SfItemInput>|Parameters $parameters = []): self;
364+
Item::fromPair(array{0:SfItemInput, 1:iterable<array{0:string, 1:SfItemInput}>|Parameters} $pair): self;
362365

363-
InnerList::fromAssociative(iterable<string, SfItemInput> $parameters, ...$members): self;
364-
InnerList::fromPair(array{0:list<Item>, iterable<array{0:string, 1:SfItemInput}>} $pair): self;
366+
InnerList::fromAssociative(iterable<SfItemInput> $members, iterable<string, SfItemInput>|Parameters $parameters): self;
367+
InnerList::fromPair(array{0:iterable<SfItemInput>, iterable<array{0:string, 1:SfItemInput}>|Parameters} $pair): self;
365368
```
366369

367370
Both classes allow return their respective pair representation via the `toPair` method.
@@ -375,7 +378,7 @@ InnerList::toPair(): array{0:list<Item>, 1:Parameters}>};
375378
Item::toPair(): array{0:mixed, 1:Parameters}>};
376379
```
377380

378-
The `InnerList` and `Item` object provide additional modifying methods to help deal with parameters.
381+
Both objects provide additional modifying methods to help deal with parameters.
379382
You can attach and update the associated `Parameters` instance using the following methods:
380383

381384
```php

src/Dictionary.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private static function filterMember(mixed $member): object
5454
$member instanceof ParameterAccess &&
5555
($member instanceof MemberList || $member instanceof ValueAccess) => $member,
5656
is_iterable($member) => InnerList::from(...$member),
57-
default => Item::from($member),
57+
default => Item::fromAssociative($member),
5858
};
5959
}
6060

@@ -112,7 +112,7 @@ public static function fromHttpValue(Stringable|string $httpValue): self
112112
{
113113
return new self((function (iterable $members) {
114114
foreach ($members as $key => $member) {
115-
yield $key => is_array($member) ? InnerList::fromAssociative($member[1], ...$member[0]) : $member;
115+
yield $key => is_array($member) ? InnerList::fromAssociative($member[0], $member[1]) : $member;
116116
}
117117
})(Parser::parseDictionary($httpValue)));
118118
}

src/DictionaryTest.php

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ final class DictionaryTest extends StructuredFieldTestCase
1717
#[Test]
1818
public function it_can_be_instantiated_with_an_collection_of_item_or_inner_list(): void
1919
{
20-
$stringItem = Item::from('helloWorld');
21-
$booleanItem = Item::from(true);
20+
$stringItem = Item::fromAssociative('helloWorld');
21+
$booleanItem = Item::fromAssociative(true);
2222
$arrayParams = ['string' => $stringItem, 'boolean' => $booleanItem];
2323
$instance = Dictionary::fromAssociative($arrayParams);
2424

@@ -34,8 +34,8 @@ public function it_can_be_instantiated_with_an_collection_of_item_or_inner_list(
3434
#[Test]
3535
public function it_can_be_instantiated_with_key_value_pairs(): void
3636
{
37-
$stringItem = Item::from('helloWorld');
38-
$booleanItem = Item::from(true);
37+
$stringItem = Item::fromAssociative('helloWorld');
38+
$booleanItem = Item::fromAssociative(true);
3939
$arrayParams = [['string', $stringItem], ['boolean', $booleanItem]];
4040
$instance = Dictionary::fromPairs($arrayParams);
4141

@@ -49,8 +49,8 @@ public function it_can_be_instantiated_with_key_value_pairs(): void
4949
#[Test]
5050
public function it_can_add_or_remove_members(): void
5151
{
52-
$stringItem = Item::from('helloWorld');
53-
$booleanItem = Item::from(true);
52+
$stringItem = Item::fromAssociative('helloWorld');
53+
$booleanItem = Item::fromAssociative(true);
5454
$arrayParams = ['string' => $stringItem, 'boolean' => $booleanItem];
5555
$instance = Dictionary::fromAssociative($arrayParams);
5656

@@ -64,10 +64,10 @@ public function it_can_add_or_remove_members(): void
6464
self::assertFalse($deletedInstance->has('boolean'));
6565
self::assertFalse($deletedInstance->hasPair(1));
6666

67-
$appendInstance = $deletedInstance->append('foobar', Item::from('BarBaz'));
67+
$appendInstance = $deletedInstance->append('foobar', Item::fromAssociative('BarBaz'));
6868
self::assertTrue($appendInstance->hasPair(1));
6969

70-
self::assertSame($appendInstance, $appendInstance->append('foobar', Item::from('BarBaz')));
70+
self::assertSame($appendInstance, $appendInstance->append('foobar', Item::fromAssociative('BarBaz')));
7171

7272
/** @var array{0:string, 1:Item} $foundItem */
7373
$foundItem = $appendInstance->pair(1);
@@ -119,15 +119,15 @@ public function it_fails_to_add_an_item_with_wrong_key(): void
119119
{
120120
$this->expectException(SyntaxError::class);
121121

122-
Dictionary::fromAssociative(['bébé'=> Item::from(false)]);
122+
Dictionary::fromAssociative(['bébé'=> Item::fromAssociative(false)]);
123123
}
124124

125125
#[Test]
126126
public function it_can_prepend_a_new_member(): void
127127
{
128128
$instance = Dictionary::create()
129-
->append('a', Item::from(false))
130-
->prepend('b', Item::from(true));
129+
->append('a', Item::fromAssociative(false))
130+
->prepend('b', Item::fromAssociative(true));
131131

132132
self::assertSame('b, a=?0', (string) $instance);
133133
}
@@ -140,74 +140,74 @@ public function it_can_returns_the_container_member_keys(): void
140140
self::assertSame([], $instance->keys());
141141

142142
$newInstance = $instance
143-
->append('a', Item::from(false))
144-
->prepend('b', Item::from(true));
143+
->append('a', Item::fromAssociative(false))
144+
->prepend('b', Item::fromAssociative(true));
145145

146146
self::assertSame(['b', 'a'], $newInstance->keys());
147147
}
148148

149149
#[Test]
150150
public function it_can_merge_one_or_more_instances_using_associative(): void
151151
{
152-
$instance1 = Dictionary::fromAssociative(['a' => Item::from(false)]);
153-
$instance2 = Dictionary::fromAssociative(['b' => Item::from(true)]);
154-
$instance3 = Dictionary::fromAssociative(['a' => Item::from(42)]);
152+
$instance1 = Dictionary::fromAssociative(['a' => Item::fromAssociative(false)]);
153+
$instance2 = Dictionary::fromAssociative(['b' => Item::fromAssociative(true)]);
154+
$instance3 = Dictionary::fromAssociative(['a' => Item::fromAssociative(42)]);
155155

156156
$instance4 = $instance1->mergeAssociative($instance2, $instance3);
157157

158158
self::assertCount(2, $instance4);
159-
self::assertEquals(Item::from(42), $instance4->get('a'));
160-
self::assertEquals(Item::from(true), $instance4->get('b'));
159+
self::assertEquals(Item::fromAssociative(42), $instance4->get('a'));
160+
self::assertEquals(Item::fromAssociative(true), $instance4->get('b'));
161161
}
162162

163163
#[Test]
164164
public function it_can_merge_two_or_more_instances_to_yield_different_result(): void
165165
{
166-
$instance1 = Dictionary::fromAssociative(['a' => Item::from(false)]);
167-
$instance2 = Dictionary::fromAssociative(['b' => Item::from(true)]);
168-
$instance3 = Dictionary::fromAssociative(['a' => Item::from(42)]);
166+
$instance1 = Dictionary::fromAssociative(['a' => Item::fromAssociative(false)]);
167+
$instance2 = Dictionary::fromAssociative(['b' => Item::fromAssociative(true)]);
168+
$instance3 = Dictionary::fromAssociative(['a' => Item::fromAssociative(42)]);
169169

170170
$instance4 = $instance3->mergeAssociative($instance2, $instance1);
171171

172172
self::assertCount(2, $instance4);
173-
self::assertEquals(Item::from(false), $instance4->get('a'));
174-
self::assertEquals(Item::from(true), $instance4->get('b'));
173+
self::assertEquals(Item::fromAssociative(false), $instance4->get('a'));
174+
self::assertEquals(Item::fromAssociative(true), $instance4->get('b'));
175175
}
176176

177177
#[Test]
178178
public function it_can_merge_without_argument_and_not_throw(): void
179179
{
180-
self::assertCount(1, Dictionary::fromAssociative(['a' => Item::from(false)])->mergeAssociative());
180+
self::assertCount(1, Dictionary::fromAssociative(['a' => Item::fromAssociative(false)])->mergeAssociative());
181181
}
182182

183183
#[Test]
184184
public function it_can_merge_one_or_more_instances_using_pairs(): void
185185
{
186-
$instance1 = Dictionary::fromPairs([['a', Item::from(false)]]);
187-
$instance2 = Dictionary::fromPairs([['b', Item::from(true)]]);
188-
$instance3 = Dictionary::fromPairs([['a', Item::from(42)]]);
186+
$instance1 = Dictionary::fromPairs([['a', Item::fromAssociative(false)]]);
187+
$instance2 = Dictionary::fromPairs([['b', Item::fromAssociative(true)]]);
188+
$instance3 = Dictionary::fromPairs([['a', Item::fromAssociative(42)]]);
189189

190190
$instance4 = $instance3->mergePairs($instance2, $instance1);
191191

192192
self::assertCount(2, $instance4);
193193

194-
self::assertEquals(Item::from(false), $instance4->get('a'));
195-
self::assertEquals(Item::from(true), $instance4->get('b'));
194+
self::assertEquals(Item::fromAssociative(false), $instance4->get('a'));
195+
self::assertEquals(Item::fromAssociative(true), $instance4->get('b'));
196196
}
197197

198198
#[Test]
199199
public function it_can_merge_without_pairs_and_not_throw(): void
200200
{
201-
$instance = Dictionary::fromAssociative(['a' => Item::from(false)]);
201+
$instance = Dictionary::fromAssociative(['a' => Item::fromAssociative(false)]);
202202

203203
self::assertCount(1, $instance->mergePairs());
204204
}
205205

206206
#[Test]
207207
public function it_can_merge_dictionary_instances_via_pairs_or_associative(): void
208208
{
209-
$instance1 = Dictionary::fromAssociative(['a' => Item::from(false)]);
210-
$instance2 = Dictionary::fromAssociative(['b' => Item::from(true)]);
209+
$instance1 = Dictionary::fromAssociative(['a' => Item::fromAssociative(false)]);
210+
$instance2 = Dictionary::fromAssociative(['b' => Item::fromAssociative(true)]);
211211

212212
$instance3 = clone $instance1;
213213
$instance4 = clone $instance2;
@@ -292,6 +292,6 @@ public function it_forbids_adding_members_using_the_array_access_interface(): vo
292292
{
293293
$this->expectException(LogicException::class);
294294

295-
Dictionary::fromPairs([['foobar', 'foobar'], ['zero', 0]])['foobar'] = Item::from(false);
295+
Dictionary::fromPairs([['foobar', 'foobar'], ['zero', 0]])['foobar'] = Item::fromAssociative(false);
296296
}
297297
}

src/InnerList.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private static function filterMember(mixed $member): object
4545
{
4646
return match (true) {
4747
$member instanceof ValueAccess && $member instanceof ParameterAccess => $member,
48-
!$member instanceof StructuredField => Item::from($member),
48+
!$member instanceof StructuredField => Item::fromAssociative($member),
4949
default => throw new InvalidArgument('Expecting a "'.ValueAccess::class.'" instance; received a "'.$member::class.'" instead.'),
5050
};
5151
}
@@ -86,12 +86,11 @@ public static function fromPair(array $pair): self
8686
/**
8787
* Returns a new instance with an iter.
8888
*
89+
* @param iterable<SfItemInput> $members
8990
* @param iterable<string, SfItemInput> $parameters
9091
*/
91-
public static function fromAssociative(
92-
iterable $parameters,
93-
StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members
94-
): self {
92+
public static function fromAssociative(iterable $members, iterable $parameters = []): self
93+
{
9594
return new self(Parameters::fromAssociative($parameters), $members);
9695
}
9796

@@ -102,9 +101,7 @@ public static function fromAssociative(
102101
*/
103102
public static function fromHttpValue(Stringable|string $httpValue): self
104103
{
105-
[$members, $parameters] = Parser::parseInnerList($httpValue);
106-
107-
return self::fromAssociative($parameters, ...$members);
104+
return self::fromAssociative(...Parser::parseInnerList($httpValue));
108105
}
109106

110107
public function parameters(): Parameters

0 commit comments

Comments
 (0)