Skip to content

Commit 7246539

Browse files
committed
Improve collection immutability if nothing is changed
1 parent e6a0d17 commit 7246539

10 files changed

+82
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
1313
- Migrate to PHPUnit 10
1414
- **[BC Break]** `OrderedList` is renamed `OuterList`.
1515
- **[BC Break]** `ParameterAccess` interface signature updated to use the `Value` interface instead of the `Item` implementation.
16+
- Improve Collection immutability with method changes
1617

1718
### Deprecated
1819

src/Dictionary.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ public function remove(string ...$keys): static
229229
unset($members[$key]);
230230
}
231231

232+
if ($members === $this->members) {
233+
return $this;
234+
}
235+
232236
return new self($members);
233237
}
234238

src/DictionaryTest.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function it_can_be_instantiated_with_an_collection_of_item_or_inner_list(
3434
}
3535

3636
#[Test]
37-
public function test_it_can_be_instantiated_with_key_value_pairs(): void
37+
public function it_can_be_instantiated_with_key_value_pairs(): void
3838
{
3939
$stringItem = Item::from('helloWorld');
4040
$booleanItem = Item::from(true);
@@ -80,6 +80,14 @@ public function it_can_add_or_remove_members(): void
8080
self::assertFalse($deleteAgain->hasMembers());
8181
}
8282

83+
#[Test]
84+
public function it_returns_the_same_object_if_no_member_is_removed(): void
85+
{
86+
$instance = Dictionary::create();
87+
88+
self::assertSame($instance, $instance->remove('foo', 'bar', 'baz'));
89+
}
90+
8391
#[Test]
8492
public function it_fails_to_return_an_member_with_invalid_key(): void
8593
{

src/InnerList.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function withParameters(Parameters $parameters): static
9595

9696
public function toHttpValue(): string
9797
{
98-
return '('.implode(' ', array_map(fn (Value $value): string => $value->toHttpValue(), $this->members)).')'.$this->parameters->toHttpValue();
98+
return '('.implode(' ', array_map(fn (StructuredField $value): string => $value->toHttpValue(), $this->members)).')'.$this->parameters->toHttpValue();
9999
}
100100

101101
public function __toString(): string
@@ -161,6 +161,10 @@ public function get(string|int $offset): Value
161161
*/
162162
public function unshift(StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
163163
{
164+
if ([] === $members) {
165+
return $this;
166+
}
167+
164168
return new self($this->parameters, [...array_map(self::filterMember(...), array_values($members)), ...$this->members]);
165169
}
166170

@@ -169,6 +173,10 @@ public function unshift(StructuredField|Token|ByteSequence|DateTimeInterface|Str
169173
*/
170174
public function push(StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
171175
{
176+
if ([] === $members) {
177+
return $this;
178+
}
179+
172180
return new self($this->parameters, [...$this->members, ...array_map(self::filterMember(...), array_values($members))]);
173181
}
174182

@@ -194,6 +202,7 @@ public function insert(int $index, StructuredField|Token|ByteSequence|DateTimeIn
194202
null === $offset => throw InvalidOffset::dueToIndexNotFound($index),
195203
0 === $offset => $this->unshift(...$members),
196204
count($this->members) === $offset => $this->push(...$members),
205+
[] === $members => $this,
197206
default => (function (array $newMembers) use ($offset, $members) {
198207
array_splice($newMembers, $offset, 0, array_map(self::filterMember(...), $members));
199208

@@ -224,6 +233,10 @@ public function remove(int ...$indexes): static
224233
fn (int|null $index): bool => null !== $index
225234
);
226235

236+
if ([] === $offsets) {
237+
return $this;
238+
}
239+
227240
$members = $this->members;
228241
foreach ($offsets as $offset) {
229242
unset($members[$offset]);

src/InnerListTest.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ public function it_can_unshift_insert_and_replace(): void
7272
self::assertSame('(:SGVsbG8gV29ybGQ=: 42.0 42)', (string) $container);
7373
}
7474

75+
#[Test]
76+
public function it_returns_the_same_object_if_nothing_is_changed(): void
77+
{
78+
$container = InnerList::from(42, 'forty-two');
79+
80+
$sameContainer = $container
81+
->unshift()
82+
->push()
83+
->insert(1);
84+
85+
self::assertSame($container, $sameContainer);
86+
}
87+
7588
#[Test]
7689
public function it_fails_to_replace_invalid_index(): void
7790
{
@@ -148,7 +161,7 @@ public function it_fails_to_insert_unknown_index_via_the_array_access_interface(
148161
}
149162

150163
#[Test]
151-
public function testArrayAccessThrowsInvalidIndex2(): void
164+
public function it_returns_the_same_object_if_no_member_is_removed(): void
152165
{
153166
self::assertCount(0, InnerList::from()->remove(0));
154167
}

src/ItemTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public function __toString(): string
201201
}
202202

203203
#[Test]
204-
public function test_in_can_be_instantiated_using_bare_items(): void
204+
public function in_can_be_instantiated_using_bare_items(): void
205205
{
206206
$parameters = [
207207
'string' => '42',

src/OuterList.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ public function get(string|int $offset): Value|InnerList
141141
*/
142142
public function unshift(StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
143143
{
144+
if ([] === $members) {
145+
return $this;
146+
}
147+
144148
return new self(...[...array_map(self::filterMember(...), array_values($members)), ...$this->members]);
145149
}
146150

@@ -149,6 +153,10 @@ public function unshift(StructuredField|Token|ByteSequence|DateTimeInterface|Str
149153
*/
150154
public function push(StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
151155
{
156+
if ([] === $members) {
157+
return $this;
158+
}
159+
152160
return new self(...[...$this->members, ...array_map(self::filterMember(...), array_values($members))]);
153161
}
154162

@@ -165,6 +173,7 @@ public function insert(int $index, StructuredField|Token|ByteSequence|DateTimeIn
165173
null === $offset => throw InvalidOffset::dueToIndexNotFound($index),
166174
0 === $offset => $this->unshift(...$members),
167175
count($this->members) === $offset => $this->push(...$members),
176+
[] === $members => $this,
168177
default => (function (array $newMembers) use ($offset, $members) {
169178
array_splice($newMembers, $offset, 0, array_map(self::filterMember(...), $members));
170179

@@ -200,6 +209,10 @@ public function remove(int ...$indexes): static
200209
fn (int|null $index): bool => null !== $index
201210
);
202211

212+
if ($offsets === []) {
213+
return $this;
214+
}
215+
203216
$members = $this->members;
204217
foreach ($offsets as $offset) {
205218
unset($members[$offset]);

src/OuterListTest.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function it_fails_to_return_an_member_with_invalid_index(): void
104104
}
105105

106106
#[Test]
107-
public function test_it_can_generate_the_same_value(): void
107+
public function it_can_generate_the_same_value(): void
108108
{
109109
$res = OuterList::fromHttpValue('token, "string", ?1; parameter, (42 42.0)');
110110

@@ -127,9 +127,17 @@ public function it_fails_to_insert_unknown_index_via_the_array_access_interface(
127127
}
128128

129129
#[Test]
130-
public function testArrayAccessThrowsInvalidIndex2(): void
130+
public function it_returns_the_same_object_if_nothing_is_changed(): void
131131
{
132-
self::assertCount(0, OuterList::from()->remove(0));
132+
$container = OuterList::from(42, 'forty-two');
133+
134+
$sameContainer = $container
135+
->unshift()
136+
->push()
137+
->insert(1)
138+
->remove(42, 46);
139+
140+
self::assertSame($container, $sameContainer);
133141
}
134142

135143
#[Test]

src/Parameters.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ public function remove(string ...$keys): static
242242
unset($members[$key]);
243243
}
244244

245+
if ($members === $this->members) {
246+
return $this;
247+
}
248+
245249
return new self($members);
246250
}
247251

src/ParametersTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ public function it_can_add_or_remove_members(): void
9797
self::assertTrue($altInstance->hasNoMembers());
9898
}
9999

100+
#[Test]
101+
public function it_returns_the_same_object_if_no_member_is_removed(): void
102+
{
103+
$stringItem = Item::from('helloWorld');
104+
$booleanItem = Item::from(true);
105+
$arrayParams = ['string' => $stringItem, 'boolean' => $booleanItem];
106+
$instance = Parameters::fromAssociative($arrayParams);
107+
108+
self::assertSame($instance, $instance->remove('foo', 'bar', 'baz'));
109+
}
110+
100111
#[Test]
101112
public function it_fails_to_add_an_item_with_wrong_key(): void
102113
{

0 commit comments

Comments
 (0)