Skip to content

Commit b939cf4

Browse files
committed
Improve Item named constructor
1 parent 3d508b2 commit b939cf4

File tree

3 files changed

+56
-28
lines changed

3 files changed

+56
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
1717
- methods `getByKey`, `hasKeys`, `indexByKey`, `keyByIndex`, `toAssociative` to all ordered map classes (`Dictionary` and `Parameters`).
1818
- `StructuredFieldProvider` interface
1919
- `Item::parameterByKey` and `Item::parameterByIndex` methods to mirror the new public API.
20-
- `Item::tryNew` and `Item::tryFromPair` which return `null` instead of throwing an exception
20+
- All `Item` named constructors have a `try` equivalent which return `null` instead of throwing an exception
2121
- `Parameters::valueByKey`
2222
- `Parameters::valueByIndex`
2323
- Added a validation mechanism to facilitate `Item` and `Parameters` validation against field definition.

docs/2.0/field-values.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ use Bakame\Http\StructuredFields\Item;
148148
use Bakame\Http\StructuredFields\Token;
149149

150150
Item:new(DateTimeInterface|Byte|Token|DisplayString|string|int|array|float|bool $value): self
151-
Item:tryNew(mixed $value): ?self
152151
Item::fromDecodedBytes(Stringable|string $value): self;
153152
Item::fromEncodedBytes(Stringable|string $value): self;
154153
Item::fromEncodedDisplayString(Stringable|string $value): self;
@@ -165,6 +164,15 @@ Item::true(): self;
165164
Item::false(): self;
166165
```
167166

167+
On error, those named constructor will throw an exception, if you do not want an exception
168+
and prefer a softer approache where the named constructor would instead return `null`, you
169+
can prepend the named constructor with the `try` prefix.
170+
171+
```php
172+
Item::fromDecimal('42'); // will throw because the value is a string;
173+
Item::tryFromDecimal('42'); // will return null instead
174+
```
175+
168176
To update the `Item` instance value, use the `withValue` method:
169177

170178
```php

src/Item.php

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
namespace Bakame\Http\StructuredFields;
66

7+
use BadMethodCallException;
78
use Bakame\Http\StructuredFields\Validation\Violation;
89
use DateTimeImmutable;
910
use DateTimeInterface;
1011
use DateTimeZone;
1112
use Exception;
13+
use ReflectionMethod;
1214
use Stringable;
1315
use Throwable;
1416
use TypeError;
@@ -27,6 +29,24 @@
2729
* @phpstan-import-type SfItemInput from StructuredFieldProvider
2830
* @phpstan-import-type SfItemPair from StructuredFieldProvider
2931
* @phpstan-import-type SfTypeInput from StructuredFieldProvider
32+
*
33+
* @method static ?Item tryFromPair(array{0: SfItemInput, 1?: Parameters|iterable<array{0:string, 1:SfItemInput}>}|array<mixed> $pair) try to create a new instance from a Pair
34+
* @method static ?Item tryFromRfc9651(Stringable|string $httpValue) try to create a new instance from a string using RFC9651
35+
* @method static ?Item tryFromRfc8941(Stringable|string $httpValue) try to create a new instance from a string using RFC8941
36+
* @method static ?Item tryFromHttpValue(Stringable|string $httpValue) try to create a new instance from a string
37+
* @method static ?Item tryFromAssociative(Bytes|Token|DisplayString|DateTimeInterface|string|int|float|bool $value, StructuredFieldProvider|Parameters|iterable<string, SfItemInput> $parameters) try to create a new instance from a value and a parameters as associative construct
38+
* @method static ?Item tryNew(mixed $value) try to create a new bare instance from a value
39+
* @method static ?Item tryFromEncodedBytes(Stringable|string $value) try to create a new instance from an encoded byte sequence
40+
* @method static ?Item tryFromDecodedBytes(Stringable|string $value) try to create a new instance from a decoded byte sequence
41+
* @method static ?Item tryFromEncodedDisplayString(Stringable|string $value) try to create a new instance from an encoded display string
42+
* @method static ?Item tryFromDecodedDisplayString(Stringable|string $value) try to create a new instance from a decoded display string
43+
* @method static ?Item tryFromToken(Stringable|string $value) try to create a new instance from a token string
44+
* @method static ?Item tryFromTimestamp(int $timestamp) try to create a new instance from a timestamp
45+
* @method static ?Item tryFromDateFormat(string $format, string $datetime) try to create a new instance from a date format
46+
* @method static ?Item tryFromDateString(string $datetime, DateTimeZone|string|null $timezone = null) try to create a new instance from a date string
47+
* @method static ?Item tryFromDate(DateTimeInterface $datetime) try to create a new instance from a DateTimeInterface object
48+
* @method static ?Item tryFromDecimal(int|float $value) try to create a new instance from a float
49+
* @method static ?Item tryFromInteger(int|float $value) try to create a new instance from an integer
3050
*/
3151
final class Item
3252
{
@@ -47,6 +67,32 @@ private function __construct(Token|Bytes|DisplayString|DateTimeInterface|int|flo
4767
$this->type = Type::fromVariable($value);
4868
}
4969

70+
/**
71+
* @throws BadMethodCallException
72+
*/
73+
public static function __callStatic(string $name, array $arguments): ?self /* @phpstan-ignore-line */
74+
{
75+
if (!str_starts_with($name, 'try')) {
76+
throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" does not exist.');
77+
}
78+
79+
$namedConstructor = lcfirst(substr($name, strlen('try')));
80+
if (!method_exists(self::class, $namedConstructor)) {
81+
throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" does not exist.');
82+
}
83+
84+
$method = new ReflectionMethod(self::class, $namedConstructor);
85+
if (!$method->isPublic() || !$method->isStatic()) {
86+
throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" can not be accessed directly.');
87+
}
88+
89+
try {
90+
return self::$namedConstructor(...$arguments); /* @phpstan-ignore-line */
91+
} catch (Throwable) { /* @phpstan-ignore-line */
92+
return null;
93+
}
94+
}
95+
5096
public static function fromRfc9651(Stringable|string $httpValue): self
5197
{
5298
return self::fromHttpValue($httpValue, Ietf::Rfc9651);
@@ -97,18 +143,6 @@ public static function fromAssociative(
97143
return new self($value, $parameters);
98144
}
99145

100-
/**
101-
* @param array{0: SfItemInput, 1?: Parameters|iterable<array{0:string, 1:SfItemInput}>}|array<mixed> $pair
102-
*/
103-
public static function tryFromPair(array $pair): ?self
104-
{
105-
try {
106-
return self::fromPair($pair);
107-
} catch (StructuredFieldError) {
108-
return null;
109-
}
110-
}
111-
112146
/**
113147
* @param array{0: SfItemInput, 1?: Parameters|iterable<array{0:string, 1:SfItemInput}>}|array<mixed> $pair
114148
*
@@ -157,20 +191,6 @@ public static function new(mixed $value): self
157191
return new self($value); /* @phpstan-ignore-line */
158192
}
159193

160-
/**
161-
* Returns a new bare instance from value or null on error.
162-
*
163-
* @param SfTypeInput|Item|array{0:SfTypeInput, 1:Parameters|iterable<array{0:string, 1:SfTypeInput}>} $value
164-
*/
165-
public static function tryNew(StructuredFieldProvider|Item|DateTimeInterface|Bytes|DisplayString|Token|array|int|float|string|bool $value): ?self
166-
{
167-
try {
168-
return self::new($value);
169-
} catch (SyntaxError) {
170-
return null;
171-
}
172-
}
173-
174194
/**
175195
* Returns a new instance from a string.
176196
*

0 commit comments

Comments
 (0)