diff --git a/src/DataType.php b/src/DataType.php index a0cdbfe..8cc5f53 100644 --- a/src/DataType.php +++ b/src/DataType.php @@ -42,36 +42,4 @@ public function serialize(iterable $data, Ietf $rfc = Ietf::Rfc9651): string self::Parameters => Parameters::fromPairs($data), })->toHttpValue($rfc); } - - /** - * @throws SyntaxError|Exception - */ - public function fromRfc9651(Stringable|string $httpValue): OuterList|InnerList|Parameters|Dictionary|Item - { - return $this->parse($httpValue, Ietf::Rfc9651); - } - - /** - * @throws SyntaxError|Exception - */ - public function toRfc9651(iterable $data): string - { - return $this->serialize($data, Ietf::Rfc9651); - } - - /** - * @throws SyntaxError|Exception - */ - public function fromRfc8941(Stringable|string $httpValue): OuterList|InnerList|Parameters|Dictionary|Item - { - return $this->parse($httpValue, Ietf::Rfc8941); - } - - /** - * @throws SyntaxError|Exception - */ - public function toRfc8941(iterable $data): string - { - return $this->serialize($data, Ietf::Rfc8941); - } } diff --git a/src/Dictionary.php b/src/Dictionary.php index ed085ad..01ed42a 100644 --- a/src/Dictionary.php +++ b/src/Dictionary.php @@ -56,14 +56,16 @@ private static function filterMember(mixed $member): InnerList|Item { if ($member instanceof StructuredFieldProvider) { $member = $member->toStructuredField(); + if ($member instanceof Item || $member instanceof InnerList) { + return $member; + } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$member::class.' given.'); } return match (true) { $member instanceof InnerList, $member instanceof Item => $member, - $member instanceof OuterList, - $member instanceof Dictionary, - $member instanceof Parameters => throw new InvalidArgument('An instance of "'.$member::class.'" can not be a member of "'.self::class.'".'), is_iterable($member) => InnerList::new(...$member), default => Item::new($member), }; @@ -122,6 +124,11 @@ public static function fromPairs(StructuredFieldProvider|Dictionary|Parameters|i $converter = function (mixed $pair): InnerList|Item { if ($pair instanceof StructuredFieldProvider) { $pair = $pair->toStructuredField(); + if ($pair instanceof Item || $pair instanceof InnerList) { + return $pair; + } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$pair::class.' given.'); } if ($pair instanceof InnerList || $pair instanceof Item) { @@ -140,13 +147,11 @@ public static function fromPairs(StructuredFieldProvider|Dictionary|Parameters|i return InnerList::new(); } - [$member, $parameters] = match (count($pair)) { - 2 => $pair, - 1 => [$pair[0], []], - default => throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.'), - }; + if (!in_array(count($pair), [1, 2], true)) { + throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.'); + } - return is_iterable($member) ? InnerList::fromPair([$member, $parameters]) : Item::fromPair([$member, $parameters]); + return is_iterable($pair[0]) ? InnerList::fromPair($pair) : Item::fromPair($pair); }; return match (true) { @@ -193,7 +198,7 @@ public static function fromRfc8941(Stringable|string $httpValue): self */ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self { - return self::fromPairs(Parser::new($rfc)->parseDictionary($httpValue)); /* @phpstan-ignore-line */ + return self::fromPairs((new Parser($rfc))->parseDictionary($httpValue)); /* @phpstan-ignore-line */ } public function toRfc9651(): string diff --git a/src/InnerList.php b/src/InnerList.php index 4c73913..02c2ed9 100644 --- a/src/InnerList.php +++ b/src/InnerList.php @@ -80,7 +80,7 @@ private function filterMember(mixed $member): Item */ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self { - return self::fromPair(Parser::new($rfc)->parseInnerList($httpValue)); + return self::fromPair((new Parser($rfc))->parseInnerList($httpValue)); } /** diff --git a/src/Item.php b/src/Item.php index c7e7ee3..9c7efac 100644 --- a/src/Item.php +++ b/src/Item.php @@ -61,11 +61,11 @@ public static function fromRfc8941(Stringable|string $httpValue): self * * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3 * - * @throws SyntaxError If the HTTP value can not be parsed + * @throws SyntaxError|Exception If the HTTP value can not be parsed */ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self { - return self::fromPair(Parser::new($rfc)->parseItem($httpValue)); + return self::fromPair((new Parser($rfc))->parseItem($httpValue)); } /** @@ -81,13 +81,15 @@ public static function fromAssociative( ): self { if ($parameters instanceof StructuredFieldProvider) { $parameters = $parameters->toStructuredField(); - if (!$parameters instanceof Parameters) { - throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.'); + if ($parameters instanceof Parameters) { + return new self($value, $parameters); } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.'); } if (!$parameters instanceof Parameters) { - $parameters = Parameters::fromAssociative($parameters); + return new self($value, Parameters::fromAssociative($parameters)); } return new self($value, $parameters); @@ -122,13 +124,15 @@ public static function fromPair(array $pair): self if ($pair[1] instanceof StructuredFieldProvider) { $pair[1] = $pair[1]->toStructuredField(); - if (!$pair[1] instanceof Parameters) { - throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$pair[1]::class.' given.'); + if ($pair[1] instanceof Parameters) { + return new self($pair[0], Parameters::fromPairs($pair[1])); } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$pair[1]::class.' given.'); } if (!$pair[1] instanceof Parameters) { - $pair[1] = Parameters::fromPairs($pair[1]); + return new self($pair[0], Parameters::fromPairs($pair[1])); } return new self($pair[0], $pair[1]); @@ -251,10 +255,11 @@ public static function fromDateFormat(string $format, string $datetime): self throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.', 0, $exception); } - return match (false) { - $value => throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.'), - default => new self($value), - }; + if (!$value instanceof DateTimeImmutable) { + throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.'); + } + + return new self($value); } /** @@ -274,12 +279,10 @@ public static function fromDateString(string $datetime, DateTimeZone|string|null } try { - $value = new DateTimeImmutable($datetime, $timezone); + return new self(new DateTimeImmutable($datetime, $timezone)); } catch (Throwable $exception) { throw new SyntaxError('Unable to create a '.DateTimeImmutable::class.' instance with the date notation `'.$datetime.'.`', 0, $exception); } - - return new self($value); } /** diff --git a/src/OuterList.php b/src/OuterList.php index 6c53c4f..eb2cf4c 100644 --- a/src/OuterList.php +++ b/src/OuterList.php @@ -45,7 +45,7 @@ final class OuterList implements ArrayAccess, Countable, IteratorAggregate * @param SfMemberInput ...$members */ private function __construct( - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members + iterable|StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members ) { $this->members = array_map($this->filterMember(...), array_values([...$members])); } @@ -57,14 +57,16 @@ private function filterMember(mixed $member): InnerList|Item { if ($member instanceof StructuredFieldProvider) { $member = $member->toStructuredField(); + if ($member instanceof Item || $member instanceof InnerList) { + return $member; + } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$member::class.' given.'); } return match (true) { $member instanceof InnerList, $member instanceof Item => $member, - $member instanceof OuterList, - $member instanceof Parameters, - $member instanceof Dictionary => throw new InvalidArgument('An instance of "'.$member::class.'" can not be a member of "'.self::class.'".'), is_iterable($member) => InnerList::new(...$member), default => Item::new($member), }; @@ -77,7 +79,7 @@ private function filterMember(mixed $member): InnerList|Item */ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self { - return self::fromPairs(Parser::new($rfc)->parseList($httpValue)); /* @phpstan-ignore-line */ + return self::fromPairs((new Parser($rfc))->parseList($httpValue)); /* @phpstan-ignore-line */ } /** @@ -96,6 +98,11 @@ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self $converter = function (mixed $pair): InnerList|Item { if ($pair instanceof StructuredFieldProvider) { $pair = $pair->toStructuredField(); + if ($pair instanceof Item || $pair instanceof InnerList) { + return $pair; + } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$pair::class.' given.'); } if ($pair instanceof InnerList || $pair instanceof Item) { @@ -114,13 +121,11 @@ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self return InnerList::new(); } - [$member, $parameters] = match (count($pair)) { - 2 => $pair, - 1 => [$pair[0], []], - default => throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.'), - }; + if (!in_array(count($pair), [1, 2], true)) { + throw new SyntaxError('The pair first member is the item value; its second member is the item parameters.'); + } - return is_iterable($member) ? InnerList::fromPair([$member, $parameters]) : Item::fromPair([$member, $parameters]); + return is_iterable($pair[0]) ? InnerList::fromPair($pair) : Item::fromPair($pair); }; return match (true) { @@ -137,7 +142,7 @@ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self /** * @param SfMemberInput ...$members */ - public static function new(iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members): self + public static function new(iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members): self { return new self(...$members); } @@ -289,7 +294,7 @@ public function last(): InnerList|Item|null * @param SfMemberInput ...$members */ public function unshift( - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|iterable|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members + StructuredFieldProvider|InnerList|Item|iterable|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members ): self { $membersToAdd = array_reduce( $members, @@ -315,7 +320,7 @@ function (array $carry, $member) { * @param SfMemberInput ...$members */ public function push( - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members + iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members ): self { $membersToAdd = array_reduce( $members, @@ -344,7 +349,7 @@ function (array $carry, $member) { */ public function insert( int $index, - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members + iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members ): self { $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index); @@ -365,7 +370,7 @@ public function insert( */ public function replace( int $index, - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index); $member = self::filterMember($member); diff --git a/src/Parameters.php b/src/Parameters.php index 1c20f25..78ba693 100644 --- a/src/Parameters.php +++ b/src/Parameters.php @@ -145,7 +145,7 @@ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self */ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self { - return self::fromPairs(Parser::new($rfc)->parseParameters($httpValue)); /* @phpstan-ignore-line */ + return self::fromPairs((new Parser($rfc))->parseParameters($httpValue)); /* @phpstan-ignore-line */ } public static function fromRfc9651(Stringable|string $httpValue): self diff --git a/src/Parser.php b/src/Parser.php index e1f95d1..a7b9f53 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -52,11 +52,6 @@ final class Parser private const FIRST_CHARACTER_RANGE_NUMBER = '-1234567890'; private const FIRST_CHARACTER_RANGE_TOKEN = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*'; - public static function new(Ietf $rfc = Ietf::Rfc9651): self - { - return new self($rfc); - } - public function __construct(private readonly Ietf $rfc) { } diff --git a/tests/DataTypeTest.php b/tests/DataTypeTest.php index fe042df..34dc21e 100644 --- a/tests/DataTypeTest.php +++ b/tests/DataTypeTest.php @@ -98,7 +98,7 @@ public function it_will_fail_to_generate_rfc8941_structured_field_text_represena { $this->expectExceptionObject(MissingFeature::dueToLackOfSupport(Type::Date, Ietf::Rfc8941)); - DataType::Dictionary->toRfc8941([['a', false], ['b', Item::fromDateString('+30 minutes')]]); + DataType::Dictionary->serialize([['a', false], ['b', Item::fromDateString('+30 minutes')]], Ietf::Rfc8941); } #[Test] @@ -108,6 +108,6 @@ public function it_will_fail_to_parse_rfc9651_structured_field_text_represenatio $string = DataType::Dictionary->serialize([['a', false], ['b', Item::fromDateString('+30 minutes')]]); - DataType::Dictionary->fromRfc8941($string); + DataType::Dictionary->parse($string, Ietf::Rfc8941); } } diff --git a/tests/DictionaryTest.php b/tests/DictionaryTest.php index 9451321..9c806ec 100644 --- a/tests/DictionaryTest.php +++ b/tests/DictionaryTest.php @@ -6,6 +6,7 @@ use LogicException; use PHPUnit\Framework\Attributes\Test; +use TypeError; final class DictionaryTest extends StructuredFieldTestCase { @@ -125,7 +126,7 @@ public function it_fails_to_add_an_item_with_wrong_key(): void #[Test] public function it_fails_to_insert_something_other_than_a_inner_list_or_an_item(): void { - $this->expectException(InvalidArgument::class); + $this->expectException(TypeError::class); Dictionary::new()->add('foo', Parameters::fromAssociative(['foo' => 'bar'])); } diff --git a/tests/OuterListTest.php b/tests/OuterListTest.php index a7b522f..7386779 100644 --- a/tests/OuterListTest.php +++ b/tests/OuterListTest.php @@ -6,6 +6,7 @@ use LogicException; use PHPUnit\Framework\Attributes\Test; +use TypeError; final class OuterListTest extends StructuredFieldTestCase { @@ -103,7 +104,7 @@ public function it_can_return_the_same_object_if_no_replace_is_needed(): void #[Test] public function it_fails_to_insert_something_other_than_a_inner_list_or_an_item(): void { - $this->expectException(InvalidArgument::class); + $this->expectException(TypeError::class); OuterList::new()->push(Dictionary::fromAssociative(['foo' => 'bar'])); } diff --git a/tests/ParserBench.php b/tests/ParserBench.php index bdd85a6..a0fde92 100644 --- a/tests/ParserBench.php +++ b/tests/ParserBench.php @@ -12,7 +12,7 @@ final class ParserBench public function __construct() { - $this->parser = Parser::new(); + $this->parser = new Parser(Ietf::Rfc9651); } #[Bench\Iterations(4)] diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 1a9d6ca..f30f9e8 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -24,7 +24,8 @@ final class ParserTest extends StructuredFieldTestCase protected function setUp(): void { parent::setUp(); - $this->parser = Parser::new(); + + $this->parser = new Parser(Ietf::Rfc9651); } #[Test]