Skip to content

Commit 638dba8

Browse files
committed
Make Value more strict
1 parent 5f26ddc commit 638dba8

File tree

11 files changed

+40
-74
lines changed

11 files changed

+40
-74
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ The `Item` value object exposes a number of named constructors to construct
213213
bare items (ie: item without parameters attached to them).
214214

215215
```php
216+
use Bakame\Http\StructuredFields\ByteSequence;
216217
use Bakame\Http\StructuredFields\Item;
218+
use Bakame\Http\StructuredFields\Token;
217219

218-
Item::new(mixed $value): self;
220+
Item:new(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): self
219221
Item::fromDecodedByteSequence(Stringable|string $value): self;
220222
Item::fromEncodedByteSequence(Stringable|string $value): self;
221223
Item::fromToken(Stringable|string $value): self;

src/InnerList.php

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Bakame\Http\StructuredFields;
66

77
use ArrayAccess;
8+
use DateTimeImmutable;
89
use DateTimeInterface;
910
use Iterator;
1011
use Stringable;
@@ -66,7 +67,7 @@ public static function fromHttpValue(Stringable|string $httpValue): self
6667
* Returns a new instance with an iter.
6768
*
6869
* @param iterable<SfItemInput> $value
69-
* @param iterable<string, SfItemInput> $parameters
70+
* @param MemberOrderedMap<string, SfItem>|iterable<string, SfItemInput> $parameters
7071
*/
7172
public static function fromAssociative(iterable $value, iterable $parameters): self
7273
{
@@ -85,19 +86,11 @@ public static function fromAssociative(iterable $value, iterable $parameters): s
8586
*/
8687
public static function fromPair(array $pair): self
8788
{
88-
if (!array_is_list($pair)) { /* @phpstan-ignore-line */
89-
throw new SyntaxError('The pair must be represented by an array as a list.');
90-
}
91-
92-
if (2 !== count($pair)) { /* @phpstan-ignore-line */
93-
throw new SyntaxError('The pair first member must be the member list and the optional second member the inner list parameters.');
94-
}
95-
96-
if (!$pair[1] instanceof Parameters) {
97-
$pair[1] = Parameters::fromPairs($pair[1]);
98-
}
99-
100-
return new self($pair[0], $pair[1]);
89+
return match (true) {
90+
!array_is_list($pair) => throw new SyntaxError('The pair must be represented by an array as a list.'), /* @phpstan-ignore-line */
91+
2 !== count($pair) => throw new SyntaxError('The pair first member must be the member list and the second member the inner list parameters.'), /* @phpstan-ignore-line */
92+
default => new self($pair[0], !$pair[1] instanceof Parameters ? Parameters::fromPairs($pair[1]) : $pair[1]),
93+
};
10194
}
10295

10396
/**
@@ -136,7 +129,7 @@ public function parameters(): Parameters
136129
return $this->parameters;
137130
}
138131

139-
public function parameter(string $key): mixed
132+
public function parameter(string $key): Token|ByteSequence|DateTimeImmutable|int|float|string|bool|null
140133
{
141134
try {
142135
return $this->parameters->get($key)->value();

src/Item.php

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public static function fromHttpValue(Stringable|string $httpValue): self
5959
*
6060
* @throws SyntaxError If the value or the parameters are not valid
6161
*/
62-
public static function fromAssociative(mixed $value, iterable $parameters): self
62+
public static function fromAssociative(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value, iterable $parameters): self
6363
{
6464
if (!$parameters instanceof Parameters) {
6565
$parameters = Parameters::fromAssociative($parameters);
@@ -70,35 +70,27 @@ public static function fromAssociative(mixed $value, iterable $parameters): self
7070

7171
/**
7272
* @param array{
73-
* 0:SfItemInput,
73+
* 0:DateTimeInterface|ByteSequence|Token|string|int|float|bool,
7474
* 1:MemberOrderedMap<string, SfItem>|iterable<array{0:string, 1:SfItemInput}>
7575
* } $pair
7676
*
7777
* @throws SyntaxError If the pair or its content is not valid.
7878
*/
7979
public static function fromPair(array $pair): self
8080
{
81-
if (!array_is_list($pair)) { /* @phpstan-ignore-line */
82-
throw new SyntaxError('The pair must be represented by an array as a list.');
83-
}
84-
85-
if (2 !== count($pair)) { /* @phpstan-ignore-line */
86-
throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.');
87-
}
88-
89-
if (!$pair[1] instanceof Parameters) {
90-
$pair[1] = Parameters::fromPairs($pair[1]);
91-
}
92-
93-
return new self(new Value($pair[0]), $pair[1]);
81+
return match (true) {
82+
!array_is_list($pair) => throw new SyntaxError('The pair must be represented by an array as a list.'), /* @phpstan-ignore-line */
83+
2 !== count($pair) => throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.'), /* @phpstan-ignore-line */
84+
default => new self(new Value($pair[0]), $pair[1] instanceof Parameters ? $pair[1] : Parameters::fromPairs($pair[1])),
85+
};
9486
}
9587

9688
/**
9789
* Returns a new bare instance from value.
9890
*
9991
* @throws SyntaxError If the value is not valid.
10092
*/
101-
public static function new(mixed $value): self
93+
public static function new(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): self
10294
{
10395
return self::fromValue(new Value($value));
10496
}
@@ -242,7 +234,7 @@ public function parameters(): Parameters
242234
return $this->parameters;
243235
}
244236

245-
public function parameter(string $key): mixed
237+
public function parameter(string $key): Token|ByteSequence|DateTimeImmutable|int|float|string|bool|null
246238
{
247239
try {
248240
return $this->parameters->get($key)->value();
@@ -274,7 +266,7 @@ public function toPair(): array
274266
return [$this->value->value, $this->parameters];
275267
}
276268

277-
public function withValue(mixed $value): static
269+
public function withValue(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): static
278270
{
279271
$value = new Value($value);
280272
if ($value->equals($this->value)) {

src/ItemTest.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class ItemTest extends StructuredFieldTestCase
3232

3333
#[Test]
3434
#[DataProvider('provideInvalidArguments')]
35-
public function it_fails_to_instantiate_an_item(mixed $value): void
35+
public function it_fails_to_instantiate_an_item(ByteSequence|Token|DateTimeInterface|string|int|float|bool $value): void
3636
{
3737
$this->expectException(SyntaxError::class);
3838

@@ -75,19 +75,16 @@ public static function provideInvalidArguments(): iterable
7575

7676
#[Test]
7777
#[DataProvider('provideFrom1stArgument')]
78-
public function it_instantiate_many_types(ValueAccess|ByteSequence|Token|DateTimeInterface|string|int|float|bool $value, string $expected): void
78+
public function it_instantiate_many_types(ByteSequence|Token|DateTimeInterface|string|int|float|bool $value, string $expected): void
7979
{
8080
self::assertSame($expected, Item::new($value)->toHttpValue());
8181
}
8282

8383
#[Test]
8484
#[DataProvider('provideFrom1stArgument')]
85-
public function it_updates_item(ValueAccess|ByteSequence|Token|DateTimeInterface|string|int|float|bool $value, string $expected): void
85+
public function it_updates_item(ByteSequence|Token|DateTimeInterface|string|int|float|bool $value, string $expected): void
8686
{
8787
$parameters = Parameters::fromAssociative(['foo' => 'bar']);
88-
if ($value instanceof ParameterAccess && $value instanceof ValueAccess) {
89-
$expected = $value->withoutAnyParameter()->toHttpValue();
90-
}
9188

9289
self::assertSame(
9390
$expected.$parameters->toHttpValue(),
@@ -100,8 +97,6 @@ public function it_updates_item(ValueAccess|ByteSequence|Token|DateTimeInterface
10097
*/
10198
public static function provideFrom1stArgument(): iterable
10299
{
103-
$item = Item::new(42);
104-
105100
return [
106101
'decimal' => ['value' => 42.0, 'expected' => '42.0'],
107102
'string' => ['value' => 'forty-two', 'expected' => '"forty-two"'],
@@ -111,7 +106,6 @@ public static function provideFrom1stArgument(): iterable
111106
'token' => ['value' => Token::fromString('helloworld'), 'expected' => 'helloworld'],
112107
'byte sequence' => ['value' => ByteSequence::fromDecoded('foobar'), 'expected' => ':Zm9vYmFy:'],
113108
'datetime' => ['value' => new DateTime('2020-03-04 19:23:15'), 'expected' => '@1583349795'],
114-
'value' => ['value' => $item, 'expected' => $item->toHttpValue()],
115109
];
116110
}
117111

src/ParameterAccess.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
namespace Bakame\Http\StructuredFields;
66

7+
use DateTimeImmutable;
8+
79
/**
8-
* @phpstan-import-type SfTypeInput from StructuredField
910
* @phpstan-import-type SfItem from StructuredField
1011
*/
1112
interface ParameterAccess
@@ -22,10 +23,8 @@ public function parameters(): Parameters;
2223

2324
/**
2425
* Returns the member value or null if no members value exists.
25-
*
26-
* @return SfTypeInput|null
2726
*/
28-
public function parameter(string $key): mixed;
27+
public function parameter(string $key): Token|ByteSequence|DateTimeImmutable|int|float|string|bool|null;
2928

3029
/**
3130
* Adds a member if its key is not present at the of the associated parameter instance or update the instance at the given key.

src/Parameters.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
/**
2121
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.1.2
2222
*
23-
* @phpstan-import-type SfTypeInput from StructuredField
2423
* @phpstan-import-type SfItem from StructuredField
2524
* @phpstan-import-type SfItemInput from StructuredField
2625
*

src/Parser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private static function parseInnerListValue(string $httpValue): array
187187
*
188188
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.2.3.1
189189
*
190-
* @return array{0:SfTypeInput, 1:int}
190+
* @return array{0:ByteSequence|Token|DateTimeImmutable|string|int|float|bool, 1:int}
191191
*/
192192
public static function parseBareItem(string $httpValue): array
193193
{

src/Type.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Bakame\Http\StructuredFields;
66

7+
use DateTimeInterface;
78
use Throwable;
89

910
/**
@@ -28,15 +29,15 @@ public function equals(mixed $other): bool
2829
return $other instanceof self && $other === $this;
2930
}
3031

31-
public static function fromValue(mixed $value): self
32+
public static function fromValue(ValueAccess|Token|ByteSequence|DateTimeInterface|int|float|string|bool $value): self
3233
{
3334
return (new Value($value))->type;
3435
}
3536

3637
public static function tryFromValue(mixed $value): self|null
3738
{
3839
try {
39-
return self::fromValue($value);
40+
return self::fromValue($value); /* @phpstan-ignore-line */
4041
} catch (Throwable) {
4142
return null;
4243
}

src/TypeTest.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@
1212

1313
final class TypeTest extends TestCase
1414
{
15-
#[Test]
16-
public function it_will_throw_if_the_type_is_no_supported(): void
17-
{
18-
$this->expectException(InvalidArgument::class);
19-
20-
Type::fromValue([]);
21-
}
22-
2315
#[Test]
2416
public function it_will_return_null_if_the_type_is_no_supported(): void
2517
{
@@ -43,7 +35,7 @@ public function it_can_tell_the_item_type_from_a_value_instance(): void
4335
#[DataProvider('itemTypeProvider')]
4436
public function it_can_tell_the_item_type(mixed $value, Type $expectedType): void
4537
{
46-
self::assertTrue($expectedType->equals(Type::fromValue($value)));
38+
self::assertTrue($expectedType->equals(Type::fromValue($value))); /* @phpstan-ignore-line */
4739
self::assertTrue($expectedType->equals(Type::tryFromValue($value)));
4840
}
4941

src/Value.php

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use function is_bool;
1616
use function is_float;
1717
use function is_int;
18-
use function is_string;
1918
use function json_encode;
2019
use function preg_match;
2120
use function preg_replace;
@@ -32,7 +31,7 @@ final class Value
3231
public readonly Token|ByteSequence|DateTimeImmutable|int|float|string|bool $value;
3332
public readonly Type $type;
3433

35-
public function __construct(mixed $value)
34+
public function __construct(ValueAccess|Token|ByteSequence|DateTimeInterface|int|float|string|bool $value)
3635
{
3736
[$this->value, $this->type] = match (true) {
3837
$value instanceof ValueAccess => [$value->value(), $value->type()],
@@ -42,8 +41,7 @@ public function __construct(mixed $value)
4241
is_int($value) => [self::filterIntegerRange($value, 'Integer'), Type::Integer],
4342
is_float($value) => [self::filterDecimal($value), Type::Decimal],
4443
is_bool($value) => [$value, Type::Boolean],
45-
is_string($value) => [self::filterString($value), Type::String],
46-
default => throw new InvalidArgument('The type "'.(is_object($value) ? $value::class : gettype($value)).'" is not supported.')
44+
default => [self::filterString($value), Type::String],
4745
};
4846
}
4947

@@ -216,13 +214,13 @@ public static function false(): self
216214
public function serialize(): string
217215
{
218216
return match (true) {
219-
is_string($this->value) => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"',
217+
$this->value instanceof DateTimeImmutable => '@'.$this->value->getTimestamp(),
218+
$this->value instanceof Token => $this->value->toString(),
219+
$this->value instanceof ByteSequence => ':'.$this->value->encoded().':',
220220
is_int($this->value) => (string) $this->value,
221221
is_float($this->value) => self::serializeDecimal($this->value),
222222
is_bool($this->value) => '?'.($this->value ? '1' : '0'),
223-
$this->value instanceof Token => $this->value->toString(),
224-
$this->value instanceof DateTimeImmutable => '@'.$this->value->getTimestamp(),
225-
default => ':'.$this->value->encoded().':',
223+
default => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"',
226224
};
227225
}
228226

src/ValueAccess.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
namespace Bakame\Http\StructuredFields;
66

77
use DateTimeImmutable;
8+
use DateTimeInterface;
89

9-
/**
10-
* @phpstan-import-type SfTypeInput from StructuredField
11-
*/
1210
interface ValueAccess extends StructuredField
1311
{
1412
/**
@@ -27,9 +25,7 @@ public function type(): Type;
2725
* This method MUST retain the state of the current instance, and return
2826
* an instance that contains the specified value change.
2927
*
30-
* @param ValueAccess|SfTypeInput $value
31-
*
3228
* @throws SyntaxError If the value is invalid or not supported
3329
*/
34-
public function withValue(mixed $value): static;
30+
public function withValue(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): static;
3531
}

0 commit comments

Comments
 (0)