diff --git a/CHANGELOG.md b/CHANGELOG.md index 287500e1..11245d59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ ## [Unreleased] +### Added + +- `Innmind\Http\Header\Custom` +- `Innmind\Http\Header\Accept\MediaType` +- `Innmind\Http\Header\Accept\Charset` +- `Innmind\Http\Header\Accept\Encoding` +- `Innmind\Http\Header\Accept\Language` +- `Innmind\Http\Header\Content\Language` +- `Innmind\Http\Header\Link\Relationship` +- `Innmind\Http\Header\WWWAuthenticate\Challenge` +- `Innmind\Http\Header\CacheControl\Directive` +- `Innmind\Http\Header\CacheControl\MaxAge` +- `Innmind\Http\Header\CacheControl\MaxStale` +- `Innmind\Http\Header\CacheControl\MinimumFresh` +- `Innmind\Http\Header\CacheControl\NoCache` +- `Innmind\Http\Header\CacheControl\PrivateCache` +- `Innmind\Http\Header\CacheControl\SharedMaxAge` +- `Innmind\Http\Header\SetCookie\Directive` +- `Innmind\Http\Header\SetCookie\Domain` +- `Innmind\Http\Header\SetCookie\Expires` +- `Innmind\Http\Header\SetCookie\MaxAge` +- `Innmind\Http\Header\SetCookie\Path` + ### Changed - Requires `innmind/filesystem:~8.0` @@ -17,6 +40,45 @@ - `Innmind\Http\Factory\HeadersFactory` is now a final class - Classes in `Innmind\Http\Factory\Header\` are now internal - `Innmind\Http\Header::values()` now returns an `Innmind\Immutable\Sequence` to allow for ordered values +- `Innmind\Http\Header\AcceptRanges` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\Age` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\Authorization` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\ContentEncoding` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\ContentLength` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\ContentLocation` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\ContentRange` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\ContentType` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Date` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Expires` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Host` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\IfModifiedSince` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\IfUnmodifiedSince` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\LastModified` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Location` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Range` constructor is now private, use `::of()` or `::maybe()` named constructors +- `Innmind\Http\Header\Referrer` constructor is now private, use `::of()` named constructor +- All custom headers now implements `Innmind\Http\Header\Custom` +- `Innmind\Http\Header` is now a final class +- `Innmind\Http\Header\Value` is now a final class +- `Innmind\Http\Header\Accept` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\AcceptCharset` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\AcceptEncoding` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Allow` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Allow::of()` now expects `Innmind\Http\Method` values +- `Innmind\Http\Header\ContentLanguage` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Link` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\WWWAuthenticate` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\CacheControl` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Cookie` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\SetCookie` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Parameter\Quality` constructor is now private, use `::of()` named constructor +- `Innmind\Http\Header\Parameter\Quality` no longer implements `Innmind\Http\Header\Parameter` +- `Innmind\Http\Header\Parameter\Quality::of()` now returns `self` +- `Innmind\Http\Header\ContentType\Boundary` no longer implements `Innmind\Http\Header\Parameter` +- `Innmind\Http\Header\Parameter` is now a final class +- `Innmind\Http\Header` constructor is now private, use `::of` named constructor +- `Innmind\Http\Header\Value` constructor is now private, use `::of` named constructor +- `Innmind\Http\Header\Parameter` constructor is now private, use `::of` named constructor ### Removed @@ -28,6 +90,42 @@ - `Innmind\Http\Factory\ServerRequest\ServerRequestFactory` - `Innmind\Http\Factory\Header\HeadersFactory` - `Innmind\Http\Factory\HeaderFactory` +- `Innmind\Http\Header\AcceptRangesValue` +- `Innmind\Http\Header\AgeValue` +- `Innmind\Http\Header\AuthorizationValue` +- `Innmind\Http\Header\ContentEncodingValue` +- `Innmind\Http\Header\ContentLengthValue` +- `Innmind\Http\Header\ContentRangeValue` +- `Innmind\Http\Header\ContentTypeValue` +- `Innmind\Http\Header\HostValue` +- `Innmind\Http\Header\DateValue` +- `Innmind\Http\Header\LocationValue` +- `Innmind\Http\Header\RangeValue` +- `Innmind\Http\Header\ReferrerValue` +- `Innmind\Http\Header\AcceptValue` +- `Innmind\Http\Header\AcceptCharsetValue` +- `Innmind\Http\Header\AcceptEncodingValue` +- `Innmind\Http\Header\AcceptLanguageValue` +- `Innmind\Http\Header\AllowValue` +- `Innmind\Http\Header\ContentLanguageValue` +- `Innmind\Http\Header\LinkValue` +- `Innmind\Http\Header\WWWAuthenticateValue` +- `Innmind\Http\Header\CacheControlValue` +- `Innmind\Http\Header\CacheControlValue\MaxAge` +- `Innmind\Http\Header\CacheControlValue\MaxStale` +- `Innmind\Http\Header\CacheControlValue\MinimumFresh` +- `Innmind\Http\Header\CacheControlValue\NoCache` +- `Innmind\Http\Header\CacheControlValue\PrivateCache` +- `Innmind\Http\Header\CacheControlValue\SharedMaxAge` +- `Innmind\Http\Header\CookieValue` +- `Innmind\Http\Header\CookieParameter\Domain` +- `Innmind\Http\Header\CookieParameter\Expires` +- `Innmind\Http\Header\CookieParameter\HttpOnly` +- `Innmind\Http\Header\CookieParameter\MaxAge` +- `Innmind\Http\Header\CookieParameter\Path` +- `Innmind\Http\Header\CookieParameter\SameSite` +- `Innmind\Http\Header\CookieParameter\Secure` +- `Innmind\Http\Header\Parameter\NullParameter` ### Fixed diff --git a/src/Factory/Header/Factories.php b/src/Factory/Header/Factories.php index 1e756296..8ffe2279 100644 --- a/src/Factory/Header/Factories.php +++ b/src/Factory/Header/Factories.php @@ -6,34 +6,21 @@ use Innmind\Http\{ Header, Header\AcceptCharset, - Header\AcceptCharsetValue, Header\AcceptEncoding, - Header\AcceptEncodingValue, Header\Accept, - Header\AcceptValue, Header\AcceptLanguage, - Header\AcceptLanguageValue, Header\AcceptRanges, - Header\AcceptRangesValue, Header\Age, - Header\AgeValue, Header\Allow, - Header\AllowValue, Header\Authorization, - Header\AuthorizationValue, Header\CacheControl, - Header\CacheControlValue, + Header\Content, Header\ContentEncoding, - Header\ContentEncodingValue, Header\ContentLanguage, - Header\ContentLanguageValue, Header\ContentLength, - Header\ContentLengthValue, Header\ContentLocation, Header\ContentRange, - Header\ContentRangeValue, Header\ContentType, - Header\ContentTypeValue, Header\Cookie, Header\Date, Header\Expires, @@ -42,14 +29,14 @@ Header\IfUnmodifiedSince, Header\LastModified, Header\Link, - Header\LinkValue, Header\Location, + Header\Custom, Header\Range, - Header\RangeValue, Header\Referrer, Header\Parameter, Header\Parameter\Quality, TimeContinuum\Format\Http, + Method, }; use Innmind\TimeContinuum\Clock; use Innmind\Validation\Is; @@ -60,6 +47,7 @@ Maybe, Sequence, Map, + Predicate, Predicate\Instance, }; @@ -135,7 +123,7 @@ public static function of(Str $name): ?self } /** - * @return Maybe
+ * @return Maybe */ public function try(Clock $clock, Str $value): Maybe { @@ -146,22 +134,28 @@ public function try(Clock $clock, Str $value): Maybe $matches = $accept->capture( '~(?[a-zA-Z0-9\-_:\(\)]+)(; ?q=(?\d+(\.\d+)?))?~', ); + /** @var Predicate> */ + $range = Is::int() + ->range(0, 100) + ->asPredicate(); $quality = $matches ->get('quality') ->map(static fn($quality) => (float) $quality->toString()) - ->otherwise(static fn() => Maybe::just(1)) - ->flatMap(Quality::of(...)); + ->map(static fn($quality) => (int) ($quality * 100.0)) + ->otherwise(static fn() => Maybe::just(100)) + ->keep($range) + ->map(Quality::of(...)); $charset = $matches ->get('charset') ->map(static fn($charset) => $charset->toString()); return Maybe::all($charset, $quality)->flatMap( - AcceptCharsetValue::of(...), + Accept\Charset::maybe(...), ); }) - ->sink(self::values(AcceptCharsetValue::class)) + ->sink(self::values(Accept\Charset::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new AcceptCharset(...$values->toList())), + ->map(static fn($values) => AcceptCharset::of(...$values->toList())), self::acceptEncoding => $value ->split(',') @@ -169,22 +163,28 @@ public function try(Clock $clock, Str $value): Maybe $matches = $accept->capture( '~(?(\w+|\*))(; ?q=(?\d+(\.\d+)?))?~', ); + /** @var Predicate> */ + $range = Is::int() + ->range(0, 100) + ->asPredicate(); $quality = $matches ->get('quality') ->map(static fn($quality) => (float) $quality->toString()) - ->otherwise(static fn() => Maybe::just(1)) - ->flatMap(Quality::of(...)); + ->map(static fn($quality) => (int) ($quality * 100.0)) + ->otherwise(static fn() => Maybe::just(100)) + ->keep($range) + ->map(Quality::of(...)); $coding = $matches ->get('coding') ->map(static fn($coding) => $coding->toString()); return Maybe::all($coding, $quality)->flatMap( - AcceptEncodingValue::of(...), + Accept\Encoding::maybe(...), ); }) - ->sink(self::values(AcceptEncodingValue::class)) + ->sink(self::values(Accept\Encoding::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new AcceptEncoding(...$values->toList())), + ->map(static fn($values) => AcceptEncoding::of(...$values->toList())), self::accept => $value ->split(',') @@ -204,16 +204,16 @@ public function try(Clock $clock, Str $value): Maybe $matches->get('type'), $matches->get('subType'), $params, - )->flatMap(static fn(Str $type, Str $subType, array $params) => AcceptValue::of( + )->flatMap(static fn(Str $type, Str $subType, array $params) => Accept\MediaType::maybe( $type->toString(), $subType->toString(), ...$params, )); }) - ->sink(self::values(AcceptValue::class)) + ->sink(self::values(Accept\MediaType::class)) ->maybe(static fn($values, $value) => $value->map($values)) ->map(static fn($values) => $values->match( - static fn($first, $rest) => new Accept($first, ...$rest->toList()), + static fn($first, $rest) => Accept::of($first, ...$rest->toList()), static fn() => null, )) ->keep(Instance::of(Accept::class)), @@ -224,40 +224,43 @@ public function try(Clock $clock, Str $value): Maybe $matches = $accept->capture( '~(?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*|\*))(; ?q=(?\d+(\.\d+)?))?~', ); + /** @var Predicate> */ + $range = Is::int() + ->range(0, 100) + ->asPredicate(); $quality = $matches ->get('quality') ->map(static fn($quality) => (float) $quality->toString()) - ->otherwise(static fn() => Maybe::just(1)) - ->flatMap(Quality::of(...)); + ->map(static fn($quality) => (int) ($quality * 100.0)) + ->otherwise(static fn() => Maybe::just(100)) + ->keep($range) + ->map(Quality::of(...)); $lang = $matches ->get('lang') ->map(static fn($lang) => $lang->toString()); return Maybe::all($lang, $quality)->flatMap( - AcceptLanguageValue::of(...), + Accept\Language::maybe(...), ); }) - ->sink(self::values(AcceptLanguageValue::class)) + ->sink(self::values(Accept\Language::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new AcceptLanguage(...$values->toList())), + ->map(static fn($values) => AcceptLanguage::of(...$values->toList())), - self::acceptRanges => AcceptRangesValue::of($value->toString())->map( - static fn($value) => new AcceptRanges($value), - ), + self::acceptRanges => AcceptRanges::maybe($value->toString()), self::age => Maybe::just($value->toString()) ->filter(\is_numeric(...)) ->map(static fn($age) => (int) $age) - ->flatMap(AgeValue::of(...)) - ->map(static fn($value) => new Age($value)), + ->flatMap(Age::maybe(...)), self::allow => $value ->split(',') ->map(static fn($allow) => $allow->trim()->toUpper()->toString()) - ->map(AllowValue::of(...)) - ->sink(self::values(AllowValue::class)) + ->map(Method::maybe(...)) + ->sink(self::values(Method::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new Allow(...$values->toList())), + ->map(static fn($values) => Allow::of(...$values->toList())), self::authorization => self::authorization($value), @@ -268,69 +271,92 @@ public function try(Clock $clock, Str $value): Maybe $split->matches('~^max-age=\d+$~') => Maybe::just($split->substring(8)->toString()) ->filter(\is_numeric(...)) ->map(static fn($age) => (int) $age) - ->flatMap(static fn($age) => CacheControlValue\MaxAge::of($age)), + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(CacheControl\MaxAge::of(...)), $split->matches('~^max-stale(=\d+)?$~') => Maybe::just($split) ->filter(static fn($split) => $split->length() > 10) ->map(static fn($split) => $split->substring(10)->toString()) ->filter(\is_numeric(...)) ->map(static fn($age) => (int) $age) ->otherwise(static fn() => Maybe::just(0)) - ->flatMap(CacheControlValue\MaxStale::of(...)), + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(CacheControl\MaxStale::of(...)), $split->matches('~^min-fresh=\d+$~') => Maybe::just($split->substring(10)->toString()) ->filter(\is_numeric(...)) ->map(static fn($age) => (int) $age) - ->flatMap(CacheControlValue\MinimumFresh::of(...)), - $split->toString() === 'must-revalidate' => Maybe::just(new CacheControlValue\MustRevalidate), + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(CacheControl\MinimumFresh::of(...)), + $split->toString() === 'must-revalidate' => Maybe::just(CacheControl\Directive::mustRevalidate), $split->matches('~^no-cache(="?\w+"?)?$~') => $split ->capture('~^no-cache(="?(?\w+)"?)?$~') ->get('field') ->map(static fn($field) => $field->toString()) ->otherwise(static fn() => Maybe::just('')) - ->flatMap(CacheControlValue\NoCache::of(...)), - $split->toString() === 'no-store' => Maybe::just(new CacheControlValue\NoStore), - $split->toString() === 'immutable' => Maybe::just(new CacheControlValue\Immutable), - $split->toString() === 'no-transform' => Maybe::just(new CacheControlValue\NoTransform), - $split->toString() === 'only-if-cached' => Maybe::just(new CacheControlValue\OnlyIfCached), + ->flatMap(CacheControl\NoCache::maybe(...)), + $split->toString() === 'no-store' => Maybe::just(CacheControl\Directive::noStore), + $split->toString() === 'immutable' => Maybe::just(CacheControl\Directive::immutable), + $split->toString() === 'no-transform' => Maybe::just(CacheControl\Directive::noTransform), + $split->toString() === 'only-if-cached' => Maybe::just(CacheControl\Directive::onlyIfCached), $split->matches('~^private(="?\w+"?)?$~') => $split ->capture('~^private(="?(?\w+)"?)?$~') ->get('field') ->map(static fn($field) => $field->toString()) ->otherwise(static fn() => Maybe::just('')) - ->flatMap(CacheControlValue\PrivateCache::of(...)), - $split->toString() === 'proxy-revalidate' => Maybe::just(new CacheControlValue\ProxyRevalidate), - $split->toString() === 'public' => Maybe::just(new CacheControlValue\PublicCache), + ->flatMap(CacheControl\PrivateCache::maybe(...)), + $split->toString() === 'proxy-revalidate' => Maybe::just(CacheControl\Directive::proxyRevalidate), + $split->toString() === 'public' => Maybe::just(CacheControl\Directive::public), $split->matches('~^s-maxage=\d+$~') => Maybe::just($split->substring(9)->toString()) ->filter(\is_numeric(...)) ->map(static fn($age) => (int) $age) - ->flatMap(CacheControlValue\SharedMaxAge::of(...)), + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(CacheControl\SharedMaxAge::of(...)), default => null, }) ->keep(Instance::of(Maybe::class)) - ->sink(self::values(CacheControlValue::class)) + // this is the wrong type but it would be too complex to express + // the correct one, and it doesn't affect the runtime + ->sink(self::values(CacheControl\Directive::class)) ->maybe(static fn($values, $value) => $value->map($values)) ->map(static fn($values) => $values->match( - static fn($first, $rest) => new CacheControl($first, ...$rest->toList()), + static fn($first, $rest) => CacheControl::of($first, ...$rest->toList()), static fn() => null, )) ->keep(Instance::of(CacheControl::class)), - self::contentEncoding => ContentEncodingValue::of($value->toString())->map( - static fn($value) => new ContentEncoding($value), - ), + self::contentEncoding => ContentEncoding::maybe($value->toString()), self::contentLanguage => $value ->split(',') ->map(static fn($language) => $language->trim()->toString()) - ->map(ContentLanguageValue::of(...)) - ->sink(self::values(ContentLanguageValue::class)) + ->map(Content\Language::maybe(...)) + ->sink(self::values(Content\Language::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new ContentLanguage(...$values->toList())), + ->map(static fn($values) => ContentLanguage::of(...$values->toList())), self::contentLength => Maybe::just($value->toString()) ->filter(\is_numeric(...)) ->map(static fn($length) => (int) $length) - ->flatMap(ContentLengthValue::of(...)) - ->map(static fn($value) => new ContentLength($value)), + ->flatMap(ContentLength::maybe(...)), self::contentLocation => Url::maybe($value->toString())->map( ContentLocation::of(...), @@ -338,19 +364,9 @@ public function try(Clock $clock, Str $value): Maybe self::contentRange => self::contentRange($value->trim()), - self::contentType => MediaType::maybe($value->toString()) - ->flatMap(static fn($mediaType) => ContentTypeValue::of( - $mediaType->topLevel(), - $mediaType->subType(), - ...$mediaType - ->parameters() - ->map(static fn($param) => new Parameter\Parameter( - $param->name(), - $param->value(), - )) - ->toList(), - )) - ->map(static fn($value) => new ContentType($value)), + self::contentType => MediaType::maybe($value->toString())->map( + ContentType::of(...), + ), self::cookie => Maybe::just($value) ->filter(static fn($value) => $value->matches( @@ -410,27 +426,32 @@ public function try(Clock $clock, Str $value): Maybe ->get('url') ->flatMap(static fn($url) => Url::maybe($url->toString())); - /** - * @psalm-suppress MixedArgumentTypeCoercion - * @psalm-suppress MixedArgument - */ - return Maybe::all($url, $params)->flatMap( - static fn(Url $url, Map $params) => LinkValue::of( - $url, - $params->get('rel')->match( - static fn(Parameter $rel) => $rel->value(), - static fn() => null, + return $params->flatMap( + static fn($params) => $params + ->get('rel') + ->otherwise(static fn() => Maybe::just(Parameter::of( + 'rel', + 'related', + ))) + ->map(static fn($rel) => $rel->value()) + ->keep(Is::string()->nonEmpty()->asPredicate()) + ->flatMap( + static fn($rel) => $url->map( + static fn($url) => Link\Relationship::of( + $url, + $rel, + ...$params + ->remove('rel') + ->values() + ->toList(), + ), + ), ), - ...$params - ->remove('rel') - ->values() - ->toList(), - ), ); }) - ->sink(self::values(LinkValue::class)) + ->sink(self::values(Link\Relationship::class)) ->maybe(static fn($values, $value) => $value->map($values)) - ->map(static fn($values) => new Link(...$values->toList())), + ->map(static fn($values) => Link::of(...$values->toList())), self::location => Url::maybe($value->toString())->map( Location::of(...), @@ -472,7 +493,7 @@ private static function buildAcceptParams(Str $params): Maybe $matches = $value->capture('~(?\w+)=\"?(?[\w\-.]+)\"?~'); return Maybe::all($matches->get('key'), $matches->get('value')) - ->map(static fn(Str $key, Str $value) => new Parameter\Parameter( + ->map(static fn(Str $key, Str $value) => Parameter::of( $key->toString(), $value->toString(), )); @@ -483,11 +504,11 @@ private static function buildAcceptParams(Str $params): Maybe } /** - * @return Maybe> + * @return Maybe> */ private static function buildLinkParams(Str $params): Maybe { - /** @var Sequence */ + /** @var Sequence */ $values = Sequence::of(); return $params @@ -497,7 +518,7 @@ private static function buildLinkParams(Str $params): Maybe $matches = $value->capture('~(?\w+)=\"?(?[ \t!#$%&\\\'()*+\-.\/\d:<=>?@A-z{|}\~]+)\"?~'); return Maybe::all($matches->get('key'), $matches->get('value')) - ->map(static fn(Str $key, Str $value) => new Parameter\Parameter( + ->map(static fn(Str $key, Str $value) => Parameter::of( $key->toString(), $value->toString(), )) @@ -521,7 +542,7 @@ private static function buildCookieParams(Str $params): Sequence $matches = $value->capture('~^(?\w+)=\"?(?[\w\-.]*)\"?$~'); return Maybe::all($matches->get('key'), $matches->get('value')) - ->map(static fn(Str $key, Str $value) => new Parameter\Parameter( + ->map(static fn(Str $key, Str $value) => Parameter::of( $key->toString(), $value->toString(), )); @@ -544,12 +565,11 @@ private static function range(Str $value): Maybe $matches->get('first')->filter(\is_numeric(...)), $matches->get('last')->filter(\is_numeric(...)), ) - ->flatMap(static fn(string $unit, string $first, string $last) => RangeValue::of( + ->flatMap(static fn(string $unit, string $first, string $last) => Range::maybe( $unit, (int) $first, (int) $last, - )) - ->map(static fn($value) => new Range($value)); + )); } /** @@ -573,13 +593,12 @@ private static function contentRange(Str $value): Maybe ); return Maybe::all($matches->get('unit'), $matches->get('first'), $matches->get('last')) - ->flatMap(static fn(Str $unit, Str $first, Str $last) => ContentRangeValue::of( + ->flatMap(static fn(Str $unit, Str $first, Str $last) => ContentRange::maybe( $unit->toString(), (int) $first->toString(), (int) $last->toString(), $length, - )) - ->map(static fn($value) => new ContentRange($value)); + )); } /** @@ -603,7 +622,6 @@ private static function authorization(Str $value): Maybe return $matches ->get('scheme') ->map(static fn($scheme) => $scheme->toString()) - ->flatMap(static fn($scheme) => AuthorizationValue::of($scheme, $param)) - ->map(static fn($value) => new Authorization($value)); + ->flatMap(static fn($scheme) => Authorization::maybe($scheme, $param)); } } diff --git a/src/Factory/Header/Factory.php b/src/Factory/Header/Factory.php index 929bab94..23c37883 100644 --- a/src/Factory/Header/Factory.php +++ b/src/Factory/Header/Factory.php @@ -21,7 +21,7 @@ private function __construct( ) { } - public function __invoke(Str $name, Str $value): Header + public function __invoke(Str $name, Str $value): Header|Header\Custom { $factory = Factories::of($name); @@ -50,10 +50,10 @@ private static function default(Str $name, Str $value): Header $values = $value ->split(',') ->map(static fn($value) => $value->trim()) - ->map(static fn($value) => new Value\Value($value->toString())) + ->map(static fn($value) => Value::of($value->toString())) ->toList(); - return new Header\Header( + return Header::of( $name->toString(), ...$values, ); diff --git a/src/Header.php b/src/Header.php index dcf23876..c594eb64 100644 --- a/src/Header.php +++ b/src/Header.php @@ -3,18 +3,53 @@ namespace Innmind\Http; -use Innmind\Immutable\Sequence; +use Innmind\Http\Header\Value; +use Innmind\Immutable\{ + Sequence, + Str, +}; /** * @psalm-immutable */ -interface Header +final class Header { - public function name(): string; + /** + * @param Sequence $values + */ + private function __construct( + private string $name, + private Sequence $values, + ) { + } + + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(string $name, Value ...$values): self + { + return new self($name, Sequence::of(...$values)); + } + + public function name(): string + { + return $this->name; + } /** - * @return Sequence + * @return Sequence */ - public function values(): Sequence; - public function toString(): string; + public function values(): Sequence + { + return $this->values; + } + + public function toString(): string + { + $values = $this->values->map(static fn($value) => $value->toString()); + $values = Str::of(', ')->join($values); + + return $values->prepend("{$this->name}: ")->toString(); + } } diff --git a/src/Header/Accept.php b/src/Header/Accept.php index 0798eb40..eee0eaf9 100644 --- a/src/Header/Accept.php +++ b/src/Header/Accept.php @@ -3,36 +3,42 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Accept\MediaType, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Accept implements HeaderInterface +final class Accept implements Custom { - private Header $header; - - public function __construct(AcceptValue $first, AcceptValue ...$values) - { - $this->header = new Header('Accept', $first, ...$values); + /** + * @param Sequence $values + */ + private function __construct( + private Sequence $values, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + */ + public static function of(MediaType $first, MediaType ...$values): self { - return $this->header->values(); + return new self(Sequence::of($first, ...$values)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Accept', + ...$this + ->values + ->map(static fn($value) => Value::of($value->toString())) + ->toList(), + ); } } diff --git a/src/Header/Accept/Charset.php b/src/Header/Accept/Charset.php new file mode 100644 index 00000000..324484d2 --- /dev/null +++ b/src/Header/Accept/Charset.php @@ -0,0 +1,56 @@ + + */ + public static function maybe(string $charset, ?Quality $quality = null): Maybe + { + $charset = Str::of($charset); + + if ( + $charset->toString() !== '*' && + !$charset->matches('~^[a-zA-Z0-9\-_:\(\)]+$~') + ) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($charset, $quality ?? Quality::max())); + } + + public function quality(): Quality + { + return $this->quality; + } + + public function toString(): string + { + return $this + ->charset + ->append(';') + ->append($this->quality->toParameter()->toString()) + ->toString(); + } +} diff --git a/src/Header/Accept/Encoding.php b/src/Header/Accept/Encoding.php new file mode 100644 index 00000000..bb0e085c --- /dev/null +++ b/src/Header/Accept/Encoding.php @@ -0,0 +1,56 @@ + + */ + public static function maybe(string $coding, ?Quality $quality = null): Maybe + { + $coding = Str::of($coding); + + if ( + $coding->toString() !== '*' && + !$coding->matches('~^\w+$~') + ) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($coding, $quality ?? Quality::max())); + } + + public function quality(): Quality + { + return $this->quality; + } + + public function toString(): string + { + return $this + ->coding + ->append(';') + ->append($this->quality->toParameter()->toString()) + ->toString(); + } +} diff --git a/src/Header/Accept/Language.php b/src/Header/Accept/Language.php new file mode 100644 index 00000000..3c3aed0e --- /dev/null +++ b/src/Header/Accept/Language.php @@ -0,0 +1,56 @@ + + */ + public static function maybe(string $language, ?Quality $quality = null): Maybe + { + $language = Str::of($language); + + if ( + $language->toString() !== '*' && + !$language->matches('~^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$~') + ) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($language, $quality ?? Quality::max())); + } + + public function quality(): Quality + { + return $this->quality; + } + + public function toString(): string + { + return $this + ->language + ->append(';') + ->append($this->quality->toParameter()->toString()) + ->toString(); + } +} diff --git a/src/Header/AcceptValue.php b/src/Header/Accept/MediaType.php similarity index 67% rename from src/Header/AcceptValue.php rename to src/Header/Accept/MediaType.php index 88580ded..3888c8a9 100644 --- a/src/Header/AcceptValue.php +++ b/src/Header/Accept/MediaType.php @@ -1,9 +1,11 @@ */ - private Map $parameters; + private function __construct( + private string $type, + private string $subType, + /** @var Map */ + private Map $parameters, + ) { + } - public function __construct( + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe( string $type, string $subType, Parameter ...$parameters, - ) { + ): Maybe { $media = Str::of('%s/%s')->sprintf($type, $subType); - /** @var Map */ - $this->parameters = Map::of(); if ( !$media->matches('~^\*/\*$~') && !$media->matches('~^[\w\-.]+/\*$~') && !$media->matches('~^[\w\-.]+/[\w\-.]+$~') ) { - throw new DomainException($media->toString()); + /** @var Maybe */ + return Maybe::nothing(); } + /** @var Map */ + $map = Map::of(); + foreach ($parameters as $parameter) { - $this->parameters = ($this->parameters)( + $map = ($map)( $parameter->name(), $parameter, ); } - $this->type = $type; - $this->subType = $subType; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of( - string $type, - string $subType, - Parameter ...$parameters, - ): Maybe { - try { - return Maybe::just(new self($type, $subType, ...$parameters)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } + return Maybe::just(new self($type, $subType, $map)); } public function type(): string @@ -84,7 +77,6 @@ public function parameters(): Map return $this->parameters; } - #[\Override] public function toString(): string { $parameters = $this->parameters->values()->map( diff --git a/src/Header/AcceptCharset.php b/src/Header/AcceptCharset.php index cbf63c0c..f5ffa965 100644 --- a/src/Header/AcceptCharset.php +++ b/src/Header/AcceptCharset.php @@ -3,39 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Accept\Charset, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class AcceptCharset implements HeaderInterface +final class AcceptCharset implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $charsets */ - public function __construct(AcceptCharsetValue ...$values) - { - $this->header = new Header('Accept-Charset', ...$values); + private function __construct( + private Sequence $charsets, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(Charset ...$charsets): self { - return $this->header->values(); + return new self(Sequence::of(...$charsets)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Accept-Charset', + ...$this + ->charsets + ->map(static fn($value) => Value::of($value->toString())) + ->toList(), + ); } } diff --git a/src/Header/AcceptCharsetValue.php b/src/Header/AcceptCharsetValue.php deleted file mode 100644 index f72d587d..00000000 --- a/src/Header/AcceptCharsetValue.php +++ /dev/null @@ -1,68 +0,0 @@ -toString() !== '*' && - !$charset->matches('~^[a-zA-Z0-9\-_:\(\)]+$~') - ) { - throw new DomainException($charset->toString()); - } - - $this->charset = $charset; - $this->quality = $quality; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $charset, ?Quality $quality = null): Maybe - { - try { - return Maybe::just(new self($charset, $quality)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function quality(): Quality - { - return $this->quality; - } - - #[\Override] - public function toString(): string - { - return $this - ->charset - ->append(';') - ->append($this->quality->toString()) - ->toString(); - } -} diff --git a/src/Header/AcceptEncoding.php b/src/Header/AcceptEncoding.php index d57efffd..bf102f98 100644 --- a/src/Header/AcceptEncoding.php +++ b/src/Header/AcceptEncoding.php @@ -3,39 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Accept\Encoding, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class AcceptEncoding implements HeaderInterface +final class AcceptEncoding implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $encodings */ - public function __construct(AcceptEncodingValue ...$values) - { - $this->header = new Header('Accept-Encoding', ...$values); + private function __construct( + private Sequence $encodings, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(Encoding ...$encodings): self { - return $this->header->values(); + return new self(Sequence::of(...$encodings)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Accept-Encoding', + ...$this + ->encodings + ->map(static fn($value) => Value::of($value->toString())) + ->toList(), + ); } } diff --git a/src/Header/AcceptEncodingValue.php b/src/Header/AcceptEncodingValue.php deleted file mode 100644 index f63f1903..00000000 --- a/src/Header/AcceptEncodingValue.php +++ /dev/null @@ -1,68 +0,0 @@ -toString() !== '*' && - !$coding->matches('~^\w+$~') - ) { - throw new DomainException($coding->toString()); - } - - $this->coding = $coding; - $this->quality = $quality; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $coding, ?Quality $quality = null): Maybe - { - try { - return Maybe::just(new self($coding, $quality)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function quality(): Quality - { - return $this->quality; - } - - #[\Override] - public function toString(): string - { - return $this - ->coding - ->append(';') - ->append($this->quality->toString()) - ->toString(); - } -} diff --git a/src/Header/AcceptLanguage.php b/src/Header/AcceptLanguage.php index 67340518..96f2bcfe 100644 --- a/src/Header/AcceptLanguage.php +++ b/src/Header/AcceptLanguage.php @@ -3,39 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Accept\Language, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class AcceptLanguage implements HeaderInterface +final class AcceptLanguage implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $languages */ - public function __construct(AcceptLanguageValue ...$values) - { - $this->header = new Header('Accept-Language', ...$values); + private function __construct( + private Sequence $languages, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(Language ...$languages): self { - return $this->header->values(); + return new self(Sequence::of(...$languages)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Accept-Language', + ...$this + ->languages + ->map(static fn($value) => Value::of($value->toString())) + ->toList(), + ); } } diff --git a/src/Header/AcceptLanguageValue.php b/src/Header/AcceptLanguageValue.php deleted file mode 100644 index 604efd88..00000000 --- a/src/Header/AcceptLanguageValue.php +++ /dev/null @@ -1,68 +0,0 @@ -toString() !== '*' && - !$language->matches('~^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$~') - ) { - throw new DomainException($language->toString()); - } - - $this->language = $language; - $this->quality = $quality; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $language, ?Quality $quality = null): Maybe - { - try { - return Maybe::just(new self($language, $quality)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function quality(): Quality - { - return $this->quality; - } - - #[\Override] - public function toString(): string - { - return $this - ->language - ->append(';') - ->append($this->quality->toString()) - ->toString(); - } -} diff --git a/src/Header/AcceptRanges.php b/src/Header/AcceptRanges.php index 617b2354..80123d41 100644 --- a/src/Header/AcceptRanges.php +++ b/src/Header/AcceptRanges.php @@ -3,44 +3,57 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Exception\DomainException, +}; +use Innmind\Immutable\{ + Str, + Maybe, +}; /** * @psalm-immutable */ -final class AcceptRanges implements HeaderInterface +final class AcceptRanges implements Custom { - private Header $header; - - public function __construct(AcceptRangesValue $ranges) - { - $this->header = new Header('Accept-Ranges', $ranges); + private function __construct( + private string $ranges, + ) { } /** * @psalm-pure + * + * @throws DomainException */ public static function of(string $range): self { - return new self(new AcceptRangesValue($range)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); + return self::maybe($range)->match( + static fn($self) => $self, + static fn() => throw new DomainException($range), + ); } - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(string $range): Maybe { - return $this->header->values(); + return Maybe::just($range) + ->map(Str::of(...)) + ->filter(static fn($range) => $range->matches('~^\w+$~')) + ->map(static fn() => new self($range)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Accept-Ranges', + Value::of($this->ranges), + ); } } diff --git a/src/Header/AcceptRangesValue.php b/src/Header/AcceptRangesValue.php deleted file mode 100644 index 58d9bbd3..00000000 --- a/src/Header/AcceptRangesValue.php +++ /dev/null @@ -1,48 +0,0 @@ -matches('~^\w+$~')) { - throw new DomainException($range); - } - - $this->range = $range; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $range): Maybe - { - try { - return Maybe::just(new self($range)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - #[\Override] - public function toString(): string - { - return $this->range; - } -} diff --git a/src/Header/Age.php b/src/Header/Age.php index 68e212eb..6a817739 100644 --- a/src/Header/Age.php +++ b/src/Header/Age.php @@ -3,54 +3,56 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\Header; +use Innmind\Immutable\Maybe; /** * @psalm-immutable */ -final class Age implements HeaderInterface +final class Age implements Custom { - private Header $header; - private AgeValue $value; - - public function __construct(AgeValue $age) - { - $this->header = new Header('Age', $age); - $this->value = $age; + /** + * @param int<0, max> $age + */ + private function __construct( + private int $age, + ) { } /** * @psalm-pure + * + * @param int<0, max> $age */ public static function of(int $age): self { - return new self(new AgeValue($age)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); + return new self($age); } - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(int $age): Maybe { - return $this->header->values(); + return Maybe::of(match (true) { + $age >= 0 => new self($age), + default => null, + }); } /** - * @return 0|positive-int + * @return int<0, max> */ public function age(): int { - return $this->value->age(); + return $this->age; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of('Age', Value::of((string) $this->age)); } } diff --git a/src/Header/AgeValue.php b/src/Header/AgeValue.php deleted file mode 100644 index 7f043036..00000000 --- a/src/Header/AgeValue.php +++ /dev/null @@ -1,55 +0,0 @@ -age = $age; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $age): Maybe - { - try { - return Maybe::just(new self($age)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - /** - * @return 0|positive-int - */ - public function age(): int - { - return $this->age; - } - - #[\Override] - public function toString(): string - { - return (string) $this->age; - } -} diff --git a/src/Header/Allow.php b/src/Header/Allow.php index 5a3ebdca..3e177bdf 100644 --- a/src/Header/Allow.php +++ b/src/Header/Allow.php @@ -3,51 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Method, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Allow implements HeaderInterface +final class Allow implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $methods */ - public function __construct(AllowValue ...$values) - { - $this->header = new Header('Allow', ...$values); + private function __construct( + private Sequence $methods, + ) { } /** - * @no-named-arguments * @psalm-pure + * @no-named-arguments */ - public static function of(string ...$values): self - { - return new self(...\array_map( - static fn(string $value): AllowValue => new AllowValue($value), - $values, - )); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + public static function of(Method ...$methods): self { - return $this->header->values(); + return new self(Sequence::of(...$methods)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Allow', + ...$this + ->methods + ->map(static fn($method) => Value::of($method->toString())) + ->toList(), + ); } } diff --git a/src/Header/AllowValue.php b/src/Header/AllowValue.php deleted file mode 100644 index aa2c65fa..00000000 --- a/src/Header/AllowValue.php +++ /dev/null @@ -1,38 +0,0 @@ -value = Method::of($value)->toString(); - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $value): Maybe - { - return Method::maybe($value)->map( - static fn($method) => new self($method->toString()), - ); - } - - #[\Override] - public function toString(): string - { - return $this->value; - } -} diff --git a/src/Header/Authorization.php b/src/Header/Authorization.php index c8ada45b..f84996c0 100644 --- a/src/Header/Authorization.php +++ b/src/Header/Authorization.php @@ -3,21 +3,24 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Exception\DomainException, +}; +use Innmind\Immutable\{ + Str, + Maybe, +}; /** * @psalm-immutable */ -final class Authorization implements HeaderInterface +final class Authorization implements Custom { - private Header $header; - private AuthorizationValue $value; - - public function __construct(AuthorizationValue $authorization) - { - $this->header = new Header('Authorization', $authorization); - $this->value = $authorization; + private function __construct( + private string $scheme, + private string $parameter, + ) { } /** @@ -25,34 +28,47 @@ public function __construct(AuthorizationValue $authorization) */ public static function of(string $scheme, string $parameter): self { - return new self(new AuthorizationValue($scheme, $parameter)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); + return self::maybe($scheme, $parameter)->match( + static fn($self) => $self, + static fn() => throw new DomainException($scheme), + ); } - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(string $scheme, string $parameter): Maybe { - return $this->header->values(); + return Maybe::just($scheme) + ->map(Str::of(...)) + ->filter(static fn($scheme) => $scheme->matches('~^\w+$~')) + ->map(static fn() => new self($scheme, $parameter)); } public function scheme(): string { - return $this->value->scheme(); + return $this->scheme; } public function parameter(): string { - return $this->value->parameter(); + return $this->parameter; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Authorization', + Value::of( + Str::of($this->scheme) + ->append(' ') + ->append($this->parameter) + ->trim() + ->toString(), + ), + ); } } diff --git a/src/Header/AuthorizationValue.php b/src/Header/AuthorizationValue.php deleted file mode 100644 index eb8c617f..00000000 --- a/src/Header/AuthorizationValue.php +++ /dev/null @@ -1,64 +0,0 @@ -matches('~^\w+$~')) { - throw new DomainException($scheme); - } - - $this->scheme = $scheme; - $this->parameter = $parameter; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $scheme, string $parameter): Maybe - { - try { - return Maybe::just(new self($scheme, $parameter)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function scheme(): string - { - return $this->scheme; - } - - public function parameter(): string - { - return $this->parameter; - } - - #[\Override] - public function toString(): string - { - return Str::of($this->scheme) - ->append(' ') - ->append($this->parameter) - ->trim() - ->toString(); - } -} diff --git a/src/Header/CacheControl.php b/src/Header/CacheControl.php index bde726a6..02a91838 100644 --- a/src/Header/CacheControl.php +++ b/src/Header/CacheControl.php @@ -3,36 +3,51 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\CacheControl\Directive, + Header\CacheControl\MaxAge, + Header\CacheControl\MaxStale, + Header\CacheControl\MinimumFresh, + Header\CacheControl\NoCache, + Header\CacheControl\PrivateCache, + Header\CacheControl\SharedMaxAge, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class CacheControl implements HeaderInterface +final class CacheControl implements Custom { - private Header $header; - - public function __construct(CacheControlValue $first, CacheControlValue ...$values) - { - $this->header = new Header('Cache-Control', $first, ...$values); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); + /** + * @param Sequence $directives + */ + private function __construct( + private Sequence $directives, + ) { } - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of( + Directive|MaxAge|MaxStale|MinimumFresh|NoCache|PrivateCache|SharedMaxAge $first, + Directive|MaxAge|MaxStale|MinimumFresh|NoCache|PrivateCache|SharedMaxAge ...$values, + ): self { + return new self(Sequence::of($first, ...$values)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Cache-Control', + ...$this + ->directives + ->map(static fn($directive) => Value::of($directive->toString())) + ->toList(), + ); } } diff --git a/src/Header/CacheControl/Directive.php b/src/Header/CacheControl/Directive.php new file mode 100644 index 00000000..604f7927 --- /dev/null +++ b/src/Header/CacheControl/Directive.php @@ -0,0 +1,32 @@ + 'immutable', + self::mustRevalidate => 'must-revalidate', + self::noStore => 'no-store', + self::noTransform => 'no-transform', + self::onlyIfCached => 'only-if-cached', + self::proxyRevalidate => 'proxy-revalidate', + self::public => 'public', + }; + } +} diff --git a/src/Header/CacheControl/MaxAge.php b/src/Header/CacheControl/MaxAge.php new file mode 100644 index 00000000..13840e5c --- /dev/null +++ b/src/Header/CacheControl/MaxAge.php @@ -0,0 +1,44 @@ + $age + */ + private function __construct( + private int $age, + ) { + } + + /** + * @psalm-pure + * + * @param int<0, max> $age + */ + public static function of(int $age): self + { + return new self($age); + } + + /** + * @return int<0, max> + */ + public function age(): int + { + return $this->age; + } + + public function toString(): string + { + return \sprintf( + 'max-age=%s', + $this->age, + ); + } +} diff --git a/src/Header/CacheControl/MaxStale.php b/src/Header/CacheControl/MaxStale.php new file mode 100644 index 00000000..021a2e35 --- /dev/null +++ b/src/Header/CacheControl/MaxStale.php @@ -0,0 +1,44 @@ + $age + */ + private function __construct( + private int $age, + ) { + } + + /** + * @psalm-pure + * + * @param int<0, max> $age + */ + public static function of(int $age): self + { + return new self($age); + } + + /** + * @return int<0, max> + */ + public function age(): int + { + return $this->age; + } + + public function toString(): string + { + return \sprintf( + 'max-stale%s', + $this->age > 0 ? '='.$this->age : '', + ); + } +} diff --git a/src/Header/CacheControl/MinimumFresh.php b/src/Header/CacheControl/MinimumFresh.php new file mode 100644 index 00000000..42bc3b3d --- /dev/null +++ b/src/Header/CacheControl/MinimumFresh.php @@ -0,0 +1,44 @@ + $age + */ + private function __construct( + private int $age, + ) { + } + + /** + * @psalm-pure + * + * @param int<0, max> $age + */ + public static function of(int $age): self + { + return new self($age); + } + + /** + * @return int<0, max> + */ + public function age(): int + { + return $this->age; + } + + public function toString(): string + { + return \sprintf( + 'min-fresh=%s', + $this->age, + ); + } +} diff --git a/src/Header/CacheControlValue/NoCache.php b/src/Header/CacheControl/NoCache.php similarity index 53% rename from src/Header/CacheControlValue/NoCache.php rename to src/Header/CacheControl/NoCache.php index 60a09973..8936cb55 100644 --- a/src/Header/CacheControlValue/NoCache.php +++ b/src/Header/CacheControl/NoCache.php @@ -1,12 +1,8 @@ matches('~^\w*$~')) { - throw new DomainException($field); - } - - $this->field = $field; + private function __construct( + private string $field, + ) { } /** @@ -33,14 +23,14 @@ public function __construct(string $field) * * @return Maybe */ - public static function of(string $field): Maybe + public static function maybe(string $field): Maybe { - try { - return Maybe::just(new self($field)); - } catch (DomainException $e) { + if (!Str::of($field)->matches('~^\w*$~')) { /** @var Maybe */ return Maybe::nothing(); } + + return Maybe::just(new self($field)); } public function field(): string @@ -48,7 +38,6 @@ public function field(): string return $this->field; } - #[\Override] public function toString(): string { return \sprintf( diff --git a/src/Header/CacheControlValue/PrivateCache.php b/src/Header/CacheControl/PrivateCache.php similarity index 52% rename from src/Header/CacheControlValue/PrivateCache.php rename to src/Header/CacheControl/PrivateCache.php index 3d49fb8c..3d1a125c 100644 --- a/src/Header/CacheControlValue/PrivateCache.php +++ b/src/Header/CacheControl/PrivateCache.php @@ -1,12 +1,8 @@ matches('~^\w*$~')) { - throw new DomainException($field); - } - - $this->field = $field; + private function __construct( + private string $field, + ) { } /** @@ -33,14 +23,14 @@ public function __construct(string $field) * * @return Maybe */ - public static function of(string $field): Maybe + public static function maybe(string $field): Maybe { - try { - return Maybe::just(new self($field)); - } catch (DomainException $e) { + if (!Str::of($field)->matches('~^\w*$~')) { /** @var Maybe */ return Maybe::nothing(); } + + return Maybe::just(new self($field)); } public function field(): string @@ -48,7 +38,6 @@ public function field(): string return $this->field; } - #[\Override] public function toString(): string { return \sprintf( diff --git a/src/Header/CacheControl/SharedMaxAge.php b/src/Header/CacheControl/SharedMaxAge.php new file mode 100644 index 00000000..d1320182 --- /dev/null +++ b/src/Header/CacheControl/SharedMaxAge.php @@ -0,0 +1,44 @@ + $age + */ + private function __construct( + private int $age, + ) { + } + + /** + * @psalm-pure + * + * @param int<0, max> $age + */ + public static function of(int $age): self + { + return new self($age); + } + + /** + * @return int<0, max> + */ + public function age(): int + { + return $this->age; + } + + public function toString(): string + { + return \sprintf( + 's-maxage=%s', + $this->age, + ); + } +} diff --git a/src/Header/CacheControlValue/Immutable.php b/src/Header/CacheControlValue/Immutable.php deleted file mode 100644 index a553076b..00000000 --- a/src/Header/CacheControlValue/Immutable.php +++ /dev/null @@ -1,19 +0,0 @@ -age = $age; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $age): Maybe - { - try { - return Maybe::just(new self($age)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function age(): int - { - return $this->age; - } - - #[\Override] - public function toString(): string - { - return \sprintf( - 'max-age=%s', - $this->age, - ); - } -} diff --git a/src/Header/CacheControlValue/MaxStale.php b/src/Header/CacheControlValue/MaxStale.php deleted file mode 100644 index c7b9408f..00000000 --- a/src/Header/CacheControlValue/MaxStale.php +++ /dev/null @@ -1,56 +0,0 @@ -age = $age; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $age): Maybe - { - try { - return Maybe::just(new self($age)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function age(): int - { - return $this->age; - } - - #[\Override] - public function toString(): string - { - return \sprintf( - 'max-stale%s', - $this->age > 0 ? '='.$this->age : '', - ); - } -} diff --git a/src/Header/CacheControlValue/MinimumFresh.php b/src/Header/CacheControlValue/MinimumFresh.php deleted file mode 100644 index 59cf0462..00000000 --- a/src/Header/CacheControlValue/MinimumFresh.php +++ /dev/null @@ -1,56 +0,0 @@ -age = $age; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $age): Maybe - { - try { - return Maybe::just(new self($age)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function age(): int - { - return $this->age; - } - - #[\Override] - public function toString(): string - { - return \sprintf( - 'min-fresh=%s', - $this->age, - ); - } -} diff --git a/src/Header/CacheControlValue/MustRevalidate.php b/src/Header/CacheControlValue/MustRevalidate.php deleted file mode 100644 index 3048490d..00000000 --- a/src/Header/CacheControlValue/MustRevalidate.php +++ /dev/null @@ -1,18 +0,0 @@ -age = $age; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $age): Maybe - { - try { - return Maybe::just(new self($age)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function age(): int - { - return $this->age; - } - - #[\Override] - public function toString(): string - { - return \sprintf( - 's-maxage=%s', - $this->age, - ); - } -} diff --git a/src/Header/Content/Language.php b/src/Header/Content/Language.php new file mode 100644 index 00000000..60eb17b9 --- /dev/null +++ b/src/Header/Content/Language.php @@ -0,0 +1,40 @@ + + */ + public static function maybe(string $language): Maybe + { + if (!Str::of($language)->matches('~^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$~')) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($language)); + } + + public function toString(): string + { + return $this->language; + } +} diff --git a/src/Header/ContentEncoding.php b/src/Header/ContentEncoding.php index f406f7c1..8adbf853 100644 --- a/src/Header/ContentEncoding.php +++ b/src/Header/ContentEncoding.php @@ -3,44 +3,57 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Exception\DomainException, +}; +use Innmind\Immutable\{ + Maybe, + Str, +}; /** * @psalm-immutable */ -final class ContentEncoding implements HeaderInterface +final class ContentEncoding implements Custom { - private Header $header; - - public function __construct(ContentEncodingValue $encoding) - { - $this->header = new Header('Content-Encoding', $encoding); + private function __construct( + private string $encoding, + ) { } /** * @psalm-pure + * + * @throws DomainException */ - public static function of(string $coding): self - { - return new self(new ContentEncodingValue($coding)); - } - - #[\Override] - public function name(): string + public static function of(string $encoding): self { - return $this->header->name(); + return self::maybe($encoding)->match( + static fn($self) => $self, + static fn() => throw new DomainException($encoding), + ); } - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(string $encoding): Maybe { - return $this->header->values(); + return Maybe::just($encoding) + ->map(Str::of(...)) + ->filter(static fn($encoding) => $encoding->matches('~^[\w\-]+$~')) + ->map(static fn() => new self($encoding)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Content-Encoding', + Value::of($this->encoding), + ); } } diff --git a/src/Header/ContentEncodingValue.php b/src/Header/ContentEncodingValue.php deleted file mode 100644 index c724bb52..00000000 --- a/src/Header/ContentEncodingValue.php +++ /dev/null @@ -1,48 +0,0 @@ -matches('~^[\w\-]+$~')) { - throw new DomainException($coding); - } - - $this->coding = $coding; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $coding): Maybe - { - try { - return Maybe::just(new self($coding)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - #[\Override] - public function toString(): string - { - return $this->coding; - } -} diff --git a/src/Header/ContentLanguage.php b/src/Header/ContentLanguage.php index 5a9a6c06..75cd28a7 100644 --- a/src/Header/ContentLanguage.php +++ b/src/Header/ContentLanguage.php @@ -3,51 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Content\Language, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class ContentLanguage implements HeaderInterface +final class ContentLanguage implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $languages */ - public function __construct(ContentLanguageValue ...$values) - { - $this->header = new Header('Content-Language', ...$values); + private function __construct( + private Sequence $languages, + ) { } /** * @no-named-arguments * @psalm-pure */ - public static function of(string ...$values): self - { - return new self(...\array_map( - static fn(string $value): ContentLanguageValue => new ContentLanguageValue($value), - $values, - )); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + public static function of(Language ...$languages): self { - return $this->header->values(); + return new self(Sequence::of(...$languages)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Content-Language', + ...$this + ->languages + ->map(static fn($language) => Value::of($language->toString())) + ->toList(), + ); } } diff --git a/src/Header/ContentLanguageValue.php b/src/Header/ContentLanguageValue.php deleted file mode 100644 index a871ca98..00000000 --- a/src/Header/ContentLanguageValue.php +++ /dev/null @@ -1,48 +0,0 @@ -matches('~^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$~')) { - throw new DomainException($language); - } - - $this->language = $language; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(string $language): Maybe - { - try { - return Maybe::just(new self($language)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - #[\Override] - public function toString(): string - { - return $this->language; - } -} diff --git a/src/Header/ContentLength.php b/src/Header/ContentLength.php index 2d733cea..131bc460 100644 --- a/src/Header/ContentLength.php +++ b/src/Header/ContentLength.php @@ -3,54 +3,59 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\Header; +use Innmind\Immutable\Maybe; /** * @psalm-immutable */ -final class ContentLength implements HeaderInterface +final class ContentLength implements Custom { - private Header $header; - private ContentLengthValue $value; - - public function __construct(ContentLengthValue $length) - { - $this->header = new Header('Content-Length', $length); - $this->value = $length; + /** + * @param int<0, max> $length + */ + private function __construct( + private int $length, + ) { } /** * @psalm-pure + * + * @param int<0, max> $length */ public static function of(int $length): self { - return new self(new ContentLengthValue($length)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); + return new self($length); } - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(int $length): Maybe { - return $this->header->values(); + return Maybe::of(match (true) { + $length >= 0 => new self($length), + default => null, + }); } /** - * @return 0|positive-int + * @return int<0, max> */ public function length(): int { - return $this->value->length(); + return $this->length; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Content-Length', + Value::of((string) $this->length), + ); } } diff --git a/src/Header/ContentLengthValue.php b/src/Header/ContentLengthValue.php deleted file mode 100644 index 224d9374..00000000 --- a/src/Header/ContentLengthValue.php +++ /dev/null @@ -1,54 +0,0 @@ -length = $length; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(int $length): Maybe - { - try { - return Maybe::just(new self($length)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - /** - * @return 0|positive-int - */ - public function length(): int - { - return $this->length; - } - - #[\Override] - public function toString(): string - { - return (string) $this->length; - } -} diff --git a/src/Header/ContentLocation.php b/src/Header/ContentLocation.php index 80c00376..a4f65c59 100644 --- a/src/Header/ContentLocation.php +++ b/src/Header/ContentLocation.php @@ -3,22 +3,17 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\Header; use Innmind\Url\Url; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class ContentLocation implements HeaderInterface +final class ContentLocation implements Custom { - private Header $header; - private LocationValue $value; - - public function __construct(LocationValue $location) - { - $this->header = new Header('Content-Location', $location); - $this->value = $location; + private function __construct( + private Url $url, + ) { } /** @@ -26,29 +21,20 @@ public function __construct(LocationValue $location) */ public static function of(Url $location): self { - return new self(new LocationValue($location)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($location); } public function url(): Url { - return $this->value->url(); + return $this->url; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Content-Location', + Value::of($this->url->toString()), + ); } } diff --git a/src/Header/ContentRange.php b/src/Header/ContentRange.php index 93173d9e..a4a2b681 100644 --- a/src/Header/ContentRange.php +++ b/src/Header/ContentRange.php @@ -3,21 +3,31 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Exception\DomainException, +}; +use Innmind\Immutable\{ + Str, + Maybe, +}; /** * @psalm-immutable */ -final class ContentRange implements HeaderInterface +final class ContentRange implements Custom { - private Header $header; - private ContentRangeValue $range; - - public function __construct(ContentRangeValue $range) - { - $this->header = new Header('Content-Range', $range); - $this->range = $range; + /** + * @param int<0, max> $firstPosition + * @param int<0, max> $lastPosition + * @param ?int<0, max> $length + */ + private function __construct( + private string $unit, + private int $firstPosition, + private int $lastPosition, + private ?int $length, + ) { } /** @@ -29,7 +39,36 @@ public static function of( int $lastPosition, ?int $length = null, ): self { - return new self(new ContentRangeValue( + return self::maybe($unit, $firstPosition, $lastPosition, $length)->match( + static fn($self) => $self, + static fn() => throw new DomainException($unit), + ); + } + + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe( + string $unit, + int $firstPosition, + int $lastPosition, + ?int $length = null, + ): Maybe { + if ( + !Str::of($unit)->matches('~^\w+$~') || + $firstPosition < 0 || + $lastPosition < 0 || + ($length !== null && $length < 0) || + $firstPosition > $lastPosition || + ($length !== null && $lastPosition > $length) + ) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self( $unit, $firstPosition, $lastPosition, @@ -37,26 +76,47 @@ public static function of( )); } - #[\Override] - public function name(): string + public function unit(): string { - return $this->header->name(); + return $this->unit; } - #[\Override] - public function values(): Sequence + /** + * @return int<0, max> + */ + public function firstPosition(): int + { + return $this->firstPosition; + } + + /** + * @return int<0, max> + */ + public function lastPosition(): int { - return $this->header->values(); + return $this->lastPosition; } - public function range(): ContentRangeValue + /** + * @return Maybe> + */ + public function length(): Maybe { - return $this->range; + return Maybe::of($this->length); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Content-Range', + Value::of(\sprintf( + '%s %s-%s/%s', + $this->unit, + $this->firstPosition, + $this->lastPosition, + $this->length ?? '*', + )), + ); } } diff --git a/src/Header/ContentRangeValue.php b/src/Header/ContentRangeValue.php deleted file mode 100644 index 6c99e4d3..00000000 --- a/src/Header/ContentRangeValue.php +++ /dev/null @@ -1,98 +0,0 @@ -matches('~^\w+$~') || - $firstPosition < 0 || - $lastPosition < 0 || - ($length !== null && $length < 0) || - $firstPosition > $lastPosition || - ($length !== null && $lastPosition > $length) - ) { - throw new DomainException($unit); - } - - $this->unit = $unit; - $this->firstPosition = $firstPosition; - $this->lastPosition = $lastPosition; - $this->length = $length; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of( - string $unit, - int $firstPosition, - int $lastPosition, - ?int $length = null, - ): Maybe { - try { - return Maybe::just(new self($unit, $firstPosition, $lastPosition, $length)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function unit(): string - { - return $this->unit; - } - - public function firstPosition(): int - { - return $this->firstPosition; - } - - public function lastPosition(): int - { - return $this->lastPosition; - } - - /** - * @return Maybe - */ - public function length(): Maybe - { - return Maybe::of($this->length); - } - - #[\Override] - public function toString(): string - { - return \sprintf( - '%s %s-%s/%s', - $this->unit, - $this->firstPosition, - $this->lastPosition, - $this->length ?? '*', - ); - } -} diff --git a/src/Header/ContentType.php b/src/Header/ContentType.php index 5e94d1ac..402cbe91 100644 --- a/src/Header/ContentType.php +++ b/src/Header/ContentType.php @@ -3,58 +3,63 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\Header; +use Innmind\MediaType\MediaType; +use Innmind\Immutable\Str; /** * @psalm-immutable */ -final class ContentType implements HeaderInterface +final class ContentType implements Custom { - private Header $header; - private ContentTypeValue $content; - - public function __construct(ContentTypeValue $content) - { - $this->header = new Header('Content-Type', $content); - $this->content = $content; + private function __construct( + private MediaType $content, + ) { } /** * @psalm-pure */ - public static function of( - string $type, - string $subType, - Parameter ...$parameters, - ): self { - return new self(new ContentTypeValue( - $type, - $subType, - ...$parameters, - )); - } - - #[\Override] - public function name(): string + public static function of(MediaType $content): self { - return $this->header->name(); + return new self($content); } - #[\Override] - public function values(): Sequence - { - return $this->header->values(); - } - - public function content(): ContentTypeValue + public function content(): MediaType { return $this->content; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + $mediaType = new MediaType( + $this->content->topLevel(), + $this->content->subType(), + $this->content->suffix(), + ); + $parameters = Str::of(';')->join( + $this + ->content + ->parameters() + ->map(static fn($parameter) => Parameter::of( // to make sure it's of the HTTP format + $parameter->name(), + $parameter->value(), + )) + ->map(static fn($parameter) => $parameter->toString()), + ); + + $content = $mediaType->toString(); + + if (!$parameters->empty()) { + $content .= ';'; + } + + $content .= $parameters->toString(); + + return Header::of( + 'Content-Type', + Value::of($content), + ); } } diff --git a/src/Header/ContentType/Boundary.php b/src/Header/ContentType/Boundary.php index 685b11dd..d12f581d 100644 --- a/src/Header/ContentType/Boundary.php +++ b/src/Header/ContentType/Boundary.php @@ -16,7 +16,7 @@ /** * @psalm-immutable */ -final class Boundary implements Parameter +final class Boundary { private function __construct( private string $value, @@ -58,24 +58,13 @@ public static function uuid(): self return self::of(Uuid::uuid4()->toString()); } - #[\Override] - public function name(): string - { - return 'boundary'; - } - - #[\Override] public function value(): string { return $this->value; } - #[\Override] - public function toString(): string + public function toParameter(): Parameter { - return \sprintf( - 'boundary="%s"', - $this->value, - ); + return Parameter::of('boundary', $this->value); } } diff --git a/src/Header/ContentTypeValue.php b/src/Header/ContentTypeValue.php deleted file mode 100644 index e5e93d0c..00000000 --- a/src/Header/ContentTypeValue.php +++ /dev/null @@ -1,98 +0,0 @@ - */ - private Map $parameters; - - public function __construct( - string $type, - string $subType, - Parameter ...$parameters, - ) { - $media = Str::of('%s/%s')->sprintf($type, $subType); - /** @var Map */ - $this->parameters = Map::of(); - - if (!$media->matches('~^[\w\-.]+/[\w\-.]+$~')) { - throw new DomainException($media->toString()); - } - - foreach ($parameters as $parameter) { - $this->parameters = ($this->parameters)( - $parameter->name(), - $parameter, - ); - } - - $this->type = $type; - $this->subType = $subType; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of( - string $type, - string $subType, - Parameter ...$parameters, - ): Maybe { - try { - return Maybe::just(new self($type, $subType, ...$parameters)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function type(): string - { - return $this->type; - } - - public function subType(): string - { - return $this->subType; - } - - /** - * @return Map - */ - public function parameters(): Map - { - return $this->parameters; - } - - #[\Override] - public function toString(): string - { - $parameters = $this->parameters->values()->map( - static fn($paramater) => $paramater->toString(), - ); - $parameters = Str::of(';')->join($parameters); - $parameters = !$parameters->empty() ? $parameters->prepend(';') : $parameters; - - return Str::of($this->type) - ->append('/') - ->append($this->subType) - ->append($parameters->toString()) - ->toString(); - } -} diff --git a/src/Header/Cookie.php b/src/Header/Cookie.php index 18cd2e91..f42e43d4 100644 --- a/src/Header/Cookie.php +++ b/src/Header/Cookie.php @@ -3,24 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\Header; use Innmind\Immutable\{ - Sequence, Map, + Str, }; /** * @psalm-immutable */ -final class Cookie implements HeaderInterface +final class Cookie implements Custom { - private Header $header; - private CookieValue $value; - - public function __construct(CookieValue $value) - { - $this->header = new Header('Cookie', $value); - $this->value = $value; + /** + * @param Map $parameters + */ + private function __construct( + private Map $parameters, + ) { } /** @@ -29,19 +28,17 @@ public function __construct(CookieValue $value) */ public static function of(Parameter ...$parameters): self { - return new self(new CookieValue(...$parameters)); - } + /** @var Map */ + $map = Map::of(); - #[\Override] - public function name(): string - { - return $this->header->name(); - } + foreach ($parameters as $paramater) { + $map = ($map)( + $paramater->name(), + $paramater, + ); + } - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($map); } /** @@ -49,12 +46,21 @@ public function values(): Sequence */ public function parameters(): Map { - return $this->value->parameters(); + return $this->parameters; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + $parameters = $this->parameters->values()->map( + static fn($paramater) => $paramater->toString(), + ); + + $value = Str::of('; ')->join($parameters)->toString(); + + return Header::of( + 'Cookie', + Value::of($value), + ); } } diff --git a/src/Header/CookieParameter/Domain.php b/src/Header/CookieParameter/Domain.php deleted file mode 100644 index 47fd9856..00000000 --- a/src/Header/CookieParameter/Domain.php +++ /dev/null @@ -1,38 +0,0 @@ -parameter = new Parameter\Parameter('Domain', $host->toString()); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/Expires.php b/src/Header/CookieParameter/Expires.php deleted file mode 100644 index 390d16fe..00000000 --- a/src/Header/CookieParameter/Expires.php +++ /dev/null @@ -1,47 +0,0 @@ -parameter = new Parameter\Parameter( - 'Expires', - $date->changeOffset(Offset::utc())->format(Http::new()), - ); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/HttpOnly.php b/src/Header/CookieParameter/HttpOnly.php deleted file mode 100644 index 40759e96..00000000 --- a/src/Header/CookieParameter/HttpOnly.php +++ /dev/null @@ -1,37 +0,0 @@ -parameter = new Parameter\Parameter('HttpOnly', ''); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/MaxAge.php b/src/Header/CookieParameter/MaxAge.php deleted file mode 100644 index 44d838ff..00000000 --- a/src/Header/CookieParameter/MaxAge.php +++ /dev/null @@ -1,52 +0,0 @@ -parameter = new Parameter\Parameter('Max-Age', (string) $number); - } - - /** - * @psalm-pure - */ - public static function expire(): Parameter - { - return new Parameter\Parameter('Max-Age', '-1'); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/Path.php b/src/Header/CookieParameter/Path.php deleted file mode 100644 index 8bf70b64..00000000 --- a/src/Header/CookieParameter/Path.php +++ /dev/null @@ -1,38 +0,0 @@ -parameter = new Parameter\Parameter('Path', $path->toString()); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/SameSite.php b/src/Header/CookieParameter/SameSite.php deleted file mode 100644 index 672d98e8..00000000 --- a/src/Header/CookieParameter/SameSite.php +++ /dev/null @@ -1,53 +0,0 @@ -parameter = new Parameter\Parameter('SameSite', $value); - } - - /** - * @psalm-pure - */ - public static function strict(): self - { - return new self('Strict'); - } - - /** - * @psalm-pure - */ - public static function lax(): self - { - return new self('Lax'); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieParameter/Secure.php b/src/Header/CookieParameter/Secure.php deleted file mode 100644 index c1f32c9d..00000000 --- a/src/Header/CookieParameter/Secure.php +++ /dev/null @@ -1,37 +0,0 @@ -parameter = new Parameter\Parameter('Secure', ''); - } - - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string - { - return $this->parameter->value(); - } - - #[\Override] - public function toString(): string - { - return $this->parameter->toString(); - } -} diff --git a/src/Header/CookieValue.php b/src/Header/CookieValue.php deleted file mode 100644 index 8697afa6..00000000 --- a/src/Header/CookieValue.php +++ /dev/null @@ -1,52 +0,0 @@ - */ - private Map $parameters; - - /** - * @no-named-arguments - */ - public function __construct(Parameter ...$parameters) - { - /** @var Map */ - $this->parameters = Map::of(); - - foreach ($parameters as $paramater) { - $this->parameters = ($this->parameters)( - $paramater->name(), - $paramater, - ); - } - } - - /** - * @return Map - */ - public function parameters(): Map - { - return $this->parameters; - } - - #[\Override] - public function toString(): string - { - $parameters = $this->parameters->values()->map( - static fn($paramater) => $paramater->toString(), - ); - - return Str::of('; ')->join($parameters)->toString(); - } -} diff --git a/src/Header/CacheControlValue.php b/src/Header/Custom.php similarity index 53% rename from src/Header/CacheControlValue.php rename to src/Header/Custom.php index 042d712d..ae595e5b 100644 --- a/src/Header/CacheControlValue.php +++ b/src/Header/Custom.php @@ -3,9 +3,12 @@ namespace Innmind\Http\Header; +use Innmind\Http\Header; + /** * @psalm-immutable */ -interface CacheControlValue extends Value +interface Custom { + public function normalize(): Header; } diff --git a/src/Header/Date.php b/src/Header/Date.php index 28fc48ce..3973f334 100644 --- a/src/Header/Date.php +++ b/src/Header/Date.php @@ -3,22 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + TimeContinuum\Format\Http, +}; +use Innmind\TimeContinuum\{ + PointInTime, + Offset, +}; /** * @psalm-immutable */ -final class Date implements HeaderInterface +final class Date implements Custom { - private Header $header; - private DateValue $value; - - public function __construct(DateValue $date) - { - $this->header = new Header('Date', $date); - $this->value = $date; + private function __construct( + private PointInTime $point, + ) { } /** @@ -26,29 +27,25 @@ public function __construct(DateValue $date) */ public static function of(PointInTime $point): self { - return new self(new DateValue($point)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($point); } public function date(): PointInTime { - return $this->value->date(); + return $this->point; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Date', + Value::of( + $this + ->point + ->changeOffset(Offset::utc()) + ->format(Http::new()), + ), + ); } } diff --git a/src/Header/DateValue.php b/src/Header/DateValue.php deleted file mode 100644 index c6af0598..00000000 --- a/src/Header/DateValue.php +++ /dev/null @@ -1,32 +0,0 @@ -date; - } - - #[\Override] - public function toString(): string - { - return $this->date->changeOffset(Offset::utc())->format(Http::new()); - } -} diff --git a/src/Header/Expires.php b/src/Header/Expires.php index d0208fe7..d9f5f13d 100644 --- a/src/Header/Expires.php +++ b/src/Header/Expires.php @@ -3,22 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + TimeContinuum\Format\Http, +}; +use Innmind\TimeContinuum\{ + PointInTime, + Offset, +}; /** * @psalm-immutable */ -final class Expires implements HeaderInterface +final class Expires implements Custom { - private Header $header; - private DateValue $value; - - public function __construct(DateValue $date) - { - $this->header = new Header('Expires', $date); - $this->value = $date; + private function __construct( + private PointInTime $point, + ) { } /** @@ -26,29 +27,25 @@ public function __construct(DateValue $date) */ public static function of(PointInTime $point): self { - return new self(new DateValue($point)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($point); } public function date(): PointInTime { - return $this->value->date(); + return $this->point; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Expires', + Value::of( + $this + ->point + ->changeOffset(Offset::utc()) + ->format(Http::new()), + ), + ); } } diff --git a/src/Header/Header.php b/src/Header/Header.php deleted file mode 100644 index 37a5d3ed..00000000 --- a/src/Header/Header.php +++ /dev/null @@ -1,50 +0,0 @@ - */ - private Sequence $values; - - /** - * @no-named-arguments - */ - public function __construct(string $name, Value ...$values) - { - $this->name = $name; - $this->values = Sequence::of(...$values); - } - - #[\Override] - public function name(): string - { - return $this->name; - } - - #[\Override] - public function values(): Sequence - { - return $this->values; - } - - #[\Override] - public function toString(): string - { - $values = $this->values->map(static fn($value) => $value->toString()); - $values = Str::of(', ')->join($values); - - return $values->prepend("{$this->name}: ")->toString(); - } -} diff --git a/src/Header/Host.php b/src/Header/Host.php index b8fd6080..740216e1 100644 --- a/src/Header/Host.php +++ b/src/Header/Host.php @@ -3,25 +3,21 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\Header; use Innmind\Url\Authority\{ Host as UrlHost, Port, }; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Host implements HeaderInterface +final class Host implements Custom { - private Header $header; - private HostValue $value; - - public function __construct(HostValue $host) - { - $this->header = new Header('Host', $host); - $this->value = $host; + private function __construct( + private UrlHost $host, + private Port $port, + ) { } /** @@ -29,34 +25,27 @@ public function __construct(HostValue $host) */ public static function of(UrlHost $host, Port $port): self { - return new self(new HostValue($host, $port)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($host, $port); } public function host(): UrlHost { - return $this->value->host(); + return $this->host; } public function port(): Port { - return $this->value->port(); + return $this->port; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Host', + Value::of( + $this->host->toString().$this->port->format(), + ), + ); } } diff --git a/src/Header/HostValue.php b/src/Header/HostValue.php deleted file mode 100644 index a78b044d..00000000 --- a/src/Header/HostValue.php +++ /dev/null @@ -1,37 +0,0 @@ -host; - } - - public function port(): Port - { - return $this->port; - } - - #[\Override] - public function toString(): string - { - return $this->host->toString().$this->port->format(); - } -} diff --git a/src/Header/IfModifiedSince.php b/src/Header/IfModifiedSince.php index 54319614..63c0c57b 100644 --- a/src/Header/IfModifiedSince.php +++ b/src/Header/IfModifiedSince.php @@ -3,22 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + TimeContinuum\Format\Http, +}; +use Innmind\TimeContinuum\{ + PointInTime, + Offset, +}; /** * @psalm-immutable */ -final class IfModifiedSince implements HeaderInterface +final class IfModifiedSince implements Custom { - private Header $header; - private DateValue $value; - - public function __construct(DateValue $date) - { - $this->header = new Header('If-Modified-Since', $date); - $this->value = $date; + private function __construct( + private PointInTime $point, + ) { } /** @@ -26,29 +27,25 @@ public function __construct(DateValue $date) */ public static function of(PointInTime $point): self { - return new self(new DateValue($point)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($point); } public function date(): PointInTime { - return $this->value->date(); + return $this->point; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'If-Modified-Since', + Value::of( + $this + ->point + ->changeOffset(Offset::utc()) + ->format(Http::new()), + ), + ); } } diff --git a/src/Header/IfUnmodifiedSince.php b/src/Header/IfUnmodifiedSince.php index 92e6de52..1c57a8fd 100644 --- a/src/Header/IfUnmodifiedSince.php +++ b/src/Header/IfUnmodifiedSince.php @@ -3,22 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + TimeContinuum\Format\Http, +}; +use Innmind\TimeContinuum\{ + PointInTime, + Offset, +}; /** * @psalm-immutable */ -final class IfUnmodifiedSince implements HeaderInterface +final class IfUnmodifiedSince implements Custom { - private Header $header; - private DateValue $value; - - public function __construct(DateValue $date) - { - $this->header = new Header('If-Unmodified-Since', $date); - $this->value = $date; + private function __construct( + private PointInTime $point, + ) { } /** @@ -26,29 +27,25 @@ public function __construct(DateValue $date) */ public static function of(PointInTime $point): self { - return new self(new DateValue($point)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($point); } public function date(): PointInTime { - return $this->value->date(); + return $this->point; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'If-Unmodified-Since', + Value::of( + $this + ->point + ->changeOffset(Offset::utc()) + ->format(Http::new()), + ), + ); } } diff --git a/src/Header/LastModified.php b/src/Header/LastModified.php index 0f796c97..9d6abdc8 100644 --- a/src/Header/LastModified.php +++ b/src/Header/LastModified.php @@ -3,22 +3,23 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + TimeContinuum\Format\Http, +}; +use Innmind\TimeContinuum\{ + PointInTime, + Offset, +}; /** * @psalm-immutable */ -final class LastModified implements HeaderInterface +final class LastModified implements Custom { - private Header $header; - private DateValue $value; - - public function __construct(DateValue $date) - { - $this->header = new Header('Last-Modified', $date); - $this->value = $date; + private function __construct( + private PointInTime $point, + ) { } /** @@ -26,29 +27,25 @@ public function __construct(DateValue $date) */ public static function of(PointInTime $point): self { - return new self(new DateValue($point)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($point); } public function date(): PointInTime { - return $this->value->date(); + return $this->point; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Last-Modified', + Value::of( + $this + ->point + ->changeOffset(Offset::utc()) + ->format(Http::new()), + ), + ); } } diff --git a/src/Header/Link.php b/src/Header/Link.php index ab8323a6..0de9a5ae 100644 --- a/src/Header/Link.php +++ b/src/Header/Link.php @@ -3,39 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\Link\Relationship, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Link implements HeaderInterface +final class Link implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $relationships */ - public function __construct(LinkValue ...$values) - { - $this->header = new Header('Link', ...$values); + private function __construct( + private Sequence $relationships, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(Relationship ...$relationships): self { - return $this->header->values(); + return new self(Sequence::of(...$relationships)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Link', + ...$this + ->relationships + ->map(static fn($relationship) => Value::of($relationship->toString())) + ->toList(), + ); } } diff --git a/src/Header/LinkValue.php b/src/Header/Link/Relationship.php similarity index 56% rename from src/Header/LinkValue.php rename to src/Header/Link/Relationship.php index 4a5b4a47..a5bda622 100644 --- a/src/Header/LinkValue.php +++ b/src/Header/Link/Relationship.php @@ -1,66 +1,52 @@ */ - private Map $parameters; - - public function __construct( - Url $url, - ?string $rel = null, - Parameter ...$parameters, + /** + * @param non-empty-string $rel + * @param Map $parameters + */ + private function __construct( + private Url $url, + private string $rel, + private Map $parameters, ) { - $rel = $rel ?? 'related'; - /** @var Map */ - $this->parameters = Map::of(); - - if (Str::of($rel)->empty()) { - throw new DomainException('Relation can\'t be empty'); - } - - foreach ($parameters as $parameter) { - $this->parameters = ($this->parameters)( - $parameter->name(), - $parameter, - ); - } - - $this->url = $url; - $this->rel = $rel; } /** * @psalm-pure * - * @return Maybe + * @param ?non-empty-string $rel */ public static function of( Url $url, ?string $rel = null, Parameter ...$parameters, - ): Maybe { - try { - return Maybe::just(new self($url, $rel, ...$parameters)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); + ): self { + /** @var Map */ + $map = Map::of(); + + foreach ($parameters as $parameter) { + $map = ($map)( + $parameter->name(), + $parameter, + ); } + + return new self($url, $rel ?? 'related', $map); } public function url(): Url @@ -68,7 +54,7 @@ public function url(): Url return $this->url; } - public function relationship(): string + public function kind(): string { return $this->rel; } @@ -81,7 +67,6 @@ public function parameters(): Map return $this->parameters; } - #[\Override] public function toString(): string { $parameters = $this->parameters->values()->map( diff --git a/src/Header/Location.php b/src/Header/Location.php index e32ace48..c4ff2d7f 100644 --- a/src/Header/Location.php +++ b/src/Header/Location.php @@ -3,22 +3,17 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\Header; use Innmind\Url\Url; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Location implements HeaderInterface +final class Location implements Custom { - private Header $header; - private Url $url; - - public function __construct(LocationValue $location) - { - $this->header = new Header('Location', $location); - $this->url = $location->url(); + private function __construct( + private Url $location, + ) { } /** @@ -26,29 +21,20 @@ public function __construct(LocationValue $location) */ public static function of(Url $location): self { - return new self(new LocationValue($location)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($location); } public function url(): Url { - return $this->url; + return $this->location; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Location', + Value::of($this->location->toString()), + ); } } diff --git a/src/Header/LocationValue.php b/src/Header/LocationValue.php deleted file mode 100644 index 7b17a814..00000000 --- a/src/Header/LocationValue.php +++ /dev/null @@ -1,28 +0,0 @@ -url; - } - - #[\Override] - public function toString(): string - { - return $this->url->toString(); - } -} diff --git a/src/Header/Parameter.php b/src/Header/Parameter.php index 666c0405..ce9adb6f 100644 --- a/src/Header/Parameter.php +++ b/src/Header/Parameter.php @@ -3,12 +3,56 @@ namespace Innmind\Http\Header; +use Innmind\Immutable\Str; + /** * @psalm-immutable */ -interface Parameter +final class Parameter { - public function name(): string; - public function value(): string; - public function toString(): string; + private string $name; + private string $value; + + private function __construct(string $name, string $value) + { + $value = Str::of($value)->trim(); + + if ($value->matches("/[ \t]/")) { + $value = $value + ->trim('"') + ->append('"') + ->prepend('"'); + } + + $this->name = $name; + $this->value = $value->toString(); + } + + /** + * @psalm-pure + */ + public static function of(string $name, string $value): self + { + return new self($name, $value); + } + + public function name(): string + { + return $this->name; + } + + public function value(): string + { + return $this->value; + } + + public function toString(): string + { + return \sprintf( + '%s%s%s', + $this->name, + \strlen($this->value) > 0 ? '=' : '', + \strlen($this->value) > 0 ? $this->value : '', + ); + } } diff --git a/src/Header/Parameter/NullParameter.php b/src/Header/Parameter/NullParameter.php deleted file mode 100644 index cf890b3a..00000000 --- a/src/Header/Parameter/NullParameter.php +++ /dev/null @@ -1,30 +0,0 @@ -trim(); - - if ($value->matches("/[ \t]/")) { - $value = $value - ->trim('"') - ->append('"') - ->prepend('"'); - } - - $this->name = $name; - $this->value = $value->toString(); - $this->string = \sprintf( - '%s%s%s', - $this->name, - \strlen($this->value) > 0 ? '=' : '', - \strlen($this->value) > 0 ? $this->value : '', - ); - } - - #[\Override] - public function name(): string - { - return $this->name; - } - - #[\Override] - public function value(): string - { - return $this->value; - } - - #[\Override] - public function toString(): string - { - return $this->string; - } -} diff --git a/src/Header/Parameter/Quality.php b/src/Header/Parameter/Quality.php index 3e4acafa..33c6ae55 100644 --- a/src/Header/Parameter/Quality.php +++ b/src/Header/Parameter/Quality.php @@ -3,58 +3,51 @@ namespace Innmind\Http\Header\Parameter; -use Innmind\Http\{ - Header\Parameter as ParameterInterface, - Exception\DomainException -}; -use Innmind\Immutable\Maybe; +use Innmind\Http\Header\Parameter; +use Innmind\Immutable\Str; /** * @psalm-immutable */ -final class Quality implements ParameterInterface +final class Quality { - private Parameter $parameter; - - public function __construct(float $value) - { - if ($value < 0 || $value > 1) { - throw new DomainException((string) $value); - } - - $this->parameter = new Parameter('q', (string) $value); + /** + * @param int<0, 100> $percent + */ + private function __construct( + private int $percent, + ) { } /** * @psalm-pure * - * @return Maybe + * @param int<0, 100> $percent */ - public static function of(float $value): Maybe + public static function of(int $percent): self { - try { - return Maybe::just(new self($value)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } + return new self($percent); } - #[\Override] - public function name(): string - { - return $this->parameter->name(); - } - - #[\Override] - public function value(): string + /** + * @psalm-pure + */ + public static function max(): self { - return $this->parameter->value(); + return new self(100); } - #[\Override] - public function toString(): string + public function toParameter(): Parameter { - return $this->parameter->toString(); + $value = Str::of(\sprintf( + '%0.2f', + $this->percent / 100, + )); + + return Parameter::of('q', match (true) { + $value->endsWith('.00') => $value->dropEnd(3)->toString(), + $value->endsWith('0') => $value->dropEnd(1)->toString(), + default => $value->toString(), + }); } } diff --git a/src/Header/Range.php b/src/Header/Range.php index 73df1e5e..14041dfd 100644 --- a/src/Header/Range.php +++ b/src/Header/Range.php @@ -3,58 +3,102 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Exception\DomainException, +}; +use Innmind\Immutable\{ + Str, + Maybe, +}; /** * @psalm-immutable */ -final class Range implements HeaderInterface +final class Range implements Custom { - private Header $header; - private RangeValue $range; - - public function __construct(RangeValue $range) - { - $this->header = new Header('Range', $range); - $this->range = $range; + /** + * @param int<0, max> $firstPosition + * @param int<0, max> $lastPosition + */ + private function __construct( + private string $unit, + private int $firstPosition, + private int $lastPosition, + ) { } /** * @psalm-pure + * + * @throws DomainException */ public static function of( string $unit, int $firstPosition, int $lastPosition, ): self { - return new self(new RangeValue( - $unit, - $firstPosition, - $lastPosition, - )); + return self::maybe($unit, $firstPosition, $lastPosition)->match( + static fn($self) => $self, + static fn() => throw new DomainException($unit), + ); } - #[\Override] - public function name(): string + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe( + string $unit, + int $firstPosition, + int $lastPosition, + ): Maybe { + if ( + !Str::of($unit)->matches('~^\w+$~') || + $firstPosition < 0 || + $lastPosition < 0 || + $firstPosition > $lastPosition + ) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($unit, $firstPosition, $lastPosition)); + } + + public function unit(): string { - return $this->header->name(); + return $this->unit; } - #[\Override] - public function values(): Sequence + /** + * @return int<0, max> + */ + public function firstPosition(): int { - return $this->header->values(); + return $this->firstPosition; } - public function range(): RangeValue + /** + * @return int<0, max> + */ + public function lastPosition(): int { - return $this->range; + return $this->lastPosition; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Range', + Value::of(\sprintf( + '%s=%s-%s', + $this->unit, + $this->firstPosition, + $this->lastPosition, + )), + ); } } diff --git a/src/Header/RangeValue.php b/src/Header/RangeValue.php deleted file mode 100644 index e89c1dfd..00000000 --- a/src/Header/RangeValue.php +++ /dev/null @@ -1,83 +0,0 @@ -matches('~^\w+$~') || - $firstPosition < 0 || - $lastPosition < 0 || - $firstPosition > $lastPosition - ) { - throw new DomainException($unit); - } - - $this->unit = $unit; - $this->firstPosition = $firstPosition; - $this->lastPosition = $lastPosition; - } - - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of( - string $unit, - int $firstPosition, - int $lastPosition, - ): Maybe { - try { - return Maybe::just(new self($unit, $firstPosition, $lastPosition)); - } catch (DomainException $e) { - /** @var Maybe */ - return Maybe::nothing(); - } - } - - public function unit(): string - { - return $this->unit; - } - - public function firstPosition(): int - { - return $this->firstPosition; - } - - public function lastPosition(): int - { - return $this->lastPosition; - } - - #[\Override] - public function toString(): string - { - return \sprintf( - '%s=%s-%s', - $this->unit, - $this->firstPosition, - $this->lastPosition, - ); - } -} diff --git a/src/Header/Referrer.php b/src/Header/Referrer.php index 06abc1b0..207f0259 100644 --- a/src/Header/Referrer.php +++ b/src/Header/Referrer.php @@ -3,22 +3,17 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\Header; use Innmind\Url\Url; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Referrer implements HeaderInterface +final class Referrer implements Custom { - private Header $header; - private ReferrerValue $referrer; - - public function __construct(ReferrerValue $referrer) - { - $this->header = new Header('Referer', $referrer); - $this->referrer = $referrer; + private function __construct( + private Url $referrer, + ) { } /** @@ -26,29 +21,20 @@ public function __construct(ReferrerValue $referrer) */ public static function of(Url $referrer): self { - return new self(new ReferrerValue($referrer)); - } - - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence - { - return $this->header->values(); + return new self($referrer); } public function referrer(): Url { - return $this->referrer->url(); + return $this->referrer; } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Referer', + Value::of($this->referrer->toString()), + ); } } diff --git a/src/Header/ReferrerValue.php b/src/Header/ReferrerValue.php deleted file mode 100644 index 4d3dc861..00000000 --- a/src/Header/ReferrerValue.php +++ /dev/null @@ -1,28 +0,0 @@ -url; - } - - #[\Override] - public function toString(): string - { - return $this->url->toString(); - } -} diff --git a/src/Header/SetCookie.php b/src/Header/SetCookie.php index d1cfacec..fd7397f1 100644 --- a/src/Header/SetCookie.php +++ b/src/Header/SetCookie.php @@ -3,59 +3,105 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; -use Innmind\Immutable\Sequence; +use Innmind\Http\{ + Header, + Header\SetCookie\Directive, + Header\SetCookie\Domain, + Header\SetCookie\Expires, + Header\SetCookie\MaxAge, + Header\SetCookie\Path, +}; +use Innmind\Immutable\{ + Sequence, + Str, +}; /** * @psalm-immutable */ -final class SetCookie implements HeaderInterface +final class SetCookie implements Custom { - private Header $header; - /** @var Sequence */ - private Sequence $cookies; - /** - * @no-named-arguments + * @param Sequence $parameters + * @param Sequence $others */ - public function __construct(CookieValue ...$values) - { - $this->header = new Header('Set-Cookie', ...$values); - $this->cookies = Sequence::of(...$values); + private function __construct( + private Parameter $value, + private Sequence $parameters, + private Sequence $others, + ) { } /** * @no-named-arguments * @psalm-pure */ - public static function of(Parameter ...$values): self + public static function of( + string $name, + string $value, + Directive|Domain|Expires|MaxAge|Path ...$parameters, + ): self { + return new self( + Parameter::of($name, $value), + Sequence::of(...$parameters), + Sequence::of(), + ); + } + + public function and(self $cookie): self { - return new self(new CookieValue(...$values)); + return new self( + $this->value, + $this->parameters, + ($this->others)($cookie), + ); } - #[\Override] public function name(): string { - return $this->header->name(); + return $this->value->name(); } - #[\Override] - public function values(): Sequence + public function value(): string + { + return $this->value->value(); + } + + /** + * @return Sequence + */ + public function parameters(): Sequence { - return $this->header->values(); + return $this->parameters; } /** - * @return Sequence + * @return Sequence */ public function cookies(): Sequence { - return $this->cookies; + return Sequence::of($this)->append($this->others); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'Set-Cookie', + ...$this + ->cookies() + ->map(static fn($self) => Value::of( + Str::of('; ') + ->join( + Sequence::of($self->value) + ->append($self->parameters->map( + static fn($parameter) => $parameter->toParameter(), + )) + ->map(static fn($parameter) => $parameter->toString()), + ) + ->toString(), + )) + ->toList(), + ); } } diff --git a/src/Header/SetCookie/Directive.php b/src/Header/SetCookie/Directive.php new file mode 100644 index 00000000..84fa5e2f --- /dev/null +++ b/src/Header/SetCookie/Directive.php @@ -0,0 +1,27 @@ + Parameter::of('SameSite', 'Lax'), + self::strictSameSite => Parameter::of('SameSite', 'Strict'), + self::secure => Parameter::of('Secure', ''), + self::httpOnly => Parameter::of('HttpOnly', ''), + }; + } +} diff --git a/src/Header/SetCookie/Domain.php b/src/Header/SetCookie/Domain.php new file mode 100644 index 00000000..8516a9cb --- /dev/null +++ b/src/Header/SetCookie/Domain.php @@ -0,0 +1,36 @@ +host; + } + + public function toParameter(): Parameter + { + return Parameter::of('Domain', $this->host->toString()); + } +} diff --git a/src/Header/SetCookie/Expires.php b/src/Header/SetCookie/Expires.php new file mode 100644 index 00000000..9dd0710f --- /dev/null +++ b/src/Header/SetCookie/Expires.php @@ -0,0 +1,45 @@ +changeOffset(Offset::utc())); + } + + public function date(): PointInTime + { + return $this->date; + } + + public function toParameter(): Parameter + { + return Parameter::of( + 'Expires', + $this->date->format(Http::new()), + ); + } +} diff --git a/src/Header/SetCookie/MaxAge.php b/src/Header/SetCookie/MaxAge.php new file mode 100644 index 00000000..bf1af28f --- /dev/null +++ b/src/Header/SetCookie/MaxAge.php @@ -0,0 +1,51 @@ + $age + */ + private function __construct( + private ?int $age, + ) { + } + + /** + * @psalm-pure + * + * @param int<1, max> $age + */ + public static function of(int $age): self + { + return new self($age); + } + + /** + * @psalm-pure + */ + public static function expire(): self + { + return new self(null); + } + + public function toInt(): int + { + return match ($this->age) { + null => -1, + default => $this->age, + }; + } + + public function toParameter(): Parameter + { + return Parameter::of('Max-Age', (string) $this->toInt()); + } +} diff --git a/src/Header/SetCookie/Path.php b/src/Header/SetCookie/Path.php new file mode 100644 index 00000000..b5eabb93 --- /dev/null +++ b/src/Header/SetCookie/Path.php @@ -0,0 +1,36 @@ +path; + } + + public function toParameter(): Parameter + { + return Parameter::of('Path', $this->path->toString()); + } +} diff --git a/src/Header/Value.php b/src/Header/Value.php index 5644c683..13f98323 100644 --- a/src/Header/Value.php +++ b/src/Header/Value.php @@ -6,7 +6,23 @@ /** * @psalm-immutable */ -interface Value +final class Value { - public function toString(): string; + private function __construct( + private string $value, + ) { + } + + /** + * @psalm-pure + */ + public static function of(string $value): self + { + return new self($value); + } + + public function toString(): string + { + return $this->value; + } } diff --git a/src/Header/Value/Value.php b/src/Header/Value/Value.php deleted file mode 100644 index 1b51d280..00000000 --- a/src/Header/Value/Value.php +++ /dev/null @@ -1,23 +0,0 @@ -value; - } -} diff --git a/src/Header/WWWAuthenticate.php b/src/Header/WWWAuthenticate.php index d459ac79..c688cd2f 100644 --- a/src/Header/WWWAuthenticate.php +++ b/src/Header/WWWAuthenticate.php @@ -3,39 +3,43 @@ namespace Innmind\Http\Header; -use Innmind\Http\Header as HeaderInterface; +use Innmind\Http\{ + Header, + Header\WWWAuthenticate\Challenge, +}; use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class WWWAuthenticate implements HeaderInterface +final class WWWAuthenticate implements Custom { - private Header $header; - /** - * @no-named-arguments + * @param Sequence $challenges */ - public function __construct(WWWAuthenticateValue ...$values) - { - $this->header = new Header('WWW-Authenticate', ...$values); + private function __construct( + private Sequence $challenges, + ) { } - #[\Override] - public function name(): string - { - return $this->header->name(); - } - - #[\Override] - public function values(): Sequence + /** + * @psalm-pure + * @no-named-arguments + */ + public static function of(Challenge ...$challenges): self { - return $this->header->values(); + return new self(Sequence::of(...$challenges)); } #[\Override] - public function toString(): string + public function normalize(): Header { - return $this->header->toString(); + return Header::of( + 'WWW-Authenticate', + ...$this + ->challenges + ->map(static fn($challenge) => Value::of($challenge->toString())) + ->toList(), + ); } } diff --git a/src/Header/WWWAuthenticate/Challenge.php b/src/Header/WWWAuthenticate/Challenge.php new file mode 100644 index 00000000..991dd59e --- /dev/null +++ b/src/Header/WWWAuthenticate/Challenge.php @@ -0,0 +1,58 @@ + + */ + public static function maybe(string $scheme, string $realm): Maybe + { + $scheme = Str::of($scheme); + + if (!$scheme->matches('~^\w+$~')) { + /** @var Maybe */ + return Maybe::nothing(); + } + + return Maybe::just(new self($scheme->toString(), $realm)); + } + + public function scheme(): string + { + return $this->scheme; + } + + public function realm(): string + { + return $this->realm; + } + + public function toString(): string + { + return Str::of($this->scheme) + ->append(' ') + ->append((Parameter::of('realm', $this->realm))->toString()) + ->trim() + ->toString(); + } +} diff --git a/src/Header/WWWAuthenticateValue.php b/src/Header/WWWAuthenticateValue.php deleted file mode 100644 index ab233813..00000000 --- a/src/Header/WWWAuthenticateValue.php +++ /dev/null @@ -1,48 +0,0 @@ -matches('~^\w+$~')) { - throw new DomainException($scheme->toString()); - } - - $this->scheme = $scheme->toString(); - $this->realm = $realm; - } - - public function scheme(): string - { - return $this->scheme; - } - - public function realm(): string - { - return $this->realm; - } - - #[\Override] - public function toString(): string - { - return Str::of($this->scheme) - ->append(' ') - ->append((new Parameter\Parameter('realm', $this->realm))->toString()) - ->trim() - ->toString(); - } -} diff --git a/src/Headers.php b/src/Headers.php index e5407976..fe202991 100644 --- a/src/Headers.php +++ b/src/Headers.php @@ -7,6 +7,7 @@ Str, SideEffect, Maybe, + Map, Sequence, Predicate\Instance, }; @@ -17,33 +18,31 @@ final class Headers implements \Countable { /** - * @param Sequence
$headers + * @param Map $headers */ private function __construct( - private Sequence $headers, + private Map $headers, ) { } - public function __invoke(Header $header): self + public function __invoke(Header|Header\Custom $header): self { - $name = self::normalize($header->name()); + $name = self::normalize(match (true) { + $header instanceof Header => $header->name(), + default => $header->normalize()->name(), + }); - return new self( - $this - ->headers - ->filter(static fn($header) => self::normalize($header->name()) !== $name) - ->add($header), - ); + return new self(($this->headers)($name, $header)); } /** * @no-named-arguments * @psalm-pure */ - public static function of(Header ...$headers): self + public static function of(Header|Header\Custom ...$headers): self { return Sequence::of(...$headers)->reduce( - new self(Sequence::of()), + new self(Map::of()), static fn(self $headers, $header) => ($headers)($header), ); } @@ -57,11 +56,17 @@ public function get(string $name): Maybe { $normalized = self::normalize($name); - return $this->headers->find(static fn($header) => self::normalize($header->name()) === $normalized); + return $this + ->headers + ->get($normalized) + ->map(static fn($header) => match (true) { + $header instanceof Header => $header, + default => $header->normalize(), + }); } /** - * @template T of Header + * @template T of Header\Custom * * @param class-string $type * @@ -71,6 +76,7 @@ public function find(string $type): Maybe { return $this ->headers + ->values() ->keep(Instance::of($type)) ->first(); } @@ -93,15 +99,18 @@ public function contains(string $name): bool */ public function filter(callable $filter): self { - return new self($this->headers->filter($filter)); + return new self($this->headers->filter(static fn($_, $header) => match (true) { + $header instanceof Header => $filter($header), + default => $filter($header->normalize()), + })); } /** - * @param callable(Header): void $function + * @param callable(Header|Header\Custom): void $function */ public function foreach(callable $function): SideEffect { - return $this->headers->foreach($function); + return $this->headers->values()->foreach($function); } /** @@ -114,7 +123,7 @@ public function foreach(callable $function): SideEffect */ public function reduce($carry, callable $reducer) { - return $this->headers->reduce($carry, $reducer); + return $this->all()->reduce($carry, $reducer); } #[\Override] @@ -128,7 +137,10 @@ public function count(): int */ public function all(): Sequence { - return $this->headers; + return $this->headers->values()->map(static fn($header) => match (true) { + $header instanceof Header => $header, + default => $header->normalize(), + }); } /** diff --git a/src/ResponseSender.php b/src/ResponseSender.php index 3a1dbb83..d7ae0fb7 100644 --- a/src/ResponseSender.php +++ b/src/ResponseSender.php @@ -6,13 +6,14 @@ use Innmind\Http\{ Header\Date, Header\SetCookie, - Header\CookieValue, - Header\Parameter, - TimeContinuum\Format\Http, Exception\LogicException, }; -use Innmind\TimeContinuum\Clock; +use Innmind\TimeContinuum\{ + Clock, + Format, +}; use Innmind\Immutable\{ + Map, Attempt, SideEffect, }; @@ -50,13 +51,17 @@ public function __invoke(Response $response): Attempt fn() => ($headers)(Date::of($this->clock->now())), ); - $_ = $headers->foreach(function(Header $header): void { + $_ = $headers->foreach(function($header): void { if ($header instanceof SetCookie) { $this->sendCookie($header); return; } + if ($header instanceof Header\Custom) { + $header = $header->normalize(); + } + \header($header->toString(), false); }); @@ -77,80 +82,41 @@ public function __invoke(Response $response): Attempt private function sendCookie(SetCookie $cookie): void { - $_ = $cookie->cookies()->foreach(static function(CookieValue $value): void { - $parameters = $value->parameters()->values()->reduce( - [], - static function(array $parameters, Parameter $parameter): array { - switch ($parameter->name()) { - case 'Domain': - $parameters['domain'] = $parameter->value(); - break; - - case 'Expires': - /** @psalm-suppress PossiblyFalseReference Expires object uses a valid date */ - $timestamp = \DateTimeImmutable::createFromFormat( - Http::new()->toString(), - \substr($parameter->value(), 1, -1), // remove double quotes - )->getTimestamp(); - // MaxAge has precedence - /** @psalm-suppress MixedAssignment */ - $parameters['expire'] = match ($parameters['expire'] ?? 0) { - 0 => $timestamp, - default => $parameters['expire'] ?? 0, - }; - break; - - case 'Max-Age': - $parameters['expire'] = (int) $parameter->value(); - break; - - case 'HttpOnly': - $parameters['httponly'] = true; - break; - - case 'Path': - $parameters['path'] = $parameter->value(); - break; - - case 'Secure': - $parameters['secure'] = true; - break; - - case 'SameSite': - $parameters['samesite'] = $parameter->value(); - break; - - default: - $parameters['key'] = $parameter->name(); - $parameters['value'] = $parameter->value(); - break; - } - - return $parameters; + $_ = $cookie->cookies()->foreach(static function(SetCookie $cookie): void { + $parameters = $cookie->parameters()->map( + static fn($parameter) => match (true) { + $parameter === SetCookie\Directive::httpOnly => ['httponly', true], + $parameter === SetCookie\Directive::secure => ['secure', true], + $parameter === SetCookie\Directive::laxSameSite => ['samesite', 'Lax'], + $parameter === SetCookie\Directive::strictSameSite => ['samesite', 'Strict'], + $parameter instanceof SetCookie\Domain => ['domain', $parameter->host()->toString()], + $parameter instanceof SetCookie\Expires => [ + 'expires', + (int) $parameter->date()->format(Format::of('U')), + ], + $parameter instanceof SetCookie\MaxAge => ['max-age', $parameter->toInt()], + $parameter instanceof SetCookie\Path => ['path', $parameter->path()->toString()], }, ); - $options = [ - 'path' => $parameters['path'] ?? '', - 'domain' => $parameters['domain'] ?? '', - 'secure' => $parameters['secure'] ?? false, - 'httponly' => $parameters['httponly'] ?? false, - ]; + $parameters = Map::of(...$parameters->toList()); + // Max age has precedence over expire + $parameters = $parameters->get('max-age')->match( + static fn($value) => ($parameters)('expires', $value), + static fn() => $parameters, + ); + $options = $parameters->reduce( + [], + static function(array $options, $key, $value) { + $options[$key] = $value; - if (isset($parameters['samesite'])) { - /** @psalm-suppress MixedAssignment */ - $options['samesite'] = $parameters['samesite']; - } + return $options; + }, + ); - /** - * @psalm-suppress MixedArgument - * @psalm-suppress InvalidArgument - * @psalm-suppress InvalidCast - */ \setcookie( - $parameters['key'] ?? '', - $parameters['value'] ?? '', - $parameters['expire'] ?? 0, + $cookie->name(), + $cookie->value(), $options, ); }); diff --git a/tests/Content/MultipartTest.php b/tests/Content/MultipartTest.php index 86030738..8a1f3cfe 100644 --- a/tests/Content/MultipartTest.php +++ b/tests/Content/MultipartTest.php @@ -135,7 +135,7 @@ public function testFunctional() \curl_setopt($handle, \CURLOPT_HEADER, false); \curl_setopt($handle, \CURLOPT_RETURNTRANSFER, true); \curl_setopt($handle, \CURLOPT_HTTPHEADER, [ - 'Content-Type: multipart/form-data; '.$boundary->toString(), + 'Content-Type: multipart/form-data; '.$boundary->toParameter()->toString(), ]); \curl_setopt($handle, \CURLOPT_CUSTOMREQUEST, 'POST'); \curl_setopt($handle, \CURLOPT_UPLOAD, true); diff --git a/tests/Factory/Header/AcceptCharsetFactoryTest.php b/tests/Factory/Header/AcceptCharsetFactoryTest.php index 3c210684..7dbb5c68 100644 --- a/tests/Factory/Header/AcceptCharsetFactoryTest.php +++ b/tests/Factory/Header/AcceptCharsetFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(AcceptCharset::class, $h); $this->assertSame( 'Accept-Charset: iso-8859-5;q=1, unicode-1-1;q=0.8', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/AcceptEncodingFactoryTest.php b/tests/Factory/Header/AcceptEncodingFactoryTest.php index 8918f213..e29f74a7 100644 --- a/tests/Factory/Header/AcceptEncodingFactoryTest.php +++ b/tests/Factory/Header/AcceptEncodingFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(AcceptEncoding::class, $h); $this->assertSame( 'Accept-Encoding: gzip;q=1, identity;q=0.5, *;q=0', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/AcceptFactoryTest.php b/tests/Factory/Header/AcceptFactoryTest.php index e40e8ea8..f152999a 100644 --- a/tests/Factory/Header/AcceptFactoryTest.php +++ b/tests/Factory/Header/AcceptFactoryTest.php @@ -24,7 +24,7 @@ public function testMake() ); $this->assertInstanceOf(Accept::class, $h); - $this->assertSame('Accept: audio/*;q=0.2;level=1, audio/basic', $h->toString()); + $this->assertSame('Accept: audio/*;q=0.2;level=1, audio/basic', $h->normalize()->toString()); } public function testReturnNothingWhenNotValid() diff --git a/tests/Factory/Header/AcceptLanguageFactoryTest.php b/tests/Factory/Header/AcceptLanguageFactoryTest.php index a106aff6..5471d429 100644 --- a/tests/Factory/Header/AcceptLanguageFactoryTest.php +++ b/tests/Factory/Header/AcceptLanguageFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(AcceptLanguage::class, $h); $this->assertSame( 'Accept-Language: da;q=1, en-gb;q=0.8, en;q=0.7', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/AcceptRangesFactoryTest.php b/tests/Factory/Header/AcceptRangesFactoryTest.php index 403d84b2..1c0b5042 100644 --- a/tests/Factory/Header/AcceptRangesFactoryTest.php +++ b/tests/Factory/Header/AcceptRangesFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(AcceptRanges::class, $header); - $this->assertSame('Accept-Ranges: bytes', $header->toString()); + $this->assertSame('Accept-Ranges: bytes', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/AgeFactoryTest.php b/tests/Factory/Header/AgeFactoryTest.php index 400f7d51..24df4ce6 100644 --- a/tests/Factory/Header/AgeFactoryTest.php +++ b/tests/Factory/Header/AgeFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(Age::class, $header); - $this->assertSame('Age: 42', $header->toString()); + $this->assertSame('Age: 42', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/AllowFactoryTest.php b/tests/Factory/Header/AllowFactoryTest.php index 748cbd7f..a96f5029 100644 --- a/tests/Factory/Header/AllowFactoryTest.php +++ b/tests/Factory/Header/AllowFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(Allow::class, $header); - $this->assertSame('Allow: GET, POST', $header->toString()); + $this->assertSame('Allow: GET, POST', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/AuthorizationFactoryTest.php b/tests/Factory/Header/AuthorizationFactoryTest.php index 08327624..a08acf0d 100644 --- a/tests/Factory/Header/AuthorizationFactoryTest.php +++ b/tests/Factory/Header/AuthorizationFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(Authorization::class, $h); $this->assertSame( 'Authorization: Basic realm="WallyWorld"', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/CacheControlFactoryTest.php b/tests/Factory/Header/CacheControlFactoryTest.php index 2d6ffb35..dfbb3d5f 100644 --- a/tests/Factory/Header/CacheControlFactoryTest.php +++ b/tests/Factory/Header/CacheControlFactoryTest.php @@ -25,7 +25,7 @@ public function testMake() $this->assertInstanceOf(CacheControl::class, $h); $this->assertSame( 'Cache-Control: no-cache="field", no-store, max-age=42, max-stale=42, min-fresh=42, no-transform, only-if-cached, public, private="field", must-revalidate, proxy-revalidate, s-maxage=42, immutable', - $h->toString(), + $h->normalize()->toString(), ); } } diff --git a/tests/Factory/Header/ContentEncodingFactoryTest.php b/tests/Factory/Header/ContentEncodingFactoryTest.php index 6d18fffe..16082d6c 100644 --- a/tests/Factory/Header/ContentEncodingFactoryTest.php +++ b/tests/Factory/Header/ContentEncodingFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(ContentEncoding::class, $header); - $this->assertSame('Content-Encoding: x-gzip', $header->toString()); + $this->assertSame('Content-Encoding: x-gzip', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/ContentLanguageFactoryTest.php b/tests/Factory/Header/ContentLanguageFactoryTest.php index 70c72223..cd0f0e27 100644 --- a/tests/Factory/Header/ContentLanguageFactoryTest.php +++ b/tests/Factory/Header/ContentLanguageFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(ContentLanguage::class, $header); - $this->assertSame('Content-Language: fr-FR, fr-CA', $header->toString()); + $this->assertSame('Content-Language: fr-FR, fr-CA', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/ContentLengthFactoryTest.php b/tests/Factory/Header/ContentLengthFactoryTest.php index 659f0f5e..bae50458 100644 --- a/tests/Factory/Header/ContentLengthFactoryTest.php +++ b/tests/Factory/Header/ContentLengthFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(ContentLength::class, $header); - $this->assertSame('Content-Length: 42', $header->toString()); + $this->assertSame('Content-Length: 42', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/ContentLocationFactoryTest.php b/tests/Factory/Header/ContentLocationFactoryTest.php index d4a36b66..154f3d0d 100644 --- a/tests/Factory/Header/ContentLocationFactoryTest.php +++ b/tests/Factory/Header/ContentLocationFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(ContentLocation::class, $header); - $this->assertSame('Content-Location: http://example.com/', $header->toString()); + $this->assertSame('Content-Location: http://example.com/', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/ContentRangeFactoryTest.php b/tests/Factory/Header/ContentRangeFactoryTest.php index d8a1e01d..8a4b86b0 100644 --- a/tests/Factory/Header/ContentRangeFactoryTest.php +++ b/tests/Factory/Header/ContentRangeFactoryTest.php @@ -22,7 +22,7 @@ public function testMakeWithoutLength() ); $this->assertInstanceOf(ContentRange::class, $header); - $this->assertSame('Content-Range: bytes 0-42/*', $header->toString()); + $this->assertSame('Content-Range: bytes 0-42/*', $header->normalize()->toString()); } public function testMakeWithLength() @@ -33,7 +33,7 @@ public function testMakeWithLength() ); $this->assertInstanceOf(ContentRange::class, $header); - $this->assertSame('Content-Range: bytes 0-42/66', $header->toString()); + $this->assertSame('Content-Range: bytes 0-42/66', $header->normalize()->toString()); } public function testReturnNothingWhenNotValid() diff --git a/tests/Factory/Header/ContentTypeFactoryTest.php b/tests/Factory/Header/ContentTypeFactoryTest.php index ccf6be5b..b250e033 100644 --- a/tests/Factory/Header/ContentTypeFactoryTest.php +++ b/tests/Factory/Header/ContentTypeFactoryTest.php @@ -22,7 +22,7 @@ public function testMakeWithoutParameters() ); $this->assertInstanceOf(ContentType::class, $header); - $this->assertSame('Content-Type: image/gif', $header->toString()); + $this->assertSame('Content-Type: image/gif', $header->normalize()->toString()); } public function testMakeWithParameters() @@ -33,7 +33,7 @@ public function testMakeWithParameters() ); $this->assertInstanceOf(ContentType::class, $header); - $this->assertSame('Content-Type: image/gif;foo=bar;q=0.5', $header->toString()); + $this->assertSame('Content-Type: image/gif;foo=bar;q=0.5', $header->normalize()->toString()); } public function testReturnNothingWhenNotValid() @@ -57,7 +57,7 @@ public function testFormEncoded() $this->assertInstanceOf(ContentType::class, $header); $this->assertSame( 'Content-Type: application/x-www-form-urlencoded', - $header->toString(), + $header->normalize()->toString(), ); } } diff --git a/tests/Factory/Header/CookieFactoryTest.php b/tests/Factory/Header/CookieFactoryTest.php index f25e1b65..e04c9ccc 100644 --- a/tests/Factory/Header/CookieFactoryTest.php +++ b/tests/Factory/Header/CookieFactoryTest.php @@ -21,10 +21,10 @@ public function testMake() ); $this->assertInstanceOf(Cookie::class, $header); - $this->assertSame('Cookie: foo=bar; bar=baz; baz=foo', $header->toString()); + $this->assertSame('Cookie: foo=bar; bar=baz; baz=foo', $header->normalize()->toString()); $this->assertSame( 'Cookie: ', - Factory::new(Clock::live())(Str::of('Cookie'), Str::of(''))->toString(), + Factory::new(Clock::live())(Str::of('Cookie'), Str::of(''))->normalize()->toString(), ); } } diff --git a/tests/Factory/Header/DateFactoryTest.php b/tests/Factory/Header/DateFactoryTest.php index a70f3dd7..200e5d3c 100644 --- a/tests/Factory/Header/DateFactoryTest.php +++ b/tests/Factory/Header/DateFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(Date::class, $h); $this->assertSame( 'Date: Tue, 15 Nov 1994 08:12:31 GMT', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/ExpiresFactoryTest.php b/tests/Factory/Header/ExpiresFactoryTest.php index 124235fb..43b0fabb 100644 --- a/tests/Factory/Header/ExpiresFactoryTest.php +++ b/tests/Factory/Header/ExpiresFactoryTest.php @@ -24,7 +24,7 @@ public function testMake() $this->assertInstanceOf(Expires::class, $header); $this->assertSame( 'Expires: Tue, 15 Nov 1994 08:12:31 GMT', - $header->toString(), + $header->normalize()->toString(), ); } diff --git a/tests/Factory/Header/HostFactoryTest.php b/tests/Factory/Header/HostFactoryTest.php index 260c7d11..a968c1c7 100644 --- a/tests/Factory/Header/HostFactoryTest.php +++ b/tests/Factory/Header/HostFactoryTest.php @@ -28,7 +28,7 @@ public function testMake(string $host) $this->assertInstanceOf(Host::class, $h); $this->assertSame( 'Host: '.$host, - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/IfModifiedSinceFactoryTest.php b/tests/Factory/Header/IfModifiedSinceFactoryTest.php index 10ab9210..edd21113 100644 --- a/tests/Factory/Header/IfModifiedSinceFactoryTest.php +++ b/tests/Factory/Header/IfModifiedSinceFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(IfModifiedSince::class, $h); $this->assertSame( 'If-Modified-Since: Tue, 15 Nov 1994 08:12:31 GMT', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/IfUnmodifiedSinceFactoryTest.php b/tests/Factory/Header/IfUnmodifiedSinceFactoryTest.php index 6af5d969..bebc669c 100644 --- a/tests/Factory/Header/IfUnmodifiedSinceFactoryTest.php +++ b/tests/Factory/Header/IfUnmodifiedSinceFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(IfUnmodifiedSince::class, $h); $this->assertSame( 'If-Unmodified-Since: Tue, 15 Nov 1994 08:12:31 GMT', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/LastModifiedFactoryTest.php b/tests/Factory/Header/LastModifiedFactoryTest.php index cf477589..55941773 100644 --- a/tests/Factory/Header/LastModifiedFactoryTest.php +++ b/tests/Factory/Header/LastModifiedFactoryTest.php @@ -24,7 +24,7 @@ public function testMake() $this->assertInstanceOf(LastModified::class, $header); $this->assertSame( 'Last-Modified: Tue, 15 Nov 1994 08:12:31 GMT', - $header->toString(), + $header->normalize()->toString(), ); } diff --git a/tests/Factory/Header/LinkFactoryTest.php b/tests/Factory/Header/LinkFactoryTest.php index 45a7df04..13e25fc9 100644 --- a/tests/Factory/Header/LinkFactoryTest.php +++ b/tests/Factory/Header/LinkFactoryTest.php @@ -24,7 +24,7 @@ public function testMake() $this->assertInstanceOf(Link::class, $header); $this->assertSame( 'Link: ; rel="next";title=foo;bar=baz, ; rel="related"', - $header->toString(), + $header->normalize()->toString(), ); } @@ -38,7 +38,7 @@ public function testMakeWithComplexParameterValue() $this->assertInstanceOf(Link::class, $header); $this->assertSame( 'Link: ; rel="next";title=!#$%&\'()*+-./0123456789:<=>?@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ[]^_`{|}~', - $header->toString(), + $header->normalize()->toString(), ); } diff --git a/tests/Factory/Header/LocationFactoryTest.php b/tests/Factory/Header/LocationFactoryTest.php index e212cae4..26dcc120 100644 --- a/tests/Factory/Header/LocationFactoryTest.php +++ b/tests/Factory/Header/LocationFactoryTest.php @@ -21,6 +21,6 @@ public function testMake() ); $this->assertInstanceOf(Location::class, $header); - $this->assertSame('Location: http://example.com/', $header->toString()); + $this->assertSame('Location: http://example.com/', $header->normalize()->toString()); } } diff --git a/tests/Factory/Header/RangeFactoryTest.php b/tests/Factory/Header/RangeFactoryTest.php index eae0c642..e62838af 100644 --- a/tests/Factory/Header/RangeFactoryTest.php +++ b/tests/Factory/Header/RangeFactoryTest.php @@ -26,7 +26,7 @@ public function testMake() $this->assertInstanceOf(Range::class, $h); $this->assertSame( 'Range: bytes=0-42', - $h->toString(), + $h->normalize()->toString(), ); } diff --git a/tests/Factory/Header/ReferrerFactoryTest.php b/tests/Factory/Header/ReferrerFactoryTest.php index 66e493c6..3a242f32 100644 --- a/tests/Factory/Header/ReferrerFactoryTest.php +++ b/tests/Factory/Header/ReferrerFactoryTest.php @@ -25,7 +25,7 @@ public function testMake() $this->assertInstanceOf(Referrer::class, $h); $this->assertSame( 'Referer: http://www.w3.org/hypertext/DataSources/Overview.html', - $h->toString(), + $h->normalize()->toString(), ); } } diff --git a/tests/Header/AcceptCharsetTest.php b/tests/Header/AcceptCharsetTest.php index c37ef114..7a01144b 100644 --- a/tests/Header/AcceptCharsetTest.php +++ b/tests/Header/AcceptCharsetTest.php @@ -5,8 +5,8 @@ use Innmind\Http\{ Header\AcceptCharset, + Header\Accept\Charset, Header, - Header\AcceptCharsetValue, Header\Parameter\Quality }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -15,18 +15,19 @@ class AcceptCharsetTest extends TestCase { public function testInterface() { - $h = new AcceptCharset( - $v = new AcceptCharsetValue('unicode-1-1', new Quality(0.8)), - ); + $h = Charset::maybe('unicode-1-1', Quality::of(80)) + ->map(AcceptCharset::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Accept-Charset', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Accept-Charset: unicode-1-1;q=0.8', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Accept-Charset: unicode-1-1;q=0.8', $h->normalize()->toString()); } public function testWithoutValues() { - $this->assertSame('Accept-Charset: ', (new AcceptCharset)->toString()); + $this->assertSame('Accept-Charset: ', AcceptCharset::of()->normalize()->toString()); } } diff --git a/tests/Header/AcceptCharsetValueTest.php b/tests/Header/AcceptCharsetValueTest.php index 7ee18e10..7690f5de 100644 --- a/tests/Header/AcceptCharsetValueTest.php +++ b/tests/Header/AcceptCharsetValueTest.php @@ -3,11 +3,9 @@ namespace Tests\Innmind\Http\Header; -use Innmind\Http\{ - Header\AcceptCharsetValue, - Header\Value, - Header\Parameter\Quality, - Exception\DomainException, +use Innmind\Http\Header\{ + Accept\Charset, + Parameter\Quality, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -16,34 +14,59 @@ class AcceptCharsetValueTest extends TestCase { public function testInterface() { - $a = new AcceptCharsetValue('unicode-1-1', $q = new Quality(0.8)); + $a = Charset::maybe('unicode-1-1', $q = Quality::of(80))->match( + static fn($charset) => $charset, + static fn() => null, + ); - $this->assertInstanceOf(Value::class, $a); + $this->assertInstanceOf(Charset::class, $a); $this->assertSame($q, $a->quality()); $this->assertSame('unicode-1-1;q=0.8', $a->toString()); - new AcceptCharsetValue('iso-8859-5', new Quality(1)); - new AcceptCharsetValue('Shift_JIS', new Quality(1)); - new AcceptCharsetValue('ISO_8859-9:1989', new Quality(1)); - new AcceptCharsetValue('NF_Z_62-010_(1973)', new Quality(1)); - new AcceptCharsetValue('*', new Quality(1)); + Charset::maybe('iso-8859-5', Quality::max())->match( + static fn($charset) => $charset, + static fn() => throw new \Exception, + ); + Charset::maybe('Shift_JIS', Quality::max())->match( + static fn($charset) => $charset, + static fn() => throw new \Exception, + ); + Charset::maybe('ISO_8859-9:1989', Quality::max())->match( + static fn($charset) => $charset, + static fn() => throw new \Exception, + ); + Charset::maybe('NF_Z_62-010_(1973)', Quality::max())->match( + static fn($charset) => $charset, + static fn() => throw new \Exception, + ); + Charset::maybe('*', Quality::max())->match( + static fn($charset) => $charset, + static fn() => throw new \Exception, + ); } public function testDefaultQuality() { $this->assertSame( '1', - (new AcceptCharsetValue('*'))->quality()->value(), + Charset::maybe('*') + ->match( + static fn($charset) => $charset, + static fn() => null, + ) + ?->quality() + ?->toParameter() + ?->value(), ); } #[DataProvider('invalids')] - public function testThrowWhenInvalidAcceptCharsetValue($value) + public function testReturnNothingWhenInvalidAcceptCharsetValue($value) { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new AcceptCharsetValue($value, new Quality(1)); + $this->assertNull(Charset::maybe($value, Quality::max())->match( + static fn($charset) => $charset, + static fn() => null, + )); } public static function invalids(): array diff --git a/tests/Header/AcceptEncodingTest.php b/tests/Header/AcceptEncodingTest.php index b93cec7a..9a9c183a 100644 --- a/tests/Header/AcceptEncodingTest.php +++ b/tests/Header/AcceptEncodingTest.php @@ -6,7 +6,7 @@ use Innmind\Http\{ Header\AcceptEncoding, Header, - Header\AcceptEncodingValue, + Header\Accept\Encoding, Header\Parameter\Quality }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -15,18 +15,19 @@ class AcceptEncodingTest extends TestCase { public function testInterface() { - $h = new AcceptEncoding( - $v = new AcceptEncodingValue('compress', new Quality(1)), - ); + $h = Encoding::maybe('compress', Quality::max()) + ->map(AcceptEncoding::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Accept-Encoding', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Accept-Encoding: compress;q=1', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Accept-Encoding: compress;q=1', $h->normalize()->toString()); } public function testWithoutValues() { - $this->assertSame('Accept-Encoding: ', (new AcceptEncoding)->toString()); + $this->assertSame('Accept-Encoding: ', AcceptEncoding::of()->normalize()->toString()); } } diff --git a/tests/Header/AcceptEncodingValueTest.php b/tests/Header/AcceptEncodingValueTest.php index 546a4ae7..37f7a22f 100644 --- a/tests/Header/AcceptEncodingValueTest.php +++ b/tests/Header/AcceptEncodingValueTest.php @@ -4,10 +4,8 @@ namespace Tests\Innmind\Http\Header; use Innmind\Http\{ - Header\AcceptEncodingValue, - Header\Value, + Header\Accept\Encoding, Header\Parameter\Quality, - Exception\DomainException, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -16,33 +14,55 @@ class AcceptEncodingValueTest extends TestCase { public function testInterface() { - $a = new AcceptEncodingValue('compress', $q = new Quality(1)); + $a = Encoding::maybe('compress', $q = Quality::max())->match( + static fn($encoding) => $encoding, + static fn() => null, + ); - $this->assertInstanceOf(Value::class, $a); + $this->assertInstanceOf(Encoding::class, $a); $this->assertSame($q, $a->quality()); $this->assertSame('compress;q=1', $a->toString()); - new AcceptEncodingValue('*', new Quality(1)); - new AcceptEncodingValue('compress', new Quality(0.5)); - new AcceptEncodingValue('identity', new Quality(0.5)); - new AcceptEncodingValue('*', new Quality(0)); + Encoding::maybe('*', Quality::max())->match( + static fn($encoding) => $encoding, + static fn() => throw new \Exception, + ); + Encoding::maybe('compress', Quality::of(50))->match( + static fn($encoding) => $encoding, + static fn() => throw new \Exception, + ); + Encoding::maybe('identity', Quality::of(50))->match( + static fn($encoding) => $encoding, + static fn() => throw new \Exception, + ); + Encoding::maybe('*', Quality::of(0))->match( + static fn($encoding) => $encoding, + static fn() => throw new \Exception, + ); } public function testDefaultQuality() { $this->assertSame( '1', - (new AcceptEncodingValue('*'))->quality()->value(), + Encoding::maybe('*') + ->match( + static fn($encoding) => $encoding, + static fn() => null, + ) + ?->quality() + ?->toParameter() + ?->value(), ); } #[DataProvider('invalids')] - public function testThrowWhenInvalidAcceptEncodingValue($value) + public function testReturnNothingWhenInvalidAcceptEncodingValue($value) { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new AcceptEncodingValue($value, new Quality(1)); + $this->assertNull(Encoding::maybe($value, Quality::max())->match( + static fn($encoding) => $encoding, + static fn() => null, + )); } public static function invalids(): array diff --git a/tests/Header/AcceptLanguageTest.php b/tests/Header/AcceptLanguageTest.php index e9d5e826..a5dec0e0 100644 --- a/tests/Header/AcceptLanguageTest.php +++ b/tests/Header/AcceptLanguageTest.php @@ -6,8 +6,8 @@ use Innmind\Http\{ Header\AcceptLanguage, Header, - Header\AcceptLanguageValue, - Header\Parameter\Quality + Header\Accept\Language, + Header\Parameter\Quality, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -15,18 +15,19 @@ class AcceptLanguageTest extends TestCase { public function testInterface() { - $h = new AcceptLanguage( - $v = new AcceptLanguageValue('fr', new Quality(0.8)), - ); + $h = Language::maybe('fr', Quality::of(80)) + ->map(AcceptLanguage::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Accept-Language', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Accept-Language: fr;q=0.8', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Accept-Language: fr;q=0.8', $h->normalize()->toString()); } public function testWithoutValues() { - $this->assertSame('Accept-Language: ', (new AcceptLanguage)->toString()); + $this->assertSame('Accept-Language: ', AcceptLanguage::of()->normalize()->toString()); } } diff --git a/tests/Header/AcceptLanguageValueTest.php b/tests/Header/AcceptLanguageValueTest.php index 5b3d3785..b5205805 100644 --- a/tests/Header/AcceptLanguageValueTest.php +++ b/tests/Header/AcceptLanguageValueTest.php @@ -3,11 +3,9 @@ namespace Tests\Innmind\Http\Header; -use Innmind\Http\{ - Header\AcceptLanguageValue, - Header\Value, - Header\Parameter\Quality, - Exception\DomainException, +use Innmind\Http\Header\{ + Accept\Language, + Parameter\Quality, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -16,33 +14,55 @@ class AcceptLanguageValueTest extends TestCase { public function testInterface() { - $a = new AcceptLanguageValue('en-gb', $q = new Quality(0.8)); + $a = Language::maybe('en-gb', $q = Quality::of(80))->match( + static fn($language) => $language, + static fn() => null, + ); - $this->assertInstanceOf(Value::class, $a); + $this->assertInstanceOf(Language::class, $a); $this->assertSame($q, $a->quality()); $this->assertSame('en-gb;q=0.8', $a->toString()); - new AcceptLanguageValue('fr', new Quality(1)); - new AcceptLanguageValue('fr-FR', new Quality(1)); - new AcceptLanguageValue('sgn-CH-DE', new Quality(1)); - new AcceptLanguageValue('*', new Quality(1)); + Language::maybe('fr', Quality::max())->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); + Language::maybe('fr-FR', Quality::max())->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); + Language::maybe('sgn-CH-DE', Quality::max())->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); + Language::maybe('*', Quality::max())->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); } public function testDefaultQuality() { $this->assertSame( '1', - (new AcceptLanguageValue('fr'))->quality()->value(), + Language::maybe('fr') + ->match( + static fn($quality) => $quality, + static fn() => null, + ) + ?->quality() + ?->toParameter() + ?->value(), ); } #[DataProvider('invalids')] - public function testThrowWhenInvalidAcceptLanguageValue($value) + public function testReturnNothingWhenInvalidAcceptLanguageValue($value) { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new AcceptLanguageValue($value, new Quality(1)); + $this->assertNull(Language::maybe($value, Quality::max())->match( + static fn($language) => $language, + static fn() => null, + )); } public static function invalids(): array diff --git a/tests/Header/AcceptRangesTest.php b/tests/Header/AcceptRangesTest.php index 727b0420..1c997124 100644 --- a/tests/Header/AcceptRangesTest.php +++ b/tests/Header/AcceptRangesTest.php @@ -6,38 +6,55 @@ use Innmind\Http\{ Header\AcceptRanges, Header, - Header\AcceptRangesValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class AcceptRangesTest extends TestCase { - public function testInterface() + public function testOf() + { + $header = AcceptRanges::of('bytes'); + + $this->assertInstanceOf(AcceptRanges::class, $header); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Accept-Ranges: bytes', $header->normalize()->toString()); + } + + public function testValid() { - $h = new AcceptRanges( - $ar = new AcceptRangesValue('bytes'), + $this->assertInstanceOf( + AcceptRanges::class, + AcceptRanges::of('bytes'), + ); + $this->assertInstanceOf( + AcceptRanges::class, + AcceptRanges::of('none'), ); + $this->assertInstanceOf( + AcceptRanges::class, + AcceptRanges::of('whatever'), + ); + } + + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidAcceptRangeValue($value) + { + $this->expectException(DomainException::class); + $this->expectExceptionMessage($value); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Accept-Ranges', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($ar, $v->find(static fn() => true)->match( - static fn($first) => $first, + $this->assertNull(AcceptRanges::maybe($value)->match( + static fn($header) => $header, static fn() => null, )); - $this->assertSame('Accept-Ranges: bytes', $h->toString()); } - public function testOf() + public static function invalids(): array { - $header = AcceptRanges::of('bytes'); - - $this->assertInstanceOf(AcceptRanges::class, $header); - $this->assertSame('Accept-Ranges', $header->name()); - $values = $header->values(); - $this->assertInstanceOf(Sequence::class, $values); - $this->assertSame('Accept-Ranges: bytes', $header->toString()); + return [ + ['*'], + ['foo/bar'], + ['bar;q=0.8'], + ]; } } diff --git a/tests/Header/AcceptRangesValueTest.php b/tests/Header/AcceptRangesValueTest.php deleted file mode 100644 index bab123d4..00000000 --- a/tests/Header/AcceptRangesValueTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('bytes', $a->toString()); - - new AcceptRangesValue('none'); - new AcceptRangesValue('whatever'); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidAcceptRangeValue($value) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new AcceptRangesValue($value); - } - - public static function invalids(): array - { - return [ - ['*'], - ['foo/bar'], - ['bar;q=0.8'], - ]; - } -} diff --git a/tests/Header/AcceptTest.php b/tests/Header/AcceptTest.php index 0a6d4ef6..ace92bcc 100644 --- a/tests/Header/AcceptTest.php +++ b/tests/Header/AcceptTest.php @@ -6,8 +6,7 @@ use Innmind\Http\{ Header\Accept, Header, - Header\AcceptValue, - Header\Parameter\Quality + Header\Parameter\Quality, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -15,17 +14,18 @@ class AcceptTest extends TestCase { public function testInterface() { - $h = new Accept( - $v = new AcceptValue( - 'text', - 'html', - new Quality(0.8), - ), - ); + $h = Accept\MediaType::maybe( + 'text', + 'html', + Quality::of(80)->toParameter(), + ) + ->map(Accept::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Accept', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Accept: text/html;q=0.8', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Accept: text/html;q=0.8', $h->normalize()->toString()); } } diff --git a/tests/Header/AcceptValueTest.php b/tests/Header/AcceptValueTest.php index 9d9257a9..6da58996 100644 --- a/tests/Header/AcceptValueTest.php +++ b/tests/Header/AcceptValueTest.php @@ -4,11 +4,9 @@ namespace Tests\Innmind\Http\Header; use Innmind\Http\{ - Header\AcceptValue, - Header\Value, + Header\Accept\MediaType, Header\Parameter\Quality, Header\Parameter, - Exception\DomainException, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -17,13 +15,16 @@ class AcceptValueTest extends TestCase { public function testInterface() { - $a = new AcceptValue( + $a = MediaType::maybe( 'text', 'x-c', - $q = new Quality(0.8), + $q = Quality::of(80)->toParameter(), + )->match( + static fn($mediaType) => $mediaType, + static fn() => null, ); - $this->assertInstanceOf(Value::class, $a); + $this->assertInstanceOf(MediaType::class, $a); $this->assertSame('text', $a->type()); $this->assertSame('x-c', $a->subType()); $this->assertSame($q, $a->parameters()->get('q')->match( @@ -32,33 +33,45 @@ public function testInterface() )); $this->assertSame('text/x-c;q=0.8', $a->toString()); - new AcceptValue( + MediaType::maybe( '*', '*', + )->match( + static fn($mediaType) => $mediaType, + static fn() => throw new \Exception, ); - new AcceptValue( + MediaType::maybe( 'application', '*', + )->match( + static fn($mediaType) => $mediaType, + static fn() => throw new \Exception, ); - new AcceptValue( + MediaType::maybe( 'application', 'octet-stream', + )->match( + static fn($mediaType) => $mediaType, + static fn() => throw new \Exception, ); - new AcceptValue( + MediaType::maybe( 'application', 'octet-stream', - new Quality(0.4), - new Parameter\Parameter('level', '1'), + Quality::of(40)->toParameter(), + Parameter::of('level', '1'), + )->match( + static fn($mediaType) => $mediaType, + static fn() => throw new \Exception, ); } #[DataProvider('invalids')] - public function testThrowWhenInvalidAcceptValue($type, $sub) + public function testReturnNothingWhenInvalidAcceptValue($type, $sub) { - $this->expectException(DomainException::class); - $this->expectExceptionMessage("$type/$sub"); - - new AcceptValue($type, $sub); + $this->assertNull(MediaType::maybe($type, $sub)->match( + static fn($mediaType) => $mediaType, + static fn() => null, + )); } public static function invalids(): array diff --git a/tests/Header/AgeTest.php b/tests/Header/AgeTest.php index c1666112..aee52499 100644 --- a/tests/Header/AgeTest.php +++ b/tests/Header/AgeTest.php @@ -6,39 +6,25 @@ use Innmind\Http\{ Header\Age, Header, - Header\AgeValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class AgeTest extends TestCase { - public function testInterface() - { - $h = new Age( - $av = new AgeValue(42), - ); - - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Age', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Age: 42', $h->toString()); - $this->assertSame(42, $h->age()); - } - public function testOf() { $header = Age::of(42); $this->assertInstanceOf(Age::class, $header); - $this->assertSame('Age', $header->name()); - $values = $header->values(); - $this->assertInstanceOf(Sequence::class, $values); - $this->assertSame('Age: 42', $header->toString()); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Age: 42', $header->normalize()->toString()); + } + + public function testReturnNothingWhenInvalidAgeValue() + { + $this->assertNull(Age::maybe(-1)->match( + static fn($header) => $header, + static fn() => null, + )); } } diff --git a/tests/Header/AgeValueTest.php b/tests/Header/AgeValueTest.php deleted file mode 100644 index e6fea38e..00000000 --- a/tests/Header/AgeValueTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('42', $a->toString()); - - new AgeValue(0); - } - - public function testThrowWhenInvalidAgeValue() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-1'); - - new AgeValue(-1); - } -} diff --git a/tests/Header/AllowTest.php b/tests/Header/AllowTest.php index 97811ff6..4131fece 100644 --- a/tests/Header/AllowTest.php +++ b/tests/Header/AllowTest.php @@ -6,7 +6,7 @@ use Innmind\Http\{ Header\Allow, Header, - Header\AllowValue + Method, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,27 +14,16 @@ class AllowTest extends TestCase { public function testInterface() { - $h = new Allow( - $v = new AllowValue('GET'), + $h = Allow::of( + Method::get, ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Allow', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Allow: GET', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Allow: GET', $h->normalize()->toString()); } public function testWithoutValues() { - $this->assertSame('Allow: ', (new Allow)->toString()); - } - - public function testOf() - { - $header = Allow::of('GET'); - - $this->assertInstanceOf(Allow::class, $header); - $this->assertSame('Allow', $header->name()); - $this->assertSame('Allow: GET', $header->toString()); + $this->assertSame('Allow: ', Allow::of()->normalize()->toString()); } } diff --git a/tests/Header/AllowValueTest.php b/tests/Header/AllowValueTest.php deleted file mode 100644 index 6addcadd..00000000 --- a/tests/Header/AllowValueTest.php +++ /dev/null @@ -1,48 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('HEAD', $a->toString()); - - new AllowValue('GET'); - new AllowValue('POST'); - new AllowValue('PUT'); - new AllowValue('DELETE'); - new AllowValue('TRACE'); - new AllowValue('CONNECT'); - new AllowValue('OPTIONS'); - new AllowValue('PATCH'); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidAllowValue($value) - { - $this->expectException(\UnhandledMatchError::class); - - new AllowValue($value); - } - - public static function invalids(): array - { - return [ - ['42'], - ['get'], - ['FOO'], - ]; - } -} diff --git a/tests/Header/AuthorizationTest.php b/tests/Header/AuthorizationTest.php index cd6f9027..decc5749 100644 --- a/tests/Header/AuthorizationTest.php +++ b/tests/Header/AuthorizationTest.php @@ -6,37 +6,59 @@ use Innmind\Http\{ Header\Authorization, Header, - Header\AuthorizationValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class AuthorizationTest extends TestCase { - public function testInterface() + public function testOf() + { + $header = Authorization::of('Basic', 'foo'); + + $this->assertInstanceOf(Authorization::class, $header); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Authorization: Basic foo', $header->normalize()->toString()); + $this->assertSame('Basic', $header->scheme()); + $this->assertSame('foo', $header->parameter()); + } + + public function testValids() { - $h = new Authorization( - $av = new AuthorizationValue('Basic', ''), + $this->assertInstanceOf( + Authorization::class, + Authorization::of('Basic', 'realm'), + ); + $this->assertInstanceOf( + Authorization::class, + Authorization::of('Basic', ''), ); + $this->assertInstanceOf( + Authorization::class, + Authorization::of('Basic', 'realm="some value"'), + ); + $this->assertInstanceOf( + Authorization::class, + Authorization::of('Foo', ''), + ); + } - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Authorization', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidAuthorizationValue($value) + { + $this->assertNull(Authorization::maybe($value, '')->match( + static fn($header) => $header, static fn() => null, )); - $this->assertSame('Authorization: Basic', $h->toString()); } - public function testOf() + public static function invalids(): array { - $header = Authorization::of('Basic', 'foo'); - - $this->assertInstanceOf(Authorization::class, $header); - $this->assertSame('Authorization: Basic foo', $header->toString()); - $this->assertSame('Basic', $header->scheme()); - $this->assertSame('foo', $header->parameter()); + return [ + ['foo@bar'], + ['foo/bar'], + ['"Basic"realm'], + ['"Basic" realm'], + ]; } } diff --git a/tests/Header/AuthorizationValueTest.php b/tests/Header/AuthorizationValueTest.php deleted file mode 100644 index 1bbe6039..00000000 --- a/tests/Header/AuthorizationValueTest.php +++ /dev/null @@ -1,48 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('Basic', $a->scheme()); - $this->assertSame('realm', $a->parameter()); - $this->assertSame('Basic realm', $a->toString()); - - new AuthorizationValue('Basic', ''); - new AuthorizationValue('Basic', 'realm="some value"'); - new AuthorizationValue('Foo', ''); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidAuthorizationValue($value) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new AuthorizationValue($value, ''); - } - - public static function invalids(): array - { - return [ - ['foo@bar'], - ['foo/bar'], - ['"Basic"realm'], - ['"Basic" realm'], - ]; - } -} diff --git a/tests/Header/CacheControlTest.php b/tests/Header/CacheControlTest.php index d4f9d6ba..800c7764 100644 --- a/tests/Header/CacheControlTest.php +++ b/tests/Header/CacheControlTest.php @@ -5,8 +5,8 @@ use Innmind\Http\{ Header\CacheControl, + Header\CacheControl\Directive, Header, - Header\CacheControlValue\PublicCache }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,13 +14,11 @@ class CacheControlTest extends TestCase { public function testInterface() { - $h = new CacheControl( - $v = new PublicCache, + $h = CacheControl::of( + Directive::public, ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Cache-Control', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Cache-Control: public', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Cache-Control: public', $h->normalize()->toString()); } } diff --git a/tests/Header/CacheControlValue/ImmutableTest.php b/tests/Header/CacheControlValue/ImmutableTest.php index 8a8eefd4..aba55762 100644 --- a/tests/Header/CacheControlValue/ImmutableTest.php +++ b/tests/Header/CacheControlValue/ImmutableTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\Immutable -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class ImmutableTest extends TestCase { public function testInterface() { - $h = new Immutable; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('immutable', $h->toString()); + $this->assertSame('immutable', Directive::immutable->toString()); } } diff --git a/tests/Header/CacheControlValue/MaxAgeTest.php b/tests/Header/CacheControlValue/MaxAgeTest.php index df70497f..ec581ba4 100644 --- a/tests/Header/CacheControlValue/MaxAgeTest.php +++ b/tests/Header/CacheControlValue/MaxAgeTest.php @@ -3,29 +3,17 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\MaxAge, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\MaxAge; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MaxAgeTest extends TestCase { public function testInterface() { - $h = new MaxAge(42); + $h = MaxAge::of(42); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(MaxAge::class, $h); $this->assertSame(42, $h->age()); $this->assertSame('max-age=42', $h->toString()); } - - public function testThrowWhenAgeIsNegative() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-42'); - - new MaxAge(-42); - } } diff --git a/tests/Header/CacheControlValue/MaxStaleTest.php b/tests/Header/CacheControlValue/MaxStaleTest.php index 5a068f0a..8715a044 100644 --- a/tests/Header/CacheControlValue/MaxStaleTest.php +++ b/tests/Header/CacheControlValue/MaxStaleTest.php @@ -3,30 +3,18 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\MaxStale, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\MaxStale; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MaxStaleTest extends TestCase { public function testInterface() { - $h = new MaxStale(42); + $h = MaxStale::of(42); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(MaxStale::class, $h); $this->assertSame(42, $h->age()); $this->assertSame('max-stale=42', $h->toString()); - $this->assertSame('max-stale', (new MaxStale(0))->toString()); - } - - public function testThrowWhenAgeIsNegative() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-42'); - - new MaxStale(-42); + $this->assertSame('max-stale', MaxStale::of(0)->toString()); } } diff --git a/tests/Header/CacheControlValue/MinimumFreshTest.php b/tests/Header/CacheControlValue/MinimumFreshTest.php index ecf66a57..45250255 100644 --- a/tests/Header/CacheControlValue/MinimumFreshTest.php +++ b/tests/Header/CacheControlValue/MinimumFreshTest.php @@ -3,29 +3,17 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\MinimumFresh, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\MinimumFresh; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MinimumFreshTest extends TestCase { public function testInterface() { - $h = new MinimumFresh(42); + $h = MinimumFresh::of(42); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(MinimumFresh::class, $h); $this->assertSame(42, $h->age()); $this->assertSame('min-fresh=42', $h->toString()); } - - public function testThrowWhenAgeIsNegative() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-42'); - - new MinimumFresh(-42); - } } diff --git a/tests/Header/CacheControlValue/MustRevalidateTest.php b/tests/Header/CacheControlValue/MustRevalidateTest.php index e9f50a14..ff4e701c 100644 --- a/tests/Header/CacheControlValue/MustRevalidateTest.php +++ b/tests/Header/CacheControlValue/MustRevalidateTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\MustRevalidate -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MustRevalidateTest extends TestCase { public function testInterface() { - $h = new MustRevalidate; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('must-revalidate', $h->toString()); + $this->assertSame('must-revalidate', Directive::mustRevalidate->toString()); } } diff --git a/tests/Header/CacheControlValue/NoCacheTest.php b/tests/Header/CacheControlValue/NoCacheTest.php index 62b3eb5a..7d7c0f23 100644 --- a/tests/Header/CacheControlValue/NoCacheTest.php +++ b/tests/Header/CacheControlValue/NoCacheTest.php @@ -3,30 +3,32 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\NoCache, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\NoCache; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class NoCacheTest extends TestCase { public function testInterface() { - $h = new NoCache('field'); + $h = NoCache::maybe('field')->match( + static fn($value) => $value, + static fn() => null, + ); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(NoCache::class, $h); $this->assertSame('field', $h->field()); $this->assertSame('no-cache="field"', $h->toString()); - $this->assertSame('no-cache', (new NoCache(''))->toString()); + $this->assertSame('no-cache', NoCache::maybe('')->match( + static fn($value) => $value->toString(), + static fn() => null, + )); } - public function testThrowWhenAgeIsNegative() + public function testReturnNothingWhenAgeIsNegative() { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('foo-bar'); - - new NoCache('foo-bar'); + $this->assertNull(NoCache::maybe('foo-bar')->match( + static fn($value) => $value, + static fn() => null, + )); } } diff --git a/tests/Header/CacheControlValue/NoStoreTest.php b/tests/Header/CacheControlValue/NoStoreTest.php index af6f66af..88562693 100644 --- a/tests/Header/CacheControlValue/NoStoreTest.php +++ b/tests/Header/CacheControlValue/NoStoreTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\NoStore -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class NoStoreTest extends TestCase { public function testInterface() { - $h = new NoStore; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('no-store', $h->toString()); + $this->assertSame('no-store', Directive::noStore->toString()); } } diff --git a/tests/Header/CacheControlValue/NoTransformTest.php b/tests/Header/CacheControlValue/NoTransformTest.php index f1af561c..2ee32a65 100644 --- a/tests/Header/CacheControlValue/NoTransformTest.php +++ b/tests/Header/CacheControlValue/NoTransformTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\NoTransform -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class NoTransformTest extends TestCase { public function testInterface() { - $h = new NoTransform; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('no-transform', $h->toString()); + $this->assertSame('no-transform', Directive::noTransform->toString()); } } diff --git a/tests/Header/CacheControlValue/OnlyIfCachedTest.php b/tests/Header/CacheControlValue/OnlyIfCachedTest.php index 9aae0b29..b195a2cd 100644 --- a/tests/Header/CacheControlValue/OnlyIfCachedTest.php +++ b/tests/Header/CacheControlValue/OnlyIfCachedTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\OnlyIfCached -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class OnlyIfCachedTest extends TestCase { public function testInterface() { - $h = new OnlyIfCached; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('only-if-cached', $h->toString()); + $this->assertSame('only-if-cached', Directive::onlyIfCached->toString()); } } diff --git a/tests/Header/CacheControlValue/PrivateCacheTest.php b/tests/Header/CacheControlValue/PrivateCacheTest.php index e819bcf0..913d63fe 100644 --- a/tests/Header/CacheControlValue/PrivateCacheTest.php +++ b/tests/Header/CacheControlValue/PrivateCacheTest.php @@ -3,30 +3,32 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\PrivateCache, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\PrivateCache; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class PrivateCacheTest extends TestCase { public function testInterface() { - $h = new PrivateCache('field'); + $h = PrivateCache::maybe('field')->match( + static fn($value) => $value, + static fn() => null, + ); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(PrivateCache::class, $h); $this->assertSame('field', $h->field()); $this->assertSame('private="field"', $h->toString()); - $this->assertSame('private', (new PrivateCache(''))->toString()); + $this->assertSame('private', PrivateCache::maybe('')->match( + static fn($value) => $value->toString(), + static fn() => null, + )); } public function testThrowWhenAgeIsNegative() { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('foo-bar'); - - new PrivateCache('foo-bar'); + $this->assertNull(PrivateCache::maybe('foo-bar')->match( + static fn($value) => $value, + static fn() => null, + )); } } diff --git a/tests/Header/CacheControlValue/ProxyRevalidateTest.php b/tests/Header/CacheControlValue/ProxyRevalidateTest.php index 57a1cbb4..cad31d4e 100644 --- a/tests/Header/CacheControlValue/ProxyRevalidateTest.php +++ b/tests/Header/CacheControlValue/ProxyRevalidateTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\ProxyRevalidate -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class ProxyRevalidateTest extends TestCase { public function testInterface() { - $h = new ProxyRevalidate; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('proxy-revalidate', $h->toString()); + $this->assertSame('proxy-revalidate', Directive::proxyRevalidate->toString()); } } diff --git a/tests/Header/CacheControlValue/PublicCacheTest.php b/tests/Header/CacheControlValue/PublicCacheTest.php index 198852c4..195ab468 100644 --- a/tests/Header/CacheControlValue/PublicCacheTest.php +++ b/tests/Header/CacheControlValue/PublicCacheTest.php @@ -3,19 +3,13 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\Header\{ - CacheControlValue, - CacheControlValue\PublicCache -}; +use Innmind\Http\Header\CacheControl\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class PublicCacheTest extends TestCase { public function testInterface() { - $h = new PublicCache; - - $this->assertInstanceOf(CacheControlValue::class, $h); - $this->assertSame('public', $h->toString()); + $this->assertSame('public', Directive::public->toString()); } } diff --git a/tests/Header/CacheControlValue/SharedMaxAgeTest.php b/tests/Header/CacheControlValue/SharedMaxAgeTest.php index 9c8a688e..1323eba8 100644 --- a/tests/Header/CacheControlValue/SharedMaxAgeTest.php +++ b/tests/Header/CacheControlValue/SharedMaxAgeTest.php @@ -3,29 +3,17 @@ namespace Tests\Innmind\Http\Header\CacheControlValue; -use Innmind\Http\{ - Header\CacheControlValue, - Header\CacheControlValue\SharedMaxAge, - Exception\DomainException, -}; +use Innmind\Http\Header\CacheControl\SharedMaxAge; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class SharedMaxAgeTest extends TestCase { public function testInterface() { - $h = new SharedMaxAge(42); + $h = SharedMaxAge::of(42); - $this->assertInstanceOf(CacheControlValue::class, $h); + $this->assertInstanceOf(SharedMaxAge::class, $h); $this->assertSame(42, $h->age()); $this->assertSame('s-maxage=42', $h->toString()); } - - public function testThrowWhenAgeIsNegative() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-42'); - - new SharedMaxAge(-42); - } } diff --git a/tests/Header/ContentEncodingTest.php b/tests/Header/ContentEncodingTest.php index 8dd3cb7f..cb46a651 100644 --- a/tests/Header/ContentEncodingTest.php +++ b/tests/Header/ContentEncodingTest.php @@ -6,35 +6,53 @@ use Innmind\Http\{ Header\ContentEncoding, Header, - Header\ContentEncodingValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class ContentEncodingTest extends TestCase { - public function testInterface() + public function testOf() + { + $header = ContentEncoding::of('compress'); + + $this->assertInstanceOf(ContentEncoding::class, $header); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Content-Encoding: compress', $header->normalize()->toString()); + } + + public function testValids() { - $h = new ContentEncoding( - $ce = new ContentEncodingValue('compress'), + $this->assertInstanceOf( + ContentEncoding::class, + ContentEncoding::of('compress'), + ); + $this->assertInstanceOf( + ContentEncoding::class, + ContentEncoding::of('x-compress'), ); + $this->assertInstanceOf( + ContentEncoding::class, + ContentEncoding::of('identity'), + ); + } - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Encoding', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($ce, $v->find(static fn() => true)->match( - static fn($first) => $first, + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidContentEncodingValue($value) + { + $this->assertNull(ContentEncoding::maybe($value)->match( + static fn($header) => $header, static fn() => null, )); - $this->assertSame('Content-Encoding: compress', $h->toString()); } - public function testOf() + public static function invalids(): array { - $header = ContentEncoding::of('compress'); - - $this->assertInstanceOf(ContentEncoding::class, $header); - $this->assertSame('Content-Encoding: compress', $header->toString()); + return [ + ['*'], + ['@'], + ['bar+suffix'], + ['foo/bar'], + ]; } } diff --git a/tests/Header/ContentEncodingValueTest.php b/tests/Header/ContentEncodingValueTest.php deleted file mode 100644 index bdf463a8..00000000 --- a/tests/Header/ContentEncodingValueTest.php +++ /dev/null @@ -1,45 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('compress', $a->toString()); - - new ContentEncodingValue('identity'); - new ContentEncodingValue('x-compress'); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidContentEncodingValue($value) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new ContentEncodingValue($value); - } - - public static function invalids(): array - { - return [ - ['*'], - ['@'], - ['bar+suffix'], - ['foo/bar'], - ]; - } -} diff --git a/tests/Header/ContentLanguageTest.php b/tests/Header/ContentLanguageTest.php index db5bd556..07f3d68d 100644 --- a/tests/Header/ContentLanguageTest.php +++ b/tests/Header/ContentLanguageTest.php @@ -6,7 +6,7 @@ use Innmind\Http\{ Header\ContentLanguage, Header, - Header\ContentLanguageValue + Header\Content\Language, }; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,26 +14,19 @@ class ContentLanguageTest extends TestCase { public function testInterface() { - $h = new ContentLanguage( - $v = new ContentLanguageValue('fr'), - ); + $h = Language::maybe('fr') + ->map(ContentLanguage::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Language', $h->name()); - $this->assertTrue($h->values()->contains($v)); - $this->assertSame('Content-Language: fr', $h->toString()); - } - - public function test() - { - $header = ContentLanguage::of('fr'); - - $this->assertInstanceOf(ContentLanguage::class, $header); - $this->assertSame('Content-Language: fr', $header->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Content-Language: fr', $h->normalize()->toString()); } public function testWithoutValues() { - $this->assertSame('Content-Language: ', (new ContentLanguage)->toString()); + $this->assertSame('Content-Language: ', ContentLanguage::of()->normalize()->toString()); } } diff --git a/tests/Header/ContentLanguageValueTest.php b/tests/Header/ContentLanguageValueTest.php index 51bcc144..c7208f3c 100644 --- a/tests/Header/ContentLanguageValueTest.php +++ b/tests/Header/ContentLanguageValueTest.php @@ -3,11 +3,7 @@ namespace Tests\Innmind\Http\Header; -use Innmind\Http\{ - Header\ContentLanguageValue, - Header\Value, - Exception\DomainException, -}; +use Innmind\Http\Header\Content\Language; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -15,23 +11,35 @@ class ContentLanguageValueTest extends TestCase { public function testInterface() { - $a = new ContentLanguageValue('en-gb'); + $a = Language::maybe('en-gb')->match( + static fn($language) => $language, + static fn() => null, + ); - $this->assertInstanceOf(Value::class, $a); + $this->assertInstanceOf(Language::class, $a); $this->assertSame('en-gb', $a->toString()); - new ContentLanguageValue('fr'); - new ContentLanguageValue('fr-FR'); - new ContentLanguageValue('sgn-CH-DE'); + Language::maybe('fr')->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); + Language::maybe('fr-FR')->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); + Language::maybe('sgn-CH-DE')->match( + static fn($language) => $language, + static fn() => throw new \Exception, + ); } #[DataProvider('invalids')] - public function testThrowWhenInvalidContentLanguageValue($value) + public function testReturnNothingWhenInvalidContentLanguageValue($value) { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($value); - - new ContentLanguageValue($value); + $this->assertNull(Language::maybe($value)->match( + static fn($language) => $language, + static fn() => null, + )); } public static function invalids(): array diff --git a/tests/Header/ContentLengthTest.php b/tests/Header/ContentLengthTest.php index 3382c8f5..c5d912bd 100644 --- a/tests/Header/ContentLengthTest.php +++ b/tests/Header/ContentLengthTest.php @@ -6,36 +6,25 @@ use Innmind\Http\{ Header\ContentLength, Header, - Header\ContentLengthValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class ContentLengthTest extends TestCase { - public function testInterface() - { - $h = new ContentLength( - $av = new ContentLengthValue(42), - ); - - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Length', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Content-Length: 42', $h->toString()); - $this->assertSame(42, $h->length()); - } - public function testOf() { $header = ContentLength::of(42); $this->assertInstanceOf(ContentLength::class, $header); - $this->assertSame('Content-Length: 42', $header->toString()); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Content-Length: 42', $header->normalize()->toString()); + } + + public function testReturnNothingWhenInvalidContentLengthValue() + { + $this->assertNull(ContentLength::maybe(-1)->match( + static fn($header) => $header, + static fn() => null, + )); } } diff --git a/tests/Header/ContentLengthValueTest.php b/tests/Header/ContentLengthValueTest.php deleted file mode 100644 index 41069462..00000000 --- a/tests/Header/ContentLengthValueTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('42', $a->toString()); - - new ContentLengthValue(0); - } - - public function testThrowWhenInvalidContentLengthValue() - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage('-1'); - - new ContentLengthValue(-1); - } -} diff --git a/tests/Header/ContentLocationTest.php b/tests/Header/ContentLocationTest.php index e55e8194..72296af4 100644 --- a/tests/Header/ContentLocationTest.php +++ b/tests/Header/ContentLocationTest.php @@ -6,9 +6,7 @@ use Innmind\Http\{ Header\ContentLocation, Header, - Header\LocationValue }; -use Innmind\Immutable\Sequence; use Innmind\Url\Url; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -16,19 +14,10 @@ class ContentLocationTest extends TestCase { public function testInterface() { - $h = new ContentLocation( - $av = new LocationValue(Url::of('/foo/bar')), - ); + $h = ContentLocation::of(Url::of('/foo/bar')); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Location', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Content-Location: /foo/bar', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Content-Location: /foo/bar', $h->normalize()->toString()); $this->assertSame('/foo/bar', $h->url()->toString()); } @@ -37,6 +26,6 @@ public function testOf() $header = ContentLocation::of(Url::of('/foo/bar')); $this->assertInstanceOf(ContentLocation::class, $header); - $this->assertSame('Content-Location: /foo/bar', $header->toString()); + $this->assertSame('Content-Location: /foo/bar', $header->normalize()->toString()); } } diff --git a/tests/Header/ContentRangeTest.php b/tests/Header/ContentRangeTest.php index a0cfc0c3..acd35ce0 100644 --- a/tests/Header/ContentRangeTest.php +++ b/tests/Header/ContentRangeTest.php @@ -6,36 +6,51 @@ use Innmind\Http\{ Header\ContentRange, Header, - Header\ContentRangeValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class ContentRangeTest extends TestCase { - public function testInterface() + public function testOf() + { + $header = ContentRange::of('bytes', 0, 42); + + $this->assertInstanceOf(ContentRange::class, $header); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Content-Range: bytes 0-42/*', $header->normalize()->toString()); + } + + public function testValids() { - $h = new ContentRange( - $cr = new ContentRangeValue('bytes', 0, 42), + $this->assertSame( + 'Content-Range: resources 0-42/*', + ContentRange::of('resources', 0, 42)->normalize()->toString(), ); + $this->assertSame( + 'Content-Range: resources 0-499/1234', + ContentRange::of('resources', 0, 499, 1234)->normalize()->toString(), + ); + } - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Range', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($cr, $v->find(static fn() => true)->match( - static fn($first) => $first, + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidContentRangeValue($unit, $first, $last, $length) + { + $this->assertNull(ContentRange::maybe($unit, $first, $last, $length)->match( + static fn($header) => $header, static fn() => null, )); - $this->assertSame('Content-Range: bytes 0-42/*', $h->toString()); - $this->assertSame('bytes 0-42/*', $h->range()->toString()); } - public function testOf() + public static function invalids() { - $header = ContentRange::of('bytes', 0, 42); - - $this->assertInstanceOf(ContentRange::class, $header); - $this->assertSame('Content-Range: bytes 0-42/*', $header->toString()); + return [ + ['', 0, 42, null], + ['foo', -1, 42, null], + ['foo', 0, -42, null], + ['foo', 0, 42, -42], + ['foo', 100, 42, 142], + ['foo', 100, 142, 42], + ]; } } diff --git a/tests/Header/ContentRangeValueTest.php b/tests/Header/ContentRangeValueTest.php deleted file mode 100644 index e62da128..00000000 --- a/tests/Header/ContentRangeValueTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertInstanceOf(Value::class, $h); - $this->assertSame('resources', $h->unit()); - $this->assertSame(0, $h->firstPosition()); - $this->assertSame(42, $h->lastPosition()); - $this->assertFalse($h->length()->match( - static fn() => true, - static fn() => false, - )); - $this->assertSame('resources 0-42/*', $h->toString()); - - $h = new ContentRangeValue('bytes', 0, 499, 1234); - $this->assertSame(1234, $h->length()->match( - static fn($length) => $length, - static fn() => null, - )); - $this->assertSame('bytes 0-499/1234', $h->toString()); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidContentRangeValue($unit, $first, $last, $length) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($unit); - - new ContentRangeValue($unit, $first, $last, $length); - } - - public static function invalids() - { - return [ - ['', 0, 42, null], - ['foo', -1, 42, null], - ['foo', 0, -42, null], - ['foo', 0, 42, -42], - ['foo', 100, 42, 142], - ['foo', 100, 142, 42], - ]; - } -} diff --git a/tests/Header/ContentType/BoundaryTest.php b/tests/Header/ContentType/BoundaryTest.php index a32fc105..ed817b11 100644 --- a/tests/Header/ContentType/BoundaryTest.php +++ b/tests/Header/ContentType/BoundaryTest.php @@ -5,7 +5,6 @@ use Innmind\Http\{ Header\ContentType\Boundary, - Header\Parameter, Exception\DomainException, }; use Innmind\BlackBox\{ @@ -18,18 +17,13 @@ class BoundaryTest extends TestCase { use BlackBox; - public function testInterface() - { - $this->assertInstanceOf(Parameter::class, Boundary::uuid()); - } - public function testOf() { $id = \uniqid(); $boundary = Boundary::of($id); $this->assertSame($id, $boundary->value()); - $this->assertSame("boundary=\"$id\"", $boundary->toString()); + $this->assertSame("boundary=$id", $boundary->toParameter()->toString()); } public function testThrowWhenRandomString() diff --git a/tests/Header/ContentTypeTest.php b/tests/Header/ContentTypeTest.php index 891525bf..869091ed 100644 --- a/tests/Header/ContentTypeTest.php +++ b/tests/Header/ContentTypeTest.php @@ -6,45 +6,46 @@ use Innmind\Http\{ Header\ContentType, Header, - Header\ContentTypeValue, - Header\Parameter }; -use Innmind\Immutable\Sequence; +use Innmind\MediaType\MediaType; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class ContentTypeTest extends TestCase { public function testInterface() { - $h = new ContentType( - $ct = new ContentTypeValue( - 'text', - 'html', - new Parameter\Parameter('charset', 'UTF-8'), - ), + $h = ContentType::of( + $ct = MediaType::of('text/html; charset="UTF-8"'), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Content-Type', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($ct, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Content-Type: text/html;charset=UTF-8', $h->toString()); - $this->assertSame('text/html;charset=UTF-8', $h->content()->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Content-Type: text/html;charset=UTF-8', $h->normalize()->toString()); + $this->assertSame('text/html; charset=UTF-8', $h->content()->toString()); } - public function testOf() + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidContentTypeValue($type, $sub) { - $header = ContentType::of( - 'text', - 'html', - new Parameter\Parameter('charset', 'UTF-8'), + $this->assertNull( + MediaType::maybe("$type/$sub") + ->map(ContentType::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ), ); + } - $this->assertInstanceOf(ContentType::class, $header); - $this->assertSame('Content-Type: text/html;charset=UTF-8', $header->toString()); + public static function invalids(): array + { + return [ + ['*', '*'], + ['*', 'octet-stream'], + ['text', '*'], + ['foo/bar', ''], + ['foo', 'bar+suffix'], + ['foo', 'bar, level=1'], + ]; } } diff --git a/tests/Header/ContentTypeValueTest.php b/tests/Header/ContentTypeValueTest.php deleted file mode 100644 index b6377d3a..00000000 --- a/tests/Header/ContentTypeValueTest.php +++ /dev/null @@ -1,66 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('text', $a->type()); - $this->assertSame('x-c', $a->subType()); - $this->assertSame($p, $a->parameters()->get('charset')->match( - static fn($charset) => $charset, - static fn() => null, - )); - $this->assertSame('text/x-c;charset=UTF-8', $a->toString()); - - new ContentTypeValue( - 'application', - 'octet-stream', - ); - new ContentTypeValue( - 'application', - 'octet-stream', - new Parameter\Parameter('charset', 'UTF-8'), - new Parameter\Parameter('level', '1'), - ); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidContentTypeValue($type, $sub) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage("$type/$sub"); - - new ContentTypeValue($type, $sub); - } - - public static function invalids(): array - { - return [ - ['*', '*'], - ['*', 'octet-stream'], - ['text', '*'], - ['foo/bar', ''], - ['foo', 'bar+suffix'], - ['foo', 'bar, level=1'], - ]; - } -} diff --git a/tests/Header/CookieParameter/DomainTest.php b/tests/Header/CookieParameter/DomainTest.php index 1f4ef219..5e9f28a4 100644 --- a/tests/Header/CookieParameter/DomainTest.php +++ b/tests/Header/CookieParameter/DomainTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\Domain, - Parameter -}; +use Innmind\Http\Header\SetCookie\Domain; use Innmind\Url\Authority\Host; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,9 +11,8 @@ class DomainTest extends TestCase { public function testInterface() { - $domain = new Domain(Host::of('localhost')); + $domain = Domain::of(Host::of('localhost')); - $this->assertInstanceOf(Parameter::class, $domain); - $this->assertSame('Domain=localhost', $domain->toString()); + $this->assertSame('Domain=localhost', $domain->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/ExpiresTest.php b/tests/Header/CookieParameter/ExpiresTest.php index d6244c5e..c6264be6 100644 --- a/tests/Header/CookieParameter/ExpiresTest.php +++ b/tests/Header/CookieParameter/ExpiresTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\Expires, - Parameter -}; +use Innmind\Http\Header\SetCookie\Expires; use Innmind\TimeContinuum\PointInTime; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,11 +11,10 @@ class ExpiresTest extends TestCase { public function testInterface() { - $expires = new Expires(PointInTime::at( + $expires = Expires::at(PointInTime::at( new \DateTimeImmutable('2018-01-01T12:13:14+0200'), )); - $this->assertInstanceOf(Parameter::class, $expires); - $this->assertSame('Expires="Mon, 01 Jan 2018 10:13:14 GMT"', $expires->toString()); + $this->assertSame('Expires="Mon, 01 Jan 2018 10:13:14 GMT"', $expires->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/HttpOnlyTest.php b/tests/Header/CookieParameter/HttpOnlyTest.php index 7c084e53..9ea616ec 100644 --- a/tests/Header/CookieParameter/HttpOnlyTest.php +++ b/tests/Header/CookieParameter/HttpOnlyTest.php @@ -3,19 +3,15 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\HttpOnly, - Parameter -}; +use Innmind\Http\Header\SetCookie\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class HttpOnlyTest extends TestCase { public function testInterface() { - $httpOnly = new HttpOnly; + $httpOnly = Directive::httpOnly; - $this->assertInstanceOf(Parameter::class, $httpOnly); - $this->assertSame('HttpOnly', $httpOnly->toString()); + $this->assertSame('HttpOnly', $httpOnly->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/MaxAgeTest.php b/tests/Header/CookieParameter/MaxAgeTest.php index fe9f36f4..febaaf95 100644 --- a/tests/Header/CookieParameter/MaxAgeTest.php +++ b/tests/Header/CookieParameter/MaxAgeTest.php @@ -3,30 +3,17 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\{ - Header\CookieParameter\MaxAge, - Header\Parameter, - Exception\DomainException -}; +use Innmind\Http\Header\SetCookie\MaxAge; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MaxAgeTest extends TestCase { public function testInterface() { - $maxAge = new MaxAge(1); + $maxAge = MaxAge::of(1); - $this->assertInstanceOf(Parameter::class, $maxAge); - $this->assertSame('Max-Age=1', $maxAge->toString()); + $this->assertSame('Max-Age=1', $maxAge->toParameter()->toString()); - $this->assertInstanceOf(Parameter::class, MaxAge::expire()); - $this->assertSame('Max-Age=-1', MaxAge::expire()->toString()); - } - - public function testThrowWhenInvalidAge() - { - $this->expectException(DomainException::class); - - new MaxAge(0); + $this->assertSame('Max-Age=-1', MaxAge::expire()->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/PathTest.php b/tests/Header/CookieParameter/PathTest.php index 28a576e0..f6662aa0 100644 --- a/tests/Header/CookieParameter/PathTest.php +++ b/tests/Header/CookieParameter/PathTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\Path, - Parameter -}; +use Innmind\Http\Header\SetCookie\Path; use Innmind\Url\Path as UrlPath; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,9 +11,8 @@ class PathTest extends TestCase { public function testInterface() { - $path = new Path(UrlPath::of('/foo')); + $path = Path::of(UrlPath::of('/foo')); - $this->assertInstanceOf(Parameter::class, $path); - $this->assertSame('Path=/foo', $path->toString()); + $this->assertSame('Path=/foo', $path->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/SameSiteTest.php b/tests/Header/CookieParameter/SameSiteTest.php index dc58d0a7..b9502897 100644 --- a/tests/Header/CookieParameter/SameSiteTest.php +++ b/tests/Header/CookieParameter/SameSiteTest.php @@ -3,24 +3,19 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\SameSite, - Parameter -}; +use Innmind\Http\Header\SetCookie\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class SameSiteTest extends TestCase { public function testInterface() { - $sameSite = SameSite::strict(); + $sameSite = Directive::strictSameSite; - $this->assertInstanceOf(Parameter::class, $sameSite); - $this->assertSame('SameSite=Strict', $sameSite->toString()); + $this->assertSame('SameSite=Strict', $sameSite->toParameter()->toString()); - $sameSite = SameSite::lax(); + $sameSite = Directive::laxSameSite; - $this->assertInstanceOf(Parameter::class, $sameSite); - $this->assertSame('SameSite=Lax', $sameSite->toString()); + $this->assertSame('SameSite=Lax', $sameSite->toParameter()->toString()); } } diff --git a/tests/Header/CookieParameter/SecureTest.php b/tests/Header/CookieParameter/SecureTest.php index c8c7b1ef..1311f128 100644 --- a/tests/Header/CookieParameter/SecureTest.php +++ b/tests/Header/CookieParameter/SecureTest.php @@ -3,19 +3,15 @@ namespace Tests\Innmind\Http\Header\CookieParameter; -use Innmind\Http\Header\{ - CookieParameter\Secure, - Parameter -}; +use Innmind\Http\Header\SetCookie\Directive; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class SecureTest extends TestCase { public function testInterface() { - $secure = new Secure; + $secure = Directive::secure; - $this->assertInstanceOf(Parameter::class, $secure); - $this->assertSame('Secure', $secure->toString()); + $this->assertSame('Secure', $secure->toParameter()->toString()); } } diff --git a/tests/Header/CookieTest.php b/tests/Header/CookieTest.php index eb817684..d8401b72 100644 --- a/tests/Header/CookieTest.php +++ b/tests/Header/CookieTest.php @@ -6,37 +6,20 @@ use Innmind\Http\{ Header\Cookie, Header, - Header\CookieValue, - Header\Parameter\Parameter + Header\Parameter, }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class CookieTest extends TestCase { public function testInterface() { - $cookie = new Cookie( - $value = new CookieValue(new Parameter('foo', 'bar')), + $cookie = Cookie::of( + Parameter::of('foo', 'bar'), ); - $this->assertInstanceOf(Header::class, $cookie); - $this->assertSame('Cookie', $cookie->name()); - $values = $cookie->values(); - $this->assertInstanceOf(Sequence::class, $values); - $this->assertSame($value, $values->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Cookie: foo=bar', $cookie->toString()); - $this->assertSame($value->parameters(), $cookie->parameters()); - } - - public function testOf() - { - $cookie = Cookie::of(new Parameter('foo', 'bar')); - $this->assertInstanceOf(Cookie::class, $cookie); - $this->assertSame('Cookie: foo=bar', $cookie->toString()); + $this->assertInstanceOf(Header\Custom::class, $cookie); + $this->assertSame('Cookie: foo=bar', $cookie->normalize()->toString()); } } diff --git a/tests/Header/CookieValueTest.php b/tests/Header/CookieValueTest.php deleted file mode 100644 index 46e7f609..00000000 --- a/tests/Header/CookieValueTest.php +++ /dev/null @@ -1,36 +0,0 @@ -assertInstanceOf(Value::class, $cookie); - $this->assertInstanceOf(Map::class, $cookie->parameters()); - $this->assertCount(2, $cookie->parameters()); - $this->assertSame('bar', $cookie->parameters()->get('foo')->match( - static fn($foo) => $foo->value(), - static fn() => null, - )); - $this->assertSame('baz', $cookie->parameters()->get('bar')->match( - static fn($bar) => $bar->value(), - static fn() => null, - )); - $this->assertSame('foo=bar; bar=baz', $cookie->toString()); - } -} diff --git a/tests/Header/DateTest.php b/tests/Header/DateTest.php index b89b93be..41f92cb0 100644 --- a/tests/Header/DateTest.php +++ b/tests/Header/DateTest.php @@ -6,32 +6,22 @@ use Innmind\Http\{ Header\Date, Header, - Header\DateValue }; use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class DateTest extends TestCase { public function testInterface() { - $h = new Date( - $d = new DateValue(PointInTime::at( + $h = Date::of( + PointInTime::at( new \DateTimeImmutable('2016-01-01 12:12:12+0200'), - )), + ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Date', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($d, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Date: Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - $this->assertSame($d->date(), $h->date()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Date: Fri, 01 Jan 2016 10:12:12 GMT', $h->normalize()->toString()); } public function testOf() @@ -41,6 +31,6 @@ public function testOf() )); $this->assertInstanceOf(Date::class, $header); - $this->assertSame('Date: Fri, 01 Jan 2016 10:12:12 GMT', $header->toString()); + $this->assertSame('Date: Fri, 01 Jan 2016 10:12:12 GMT', $header->normalize()->toString()); } } diff --git a/tests/Header/DateValueTest.php b/tests/Header/DateValueTest.php deleted file mode 100644 index 842415b6..00000000 --- a/tests/Header/DateValueTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertInstanceOf(Value::class, $h); - $this->assertSame('Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - } -} diff --git a/tests/Header/ExpiresTest.php b/tests/Header/ExpiresTest.php index 8a4b4bb9..a40a2bde 100644 --- a/tests/Header/ExpiresTest.php +++ b/tests/Header/ExpiresTest.php @@ -6,32 +6,22 @@ use Innmind\Http\{ Header\Expires, Header, - Header\DateValue }; use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class ExpiresTest extends TestCase { public function testInterface() { - $h = new Expires( - $d = new DateValue(PointInTime::at( + $h = Expires::of( + PointInTime::at( new \DateTimeImmutable('2016-01-01 12:12:12+0200'), - )), + ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Expires', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($d, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Expires: Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - $this->assertSame($d->date(), $h->date()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Expires: Fri, 01 Jan 2016 10:12:12 GMT', $h->normalize()->toString()); } public function testOf() @@ -40,7 +30,7 @@ public function testOf() new \DateTimeImmutable('2016-01-01 12:12:12+0200'), )); - $this->assertInstanceOf(Header::class, $header); - $this->assertSame('Expires: Fri, 01 Jan 2016 10:12:12 GMT', $header->toString()); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('Expires: Fri, 01 Jan 2016 10:12:12 GMT', $header->normalize()->toString()); } } diff --git a/tests/Header/HeaderTest.php b/tests/Header/HeaderTest.php index 0543a9bc..50b1a4b1 100644 --- a/tests/Header/HeaderTest.php +++ b/tests/Header/HeaderTest.php @@ -13,10 +13,10 @@ class HeaderTest extends TestCase { public function testInterface() { - $h = new Header\Header( + $h = Header::of( 'Accept', - $v1 = new Value\Value('application/json'), - $v2 = new Value\Value('*/*'), + $v1 = Value::of('application/json'), + $v2 = Value::of('*/*'), ); $this->assertInstanceOf(Header::class, $h); @@ -28,6 +28,6 @@ public function testInterface() public function testWithoutValues() { - $this->assertSame('X-Foo: ', (new Header\Header('X-Foo'))->toString()); + $this->assertSame('X-Foo: ', Header::of('X-Foo')->toString()); } } diff --git a/tests/Header/HeaderValueTest.php b/tests/Header/HeaderValueTest.php index 373e2b66..b8b2e243 100644 --- a/tests/Header/HeaderValueTest.php +++ b/tests/Header/HeaderValueTest.php @@ -10,7 +10,7 @@ class HeaderValueTest extends TestCase { public function testInterface() { - $hv = new Value\Value('foo'); + $hv = Value::of('foo'); $this->assertInstanceOf(Value::class, $hv); $this->assertSame('foo', $hv->toString()); diff --git a/tests/Header/HostTest.php b/tests/Header/HostTest.php index 174ed0e1..5ce83218 100644 --- a/tests/Header/HostTest.php +++ b/tests/Header/HostTest.php @@ -6,9 +6,7 @@ use Innmind\Http\{ Header\Host, Header, - Header\HostValue }; -use Innmind\Immutable\Sequence; use Innmind\Url\Authority\{ Host as UrlHost, Port, @@ -19,21 +17,13 @@ class HostTest extends TestCase { public function testInterface() { - $h = new Host( - $av = new HostValue(UrlHost::of('example.com'), Port::none()), + $h = Host::of( + UrlHost::of('example.com'), + Port::of(8080), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Host', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Host: example.com', $h->toString()); - $this->assertSame($av->host(), $h->host()); - $this->assertSame($av->port(), $h->port()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Host: example.com:8080', $h->normalize()->toString()); } public function testOf() @@ -41,6 +31,6 @@ public function testOf() $header = Host::of(UrlHost::of('example.com'), Port::none()); $this->assertInstanceOf(Host::class, $header); - $this->assertSame('Host: example.com', $header->toString()); + $this->assertSame('Host: example.com', $header->normalize()->toString()); } } diff --git a/tests/Header/HostValueTest.php b/tests/Header/HostValueTest.php deleted file mode 100644 index 94dde4ac..00000000 --- a/tests/Header/HostValueTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertInstanceOf(Value::class, $h); - $this->assertSame($host, $h->host()); - $this->assertSame($p, $h->port()); - $this->assertSame('example.com', $h->toString()); - $this->assertSame( - 'example.com:8080', - (new HostValue(Host::of('example.com'), Port::of(8080)))->toString(), - ); - } -} diff --git a/tests/Header/IfModifiedSinceTest.php b/tests/Header/IfModifiedSinceTest.php index bcb7acaa..93b22953 100644 --- a/tests/Header/IfModifiedSinceTest.php +++ b/tests/Header/IfModifiedSinceTest.php @@ -6,32 +6,22 @@ use Innmind\Http\{ Header\IfModifiedSince, Header, - Header\DateValue }; use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class IfModifiedSinceTest extends TestCase { public function testInterface() { - $h = new IfModifiedSince( - $d = new DateValue(PointInTime::at( + $h = IfModifiedSince::of( + PointInTime::at( new \DateTimeImmutable('2016-01-01 12:12:12+0200'), - )), + ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('If-Modified-Since', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($d, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('If-Modified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - $this->assertSame($d->date(), $h->date()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('If-Modified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $h->normalize()->toString()); } public function testOf() @@ -41,6 +31,6 @@ public function testOf() )); $this->assertInstanceOf(IfModifiedSince::class, $header); - $this->assertSame('If-Modified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $header->toString()); + $this->assertSame('If-Modified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $header->normalize()->toString()); } } diff --git a/tests/Header/IfUnmodifiedSinceTest.php b/tests/Header/IfUnmodifiedSinceTest.php index 1b307344..49c36cc1 100644 --- a/tests/Header/IfUnmodifiedSinceTest.php +++ b/tests/Header/IfUnmodifiedSinceTest.php @@ -6,32 +6,22 @@ use Innmind\Http\{ Header\IfUnmodifiedSince, Header, - Header\DateValue }; use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class IfUnmodifiedSinceTest extends TestCase { public function testInterface() { - $h = new IfUnmodifiedSince( - $d = new DateValue(PointInTime::at( + $h = IfUnmodifiedSince::of( + PointInTime::at( new \DateTimeImmutable('2016-01-01 12:12:12+0200'), - )), + ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('If-Unmodified-Since', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($d, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('If-Unmodified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - $this->assertSame($d->date(), $h->date()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('If-Unmodified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $h->normalize()->toString()); } public function testOf() @@ -41,6 +31,6 @@ public function testOf() )); $this->assertInstanceOf(IfUnmodifiedSince::class, $header); - $this->assertSame('If-Unmodified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $header->toString()); + $this->assertSame('If-Unmodified-Since: Fri, 01 Jan 2016 10:12:12 GMT', $header->normalize()->toString()); } } diff --git a/tests/Header/LastModifiedTest.php b/tests/Header/LastModifiedTest.php index c4b7bf76..687c3a1e 100644 --- a/tests/Header/LastModifiedTest.php +++ b/tests/Header/LastModifiedTest.php @@ -6,32 +6,22 @@ use Innmind\Http\{ Header\LastModified, Header, - Header\DateValue }; use Innmind\TimeContinuum\PointInTime; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class LastModifiedTest extends TestCase { public function testInterface() { - $h = new LastModified( - $d = new DateValue(PointInTime::at( + $h = LastModified::of( + PointInTime::at( new \DateTimeImmutable('2016-01-01 12:12:12+0200'), - )), + ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Last-Modified', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($d, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Last-Modified: Fri, 01 Jan 2016 10:12:12 GMT', $h->toString()); - $this->assertSame($d->date(), $h->date()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Last-Modified: Fri, 01 Jan 2016 10:12:12 GMT', $h->normalize()->toString()); } public function testOf() @@ -41,6 +31,6 @@ public function testOf() )); $this->assertInstanceOf(LastModified::class, $header); - $this->assertSame('Last-Modified: Fri, 01 Jan 2016 10:12:12 GMT', $header->toString()); + $this->assertSame('Last-Modified: Fri, 01 Jan 2016 10:12:12 GMT', $header->normalize()->toString()); } } diff --git a/tests/Header/LinkTest.php b/tests/Header/LinkTest.php index 98e47258..9efb1ab9 100644 --- a/tests/Header/LinkTest.php +++ b/tests/Header/LinkTest.php @@ -6,7 +6,6 @@ use Innmind\Http\{ Header\Link, Header, - Header\LinkValue, Header\Parameter }; use Innmind\Url\Url; @@ -16,25 +15,23 @@ class LinkTest extends TestCase { public function testInterface() { - $h = new Link( - $v = new LinkValue( + $h = Link::of( + Link\Relationship::of( Url::of('/some/resource'), 'some relation', - new Parameter\Parameter('title', 'Foo'), + Parameter::of('title', 'Foo'), ), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Link', $h->name()); - $this->assertTrue($h->values()->contains($v)); + $this->assertInstanceOf(Header\Custom::class, $h); $this->assertSame( 'Link: ; rel="some relation";title=Foo', - $h->toString(), + $h->normalize()->toString(), ); } public function testWithoutValues() { - $this->assertSame('Link: ', (new Link)->toString()); + $this->assertSame('Link: ', Link::of()->normalize()->toString()); } } diff --git a/tests/Header/LinkValueTest.php b/tests/Header/LinkValueTest.php index dc5463bb..bd132f56 100644 --- a/tests/Header/LinkValueTest.php +++ b/tests/Header/LinkValueTest.php @@ -3,11 +3,9 @@ namespace Tests\Innmind\Http\Header; -use Innmind\Http\{ - Header\LinkValue, - Header\Value, - Header\Parameter, - Exception\DomainException, +use Innmind\Http\Header\{ + Link\Relationship, + Parameter, }; use Innmind\Url\Url; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -16,15 +14,15 @@ class LinkValueTest extends TestCase { public function testInterface() { - $l = new LinkValue( + $l = Relationship::of( $url = Url::of('/some/resource'), 'relationship', - $p = new Parameter\Parameter('title', 'Foo'), + $p = Parameter::of('title', 'Foo'), ); - $this->assertInstanceOf(Value::class, $l); + $this->assertInstanceOf(Relationship::class, $l); $this->assertSame($url, $l->url()); - $this->assertSame('relationship', $l->relationship()); + $this->assertSame('relationship', $l->kind()); $this->assertSame($p, $l->parameters()->get('title')->match( static fn($title) => $title, static fn() => null, @@ -39,17 +37,7 @@ public function testDefaultRelationship() { $this->assertSame( 'related', - (new LinkValue(Url::of('/')))->relationship(), - ); - } - - public function testThrowWhenInvalidLinkValue() - { - $this->expectException(DomainException::class); - - new LinkValue( - Url::of('/foo'), - '', + Relationship::of(Url::of('/'))->kind(), ); } } diff --git a/tests/Header/LocationTest.php b/tests/Header/LocationTest.php index 68115bb9..3c05c152 100644 --- a/tests/Header/LocationTest.php +++ b/tests/Header/LocationTest.php @@ -6,9 +6,7 @@ use Innmind\Http\{ Header\Location, Header, - Header\LocationValue }; -use Innmind\Immutable\Sequence; use Innmind\Url\Url; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -16,19 +14,12 @@ class LocationTest extends TestCase { public function testInterface() { - $h = new Location( - $av = new LocationValue(Url::of('/foo/bar')), + $h = Location::of( + Url::of('/foo/bar'), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Location', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Location: /foo/bar', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Location: /foo/bar', $h->normalize()->toString()); } public function testOf() @@ -36,6 +27,6 @@ public function testOf() $header = Location::of(Url::of('/foo/bar')); $this->assertInstanceOf(Location::class, $header); - $this->assertSame('Location: /foo/bar', $header->toString()); + $this->assertSame('Location: /foo/bar', $header->normalize()->toString()); } } diff --git a/tests/Header/LocationValueTest.php b/tests/Header/LocationValueTest.php deleted file mode 100644 index 934b4a81..00000000 --- a/tests/Header/LocationValueTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('/foo/bar', $a->toString()); - } -} diff --git a/tests/Header/Parameter/NullParameterTest.php b/tests/Header/Parameter/NullParameterTest.php deleted file mode 100644 index b58c3c3b..00000000 --- a/tests/Header/Parameter/NullParameterTest.php +++ /dev/null @@ -1,23 +0,0 @@ -assertInstanceOf(Parameter::class, $p); - $this->assertSame('', $p->name()); - $this->assertSame('', $p->value()); - $this->assertSame('', $p->toString()); - } -} diff --git a/tests/Header/Parameter/ParameterTest.php b/tests/Header/Parameter/ParameterTest.php index 69706781..7d4e0700 100644 --- a/tests/Header/Parameter/ParameterTest.php +++ b/tests/Header/Parameter/ParameterTest.php @@ -3,31 +3,27 @@ namespace Tests\Innmind\Http\Header\Parameter; -use Innmind\Http\Header\{ - Parameter\Parameter, - Parameter as ParameterInterface -}; +use Innmind\Http\Header\Parameter; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class ParameterTest extends TestCase { public function testInterface() { - $p = new Parameter('q', 'foo'); + $p = Parameter::of('q', 'foo'); - $this->assertInstanceOf(ParameterInterface::class, $p); $this->assertSame('q', $p->name()); $this->assertSame('foo', $p->value()); $this->assertSame('q=foo', $p->toString()); - $this->assertSame('level', (new Parameter('level', ''))->toString()); + $this->assertSame('level', (Parameter::of('level', ''))->toString()); } public function testQuoteWhenThereIsAWithespace() { $this->assertSame( 'foo="bar baz"', - (new Parameter('foo', 'bar baz'))->toString(), + (Parameter::of('foo', 'bar baz'))->toString(), ); } @@ -35,7 +31,7 @@ public function testQuoteWhenThereIsATab() { $this->assertSame( "foo=\"bar\tbaz\"", - (new Parameter('foo', "bar\tbaz"))->toString(), + (Parameter::of('foo', "bar\tbaz"))->toString(), ); } @@ -43,7 +39,7 @@ public function testDoesntDuplicateQuotes() { $this->assertSame( 'foo="bar baz"', - (new Parameter('foo', '"bar baz"'))->toString(), + (Parameter::of('foo', '"bar baz"'))->toString(), ); } @@ -51,7 +47,7 @@ public function testDoesntChangeIfAlreadyQuotedEvenIfNotNeeded() { $this->assertSame( 'foo="bar"', - (new Parameter('foo', '"bar"'))->toString(), + (Parameter::of('foo', '"bar"'))->toString(), ); } } diff --git a/tests/Header/Parameter/QualityTest.php b/tests/Header/Parameter/QualityTest.php index d1dbc5b9..766a6eff 100644 --- a/tests/Header/Parameter/QualityTest.php +++ b/tests/Header/Parameter/QualityTest.php @@ -3,42 +3,19 @@ namespace Tests\Innmind\Http\Header\Parameter; -use Innmind\Http\{ - Header\Parameter\Quality, - Header\Parameter, - Exception\DomainException, -}; +use Innmind\Http\Header\Parameter\Quality; use Innmind\BlackBox\PHPUnit\Framework\TestCase; -use PHPUnit\Framework\Attributes\DataProvider; class QualityTest extends TestCase { public function testInterface() { - $p = new Quality(0.8); + $p = Quality::of(80); - $this->assertInstanceOf(Parameter::class, $p); - $this->assertSame('q', $p->name()); - $this->assertSame('0.8', $p->value()); - $this->assertSame('q=0.8', $p->toString()); + $this->assertSame('q', $p->toParameter()->name()); + $this->assertSame('0.8', $p->toParameter()->value()); + $this->assertSame('q=0.8', $p->toParameter()->toString()); - $this->assertSame('q=0', (new Quality(0))->toString()); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidQualityValue($v) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage((string) $v); - - new Quality($v); - } - - public static function invalids() - { - return [ - [-1], - [2], - ]; + $this->assertSame('q=0', Quality::of(0)->toParameter()->toString()); } } diff --git a/tests/Header/RangeTest.php b/tests/Header/RangeTest.php index 84def873..51dd16f3 100644 --- a/tests/Header/RangeTest.php +++ b/tests/Header/RangeTest.php @@ -6,29 +6,18 @@ use Innmind\Http\{ Header\Range, Header, - Header\RangeValue }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class RangeTest extends TestCase { public function testInterface() { - $h = new Range( - $cr = new RangeValue('bytes', 0, 42), - ); + $h = Range::of('bytes', 0, 42); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Range', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($cr, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Range: bytes=0-42', $h->toString()); - $this->assertSame($cr, $h->range()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Range: bytes=0-42', $h->normalize()->toString()); } public function testOf() @@ -36,6 +25,33 @@ public function testOf() $header = Range::of('bytes', 0, 42); $this->assertInstanceOf(Range::class, $header); - $this->assertSame('Range: bytes=0-42', $header->toString()); + $this->assertSame('Range: bytes=0-42', $header->normalize()->toString()); + } + + public function testValid() + { + $this->assertSame( + 'Range: resources=0-42', + Range::of('resources', 0, 42)->normalize()->toString(), + ); + } + + #[DataProvider('invalids')] + public function testReturnNothingWhenInvalidRangeValue($unit, $first, $last) + { + $this->assertNull(Range::maybe($unit, $first, $last)->match( + static fn($header) => $header, + static fn() => null, + )); + } + + public static function invalids() + { + return [ + ['', 0, 42], + ['foo', -1, 42], + ['foo', 0, -42], + ['foo', 100, 42], + ]; } } diff --git a/tests/Header/RangeValueTest.php b/tests/Header/RangeValueTest.php deleted file mode 100644 index 4c381a6f..00000000 --- a/tests/Header/RangeValueTest.php +++ /dev/null @@ -1,45 +0,0 @@ -assertInstanceOf(Value::class, $h); - $this->assertSame('resources', $h->unit()); - $this->assertSame(0, $h->firstPosition()); - $this->assertSame(42, $h->lastPosition()); - $this->assertSame('resources=0-42', $h->toString()); - } - - #[DataProvider('invalids')] - public function testThrowWhenInvalidRangeValue($unit, $first, $last) - { - $this->expectException(DomainException::class); - $this->expectExceptionMessage($unit); - - new RangeValue($unit, $first, $last); - } - - public static function invalids() - { - return [ - ['', 0, 42], - ['foo', -1, 42], - ['foo', 0, -42], - ['foo', 100, 42], - ]; - } -} diff --git a/tests/Header/ReferrerTest.php b/tests/Header/ReferrerTest.php index c860a63f..5beb66e0 100644 --- a/tests/Header/ReferrerTest.php +++ b/tests/Header/ReferrerTest.php @@ -6,9 +6,7 @@ use Innmind\Http\{ Header\Referrer, Header, - Header\ReferrerValue }; -use Innmind\Immutable\Sequence; use Innmind\Url\Url; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -16,19 +14,12 @@ class ReferrerTest extends TestCase { public function testInterface() { - $h = new Referrer( - $av = new ReferrerValue(Url::of('/foo/bar')), + $h = Referrer::of( + Url::of('/foo/bar'), ); - $this->assertInstanceOf(Header::class, $h); - $this->assertSame('Referer', $h->name()); - $v = $h->values(); - $this->assertInstanceOf(Sequence::class, $v); - $this->assertSame($av, $v->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Referer: /foo/bar', $h->toString()); + $this->assertInstanceOf(Header\Custom::class, $h); + $this->assertSame('Referer: /foo/bar', $h->normalize()->toString()); $this->assertSame('/foo/bar', $h->referrer()->toString()); } @@ -37,6 +28,6 @@ public function testOf() $header = Referrer::of(Url::of('/foo/bar')); $this->assertInstanceOf(Referrer::class, $header); - $this->assertSame('Referer: /foo/bar', $header->toString()); + $this->assertSame('Referer: /foo/bar', $header->normalize()->toString()); } } diff --git a/tests/Header/ReferrerValueTest.php b/tests/Header/ReferrerValueTest.php deleted file mode 100644 index 3652a7c2..00000000 --- a/tests/Header/ReferrerValueTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertInstanceOf(Value::class, $a); - $this->assertSame('/foo/bar', $a->toString()); - } -} diff --git a/tests/Header/SetCookieTest.php b/tests/Header/SetCookieTest.php index b29beec4..5f44800a 100644 --- a/tests/Header/SetCookieTest.php +++ b/tests/Header/SetCookieTest.php @@ -6,46 +6,34 @@ use Innmind\Http\{ Header\SetCookie, Header, - Header\CookieValue, - Header\Parameter\Parameter, - Header\CookieParameter\Secure + Header\SetCookie\Directive, + Header\SetCookie\MaxAge, }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class SetCookieTest extends TestCase { public function testInterface() { - $cookie = new SetCookie( - $value = new CookieValue( - new Parameter('foo', 'bar'), - new Secure, - ), - new CookieValue( - new Parameter('bar', 'baz'), - ), - ); + $cookie = SetCookie::of( + 'foo', + 'bar', + Directive::secure, + )->and(SetCookie::of('bar', 'baz')); - $this->assertInstanceOf(Header::class, $cookie); - $this->assertSame('Set-Cookie', $cookie->name()); - $values = $cookie->values(); - $this->assertInstanceOf(Sequence::class, $values); - $this->assertSame($value, $values->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('Set-Cookie: foo=bar; Secure, bar=baz', $cookie->toString()); + $this->assertInstanceOf(Header\Custom::class, $cookie); + $this->assertSame('Set-Cookie: foo=bar; Secure, bar=baz', $cookie->normalize()->toString()); } public function testOf() { $cookie = SetCookie::of( - new Parameter('foo', 'bar'), - new Parameter('bar', 'baz'), + 'foo', + 'bar', + MaxAge::expire(), ); $this->assertInstanceOf(SetCookie::class, $cookie); - $this->assertSame('Set-Cookie: foo=bar; bar=baz', $cookie->toString()); + $this->assertSame('Set-Cookie: foo=bar; Max-Age=-1', $cookie->normalize()->toString()); } } diff --git a/tests/Header/WWWAuthenticateTest.php b/tests/Header/WWWAuthenticateTest.php index 9f2cbd87..fc992e38 100644 --- a/tests/Header/WWWAuthenticateTest.php +++ b/tests/Header/WWWAuthenticateTest.php @@ -5,27 +5,22 @@ use Innmind\Http\{ Header\WWWAuthenticate, - Header\WWWAuthenticateValue, - Header + Header, }; -use Innmind\Immutable\Sequence; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class WWWAuthenticateTest extends TestCase { public function testInterface() { - $header = new WWWAuthenticate( - $value = new WWWAuthenticateValue('Bearer', 'some value'), - ); + $header = WWWAuthenticate\Challenge::maybe('Bearer', 'some value') + ->map(WWWAuthenticate::of(...)) + ->match( + static fn($header) => $header, + static fn() => null, + ); - $this->assertInstanceOf(Header::class, $header); - $this->assertInstanceOf(Sequence::class, $header->values()); - $this->assertCount(1, $header->values()); - $this->assertSame($value, $header->values()->find(static fn() => true)->match( - static fn($first) => $first, - static fn() => null, - )); - $this->assertSame('WWW-Authenticate: Bearer realm="some value"', $header->toString()); + $this->assertInstanceOf(Header\Custom::class, $header); + $this->assertSame('WWW-Authenticate: Bearer realm="some value"', $header->normalize()->toString()); } } diff --git a/tests/Header/WWWAuthenticateValueTest.php b/tests/Header/WWWAuthenticateValueTest.php index 85c76f9a..1222e3b6 100644 --- a/tests/Header/WWWAuthenticateValueTest.php +++ b/tests/Header/WWWAuthenticateValueTest.php @@ -3,29 +3,29 @@ namespace Tests\Innmind\Http\Header; -use Innmind\Http\{ - Header\WWWAuthenticateValue, - Header\Value, - Exception\DomainException -}; +use Innmind\Http\Header\WWWAuthenticate\Challenge; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class WWWAuthenticateValueTest extends TestCase { public function testInterface() { - $value = new WWWAuthenticateValue('Basic', 'some value'); + $value = Challenge::maybe('Basic', 'some value')->match( + static fn($challenge) => $challenge, + static fn() => null, + ); - $this->assertInstanceOf(Value::class, $value); + $this->assertInstanceOf(Challenge::class, $value); $this->assertSame('Basic', $value->scheme()); $this->assertSame('some value', $value->realm()); $this->assertSame('Basic realm="some value"', $value->toString()); } - public function testThrowWhenInvalidSchemeFormat() + public function testReturnNothingWhenInvalidSchemeFormat() { - $this->expectException(DomainException::class); - - new WWWAuthenticateValue('Foo bar', 'some value'); + $this->assertNull(Challenge::maybe('Foo bar', 'some value')->match( + static fn($challenge) => $challenge, + static fn() => null, + )); } } diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index 8a60b2e9..2f2fdbf8 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -5,13 +5,12 @@ use Innmind\Http\{ Headers, - Header as HeaderInterface, - Header\Header, + Header, Header\Allow, Header\ContentType, - Header\ContentTypeValue, - Header\Value\Value, + Header\Value, }; +use Innmind\MediaType\MediaType; use Innmind\Immutable\SideEffect; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -20,10 +19,9 @@ class HeadersTest extends TestCase public function testInterface() { $hs = Headers::of( - $ct = new ContentType( - new ContentTypeValue( - 'application', - 'json', + $ct = ContentType::of( + MediaType::of( + 'application/json', ), ), ); @@ -31,11 +29,11 @@ public function testInterface() $this->assertTrue($hs->contains('content-type')); $this->assertTrue($hs->contains('Content-Type')); $this->assertFalse($hs->contains('content_type')); - $this->assertSame($ct, $hs->get('content-type')->match( + $this->assertEquals($ct->normalize(), $hs->get('content-type')->match( static fn($header) => $header, static fn() => null, )); - $this->assertSame($ct, $hs->get('Content-Type')->match( + $this->assertEquals($ct->normalize(), $hs->get('Content-Type')->match( static fn($header) => $header, static fn() => null, )); @@ -45,10 +43,9 @@ public function testInterface() public function testOf() { $headers = Headers::of( - new ContentType( - new ContentTypeValue( - 'application', - 'json', + ContentType::of( + MediaType::of( + 'application/json', ), ), ); @@ -68,8 +65,12 @@ public function testReturnNothingWhenAccessingUnknownHeader() public function testAdd() { $headers1 = Headers::of(); - $headers2 = ($headers1)(ContentType::of('application', 'json')); - $headers3 = ($headers2)($header = ContentType::of('application', 'json')); + $headers2 = ($headers1)(ContentType::of( + MediaType::of('application/json'), + )); + $headers3 = ($headers2)($header = ContentType::of( + MediaType::of('application/json'), + )->normalize()); $this->assertNotSame($headers1, $headers2); $this->assertInstanceOf(Headers::class, $headers2); @@ -84,14 +85,16 @@ public function testAdd() public function testForeach() { $headers = Headers::of( - ContentType::of('application', 'json'), - new Header('x-foo'), + ContentType::of( + MediaType::of('application/json'), + ), + Header::of('x-foo'), ); $called = 0; $this->assertInstanceOf( SideEffect::class, - $headers->foreach(static function(HeaderInterface $header) use (&$called) { + $headers->foreach(static function($header) use (&$called) { ++$called; }), ); @@ -101,8 +104,10 @@ public function testForeach() public function testReduce() { $headers = Headers::of( - ContentType::of('application', 'json'), - new Header('x-foo'), + ContentType::of( + MediaType::of('application/json'), + ), + Header::of('x-foo'), ); $reduced = $headers->reduce( @@ -120,8 +125,10 @@ static function($carry, $header) { public function testFind() { $headers = Headers::of( - ContentType::of('application', 'json'), - new Header('Allow'), + ContentType::of( + MediaType::of('application/json'), + ), + Header::of('Allow'), ); $this->assertTrue($headers->find(ContentType::class)->match( @@ -134,7 +141,7 @@ public function testFind() )); $headers = Headers::of( - new Header('Content-Type', new Value('application/json')), + Header::of('Content-Type', Value::of('application/json')), ); $this->assertFalse($headers->find(ContentType::class)->match( @@ -146,8 +153,10 @@ public function testFind() public function testFilter() { $headers = Headers::of( - ContentType::of('application', 'json'), - new Header('x-foo'), + ContentType::of( + MediaType::of('application/json'), + ), + Header::of('x-foo'), ); $this->assertCount( @@ -160,19 +169,21 @@ public function testFilter() ); $this->assertCount( 1, - $headers->filter(static fn($header) => $header instanceof ContentType), + $headers->filter(static fn($header) => $header->name() === 'Content-Type'), ); } public function testAll() { $headers = Headers::of( - $contentType = ContentType::of('application', 'json'), - $foo = new Header('x-foo'), + $contentType = ContentType::of( + MediaType::of('application/json'), + ), + $foo = Header::of('x-foo'), ); - $this->assertSame( - [$contentType, $foo], + $this->assertEquals( + [$contentType->normalize(), $foo], $headers->all()->toList(), ); } diff --git a/tests/Request/StringableTest.php b/tests/Request/StringableTest.php index 275bdb47..c5d63e4f 100644 --- a/tests/Request/StringableTest.php +++ b/tests/Request/StringableTest.php @@ -10,9 +10,9 @@ ProtocolVersion, Headers, Header\ContentType, - Header\ContentTypeValue }; use Innmind\Filesystem\File\Content; +use Innmind\MediaType\MediaType; use Innmind\Url\Url; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -25,8 +25,8 @@ public function testInterface() Method::post, ProtocolVersion::v20, Headers::of( - new ContentType( - new ContentTypeValue('text', 'plain'), + ContentType::of( + MediaType::of('text/plain'), ), ), Content::ofString('some body'), diff --git a/tests/Response/StringableTest.php b/tests/Response/StringableTest.php index 95c2ba98..876aeb71 100644 --- a/tests/Response/StringableTest.php +++ b/tests/Response/StringableTest.php @@ -10,11 +10,11 @@ ProtocolVersion, Headers, Header\ContentType, - Header\ContentTypeValue, Header\Allow, - Header\AllowValue + Method, }; use Innmind\Filesystem\File\Content; +use Innmind\MediaType\MediaType; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class StringableTest extends TestCase @@ -25,12 +25,10 @@ public function testInterface() $code = StatusCode::ok, ProtocolVersion::v20, Headers::of( - new ContentType( - new ContentTypeValue('text', 'plain'), - ), - new Allow( - new AllowValue('GET'), + ContentType::of( + MediaType::of('text/plain'), ), + Allow::of(Method::get), ), Content::ofString('{"some":"json", "value":42}'), ); diff --git a/tests/ServerRequest/StringableTest.php b/tests/ServerRequest/StringableTest.php index 0d11dda6..622527a3 100644 --- a/tests/ServerRequest/StringableTest.php +++ b/tests/ServerRequest/StringableTest.php @@ -12,7 +12,6 @@ ProtocolVersion, Headers, Header\Host, - Header\HostValue }; use Innmind\Filesystem\File\Content; use Innmind\Url\Url; @@ -27,11 +26,9 @@ public function testInterface() Method::post, ProtocolVersion::v20, Headers::of( - new Host( - new HostValue( - $url->authority()->host(), - $url->authority()->port(), - ), + Host::of( + $url->authority()->host(), + $url->authority()->port(), ), ), Content::ofString('some body'), @@ -54,11 +51,9 @@ public function testIntegrateQuery() Method::post, ProtocolVersion::v20, Headers::of( - new Host( - new HostValue( - $url->authority()->host(), - $url->authority()->port(), - ), + Host::of( + $url->authority()->host(), + $url->authority()->port(), ), ), Content::ofString('some body'), @@ -88,11 +83,9 @@ public function testIntegrateFormWhenNoBody() Method::post, ProtocolVersion::v20, Headers::of( - new Host( - new HostValue( - $url->authority()->host(), - $url->authority()->port(), - ), + Host::of( + $url->authority()->host(), + $url->authority()->port(), ), ), null,