Skip to content

Commit 12e0d9d

Browse files
committed
Improve Container constraints
1 parent 88f73b5 commit 12e0d9d

File tree

9 files changed

+178
-136
lines changed

9 files changed

+178
-136
lines changed

src/Dictionary.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ private static function filterMember(mixed $member): object
5353
{
5454
return match (true) {
5555
$member instanceof ParameterAccess && ($member instanceof MemberList || $member instanceof ValueAccess) => $member,
56-
!$member instanceof ParameterAccess && ($member instanceof MemberList || $member instanceof ValueAccess),
57-
$member instanceof MemberOrderedMap => throw new InvalidArgument('An instance of "'.$member::class.'" can not be a member of "'.self::class.'".'),
56+
$member instanceof StructuredField => throw new InvalidArgument('An instance of "'.$member::class.'" can not be a member of "'.self::class.'".'),
5857
is_iterable($member) => InnerList::new(...$member),
5958
default => Item::new($member),
6059
};
@@ -186,6 +185,8 @@ public function has(string|int ...$keys): bool
186185
/**
187186
* @throws SyntaxError If the key is invalid
188187
* @throws InvalidOffset If the key is not found
188+
*
189+
* @return SfMember
189190
*/
190191
public function get(string|int $key): StructuredField
191192
{
@@ -236,15 +237,15 @@ public function pair(int $index): array
236237
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
237238
{
238239
$members = $this->members;
239-
$members[$key] = self::filterMember($member);
240+
$members[MapKey::from($key)->value] = self::filterMember($member);
240241

241242
return $this->newInstance($members);
242243
}
243244

244245
/**
245246
* @param array<string, SfMember> $members
246247
*/
247-
private function newInstance(array $members): static
248+
private function newInstance(array $members): self
248249
{
249250
if ($members == $this->members) {
250251
return $this;
@@ -268,7 +269,7 @@ public function append(string $key, StructuredField|Token|ByteSequence|DateTimeI
268269
{
269270
$members = $this->members;
270271
unset($members[$key]);
271-
$members[$key] = self::filterMember($member);
272+
$members[MapKey::from($key)->value] = self::filterMember($member);
272273

273274
return $this->newInstance($members);
274275
}
@@ -277,7 +278,7 @@ public function prepend(string $key, StructuredField|Token|ByteSequence|DateTime
277278
{
278279
$members = $this->members;
279280
unset($members[$key]);
280-
$members = [$key => self::filterMember($member), ...$members];
281+
$members = [MapKey::from($key)->value => self::filterMember($member), ...$members];
281282

282283
return $this->newInstance($members);
283284
}

src/DictionaryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function it_fails_to_add_an_item_with_wrong_key(): void
123123
}
124124

125125
#[Test]
126-
public function it_fails_to_insert_somethine_other_than_a_inner_list_or_an_item(): void
126+
public function it_fails_to_insert_something_other_than_a_inner_list_or_an_item(): void
127127
{
128128
$this->expectException(InvalidArgument::class);
129129

src/InnerList.php

Lines changed: 67 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*
2323
* @phpstan-import-type SfItem from StructuredField
2424
* @phpstan-import-type SfItemInput from StructuredField
25+
*
2526
* @implements MemberList<int, SfItem>
2627
*/
2728
final class InnerList implements MemberList, ParameterAccess
@@ -62,11 +63,18 @@ public static function fromHttpValue(Stringable|string $httpValue): self
6263
}
6364

6465
/**
65-
* Returns a new instance.
66+
* Returns a new instance with an iter.
67+
*
68+
* @param iterable<SfItemInput> $value
69+
* @param iterable<string, SfItemInput> $parameters
6670
*/
67-
public static function new(StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool ...$members): self
71+
public static function fromAssociative(iterable $value, iterable $parameters): self
6872
{
69-
return new self($members, Parameters::new());
73+
if (!$parameters instanceof Parameters) {
74+
$parameters = Parameters::fromAssociative($parameters);
75+
}
76+
77+
return new self($value, $parameters);
7078
}
7179

7280
/**
@@ -93,32 +101,11 @@ public static function fromPair(array $pair): self
93101
}
94102

95103
/**
96-
* Returns a new instance with an iter.
97-
*
98-
* @param iterable<SfItemInput> $value
99-
* @param iterable<string, SfItemInput> $parameters
104+
* Returns a new instance.
100105
*/
101-
public static function fromAssociative(iterable $value, iterable $parameters): self
102-
{
103-
if (!$parameters instanceof Parameters) {
104-
$parameters = Parameters::fromAssociative($parameters);
105-
}
106-
107-
return new self($value, $parameters);
108-
}
109-
110-
public function parameters(): Parameters
111-
{
112-
return $this->parameters;
113-
}
114-
115-
public function parameter(string $key): mixed
106+
public static function new(StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool ...$members): self
116107
{
117-
try {
118-
return $this->parameters->get($key)->value();
119-
} catch (StructuredFieldError) {
120-
return null;
121-
}
108+
return new self($members, Parameters::new());
122109
}
123110

124111
public function toHttpValue(): string
@@ -139,6 +126,25 @@ public function toPair(): array
139126
return [$this->members, $this->parameters];
140127
}
141128

129+
public function getIterator(): Iterator
130+
{
131+
yield from $this->members;
132+
}
133+
134+
public function parameters(): Parameters
135+
{
136+
return $this->parameters;
137+
}
138+
139+
public function parameter(string $key): mixed
140+
{
141+
try {
142+
return $this->parameters->get($key)->value();
143+
} catch (StructuredFieldError) {
144+
return null;
145+
}
146+
}
147+
142148
public function count(): int
143149
{
144150
return count($this->members);
@@ -162,11 +168,6 @@ public function keys(): array
162168
return array_keys($this->members);
163169
}
164170

165-
public function getIterator(): Iterator
166-
{
167-
yield from $this->members;
168-
}
169-
170171
public function has(string|int ...$keys): bool
171172
{
172173
foreach ($keys as $offset) {
@@ -208,34 +209,6 @@ public function get(string|int $key): StructuredField
208209
return $this->members[$index];
209210
}
210211

211-
/**
212-
* @param int $offset
213-
*/
214-
public function offsetExists(mixed $offset): bool
215-
{
216-
return $this->has($offset);
217-
}
218-
219-
/**
220-
* @param int $offset
221-
*
222-
* @return SfItem
223-
*/
224-
public function offsetGet(mixed $offset): mixed
225-
{
226-
return $this->get($offset);
227-
}
228-
229-
public function offsetUnset(mixed $offset): void
230-
{
231-
throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
232-
}
233-
234-
public function offsetSet(mixed $offset, mixed $value): void
235-
{
236-
throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
237-
}
238-
239212
/**
240213
* Inserts members at the beginning of the list.
241214
*/
@@ -288,8 +261,13 @@ public function replace(int $key, StructuredField|Token|ByteSequence|DateTimeInt
288261
throw InvalidOffset::dueToIndexNotFound($key);
289262
}
290263

264+
$member = self::filterMember($member);
265+
if ($member == $this->members[$offset]) {
266+
return $this;
267+
}
268+
291269
$members = $this->members;
292-
$members[$offset] = $member;
270+
$members[$offset] = self::filterMember($member);
293271

294272
return new self($members, $this->parameters);
295273
}
@@ -315,6 +293,34 @@ public function remove(string|int ...$keys): static
315293
), $this->parameters);
316294
}
317295

296+
/**
297+
* @param int $offset
298+
*/
299+
public function offsetExists(mixed $offset): bool
300+
{
301+
return $this->has($offset);
302+
}
303+
304+
/**
305+
* @param int $offset
306+
*
307+
* @return SfItem
308+
*/
309+
public function offsetGet(mixed $offset): mixed
310+
{
311+
return $this->get($offset);
312+
}
313+
314+
public function offsetUnset(mixed $offset): void
315+
{
316+
throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
317+
}
318+
319+
public function offsetSet(mixed $offset, mixed $value): void
320+
{
321+
throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
322+
}
323+
318324
public function withParameters(Parameters $parameters): static
319325
{
320326
return ($this->parameters->toHttpValue() === $parameters->toHttpValue()) ? $this : new self($this->members, $parameters);

src/InnerListTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ public function it_can_unshift_insert_and_replace(): void
7575
self::assertSame('(:SGVsbG8gV29ybGQ=: 42.0 42)', (string) $container);
7676
}
7777

78+
#[Test]
79+
public function it_can_return_the_same_object_if_no_replace_is_needed(): void
80+
{
81+
$item = Item::new(ByteSequence::fromDecoded('Hello World'));
82+
$field = InnerList::new($item);
83+
84+
self::assertSame($field, $field->replace(0, ByteSequence::fromDecoded('Hello World')));
85+
}
86+
7887
#[Test]
7988
public function it_returns_the_same_object_if_nothing_is_changed(): void
8089
{

src/Item.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public static function fromPair(array $pair): self
8383
}
8484

8585
if (2 !== count($pair)) { /* @phpstan-ignore-line */
86-
throw new SyntaxError('The pair first member must be the item value and the optional second member the item parameters.');
86+
throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.');
8787
}
8888

8989
if (!$pair[1] instanceof Parameters) {

0 commit comments

Comments
 (0)