Skip to content

Commit ed27770

Browse files
committed
Restrict Stringable support on Item and Value construction
1 parent 7d46a96 commit ed27770

File tree

10 files changed

+48
-69
lines changed

10 files changed

+48
-69
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
1414
- `Item::fromDate` to improve and complete the Item Date public API;
1515
- `Item::fromAssociative` to improve Item public API;
1616
- `Item::fromString` to improve Item public API;
17-
- `Value` internal class to improve Item public API;
1817
- `Token::toString` to return the string representation of the token.
1918
- `Item::new`, `Parameters::new`, `Dictionary::new`, `InnerList::new` and `OuterList::new` to return a new instance
2019

2120
### Fixed
2221

2322
- Improve annotation using `@phpstan-type`
23+
- `Value` internal class to improve Item public API;
2424

2525
### Deprecated
2626

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,15 @@ Per the RFC, items can have different types that are translated to PHP using:
9191

9292
The table below summarizes the item value type.
9393

94-
| RFC Type | PHP Type | Package Enum Type |
95-
|---------------|--------------------------------|----------------------|
96-
| Integer | `int` | `Type::Integer` |
97-
| Decimal | `float` | `Type::Decimal` |
98-
| String | `string` or `Stringable` class | `Tyoe::String` |
99-
| Boolean | `bool` | `Type::Boolean` |
100-
| Token | class `Token` | `Type::Token` |
101-
| Byte Sequence | class `ByteSequence` | `Type::ByteSequence` |
102-
| Date | class `DateTimeImmutable` | `Type::Date` |
94+
| RFC Type | PHP Type | Package Enum Type |
95+
|---------------|---------------------------|----------------------|
96+
| Integer | `int` | `Type::Integer` |
97+
| Decimal | `float` | `Type::Decimal` |
98+
| String | `string` | `Type::String` |
99+
| Boolean | `bool` | `Type::Boolean` |
100+
| Token | class `Token` | `Type::Token` |
101+
| Byte Sequence | class `ByteSequence` | `Type::ByteSequence` |
102+
| Date | class `DateTimeImmutable` | `Type::Date` |
103103

104104
As shown in the table, the RFC define two (2) specific data types that can not be represented by
105105
PHP default type system, for them, we have defined two classes `Token` and `ByteSequence` to help

src/Dictionary.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public function pair(int $index): array
231231
return [...$this->toPairs()][$this->filterIndex($index)];
232232
}
233233

234-
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
234+
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
235235
{
236236
$members = $this->members;
237237
$members[$key] = $member;
@@ -253,7 +253,7 @@ public function remove(string|int ...$keys): static
253253
return new self($members);
254254
}
255255

256-
public function append(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
256+
public function append(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
257257
{
258258
$members = $this->members;
259259
unset($members[$key]);
@@ -266,7 +266,7 @@ public function append(string $key, StructuredField|Token|ByteSequence|DateTimeI
266266
return new self($members);
267267
}
268268

269-
public function prepend(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
269+
public function prepend(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
270270
{
271271
$members = $this->members;
272272
unset($members[$key]);

src/InnerList.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,17 @@ public function withParameters(Parameters $parameters): static
131131
return new static($parameters, $this->members);
132132
}
133133

134-
public function addParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
134+
public function addParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
135135
{
136136
return $this->withParameters($this->parameters()->add($key, $member));
137137
}
138138

139-
public function prependParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
139+
public function prependParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
140140
{
141141
return $this->withParameters($this->parameters()->prepend($key, $member));
142142
}
143143

144-
public function appendParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
144+
public function appendParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
145145
{
146146
return $this->withParameters($this->parameters()->append($key, $member));
147147
}
@@ -337,9 +337,6 @@ public function replace(int $key, StructuredField|Token|ByteSequence|DateTimeInt
337337
return $this->newInstance($members);
338338
}
339339

340-
/**
341-
* Deletes members associated with the list of instance indexes.
342-
*/
343340
public function remove(string|int ...$keys): static
344341
{
345342
$offsets = array_filter(

src/Item.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,17 @@ public function withParameters(Parameters $parameters): static
275275
return $this->parameters->toHttpValue() === $parameters->toHttpValue() ? $this : new static($this->value, $parameters);
276276
}
277277

278-
public function addParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
278+
public function addParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
279279
{
280280
return $this->withParameters($this->parameters()->add($key, $member));
281281
}
282282

283-
public function prependParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
283+
public function prependParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
284284
{
285285
return $this->withParameters($this->parameters()->prepend($key, $member));
286286
}
287287

288-
public function appendParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
288+
public function appendParameter(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
289289
{
290290
return $this->withParameters($this->parameters()->append($key, $member));
291291
}

src/ItemTest.php

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,6 @@ public static function provideFrom1stArgument(): iterable
105105
return [
106106
'decimal' => ['value' => 42.0, 'expected' => '42.0'],
107107
'string' => ['value' => 'forty-two', 'expected' => '"forty-two"'],
108-
'stringable' => ['value' => new class() implements Stringable {
109-
public function __toString(): string
110-
{
111-
return 'forty-two';
112-
}
113-
}, 'expected' => '"forty-two"'],
114108
'integer' => ['value' => 42, 'expected' => '42'],
115109
'boolean true' => ['value' => true, 'expected' => '?1'],
116110
'boolean false' => ['value' => false, 'expected' => '?0'],
@@ -127,6 +121,19 @@ public function it_instantiates_a_token(): void
127121
self::assertSame('helloworld', Item::fromToken('helloworld')->toHttpValue());
128122
}
129123

124+
#[Test]
125+
public function it_instantiates_a_stringable_object_from_string(): void
126+
{
127+
$object = new class() implements Stringable {
128+
public function __toString(): string
129+
{
130+
return 'forty-two';
131+
}
132+
};
133+
134+
self::assertSame('"forty-two"', Item::fromString($object)->toHttpValue());
135+
}
136+
130137
#[Test]
131138
public function it_instantiates_a_date(): void
132139
{
@@ -283,15 +290,6 @@ public static function itemTypeProvider(): iterable
283290
'item' => Item::new(ByteSequence::fromDecoded('😊')),
284291
'expectedType' => Type::ByteSequence,
285292
],
286-
'stringable object' => [
287-
'item' => Item::new(new class() implements Stringable {
288-
public function __toString(): string
289-
{
290-
return '42';
291-
}
292-
}),
293-
'expectedType' => Type::String,
294-
],
295293
'date-immutable' => [
296294
'item' => Item::new(new DateTimeImmutable('2020-07-12 13:37:00')),
297295
'expectedType' => Type::Date,
@@ -405,16 +403,9 @@ public function it_can_create_via_with_value_method_a_new_object(): void
405403
{
406404
$instance1 = Item::fromAssociative(Token::fromString('babayaga'), ['a' => true]);
407405
$instance2 = $instance1->withValue(Token::fromString('babayaga'));
408-
$instance3 = $instance1->withValue(new class() implements Stringable {
409-
public function __toString(): string
410-
{
411-
return 'babayaga';
412-
}
413-
});
414406

415407
self::assertSame($instance1, $instance2);
416-
self::assertNotSame($instance1, $instance3);
417-
self::assertSame($instance1->parameters(), $instance3->parameters());
408+
self::assertSame($instance1->parameters(), $instance2->parameters());
418409
}
419410

420411
#[Test]

src/Parameters.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static function new(): self
7070
* its keys represent the dictionary entry key
7171
* its values represent the dictionary entry value
7272
*
73-
* @param iterable<array-key, SfItemInput> $members
73+
* @param iterable<string, SfItemInput> $members
7474
*/
7575
public static function fromAssociative(iterable $members): self
7676
{
@@ -233,7 +233,7 @@ public function pair(int $index): array
233233
return [...$this->toPairs()][$this->filterIndex($index)];
234234
}
235235

236-
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
236+
public function add(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
237237
{
238238
$members = $this->members;
239239
$members[$key] = $member;
@@ -255,10 +255,7 @@ public function remove(string|int ...$keys): static
255255
return new self($members);
256256
}
257257

258-
/**
259-
* @param SfItemInput $member
260-
*/
261-
public function append(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
258+
public function append(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
262259
{
263260
$members = $this->members;
264261
unset($members[$key]);
@@ -271,10 +268,7 @@ public function append(string $key, StructuredField|Token|ByteSequence|DateTimeI
271268
return new self($members);
272269
}
273270

274-
/**
275-
* @param SfItemInput $member
276-
*/
277-
public function prepend(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
271+
public function prepend(string $key, StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
278272
{
279273
$members = $this->members;
280274
unset($members[$key]);

src/ParametersTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ public function it_fails_to_instantiate_with_an_item_containing_already_paramete
6262
]);
6363
}
6464

65+
#[Test]
66+
public function it_fails_to_instantiate_with_an_parameter_key_as_int(): void
67+
{
68+
$this->expectException(SyntaxError::class);
69+
70+
Parameters::fromAssociative([1 => Item::true()]); // @phpstan-ignore-line
71+
}
72+
6573
#[Test]
6674
public function it_can_add_or_remove_members(): void
6775
{

src/TypeTest.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PHPUnit\Framework\Attributes\DataProvider;
1010
use PHPUnit\Framework\Attributes\Test;
1111
use PHPUnit\Framework\TestCase;
12-
use Stringable;
1312

1413
final class TypeTest extends TestCase
1514
{
@@ -78,15 +77,6 @@ public static function itemTypeProvider(): iterable
7877
'value' => ByteSequence::fromDecoded('😊'),
7978
'expectedType' => Type::ByteSequence,
8079
],
81-
'stringable object' => [
82-
'value' => new class() implements Stringable {
83-
public function __toString(): string
84-
{
85-
return '42';
86-
}
87-
},
88-
'expectedType' => Type::String,
89-
],
9080
'datetime implementing object' => [
9181
'value' => new DateTime('2020-07-12 13:37:00'),
9282
'expectedType' => Type::Date,

src/Value.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function __construct(mixed $value)
3030
is_int($value) => [self::filterIntegerRange($value, 'Integer'), Type::Integer],
3131
is_float($value) => [self::filterDecimal($value), Type::Decimal],
3232
is_bool($value) => [$value, Type::Boolean],
33-
is_string($value) || $value instanceof Stringable => [self::filterString($value), Type::String],
33+
is_string($value) => [self::filterString($value), Type::String],
3434
default => throw new SyntaxError('The type "'.(is_object($value) ? $value::class : gettype($value)).'" is not supported.')
3535
};
3636
}
@@ -68,9 +68,8 @@ private static function filterDecimal(float $value): float
6868
*
6969
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.3
7070
*/
71-
private static function filterString(Stringable|string $value): string
71+
private static function filterString(string $value): string
7272
{
73-
$value = (string) $value;
7473
if (1 === preg_match('/[^\x20-\x7E]/i', $value)) {
7574
throw new SyntaxError('The string contains invalid characters.');
7675
}
@@ -184,7 +183,7 @@ public static function fromInteger(int|float $value): self
184183

185184
public static function fromString(Stringable|string $value): self
186185
{
187-
return new self($value);
186+
return new self((string) $value);
188187
}
189188

190189
public static function true(): self

0 commit comments

Comments
 (0)