diff --git a/CHANGELOG.md b/CHANGELOG.md index 9632954..b079a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this - `Dictionary::toPairs` and `Parameters::toPairs` - `ByteSequence` class replaced by `Bytes` class - `DataType::create` method use a specific DataType class instead. +- `Value` internal class. ## [1.3.0](https://github.com/bakame-php/http-structured-fields/compare/1.2.2...1.3.0) - 2024-01-05 diff --git a/phpstan.neon b/phpstan.neon index 9d0a683..ee34f5f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,7 +11,7 @@ parameters: ignoreErrors: - message: '#it_fails_to_create_an_item_from_an_array_of_pairs\(\)#' path: tests/ItemTest.php - - message: '#Method Bakame\\Http\\StructuredFields\\DataType::(serialize|build|create|toRfc9651|toRfc8941)\(\) has parameter \$data with no value type specified in iterable type iterable.#' + - message: '#Method Bakame\\Http\\StructuredFields\\DataType::(serialize|parse)\(\) has parameter \$data with no value type specified in iterable type iterable.#' path: src/DataType.php excludePaths: - tests/Record.php diff --git a/src/DataType.php b/src/DataType.php index b411039..8cc5f53 100644 --- a/src/DataType.php +++ b/src/DataType.php @@ -18,7 +18,7 @@ enum DataType: string /** * @throws SyntaxError|Exception */ - public function parse(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): OuterList|InnerList|Parameters|Dictionary|Item + public function parse(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): OuterList|InnerList|Parameters|Dictionary|Item { return match ($this) { self::List => OuterList::fromHttpValue($httpValue, $rfc), @@ -32,7 +32,7 @@ public function parse(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): /** * @throws SyntaxError|Exception */ - public function serialize(iterable $data, ?Ietf $rfc = Ietf::Rfc9651): string + public function serialize(iterable $data, Ietf $rfc = Ietf::Rfc9651): string { return (match ($this) { self::List => OuterList::fromPairs($data), @@ -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 28fb2be..5fcae80 100644 --- a/src/Dictionary.php +++ b/src/Dictionary.php @@ -14,14 +14,18 @@ use Stringable; use Throwable; +use function array_is_list; use function array_key_exists; use function array_keys; +use function array_map; use function count; use function implode; use function is_array; +use function is_bool; use function is_int; use function is_iterable; use function is_string; +use function uasort; /** * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.2 @@ -56,14 +60,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 +128,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 +151,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) { @@ -191,9 +200,9 @@ public static function fromRfc8941(Stringable|string $httpValue): self * * @throws StructuredFieldError|Throwable If the string is not a valid */ - public static function fromHttpValue(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): 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 @@ -206,18 +215,14 @@ public function toRfc8941(): string return $this->toHttpValue(Ietf::Rfc8941); } - public function toHttpValue(?Ietf $rfc = Ietf::Rfc9651): string + public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string { - $rfc ??= Ietf::Rfc9651; - $members = []; - foreach ($this->members as $key => $member) { - $members[] = match (true) { - $member instanceof Item && true === $member->value() => $key.$member->parameters()->toHttpValue($rfc), - default => $key.'='.$member->toHttpValue($rfc), - }; - } + $formatter = static fn (Item|InnerList $member, string $offset): string => match (true) { + $member instanceof Item && true === $member->value() => $offset.$member->parameters()->toHttpValue($rfc), + default => $offset.'='.$member->toHttpValue($rfc), + }; - return implode(', ', $members); + return implode(', ', array_map($formatter, $this->members, array_keys($this->members))); } public function __toString(): string @@ -506,7 +511,7 @@ public function last(): array */ public function add( string $key, - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool|null $member + iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool|null $member ): self { if (null === $member) { return $this; @@ -596,7 +601,7 @@ public function removeByKeys(string ...$keys): self */ public function append( string $key, - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { $members = $this->members; unset($members[$key]); @@ -616,7 +621,7 @@ public function append( */ public function prepend( string $key, - iterable|StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { $members = $this->members; unset($members[$key]); @@ -840,7 +845,7 @@ public function filter(callable $callback): self public function sort(callable $callback): self { $members = iterator_to_array($this); - usort($members, $callback); + uasort($members, $callback); return self::fromPairs($members); } diff --git a/src/DisplayString.php b/src/DisplayString.php index 741391d..361f91e 100644 --- a/src/DisplayString.php +++ b/src/DisplayString.php @@ -11,6 +11,7 @@ use function preg_replace_callback; use function rawurldecode; use function rawurlencode; +use function str_contains; /** * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-sfbis#section-4.2.10 diff --git a/src/InnerList.php b/src/InnerList.php index 8c302d7..00d5675 100644 --- a/src/InnerList.php +++ b/src/InnerList.php @@ -20,6 +20,7 @@ use function array_values; use function count; use function implode; +use function uasort; use const ARRAY_FILTER_USE_BOTH; use const ARRAY_FILTER_USE_KEY; @@ -46,10 +47,10 @@ final class InnerList implements ArrayAccess, Countable, IteratorAggregate /** * @param iterable $members */ - private function __construct(iterable $members, Parameters $parameters) + private function __construct(iterable $members, ?Parameters $parameters = null) { $this->members = array_map($this->filterMember(...), array_values([...$members])); - $this->parameters = $parameters; + $this->parameters = $parameters ?? Parameters::new(); } /** @@ -59,12 +60,18 @@ private function filterMember(mixed $member): Item { if ($member instanceof StructuredFieldProvider) { $member = $member->toStructuredField(); + if (!$member instanceof Item) { + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.'; '.$member::class.' given.'); + } + + return $member; } - return match (true) { - $member instanceof Item => $member, - default => Item::new($member), - }; + if (!$member instanceof Item) { + return Item::new($member); + } + + return $member; } /** @@ -72,9 +79,9 @@ private function filterMember(mixed $member): Item * * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1 */ - public static function fromHttpValue(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): self + 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)); } /** @@ -95,7 +102,7 @@ public static function fromAssociative( } if (!$parameters instanceof Parameters) { - $parameters = Parameters::fromAssociative($parameters); + return new self($value, Parameters::fromAssociative($parameters)); } return new self($value, $parameters); @@ -115,7 +122,7 @@ public static function fromPair(array $pair): self } if (1 === count($pair)) { - return new self($pair[0], Parameters::new()); + return new self($pair[0]); } if ($pair[1] instanceof StructuredFieldProvider) { @@ -126,7 +133,7 @@ public static function fromPair(array $pair): self } 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]); @@ -138,7 +145,7 @@ public static function fromPair(array $pair): self public static function new( StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members ): self { - return new self($members, Parameters::new()); + return new self($members); } public static function fromRfc9651(Stringable|string $httpValue): self @@ -151,10 +158,8 @@ public static function fromRfc8941(Stringable|string $httpValue): self return self::fromHttpValue($httpValue, Ietf::Rfc8941); } - public function toHttpValue(?Ietf $rfc = Ietf::Rfc9651): string + public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string { - $rfc ??= Ietf::Rfc9651; - return '('.implode(' ', array_map(fn (Item $value): string => $value->toHttpValue($rfc), $this->members)).')'.$this->parameters->toHttpValue($rfc); } @@ -483,7 +488,7 @@ public function filter(callable $callback): self public function sort(callable $callback): self { $members = $this->members; - usort($members, $callback); + uasort($members, $callback); return new self($members, $this->parameters); } diff --git a/src/Item.php b/src/Item.php index af969df..67f2698 100644 --- a/src/Item.php +++ b/src/Item.php @@ -8,11 +8,16 @@ use DateTimeImmutable; use DateTimeInterface; use DateTimeZone; +use Exception; use Stringable; +use Throwable; use function array_is_list; use function count; +use const JSON_PRESERVE_ZERO_FRACTION; +use const PHP_ROUND_HALF_EVEN; + /** * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3 * @@ -25,8 +30,19 @@ final class Item { use ParameterAccess; - private function __construct(private readonly Value $value, private readonly Parameters $parameters) + private readonly Token|Bytes|DisplayString|DateTimeImmutable|int|float|string|bool $value; + private readonly Parameters $parameters; + private readonly Type $type; + + private function __construct(Token|Bytes|DisplayString|DateTimeInterface|int|float|string|bool $value, ?Parameters $parameters = null) { + if ($value instanceof DateTimeInterface && !$value instanceof DateTimeImmutable) { + $value = DateTimeImmutable::createFromInterface($value); + } + + $this->value = $value; + $this->parameters = $parameters ?? Parameters::new(); + $this->type = Type::fromVariable($value); } public static function fromRfc9651(Stringable|string $httpValue): self @@ -45,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 + 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)); } /** @@ -65,16 +81,18 @@ 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(new Value($value), $parameters); + return new self($value, $parameters); } /** @@ -97,22 +115,27 @@ public static function tryFromPair(array $pair): ?self public static function fromPair(array $pair): self { if ([] === $pair || !array_is_list($pair) || 2 < count($pair)) { - throw new SyntaxError('The pair must be represented by an non-empty array as a list containing at most 2 members.'); + throw new SyntaxError('The pair must be represented by an non-empty array as a list containing exactly 1 or 2 members.'); } if (1 === count($pair)) { - return new self(new Value($pair[0]), Parameters::new()); + return new self($pair[0]); } if ($pair[1] instanceof StructuredFieldProvider) { $pair[1] = $pair[1]->toStructuredField(); + 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(new Value($pair[0]), $pair[1]); + return new self($pair[0], $pair[1]); } /** @@ -125,14 +148,14 @@ public static function fromPair(array $pair): self public static function new(mixed $value): self { if ($value instanceof Item) { - return new self(new Value($value->value()), $value->parameters()); + return new self($value->value(), $value->parameters()); } if (is_array($value)) { return self::fromPair($value); } - return self::fromValue(new Value($value)); /* @phpstan-ignore-line */ + return new self($value); /* @phpstan-ignore-line */ } /** @@ -140,7 +163,7 @@ public static function new(mixed $value): self * * @param SfTypeInput|Item|array{0:SfTypeInput, 1:Parameters|iterable} $value */ - public static function tryNew(StructuredFieldProvider|Item|DateTimeInterface|Bytes|DisplayString|Token|array|int|float|string|bool $value): ?self + public static function tryNew(StructuredFieldProvider|Item|DateTimeInterface|Bytes|DisplayString|Token|array|int|float|string|bool $value): ?self { try { return self::new($value); @@ -149,14 +172,6 @@ public static function tryNew(StructuredFieldProvider|Item|DateTimeInterface|Byt } } - /** - * Returns a new bare instance from value. - */ - private static function fromValue(Value $value): self - { - return new self($value, Parameters::new()); - } - /** * Returns a new instance from a string. * @@ -164,7 +179,7 @@ private static function fromValue(Value $value): self */ public static function fromString(Stringable|string $value): self { - return self::fromValue(Value::fromString($value)); + return new self((string)$value); } /** @@ -174,7 +189,7 @@ public static function fromString(Stringable|string $value): self */ public static function fromEncodedBytes(Stringable|string $value): self { - return self::fromValue(Value::fromEncodedBytes($value)); + return new self(Bytes::fromEncoded($value)); } /** @@ -184,7 +199,7 @@ public static function fromEncodedBytes(Stringable|string $value): self */ public static function fromDecodedBytes(Stringable|string $value): self { - return self::fromValue(Value::fromDecodedBytes($value)); + return new self(Bytes::fromDecoded($value)); } /** @@ -194,7 +209,7 @@ public static function fromDecodedBytes(Stringable|string $value): self */ public static function fromEncodedDisplayString(Stringable|string $value): self { - return self::fromValue(Value::fromEncodedDisplayString($value)); + return new self(DisplayString::fromEncoded($value)); } /** @@ -204,7 +219,7 @@ public static function fromEncodedDisplayString(Stringable|string $value): self */ public static function fromDecodedDisplayString(Stringable|string $value): self { - return self::fromValue(Value::fromDecodedDisplayString($value)); + return new self(DisplayString::fromDecoded($value)); } /** @@ -214,7 +229,7 @@ public static function fromDecodedDisplayString(Stringable|string $value): self */ public static function fromToken(Stringable|string $value): self { - return self::fromValue(Value::fromToken($value)); + return new self(Token::fromString($value)); } /** @@ -224,7 +239,7 @@ public static function fromToken(Stringable|string $value): self */ public static function fromTimestamp(int $timestamp): self { - return self::fromValue(Value::fromTimestamp($timestamp)); + return new self((new DateTimeImmutable())->setTimestamp($timestamp)); } /** @@ -234,7 +249,17 @@ public static function fromTimestamp(int $timestamp): self */ public static function fromDateFormat(string $format, string $datetime): self { - return self::fromValue(Value::fromDateFormat($format, $datetime)); + try { + $value = DateTimeImmutable::createFromFormat($format, $datetime); + } catch (Exception $exception) { + throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.', 0, $exception); + } + + if (!$value instanceof DateTimeImmutable) { + throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.'); + } + + return new self($value); } /** @@ -244,7 +269,20 @@ public static function fromDateFormat(string $format, string $datetime): self */ public static function fromDateString(string $datetime, DateTimeZone|string|null $timezone = null): self { - return self::fromValue(Value::fromDateString($datetime, $timezone)); + $timezone ??= date_default_timezone_get(); + if (!$timezone instanceof DateTimeZone) { + try { + $timezone = new DateTimeZone($timezone); + } catch (Throwable $exception) { + throw new SyntaxError('The timezone could not be instantiated.', 0, $exception); + } + } + + try { + 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); + } } /** @@ -254,7 +292,7 @@ public static function fromDateString(string $datetime, DateTimeZone|string|null */ public static function fromDate(DateTimeInterface $datetime): self { - return self::fromValue(Value::fromDate($datetime)); + return new self($datetime); } /** @@ -264,7 +302,7 @@ public static function fromDate(DateTimeInterface $datetime): self */ public static function fromDecimal(int|float $value): self { - return self::fromValue(Value::fromDecimal($value)); + return new self((float)$value); } /** @@ -274,7 +312,7 @@ public static function fromDecimal(int|float $value): self */ public static function fromInteger(int|float $value): self { - return self::fromValue(Value::fromInteger($value)); + return new self((int)$value); } /** @@ -282,7 +320,7 @@ public static function fromInteger(int|float $value): self */ public static function true(): self { - return self::fromValue(Value::true()); + return new self(true); } /** @@ -290,7 +328,7 @@ public static function true(): self */ public static function false(): self { - return self::fromValue(Value::false()); + return new self(false); } /** @@ -306,26 +344,25 @@ public static function false(): self */ public function value(?callable $validate = null): Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool { - $value = $this->value->value; if (null === $validate) { - return $value; + return $this->value; } - $exceptionMessage = $validate($value); + $exceptionMessage = $validate($this->value); if (true === $exceptionMessage) { - return $value; + return $this->value; } if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) { $exceptionMessage = "The item value '{value}' failed validation."; } - throw new Violation(strtr($exceptionMessage, ['{value}' => $this->value->serialize()])); + throw new Violation(strtr($exceptionMessage, ['{value}' => $this->serialize()])); } public function type(): Type { - return $this->value->type; + return $this->type; } /** @@ -333,11 +370,30 @@ public function type(): Type * * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.1 */ - public function toHttpValue(?Ietf $rfc = Ietf::Rfc9651): string + public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string { - $rfc ??= Ietf::Rfc9651; + return $this->serialize($rfc).$this->parameters->toHttpValue($rfc); + } - return $this->value->serialize($rfc).$this->parameters->toHttpValue($rfc); + /** + * Serialize the Item value according to RFC8941. + * + * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.1 + */ + private function serialize(Ietf $rfc = Ietf::Rfc9651): string + { + return match (true) { + !$rfc->supports($this->type) => throw MissingFeature::dueToLackOfSupport($this->type, $rfc), + $this->value instanceof DateTimeImmutable => '@'.$this->value->getTimestamp(), + $this->value instanceof Token => $this->value->toString(), + $this->value instanceof Bytes => ':'.$this->value->encoded().':', + $this->value instanceof DisplayString => '%"'.$this->value->encoded().'"', + is_int($this->value) => (string)$this->value, + is_float($this->value) => (string)json_encode(round($this->value, 3, PHP_ROUND_HALF_EVEN), JSON_PRESERVE_ZERO_FRACTION), + $this->value, + false === $this->value => '?'.($this->value ? '1' : '0'), + default => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"', + }; } public function toRfc9651(): string @@ -360,7 +416,7 @@ public function __toString(): string */ public function toPair(): array { - return [$this->value->value, $this->parameters]; + return [$this->value, $this->parameters]; } public function equals(mixed $other): bool @@ -396,11 +452,17 @@ public function when(callable|bool $condition, callable $onSuccess, ?callable $o * * @throws SyntaxError If the value is invalid or not supported */ - public function withValue( - DateTimeInterface|Bytes|Token|DisplayString|string|int|float|bool $value - ): self { - $value = new Value($value); - if ($value->equals($this->value)) { + public function withValue(DateTimeInterface|Bytes|Token|DisplayString|string|int|float|bool $value): self + { + $isEqual = match (true) { + $this->value instanceof Bytes, + $this->value instanceof Token, + $this->value instanceof DisplayString => $this->value->equals($value), + $this->value instanceof DateTimeInterface && $value instanceof DateTimeInterface => $value == $this->value, + default => $value === $this->value, + }; + + if ($isEqual) { return $this; } diff --git a/src/OuterList.php b/src/OuterList.php index c6fcad1..c3a970b 100644 --- a/src/OuterList.php +++ b/src/OuterList.php @@ -22,6 +22,7 @@ use function implode; use function is_array; use function is_iterable; +use function uasort; use const ARRAY_FILTER_USE_BOTH; use const ARRAY_FILTER_USE_KEY; @@ -45,7 +46,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 +58,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), }; @@ -75,9 +78,9 @@ private function filterMember(mixed $member): InnerList|Item * * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1 */ - public static function fromHttpValue(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): self + 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 +99,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 +122,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 +143,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); } @@ -152,10 +158,8 @@ public static function fromRfc8941(Stringable|string $httpValue): self return self::fromHttpValue($httpValue, Ietf::Rfc8941); } - public function toHttpValue(?Ietf $rfc = Ietf::Rfc9651): string + public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string { - $rfc ??= Ietf::Rfc9651; - return implode(', ', array_map(fn (Item|InnerList $member): string => $member->toHttpValue($rfc), $this->members)); } @@ -291,7 +295,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, @@ -317,7 +321,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, @@ -346,7 +350,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); @@ -367,7 +371,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); @@ -473,7 +477,7 @@ public function filter(callable $callback): self public function sort(callable $callback): self { $members = $this->members; - usort($members, $callback); + uasort($members, $callback); return new self(...$members); } diff --git a/src/ParameterAccess.php b/src/ParameterAccess.php index 5750126..9c71f09 100644 --- a/src/ParameterAccess.php +++ b/src/ParameterAccess.php @@ -76,13 +76,13 @@ abstract public function withParameters(Parameters $parameters): static; * This method MUST retain the state of the current instance, and return * an instance that contains the specified parameter change. * - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType|null $member + * @param StructuredFieldProvider|Item|SfType $member * * @throws SyntaxError If the string key is not a valid */ public function addParameter( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool|null $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): static { return $this->withParameters($this->parameters()->add($key, $member)); } @@ -93,13 +93,13 @@ public function addParameter( * This method MUST retain the state of the current instance, and return * an instance that contains the specified parameter change. * - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType $member + * @param StructuredFieldProvider|Item|SfType $member * * @throws SyntaxError If the string key is not a valid */ public function prependParameter( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): static { return $this->withParameters($this->parameters()->prepend($key, $member)); } @@ -110,13 +110,13 @@ public function prependParameter( * This method MUST retain the state of the current instance, and return * an instance that contains the specified parameter change. * - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType $member + * @param StructuredFieldProvider|Item|SfType $member * * @throws SyntaxError If the string key is not a valid */ public function appendParameter( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): static { return $this->withParameters($this->parameters()->append($key, $member)); } diff --git a/src/Parameters.php b/src/Parameters.php index a10c2cf..9ad342f 100644 --- a/src/Parameters.php +++ b/src/Parameters.php @@ -22,6 +22,7 @@ use function implode; use function is_int; use function is_string; +use function uasort; /** * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.2 @@ -29,7 +30,7 @@ * @phpstan-import-type SfItemInput from StructuredFieldProvider * @phpstan-import-type SfType from StructuredFieldProvider * - * @implements ArrayAccess + * @implements ArrayAccess * @implements IteratorAggregate */ final class Parameters implements ArrayAccess, Countable, IteratorAggregate @@ -57,13 +58,22 @@ private static function filterMember(mixed $member): Item { if ($member instanceof StructuredFieldProvider) { $member = $member->toStructuredField(); + if ($member instanceof Item) { + return self::filterMember($member); + } + + throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.'; '.$member::class.' given.'); } - return match (true) { - !$member instanceof Item => Item::new($member), - $member->parameters()->isEmpty() => $member, - default => throw new InvalidArgument('The "'.$member::class.'" instance is not a Bare Item.'), - }; + if (!$member instanceof Item) { + $member = Item::new($member); + } + + if ($member->parameters()->isNotEmpty()) { + throw new InvalidArgument('The "'.$member::class.'" instance is not a Bare Item.'); + } + + return $member; } /** @@ -134,9 +144,9 @@ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self * * @throws SyntaxError|Exception If the string is not a valid */ - public static function fromHttpValue(Stringable|string $httpValue, ?Ietf $rfc = Ietf::Rfc9651): 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 @@ -149,9 +159,8 @@ public static function fromRfc8941(Stringable|string $httpValue): self return self::fromHttpValue($httpValue, Ietf::Rfc8941); } - public function toHttpValue(?Ietf $rfc = Ietf::Rfc9651): string + public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string { - $rfc ??= Ietf::Rfc9651; $formatter = static fn (Item $member, string $offset): string => match (true) { true === $member->value() => ';'.$offset, default => ';'.$offset.'='.$member->toHttpValue($rfc), @@ -497,15 +506,12 @@ public function valueByIndex(int $index, ?callable $validate = null, bool|string } /** - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType|null $member + * @param StructuredFieldProvider|Item|SfType $member */ public function add( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool|null $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { - if (null === $member) { - return $this; - } $members = $this->members; $members[MapKey::from($key)->value] = self::filterMember($member); @@ -563,11 +569,11 @@ public function removeByKeys(string ...$keys): self } /** - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType $member + * @param StructuredFieldProvider|Item|SfType $member */ public function append( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { $members = $this->members; unset($members[$key]); @@ -576,11 +582,11 @@ public function append( } /** - * @param StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|SfType $member + * @param StructuredFieldProvider|Item|SfType $member */ public function prepend( string $key, - StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member + StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member ): self { $members = $this->members; unset($members[$key]); diff --git a/src/Parser.php b/src/Parser.php index 3b38e0e..e57ce87 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -14,6 +14,7 @@ use function str_contains; use function strlen; use function substr; +use function trim; /** * A class to parse HTTP Structured Fields from their HTTP textual representation according to RFC8941. @@ -52,11 +53,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 ?? Ietf::Rfc9651); - } - public function __construct(private readonly Ietf $rfc) { } @@ -518,10 +514,10 @@ private static function extractToken(string $httpValue): array */ private static function extractBytes(string $httpValue): array { - if (1 !== preg_match(self::REGEXP_BYTES, $httpValue, $matches)) { + if (1 !== preg_match(self::REGEXP_BYTES, $httpValue, $found)) { throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Byte Sequence contains invalid characters."); } - return [Bytes::fromEncoded($matches['byte']), strlen($matches['sequence'])]; + return [Bytes::fromEncoded($found['byte']), strlen($found['sequence'])]; } } diff --git a/src/Token.php b/src/Token.php index 407d26f..88f41db 100644 --- a/src/Token.php +++ b/src/Token.php @@ -37,7 +37,7 @@ public static function tryFromString(Stringable|string $value): ?self public static function fromString(Stringable|string $value): self { - return new self((string) $value); + return new self((string)$value); } public function equals(mixed $other): bool diff --git a/src/Type.php b/src/Type.php index 112d87a..44eb876 100644 --- a/src/Type.php +++ b/src/Type.php @@ -6,6 +6,15 @@ use DateTimeInterface; +use function abs; +use function floor; +use function gettype; +use function is_float; +use function is_int; +use function is_object; +use function is_string; +use function preg_match; + /** * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3 */ diff --git a/src/Value.php b/src/Value.php deleted file mode 100644 index e2b409c..0000000 --- a/src/Value.php +++ /dev/null @@ -1,224 +0,0 @@ -value, $this->type] = match (true) { - $value instanceof Item => [$value->value(), $value->type()], - $value instanceof Token, - $value instanceof Bytes, - $value instanceof DisplayString => [$value, $value->type()], - false === $value, - $value => [$value, Type::Boolean], - $value instanceof DateTimeInterface => [self::filterDate($value), Type::Date], - default => [$value, Type::fromVariable($value)], - }; - } - - /** - * Filter a date according to draft-ietf-httpbis-sfbis-latest. - * - * @see https://httpwg.org/http-extensions/draft-ietf-httpbis-sfbis.html#section-3.3.7 - */ - private static function filterDate(DateTimeInterface $value): DateTimeImmutable - { - return match (true) { - !Type::Integer->supports($value->getTimestamp()) => throw new SyntaxError('Date timestamp are limited to 15 digits.'), - !$value instanceof DateTimeImmutable => DateTimeImmutable::createFromInterface($value), - default => $value, - }; - } - - /** - * Returns a new instance from an encoded byte sequence and an iterable of key-value parameters. - */ - public static function fromEncodedBytes(Stringable|string $value): self - { - return new self(Bytes::fromEncoded($value)); - } - - /** - * Returns a new instance from a decoded byte sequence and an iterable of key-value parameters. - */ - public static function fromDecodedBytes(Stringable|string $value): self - { - return new self(Bytes::fromDecoded($value)); - } - - public static function fromEncodedDisplayString(Stringable|string $value): self - { - return new self(DisplayString::fromEncoded($value)); - } - - public static function fromDecodedDisplayString(Stringable|string $value): self - { - return new self(DisplayString::fromDecoded($value)); - } - - /** - * Returns a new instance from a Token and an iterable of key-value parameters. - */ - public static function fromToken(Stringable|string $value): self - { - return new self(Token::fromString($value)); - } - - /** - * Returns a new instance from a timestamp and an iterable of key-value parameters. - */ - public static function fromTimestamp(int $timestamp): self - { - return new self((new DateTimeImmutable())->setTimestamp($timestamp)); - } - - /** - * Returns a new instance from a date format its date string representation and an iterable of key-value parameters. - * - * @throws SyntaxError if the format is invalid - */ - public static function fromDateFormat(string $format, string $datetime): self - { - try { - $value = DateTimeImmutable::createFromFormat($format, $datetime); - } catch (Exception $exception) { - 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), - }; - } - - /** - * Returns a new instance from a string parsable by DateTimeImmutable constructor, an optional timezone and an iterable of key-value parameters. - * - * @throws SyntaxError if the format is invalid - */ - public static function fromDateString(string $datetime, DateTimeZone|string|null $timezone = null): self - { - $timezone ??= date_default_timezone_get(); - if (!$timezone instanceof DateTimeZone) { - try { - $timezone = new DateTimeZone($timezone); - } catch (Throwable $exception) { - throw new SyntaxError('The timezone could not be instantiated.', 0, $exception); - } - } - - try { - $value = 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); - } - - /** - * Returns a new instance from a DatetimeInterface implementing object. - * - * @throws SyntaxError if the format is invalid - */ - public static function fromDate(DateTimeInterface $datetime): self - { - return new self($datetime); - } - - public static function fromDecimal(int|float $value): self - { - return new self((float) $value); - } - - public static function fromInteger(int|float $value): self - { - return new self((int) $value); - } - - public static function fromString(Stringable|string $value): self - { - return new self((string) $value); - } - - public static function true(): self - { - return new self(true); - } - - public static function false(): self - { - return new self(false); - } - - /** - * Serialize the Item value according to RFC8941. - * - * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.1 - */ - public function serialize(?Ietf $rfc = Ietf::Rfc9651): string - { - $rfc ??= Ietf::Rfc9651; - if (!$rfc->supports($this->type)) { - throw MissingFeature::dueToLackOfSupport($this->type, $rfc); - } - - return match (true) { - $this->value instanceof DateTimeImmutable => '@'.$this->value->getTimestamp(), - $this->value instanceof Token => $this->value->toString(), - $this->value instanceof Bytes => ':'.$this->value->encoded().':', - $this->value instanceof DisplayString => '%"'.$this->value->encoded().'"', - is_int($this->value) => (string) $this->value, - is_float($this->value) => (string) json_encode(round($this->value, 3, PHP_ROUND_HALF_EVEN), JSON_PRESERVE_ZERO_FRACTION), - $this->value, - false === $this->value => '?'.($this->value ? '1' : '0'), - default => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"', - }; - } - - public function equals(mixed $value): bool - { - if ($value instanceof self) { - $value = $value->value; - } - - return match (true) { - $this->value instanceof Bytes, - $this->value instanceof Token, - $this->value instanceof DisplayString => $this->value->equals($value), - $this->value instanceof DateTimeInterface && $value instanceof DateTimeInterface => $value == $this->value, - default => $value === $this->value, - }; - } -} 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]