From b3e393a8662786da263badc805dca641a65d0a41 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 10:17:42 +0100 Subject: [PATCH 01/33] flag internal classes --- src/Expression/Level1.php | 1 + src/Expression/Level2/Fragment.php | 1 + src/Expression/Level2/Reserved.php | 1 + src/Expression/Level3.php | 1 + src/Expression/Level3/Fragment.php | 1 + src/Expression/Level3/Label.php | 1 + src/Expression/Level3/NamedValues.php | 1 + src/Expression/Level3/Parameters.php | 1 + src/Expression/Level3/Path.php | 1 + src/Expression/Level3/Query.php | 1 + src/Expression/Level3/QueryContinuation.php | 1 + src/Expression/Level3/Reserved.php | 1 + src/Expression/Level4.php | 1 + src/Expression/Level4/Composite.php | 1 + src/Expression/Level4/Fragment.php | 1 + src/Expression/Level4/Label.php | 1 + src/Expression/Level4/Parameters.php | 1 + src/Expression/Level4/Parse.php | 4 ++++ src/Expression/Level4/Path.php | 1 + src/Expression/Level4/Query.php | 1 + src/Expression/Level4/QueryContinuation.php | 1 + src/Expression/Level4/Reserved.php | 1 + 22 files changed, 25 insertions(+) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index e63f980..3d7da71 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -15,6 +15,7 @@ /** * @psalm-immutable + * @internal */ final class Level1 implements Expression { diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index a2c5f97..37e9ac7 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 6c33602..877a46f 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 28ca6ec..e9ea7f0 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -13,6 +13,7 @@ /** * @psalm-immutable + * @internal */ final class Level3 implements Expression { diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 506bc4f..ca99e48 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 1e94e75..5533054 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class Label implements Expression { diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index b518f82..81ea0ec 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class NamedValues implements Expression { diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index a5d408b..d1c4eae 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Parameters implements Expression { diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 2b80602..044ec3e 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class Path implements Expression { diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index e89c76a..48d8dcd 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Query implements Expression { diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index df56af6..9c53443 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class QueryContinuation implements Expression { diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 525ac87..ccdfa20 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 6599f67..5c6b83e 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -16,6 +16,7 @@ /** * @psalm-immutable + * @internal */ final class Level4 implements Expression { diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index dd0a403..5d3aa68 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Composite implements Expression { diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index ffc5d2b..5a411bb 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -18,6 +18,7 @@ /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index ca02c63..221b04f 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Label implements Expression { diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 0437c02..9314df9 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -20,6 +20,7 @@ /** * @psalm-immutable + * @internal */ final class Parameters implements Expression { diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index bbe0948..3ce29ea 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -13,10 +13,14 @@ Maybe, }; +/** + * @internal + */ final class Parse { /** * @psalm-pure + * @internal * * @param pure-callable(Name): Expression $standard * @param pure-callable(Name): Expression $explode diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 3417f4d..3b9faa4 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -17,6 +17,7 @@ /** * @psalm-immutable + * @internal */ final class Path implements Expression { diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 0f79a9d..d85d302 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -20,6 +20,7 @@ /** * @psalm-immutable + * @internal */ final class Query implements Expression { diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 8673b21..cb4afc2 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -20,6 +20,7 @@ /** * @psalm-immutable + * @internal */ final class QueryContinuation implements Expression { diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 3f6b2a3..5173d7e 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -19,6 +19,7 @@ /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { From 44cd3cdf82987b076c4462c610ff5d5d1bf8e057 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 10:24:21 +0100 Subject: [PATCH 02/33] require php 8.4 --- .github/workflows/ci.yml | 10 ++++------ CHANGELOG.md | 1 + composer.json | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 189105d..779f162 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,13 +4,11 @@ on: [push, pull_request] jobs: blackbox: - uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@next coverage: - uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@next secrets: inherit psalm: - uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@next cs: - uses: innmind/github-workflows/.github/workflows/cs.yml@main - with: - php-version: '8.2' + uses: innmind/github-workflows/.github/workflows/cs.yml@next diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b4cd1d..2fb2a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Requires `innmind/immutable:~5.18` +- Requires PHP `8.4` ## 3.1.0 - 2023-09-16 diff --git a/composer.json b/composer.json index df5f200..661285d 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "issues": "http://github.com/Innmind/UrlTemplate/issues" }, "require": { - "php": "~8.2", + "php": "~8.4", "innmind/url": "~4.1", "innmind/immutable": "~5.18" }, From c8271d2fa3d0330ac36cb6f890a3bdec0fa3beb2 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 10:31:45 +0100 Subject: [PATCH 03/33] remove named constructor from interface as it is not always implementable --- src/Expression.php | 13 +------------ src/Expression/Level1.php | 4 ++-- src/Expression/Level2/Fragment.php | 4 ++-- src/Expression/Level2/Reserved.php | 4 ++-- src/Expression/Level3.php | 4 ++-- src/Expression/Level3/Fragment.php | 4 ++-- src/Expression/Level3/Label.php | 4 ++-- src/Expression/Level3/NamedValues.php | 10 ---------- src/Expression/Level3/Parameters.php | 4 ++-- src/Expression/Level3/Path.php | 4 ++-- src/Expression/Level3/Query.php | 4 ++-- src/Expression/Level3/QueryContinuation.php | 4 ++-- src/Expression/Level3/Reserved.php | 4 ++-- src/Expression/Level4.php | 3 ++- src/Expression/Level4/Composite.php | 4 ++-- src/Expression/Level4/Fragment.php | 3 ++- src/Expression/Level4/Label.php | 3 ++- src/Expression/Level4/Parameters.php | 3 ++- src/Expression/Level4/Parse.php | 19 +++++++++++-------- src/Expression/Level4/Path.php | 3 ++- src/Expression/Level4/Query.php | 3 ++- src/Expression/Level4/QueryContinuation.php | 3 ++- src/Expression/Level4/Reserved.php | 3 ++- 23 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/Expression.php b/src/Expression.php index 240084b..39fb821 100644 --- a/src/Expression.php +++ b/src/Expression.php @@ -3,11 +3,7 @@ namespace Innmind\UrlTemplate; -use Innmind\Immutable\{ - Map, - Str, - Maybe, -}; +use Innmind\Immutable\Map; /** * @psalm-immutable @@ -15,13 +11,6 @@ */ interface Expression { - /** - * @psalm-pure - * - * @return Maybe - */ - public static function of(Str $string): Maybe; - public function expansion(): Expression\Expansion; /** diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 3d7da71..a58efba 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -30,11 +30,11 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::one($string, Expansion::simple)->map( static fn($name) => new self($name), ); diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 37e9ac7..dc07916 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -32,11 +32,11 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::one($string, Expansion::fragment)->map( static fn($name) => new self($name), ); diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 877a46f..dc82136 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -32,11 +32,11 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::one($string, Expansion::reserved)->map( static fn($name) => new self($name), ); diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index e9ea7f0..1a3f919 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -33,11 +33,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::simple)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index ca99e48..3b363de 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -39,11 +39,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::fragment)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 5533054..ccf4b06 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -39,11 +39,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::label)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 81ea0ec..eded4fd 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -13,7 +13,6 @@ Map, Sequence, Str, - Maybe, }; /** @@ -48,15 +47,6 @@ public function __construct(Expansion $expansion, Sequence $names) ); } - /** - * @psalm-pure - */ - #[\Override] - public static function of(Str $string): Maybe - { - throw new \LogicException('should not be used directly'); - } - /** * @psalm-pure * diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index d1c4eae..ffdebab 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -33,11 +33,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::parameter)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 044ec3e..71b22d0 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -39,11 +39,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::path)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index 48d8dcd..31d4c98 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -33,11 +33,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::query)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index 9c53443..f077a5c 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -33,11 +33,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::queryContinuation)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index ccdfa20..17890c1 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -39,11 +39,11 @@ private function __construct(Sequence $names) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Name::many($string, Expansion::reserved)->map( static fn($names) => new self($names), ); diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 5c6b83e..6913edd 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -36,8 +36,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Level4\Parse::of( diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index 5d3aa68..d48fb53 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -36,11 +36,11 @@ private function __construct(Expansion $expansion, Sequence $expressions) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { - /** @var Maybe */ return Maybe::just($string) ->filter(Expansion::matchesLevel4(...)) ->map(Expansion::simple->clean(...)) diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index 5a411bb..d111b31 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -33,8 +33,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 221b04f..9b016e4 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -30,8 +30,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 9314df9..ba6b14f 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -38,8 +38,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index 3ce29ea..f97d254 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -21,12 +21,13 @@ final class Parse /** * @psalm-pure * @internal + * @template T of Expression * - * @param pure-callable(Name): Expression $standard - * @param pure-callable(Name): Expression $explode - * @param pure-callable(Name, positive-int): Expression $limit + * @param pure-callable(Name): T $standard + * @param pure-callable(Name): T $explode + * @param pure-callable(Name, positive-int): T $limit * - * @return Maybe + * @return Maybe */ public static function of( Str $string, @@ -43,10 +44,11 @@ public static function of( /** * @psalm-pure + * @template T of Expression * - * @param pure-callable(Name): Expression $explode + * @param pure-callable(Name): T $explode * - * @return Maybe + * @return Maybe */ private static function explode( Str $string, @@ -58,10 +60,11 @@ private static function explode( /** * @psalm-pure + * @template T of Expression * - * @param pure-callable(Name, positive-int): Expression $limit + * @param pure-callable(Name, positive-int): T $limit * - * @return Maybe + * @return Maybe */ private static function limit( Str $string, diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 3b9faa4..460e7a7 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -30,8 +30,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index d85d302..6e71703 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -38,8 +38,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index cb4afc2..3e79227 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -38,8 +38,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 5173d7e..bdc2067 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -39,8 +39,9 @@ private function __construct(Name $name) /** * @psalm-pure + * + * @return Maybe */ - #[\Override] public static function of(Str $string): Maybe { return Parse::of( From 73dd8638b2043a58b1b668944c96f8eadbfc2c07 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 10:37:24 +0100 Subject: [PATCH 04/33] update dependencies --- composer.json | 4 +-- tests/TemplateTest.php | 58 ++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index 661285d..c7004aa 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,8 @@ }, "require": { "php": "~8.4", - "innmind/url": "~4.1", - "innmind/immutable": "~5.18" + "innmind/url": "dev-next", + "innmind/immutable": "dev-next" }, "autoload": { "psr-4": { diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index e8dc56e..1a14bd9 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -55,9 +55,11 @@ public function testExpand($pattern, $expected) public function testReturnEmptyMapWhenUrlDoesntMatchTemplate() { - $this->assertCount( + $this->assertSame( 0, - Template::of('/{foo}')->extract(Url::of('/hello%20world%21/foo')), + Template::of('/{foo}') + ->extract(Url::of('/hello%20world%21/foo')) + ->size(), ); } @@ -66,7 +68,7 @@ public function testLevel1Extraction() $variables = Template::of('/{foo}/{bar}')->extract(Url::of('/hello%20world%21/foo')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('hello world!', $variables->get('foo')->match( static fn($value) => $value, static fn() => null, @@ -82,7 +84,7 @@ public function testLevel2Extraction() $variables = Template::of('{+path}/here')->extract(Url::of('/foo/bar/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/bar', $variables->get('path')->match( static fn($value) => $value, static fn() => null, @@ -91,7 +93,7 @@ public function testLevel2Extraction() $variables = Template::of('X{#hello}')->extract(Url::of('X#Hello%20World!')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('Hello World!', $variables->get('hello')->match( static fn($value) => $value, static fn() => null, @@ -103,7 +105,7 @@ public function testLevel3Extraction() $variables = Template::of('/map?{x,y}')->extract(Url::of('/map?1024,768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -116,7 +118,7 @@ public function testLevel3Extraction() $variables = Template::of('/{x,hello,y}')->extract(Url::of('/1024,Hello%20World%21,768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -133,7 +135,7 @@ public function testLevel3Extraction() $variables = Template::of('/{+x,hello,y}')->extract(Url::of('/1024,Hello%20World!,768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -150,7 +152,7 @@ public function testLevel3Extraction() $variables = Template::of('{+path,x}/here')->extract(Url::of('/foo/bar,1024/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -163,7 +165,7 @@ public function testLevel3Extraction() $variables = Template::of('{#x,hello,y}')->extract(Url::of('#1024,Hello%20World!,768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -180,7 +182,7 @@ public function testLevel3Extraction() $variables = Template::of('{#path,x}/here')->extract(Url::of('#/foo/bar,1024/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -193,7 +195,7 @@ public function testLevel3Extraction() $variables = Template::of('{.x,y}')->extract(Url::of('.1024.768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -206,7 +208,7 @@ public function testLevel3Extraction() $variables = Template::of('{/var,x}/here')->extract(Url::of('/value/1024/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('value', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -219,7 +221,7 @@ public function testLevel3Extraction() $variables = Template::of('{;x,y}')->extract(Url::of(';x=1024;y=768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -232,7 +234,7 @@ public function testLevel3Extraction() $variables = Template::of('{;x,y,empty}')->extract(Url::of(';x=1024;y=768;empty')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -249,7 +251,7 @@ public function testLevel3Extraction() $variables = Template::of('{?x,y}')->extract(Url::of('?x=1024&y=768')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -262,7 +264,7 @@ public function testLevel3Extraction() $variables = Template::of('{?x,y,empty}')->extract(Url::of('?x=1024&y=768&empty=')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -279,7 +281,7 @@ public function testLevel3Extraction() $variables = Template::of('?fixed=yes{&x}')->extract(Url::of('?fixed=yes&x=1024')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -288,7 +290,7 @@ public function testLevel3Extraction() $variables = Template::of('{&x,y,empty}')->extract(Url::of('&x=1024&y=768&empty=')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -308,7 +310,7 @@ public function testLevel4Extraction() $variables = Template::of('{var:3}')->extract(Url::of('val')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('val', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -317,7 +319,7 @@ public function testLevel4Extraction() $variables = Template::of('{+path:6}/here')->extract(Url::of('/foo/b/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/b', $variables->get('path')->match( static fn($value) => $value, static fn() => null, @@ -326,7 +328,7 @@ public function testLevel4Extraction() $variables = Template::of('{#path:6}/here')->extract(Url::of('#/foo/b/here')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/b', $variables->get('path')->match( static fn($value) => $value, static fn() => null, @@ -335,7 +337,7 @@ public function testLevel4Extraction() $variables = Template::of('{.var:3}')->extract(Url::of('.val')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('val', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -344,7 +346,7 @@ public function testLevel4Extraction() $variables = Template::of('{/var:1}')->extract(Url::of('/v')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('v', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -353,7 +355,7 @@ public function testLevel4Extraction() $variables = Template::of('{;var:5}')->extract(Url::of(';var=hello')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hello', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -362,7 +364,7 @@ public function testLevel4Extraction() $variables = Template::of('{?var:3}')->extract(Url::of('?var=hel')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hel', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -371,7 +373,7 @@ public function testLevel4Extraction() $variables = Template::of('{&var:3}')->extract(Url::of('&var=hel')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hel', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -384,7 +386,7 @@ public function testExtraction() ->extract(Url::of('http://example.com/search?q=chien&lang=fr')); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('chien', $variables->get('q')->match( static fn($value) => $value, static fn() => null, From e5ecc0dc7e4a430dd46fe0377a128a15c71fef7a Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 12:10:23 +0100 Subject: [PATCH 05/33] CS --- src/Expression/Level3/NamedValues.php | 26 +++++++++++++------------- src/Expressions.php | 14 ++++---------- src/Template.php | 10 ++++------ 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index eded4fd..a92cc34 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -69,19 +69,19 @@ public function expansion(): Expansion #[\Override] public function expand(Map $variables): string { - /** @var Sequence */ - $expanded = $this->expressions->reduce( - Sequence::strings(), - function(Sequence $expanded, string $name, Expression $expression) use ($variables): Sequence { - $value = Str::of($expression->expand($variables)); - - if ($value->empty() && $this->keyOnlyWhenEmpty) { - return ($expanded)($name); - } - - return ($expanded)("$name={$value->toString()}"); - }, - ); + $expanded = $this + ->expressions + ->map(static fn($_, $expression) => $expression->expand($variables)) + ->map(static fn($_, $expression) => Str::of($expression)) + ->toSequence() + ->map(fn($pair) => match ([$pair->value()->empty(), $this->keyOnlyWhenEmpty]) { + [true, true] => $pair->key(), + default => \sprintf( + '%s=%s', + $pair->key(), + $pair->value()->toString(), + ), + }); return Str::of($this->expansion->continuation()->toString()) ->join($expanded) diff --git a/src/Expressions.php b/src/Expressions.php index 59c186f..58c76ff 100644 --- a/src/Expressions.php +++ b/src/Expressions.php @@ -22,16 +22,10 @@ final class Expressions */ public static function of(Str $string): Maybe { - /** - * @psalm-suppress MixedReturnTypeCoercion - * @var Maybe - */ - return self::expressions()->reduce( - Maybe::nothing(), - static fn(Maybe $expression, $attempt) => $expression->otherwise( - static fn() => $attempt($string), - ), - ); + return self::expressions() + ->lookup() + ->first() + ->maybe(static fn($expression) => $expression($string)); } /** diff --git a/src/Template.php b/src/Template.php index 4bbe08f..60316f1 100644 --- a/src/Template.php +++ b/src/Template.php @@ -69,12 +69,10 @@ public function expand(Map $variables): Url { $url = $this->expressions->reduce( $this->template, - static function(Str $template, Expression $expression) use ($variables): Str { - return $template->replace( - $expression->toString(), - $expression->expand($variables), - ); - }, + static fn(Str $template, $expression) => $template->replace( + $expression->toString(), + $expression->expand($variables), + ), ); return Url::of($url->toString()); From 50edb83ba698048bebfeb11134b23e411efc93b3 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 14:52:42 +0100 Subject: [PATCH 06/33] add Template::attempt() --- CHANGELOG.md | 4 +++ src/Expression/Level1.php | 12 +++---- src/Expression/Level2/Fragment.php | 12 +++---- src/Expression/Level2/Reserved.php | 12 +++---- src/Expression/Level3.php | 12 +++---- src/Expression/Level3/Fragment.php | 12 +++---- src/Expression/Level3/Label.php | 12 +++---- src/Expression/Level3/Parameters.php | 12 +++---- src/Expression/Level3/Path.php | 12 +++---- src/Expression/Level3/Query.php | 12 +++---- src/Expression/Level3/QueryContinuation.php | 12 +++---- src/Expression/Level3/Reserved.php | 12 +++---- src/Expression/Level4.php | 6 ++-- src/Expression/Level4/Composite.php | 29 +++++++++-------- src/Expression/Level4/Fragment.php | 6 ++-- src/Expression/Level4/Label.php | 6 ++-- src/Expression/Level4/Parameters.php | 6 ++-- src/Expression/Level4/Parse.php | 8 +++-- src/Expression/Level4/Path.php | 6 ++-- src/Expression/Level4/Query.php | 6 ++-- src/Expression/Level4/QueryContinuation.php | 6 ++-- src/Expression/Level4/Reserved.php | 6 ++-- src/Expressions.php | 15 +++++---- src/Template.php | 36 ++++++++++++--------- 24 files changed, 144 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fb2a6e..cf1e613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- `Innmind\UrlTemplate\Template::attempt()` + ### Changed - Requires `innmind/immutable:~5.18` diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index a58efba..7a5c670 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -10,7 +10,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -31,13 +31,13 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::one($string, Expansion::simple)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::simple) + ->map(static fn($name) => new self($name)) + ->attempt(static fn() => new \LogicException('Cannot parse level 1')); } /** diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index dc07916..3c96b5c 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -12,7 +12,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -33,13 +33,13 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::one($string, Expansion::fragment)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::fragment) + ->map(static fn($name) => new self($name)) + ->attempt(static fn() => new \LogicException('Cannot parse level 2')); } #[\Override] diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index dc82136..5f5a3cb 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -12,7 +12,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -33,13 +33,13 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::one($string, Expansion::reserved)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::reserved) + ->map(static fn($name) => new self($name)) + ->attempt(static fn() => new \LogicException('Cannot parse level 2')); } /** diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 1a3f919..9a23f84 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -8,7 +8,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -34,13 +34,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::simple)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::simple) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } #[\Override] diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 3b363de..55338df 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -13,7 +13,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -40,13 +40,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::fragment)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::fragment) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } #[\Override] diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index ccf4b06..72c9268 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -13,7 +13,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -40,13 +40,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::label)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::label) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } #[\Override] diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index ffdebab..079553a 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -12,7 +12,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -34,13 +34,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::parameter)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::parameter) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } /** diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 71b22d0..4ff36a3 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -13,7 +13,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -40,13 +40,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::path)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::path) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } #[\Override] diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index 31d4c98..34293cf 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -12,7 +12,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -34,13 +34,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::query)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::query) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } /** diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index f077a5c..5b1ffe5 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -12,7 +12,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -34,13 +34,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::queryContinuation)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::queryContinuation) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } public static function named(Name $name): self diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 17890c1..8da3e76 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -13,7 +13,7 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** @@ -40,13 +40,13 @@ private function __construct(Sequence $names) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - return Name::many($string, Expansion::reserved)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::reserved) + ->map(static fn($names) => new self($names)) + ->attempt(static fn() => new \LogicException('Cannot parse level 3')); } #[\Override] diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 6913edd..71360ac 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -11,7 +11,7 @@ Map, Str, Sequence, - Maybe, + Attempt, }; /** @@ -37,9 +37,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Level4\Parse::of( $string, diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index d48fb53..571faea 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -13,6 +13,7 @@ Sequence, Str, Maybe, + Attempt, }; /** @@ -37,18 +38,20 @@ private function __construct(Expansion $expansion, Sequence $expressions) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Maybe::just($string) ->filter(Expansion::matchesLevel4(...)) ->map(Expansion::simple->clean(...)) ->map(static fn($string) => $string->split(',')) + ->attempt(static fn() => new \LogicException('Cannot parse level 4 composite')) ->flatMap( static fn($expressions) => $expressions ->first() ->map(static fn($first) => $first->prepend('{')->append('}')) + ->attempt(static fn() => new \LogicException('First expression not found')) ->flatMap(Expressions::of(...)) ->flatMap( static fn($first) => self::parse($first, $expressions->drop(1)) @@ -149,20 +152,18 @@ public function toString(): string * * @param Sequence $expressions * - * @return Maybe> + * @return Attempt> */ - private static function parse(Expression $first, Sequence $expressions): Maybe + private static function parse(Expression $first, Sequence $expressions): Attempt { - /** @var Maybe> */ - return Maybe::all( - Maybe::just($first), - ...$expressions - ->map(static fn($expression) => $expression->prepend($first->expansion()->continuation()->toString())) - ->map(static fn($expression) => $expression->prepend('{')->append('}')) - ->map(Expressions::of(...)) - ->toList(), - ) - ->map(Sequence::of(...)); + return $expressions + ->map(static fn($expression) => $expression->prepend( + $first->expansion()->continuation()->toString(), + )) + ->map(static fn($expression) => $expression->prepend('{')->append('}')) + ->map(Expressions::of(...)) + ->sink(Sequence::of($first)) + ->attempt(static fn($expressions, $expression) => $expression->map($expressions)); } private function removeLead(): bool diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index d111b31..e582fd7 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -13,7 +13,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -34,9 +34,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 9b016e4..1841d82 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -12,7 +12,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -31,9 +31,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index ba6b14f..f3a036b 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -15,7 +15,7 @@ Map, Str, Sequence, - Maybe, + Attempt, }; /** @@ -39,9 +39,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index f97d254..d0ba2ee 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -11,6 +11,7 @@ use Innmind\Immutable\{ Str, Maybe, + Attempt, }; /** @@ -27,7 +28,7 @@ final class Parse * @param pure-callable(Name): T $explode * @param pure-callable(Name, positive-int): T $limit * - * @return Maybe + * @return Attempt */ public static function of( Str $string, @@ -35,11 +36,12 @@ public static function of( callable $explode, callable $limit, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::one($string, $expansion) ->map($standard) ->otherwise(static fn() => self::explode($string, $explode, $expansion)) - ->otherwise(static fn() => self::limit($string, $limit, $expansion)); + ->otherwise(static fn() => self::limit($string, $limit, $expansion)) + ->attempt(static fn() => new \LogicException('Cannot parse level 4')); } /** diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 460e7a7..1e4d501 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -12,7 +12,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -31,9 +31,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 6e71703..cb62497 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -15,7 +15,7 @@ Map, Str, Sequence, - Maybe, + Attempt, }; /** @@ -39,9 +39,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 3e79227..2ea2522 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -15,7 +15,7 @@ Map, Str, Sequence, - Maybe, + Attempt, }; /** @@ -39,9 +39,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index bdc2067..646ee50 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -14,7 +14,7 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** @@ -40,9 +40,9 @@ private function __construct(Name $name) /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, diff --git a/src/Expressions.php b/src/Expressions.php index 58c76ff..0ea5d50 100644 --- a/src/Expressions.php +++ b/src/Expressions.php @@ -6,7 +6,7 @@ use Innmind\Immutable\{ Sequence, Str, - Maybe, + Attempt, }; /** @@ -18,24 +18,27 @@ final class Expressions /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return self::expressions() ->lookup() ->first() - ->maybe(static fn($expression) => $expression($string)); + ->attempt( + new \LogicException('Failed to parse template'), + static fn($expression) => $expression($string), + ); } /** * @psalm-pure * - * @return Sequence> + * @return Sequence> */ private static function expressions(): Sequence { - /** @var Sequence> */ + /** @var Sequence> */ return Sequence::of( Expression\Level4::of(...), Expression\Level4\Reserved::of(...), diff --git a/src/Template.php b/src/Template.php index 60316f1..98ca083 100644 --- a/src/Template.php +++ b/src/Template.php @@ -13,6 +13,7 @@ Sequence, Str, Maybe, + Attempt, }; /** @@ -42,18 +43,15 @@ private function __construct(Str $template, Sequence $expressions) */ public static function of(string $template): self { - return self::maybe($template)->match( - static fn($self) => $self, - static fn() => throw new DomainException($template), - ); + return self::attempt($template)->unwrap(); } /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function maybe(string $template): Maybe + public static function attempt(string $template): Attempt { $template = Str::of($template); @@ -62,6 +60,16 @@ public static function maybe(string $template): Maybe ); } + /** + * @psalm-pure + * + * @return Maybe + */ + public static function maybe(string $template): Maybe + { + return self::attempt($template)->maybe(); + } + /** * @param Map|list> $variables */ @@ -143,9 +151,9 @@ private function regex(): string * Recursively find the expressions as Str::capture doesnt capture all of * them at the same time * - * @return Maybe> + * @return Attempt> */ - private static function parse(Str $template): Maybe + private static function parse(Str $template): Attempt { /** @var Sequence */ $expressions = Sequence::of(); @@ -164,14 +172,12 @@ private static function parse(Str $template): Maybe ); } while (!$captured->empty()); - /** @var Maybe> */ + /** @var Sequence */ + $parsed = Sequence::of(); + return $expressions ->map(Expressions::of(...)) - ->match( - static fn($first, $rest) => Maybe::all($first, ...$rest->toList())->map( - Sequence::of(...), - ), - static fn() => Maybe::just(Sequence::of()), - ); + ->sink($parsed) + ->attempt(static fn($expressions, $expression) => $expression->map($expressions)); } } From 2f9afb4b17a474af30532363be89cc955155b329 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 15:01:44 +0100 Subject: [PATCH 07/33] use Attempt when parsing expansion names for more explicit errors --- src/Expression/Level1.php | 3 +- src/Expression/Level2/Fragment.php | 3 +- src/Expression/Level2/Reserved.php | 3 +- src/Expression/Level3.php | 3 +- src/Expression/Level3/Fragment.php | 3 +- src/Expression/Level3/Label.php | 3 +- src/Expression/Level3/Parameters.php | 3 +- src/Expression/Level3/Path.php | 3 +- src/Expression/Level3/Query.php | 3 +- src/Expression/Level3/QueryContinuation.php | 3 +- src/Expression/Level3/Reserved.php | 3 +- src/Expression/Level4/Parse.php | 14 +++--- src/Expression/Name.php | 47 +++++++++++++++------ 13 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 7a5c670..f1c94ef 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -36,8 +36,7 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::simple) - ->map(static fn($name) => new self($name)) - ->attempt(static fn() => new \LogicException('Cannot parse level 1')); + ->map(static fn($name) => new self($name)); } /** diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 3c96b5c..86c84a9 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -38,8 +38,7 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::fragment) - ->map(static fn($name) => new self($name)) - ->attempt(static fn() => new \LogicException('Cannot parse level 2')); + ->map(static fn($name) => new self($name)); } #[\Override] diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 5f5a3cb..8398806 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -38,8 +38,7 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::reserved) - ->map(static fn($name) => new self($name)) - ->attempt(static fn() => new \LogicException('Cannot parse level 2')); + ->map(static fn($name) => new self($name)); } /** diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 9a23f84..cb172fb 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -39,8 +39,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::simple) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } #[\Override] diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 55338df..a20e79a 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -45,8 +45,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::fragment) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } #[\Override] diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 72c9268..ba25bc3 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -45,8 +45,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::label) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } #[\Override] diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index 079553a..a538d27 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -39,8 +39,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::parameter) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } /** diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 4ff36a3..a002fb3 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -45,8 +45,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::path) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } #[\Override] diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index 34293cf..2a9aeff 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -39,8 +39,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::query) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } /** diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index 5b1ffe5..8f55303 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -39,8 +39,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::queryContinuation) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } public static function named(Name $name): self diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 8da3e76..494e0f9 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -45,8 +45,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::reserved) - ->map(static fn($names) => new self($names)) - ->attempt(static fn() => new \LogicException('Cannot parse level 3')); + ->map(static fn($names) => new self($names)); } #[\Override] diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index d0ba2ee..45ae3cb 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -10,7 +10,6 @@ }; use Innmind\Immutable\{ Str, - Maybe, Attempt, }; @@ -39,9 +38,8 @@ public static function of( ): Attempt { return Name::one($string, $expansion) ->map($standard) - ->otherwise(static fn() => self::explode($string, $explode, $expansion)) - ->otherwise(static fn() => self::limit($string, $limit, $expansion)) - ->attempt(static fn() => new \LogicException('Cannot parse level 4')); + ->recover(static fn() => self::explode($string, $explode, $expansion)) + ->recover(static fn() => self::limit($string, $limit, $expansion)); } /** @@ -50,13 +48,13 @@ public static function of( * * @param pure-callable(Name): T $explode * - * @return Maybe + * @return Attempt */ private static function explode( Str $string, callable $explode, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::explode($string, $expansion)->map($explode); } @@ -66,13 +64,13 @@ private static function explode( * * @param pure-callable(Name, positive-int): T $limit * - * @return Maybe + * @return Attempt */ private static function limit( Str $string, callable $limit, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::limit($string, $expansion)->map( static fn($tuple) => $limit($tuple[0], $tuple[1]), ); diff --git a/src/Expression/Name.php b/src/Expression/Name.php index fe8dd8b..e8bb5fc 100644 --- a/src/Expression/Name.php +++ b/src/Expression/Name.php @@ -7,6 +7,7 @@ use Innmind\Immutable\{ Str, Maybe, + Attempt, Sequence, }; @@ -45,49 +46,59 @@ public static function of(string $value): self /** * @psalm-pure * - * @return Maybe + * @return Attempt */ public static function one( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matches(...)) ->map($expansion->clean(...)) ->map(static fn($value) => $value->toString()) - ->map(static fn($value) => new self($value)); + ->map(static fn($value) => new self($value)) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe + * @return Attempt */ public static function explode( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matchesExplode(...)) ->map($expansion->cleanExplode(...)) ->map(static fn($value) => $value->toString()) - ->map(static fn($value) => new self($value)); + ->map(static fn($value) => new self($value)) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match exploding %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe + * @return Attempt */ public static function limit( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** * @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string - * @var Maybe + * @var Attempt */ return Maybe::just($value) ->filter($expansion->matchesLimit(...)) @@ -106,18 +117,23 @@ public static function limit( ->filter(static fn(int $limit) => $limit > 0) ->map(static fn($int) => [$name, $int]), ), - ); + ) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match limited %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe> + * @return Attempt> */ public static function many( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matchesMany(...)) @@ -127,7 +143,12 @@ public static function many( ->split(',') ->map(static fn($value) => $value->toString()) ->map(static fn($value) => new self($value)), - ); + ) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match many %s expansions', + $value->toString(), + $expansion->name, + ))); } /** From fed361d367a1ca2824634dddbcc1b45028d9ea28 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 15:13:49 +0100 Subject: [PATCH 08/33] add fluent api to define expansions --- CHANGELOG.md | 5 ++++ src/Expansion.php | 66 ++++++++++++++++++++++++++++++++++++++++++ src/Template.php | 15 ++-------- tests/TemplateTest.php | 28 +++++++++--------- 4 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 src/Expansion.php diff --git a/CHANGELOG.md b/CHANGELOG.md index cf1e613..f3f0eef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,17 @@ ### Added - `Innmind\UrlTemplate\Template::attempt()` +- `Innmind\UrlTemplate\Template::expansion()` ### Changed - Requires `innmind/immutable:~5.18` - Requires PHP `8.4` +### Removed + +- `Innmind\UrlTemplate\Template::expand()`, use `::expansion()` instead + ## 3.1.0 - 2023-09-16 ### Added diff --git a/src/Expansion.php b/src/Expansion.php new file mode 100644 index 0000000..28f7883 --- /dev/null +++ b/src/Expansion.php @@ -0,0 +1,66 @@ + $expressions + * @param Map|list> $variables + */ + private function __construct( + private Str $template, + private Sequence $expressions, + private Map $variables, + ) { + } + + /** + * @psalm-pure + * @internal + * + * @param Sequence $expressions + */ + public static function of(Str $template, Sequence $expressions): self + { + return new self($template, $expressions, Map::of()); + } + + /** + * @param non-empty-string $name Todo use literal strings + * @param string|list|list $value + */ + public function with(string $name, string|array $value): self + { + return new self( + $this->template, + $this->expressions, + ($this->variables)($name, $value), + ); + } + + public function expand(): Url + { + $variables = $this->variables; + $url = $this->expressions->reduce( + $this->template, + static fn(Str $template, $expression) => $template->replace( + $expression->toString(), + $expression->expand($variables), + ), + ); + + return Url::of($url->toString()); + } +} diff --git a/src/Template.php b/src/Template.php index 98ca083..4e5f620 100644 --- a/src/Template.php +++ b/src/Template.php @@ -70,20 +70,9 @@ public static function maybe(string $template): Maybe return self::attempt($template)->maybe(); } - /** - * @param Map|list> $variables - */ - public function expand(Map $variables): Url + public function expansion(): Expansion { - $url = $this->expressions->reduce( - $this->template, - static fn(Str $template, $expression) => $template->replace( - $expression->toString(), - $expression->expand($variables), - ), - ); - - return Url::of($url->toString()); + return Expansion::of($this->template, $this->expressions); } /** diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index 1a14bd9..cb02cfe 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -32,22 +32,22 @@ public function testOf() #[DataProvider('cases')] public function testExpand($pattern, $expected) { - $variables = Map::of() - ('var', 'value') - ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]) - ('username', 'fred') - ('term', 'dog') - ('q', 'chien') - ('lang', 'fr') - ('x', '1024') - ('y', '768'); - $template = Template::of($pattern); - $url = $template->expand($variables); + $url = $template + ->expansion() + ->with('var', 'value') + ->with('hello', 'Hello World!') + ->with('path', '/foo/bar') + ->with('list', ['red', 'green', 'blue']) + ->with('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]) + ->with('username', 'fred') + ->with('term', 'dog') + ->with('q', 'chien') + ->with('lang', 'fr') + ->with('x', '1024') + ->with('y', '768') + ->expand(); $this->assertInstanceOf(Url::class, $url); $this->assertSame($expected, $url->toString()); From cf1b422106fd7b347e7bba278f8d680edfc0b2c0 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 16:19:28 +0100 Subject: [PATCH 09/33] better decompose the steps to explode level 4 expansions --- src/Expression/Level4.php | 44 ++++++++++----------- src/Expression/Level4/Parameters.php | 30 +++++++------- src/Expression/Level4/Query.php | 34 ++++++++-------- src/Expression/Level4/QueryContinuation.php | 28 +++++++------ 4 files changed, 67 insertions(+), 69 deletions(-) diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 71360ac..ae66a1d 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -232,30 +232,26 @@ function($variableToExpand) use ($variables): string { */ private function explodeList(Map $variables, array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map( - function($variableToExpand) use ($variables): string { - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $variableToExpand = $value; - } - - $variables = ($variables)($this->name->toString(), $variableToExpand); - - $value = $this->expression->expand($variables); - - if (isset($name)) { - /** @psalm-suppress MixedArgument */ - $name = Name::of($name); - $value = \sprintf( - '%s=%s', - $name->toString(), - $value, - ); - } - - return $value; - }, - ); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => $this->expression->expand( + ($variables)($this->name->toString(), $value), + ), + default => [ + $value[0], + $this->expression->expand( + ($variables)($this->name->toString(), $value[1]), + ), + ], + }) + ->map(static fn($value) => match (true) { + \is_string($value) => $value, + default => \sprintf( + '%s=%s', + Name::of($value[0])->toString(), // todo move verification earlier on + $value[1], + ), + }); return $this->separator() ->join($expanded) diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index f3a036b..db4cd89 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -200,21 +200,21 @@ function($variableToExpand) use ($variables): string { */ private function explodeList(Map $variables, array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map( - function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - return Level3\Parameters::named($name)->expand($variables); - }, - ); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => [ + Name::of($value[0]), // todo move wrapping earlier on + $value[1], + ], + }) + ->map(static fn($pair) => [ + $pair[0], + ($variables)($pair[0]->toString(), $pair[1]), + ]) + ->map(static fn($pair) => Level3\Parameters::named($pair[0])->expand( + $pair[1], + )); return Str::of('')->join($expanded)->toString(); } diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index cb62497..ddc7082 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -199,23 +199,23 @@ static function($variableToExpand): Sequence { */ private function explodeList(Map $variables, array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map(function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - $value = Level3\Query::named($name)->expand($variables); - - // the substring is here to remove the '?' as it should be a '&' - // done below in the join - return Str::of($value)->drop(1)->toString(); - }); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => [ + Name::of($value[0]), // todo move wrapping earlier on + $value[1], + ], + }) + ->map(static fn($pair) => [ + $pair[0], + ($variables)($pair[0]->toString(), $pair[1]), + ]) + ->map(static fn($pair) => Level3\Query::named($pair[0])->expand( + $pair[1], + )) + ->map(Str::of(...)) + ->map(static fn($value) => $value->drop(1)->toString()); // to remove the '?' as it should be a '&' done below in the join return Str::of('&') ->join($expanded) diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 2ea2522..e4bfccb 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -198,19 +198,21 @@ static function($variableToExpand): Sequence { */ private function explodeList(Map $variables, array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map(function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - return Level3\QueryContinuation::named($name)->expand($variables); - }); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => [ + Name::of($value[0]), // todo move wrapping earlier on + $value[1], + ], + }) + ->map(static fn($pair) => [ + $pair[0], + ($variables)($pair[0]->toString(), $pair[1]), + ]) + ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( + $pair[1], + )); return Str::of('')->join($expanded)->toString(); } From f627f6e42ae5bf016a5b7c550373e1880a1adad3 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 16:55:00 +0100 Subject: [PATCH 10/33] better decompose the kind of variables to use to expand --- src/Expansion.php | 22 ++++++- src/Expression.php | 6 +- src/Expression/Level1.php | 8 +-- src/Expression/Level2/Fragment.php | 9 ++- src/Expression/Level2/Reserved.php | 8 +-- src/Expression/Level3.php | 4 +- src/Expression/Level3/Fragment.php | 4 +- src/Expression/Level3/Label.php | 4 +- src/Expression/Level3/NamedValues.php | 4 +- src/Expression/Level3/Parameters.php | 4 +- src/Expression/Level3/Path.php | 4 +- src/Expression/Level3/Query.php | 4 +- src/Expression/Level3/QueryContinuation.php | 4 +- src/Expression/Level3/Reserved.php | 4 +- src/Expression/Level4.php | 60 ++++++++++++++----- src/Expression/Level4/Composite.php | 4 +- src/Expression/Level4/Fragment.php | 4 +- src/Expression/Level4/Label.php | 4 +- src/Expression/Level4/Parameters.php | 52 +++++++++++----- src/Expression/Level4/Path.php | 4 +- src/Expression/Level4/Query.php | 52 +++++++++++----- src/Expression/Level4/QueryContinuation.php | 52 +++++++++++----- src/Expression/Level4/Reserved.php | 4 +- tests/Expression/Level1Test.php | 8 +++ tests/Expression/Level2/FragmentTest.php | 10 ++++ tests/Expression/Level2/ReservedTest.php | 10 ++++ tests/Expression/Level3/FragmentTest.php | 4 +- tests/Expression/Level3/LabelTest.php | 4 +- tests/Expression/Level3/ParametersTest.php | 4 +- tests/Expression/Level3/PathTest.php | 4 +- .../Level3/QueryContinuationTest.php | 4 +- tests/Expression/Level3/QueryTest.php | 4 +- tests/Expression/Level3/ReservedTest.php | 4 +- tests/Expression/Level3Test.php | 4 +- tests/Expression/Level4/CompositeTest.php | 22 ++++--- tests/Expression/Level4/FragmentTest.php | 18 +++--- tests/Expression/Level4/LabelTest.php | 18 +++--- tests/Expression/Level4/ParametersTest.php | 18 +++--- tests/Expression/Level4/PathTest.php | 16 ++--- .../Level4/QueryContinuationTest.php | 18 +++--- tests/Expression/Level4/QueryTest.php | 18 +++--- tests/Expression/Level4/ReservedTest.php | 18 +++--- tests/Expression/Level4Test.php | 22 +++---- 43 files changed, 355 insertions(+), 198 deletions(-) diff --git a/src/Expansion.php b/src/Expansion.php index 28f7883..b8436e0 100644 --- a/src/Expansion.php +++ b/src/Expansion.php @@ -52,12 +52,30 @@ public function with(string $name, string|array $value): self public function expand(): Url { - $variables = $this->variables; + /** @var Map */ + $values = $this->variables->filter( + static fn($_, $value) => \is_string($value), + ); + /** @var Map> */ + $lists = $this->variables->filter( + static fn($_, $value) => \is_array($value) && \array_all( + $value, + static fn($value) => \is_string($value), + ), + ); + /** @var Map> */ + $keys = $this->variables->filter( + static fn($_, $value) => \is_array($value) && \array_all( + $value, + static fn($value) => \is_array($value), + ), + ); + $url = $this->expressions->reduce( $this->template, static fn(Str $template, $expression) => $template->replace( $expression->toString(), - $expression->expand($variables), + $expression->expand($values, $lists, $keys), ), ); diff --git a/src/Expression.php b/src/Expression.php index 39fb821..7076536 100644 --- a/src/Expression.php +++ b/src/Expression.php @@ -14,9 +14,11 @@ interface Expression public function expansion(): Expression\Expansion; /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys */ - public function expand(Map $variables): string; + public function expand(Map $values, Map $lists, Map $keys): string; public function regex(): string; public function toString(): string; } diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index f1c94ef..63d70c3 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -54,14 +54,12 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) ->match( - fn(string $variable) => ($this->encode)($variable), + $this->encode, static fn() => '', ); } diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 86c84a9..0bf824f 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -48,14 +48,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) + ->map($this->encode) ->match( - fn(string $variable) => '#'.($this->encode)($variable), + static fn(string $variable) => '#'.$variable, static fn() => '', ); } diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 8398806..2d07a39 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -56,14 +56,12 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) ->match( - fn(string $variable) => ($this->encode)($variable), + $this->encode, static fn() => '', ); } diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index cb172fb..6dc9e61 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -49,10 +49,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); return Str::of(',')->join($expanded)->toString(); diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index a20e79a..7edc984 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -55,10 +55,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); return Str::of(',') diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index ba25bc3..6791d8e 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -55,10 +55,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); return Str::of('.') diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index a92cc34..709e427 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -67,11 +67,11 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this ->expressions - ->map(static fn($_, $expression) => $expression->expand($variables)) + ->map(static fn($_, $expression) => $expression->expand($values, $lists, $keys)) ->map(static fn($_, $expression) => Str::of($expression)) ->toSequence() ->map(fn($pair) => match ([$pair->value()->empty(), $this->keyOnlyWhenEmpty]) { diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index a538d27..fcde649 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -57,9 +57,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index a002fb3..da1c77b 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -55,11 +55,11 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { return Str::of('/') ->join($this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), )) ->prepend('/') ->toString(); diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index 2a9aeff..e33237c 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -57,9 +57,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index 8f55303..e335db7 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -54,9 +54,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 494e0f9..c5be608 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -55,10 +55,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); return Str::of(',')->join($expanded)->toString(); diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index ae66a1d..42f836c 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -111,8 +111,16 @@ public function withExpression(callable $expression): self } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { + // todo break up + /** + * @psalm-suppress InvalidArgument + * @var Map|list> + */ + $variables = $values + ->merge($lists) + ->merge($keys); $variable = $variables->get($this->name->toString())->match( static fn($variable) => $variable, static fn() => null, @@ -123,20 +131,22 @@ public function expand(Map $variables): string } if (\is_array($variable)) { - return $this->expandList($variables, $variable); + return $this->expandList($values, $lists, $keys, $variable); } if ($this->explode) { - return $this->explodeList($variables, [$variable]); + return $this->explodeList($values, $lists, $keys, [$variable]); } if ($this->mustLimit()) { $value = Str::of($variable)->take($this->limit); $value = $this->expression->expand( - ($variables)($this->name->toString(), $value->toString()), + ($values)($this->name->toString(), $value->toString()), + $lists, + $keys, ); } else { - $value = $this->expression->expand($variables); + $value = $this->expression->expand($values, $lists, $keys); } return "{$this->expansion->toString()}$value"; @@ -189,13 +199,19 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string - { + private function expandList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($values, $lists, $keys, $variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -211,11 +227,13 @@ static function($variableToExpand): Sequence { ); $expanded = $flattenedVariables->map( - function($variableToExpand) use ($variables): string { + function($variableToExpand) use ($values, $lists, $keys): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation return $this->expression->expand( - ($variables)($this->name->toString(), $variableToExpand), + ($values)($this->name->toString(), $variableToExpand), + $lists, + $keys, ); }, ); @@ -227,20 +245,30 @@ function($variableToExpand) use ($variables): string { } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string - { + private function explodeList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => $this->expression->expand( - ($variables)($this->name->toString(), $value), + ($values)($this->name->toString(), $value), + $lists, + $keys, ), default => [ $value[0], $this->expression->expand( - ($variables)($this->name->toString(), $value[1]), + ($values)($this->name->toString(), $value[1]), + $lists, + $keys, ), ], }) diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index 571faea..5584a43 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -70,10 +70,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); //potentially remove the lead characters from the expressions except for diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index e582fd7..b053b88 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -88,9 +88,9 @@ public function regex(): string } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 1841d82..37e9b7a 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -76,9 +76,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index db4cd89..8df7d9d 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -83,8 +83,16 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { + // todo break up + /** + * @psalm-suppress InvalidArgument + * @var Map|list> + */ + $variables = $values + ->merge($lists) + ->merge($keys); $variable = $variables->get($this->name->toString())->match( static fn($variable) => $variable, static fn() => null, @@ -95,14 +103,14 @@ public function expand(Map $variables): string } if (\is_array($variable)) { - return $this->expandList($variables, $variable); + return $this->expandList($values, $lists, $keys, $variable); } if ($this->explode) { - return $this->explodeList($variables, [$variable]); + return $this->explodeList($values, $lists, $keys, [$variable]); } - $value = Str::of($this->expression->expand($variables)); + $value = Str::of($this->expression->expand($values, $lists, $keys)); if ($this->mustLimit()) { return ";{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -158,13 +166,19 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string - { + private function expandList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($values, $lists, $keys, $variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -179,12 +193,12 @@ static function($variableToExpand): Sequence { }, ); $expanded = $flattenedVariables->map( - function($variableToExpand) use ($variables): string { + function($variableToExpand) use ($values, $lists, $keys): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - $variables = ($variables)($this->name->toString(), $variableToExpand); + $values = ($values)($this->name->toString(), $variableToExpand); - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); }, ); @@ -195,11 +209,17 @@ function($variableToExpand) use ($variables): string { } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string - { + private function explodeList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -210,10 +230,12 @@ private function explodeList(Map $variables, array $variablesToExpand): string }) ->map(static fn($pair) => [ $pair[0], - ($variables)($pair[0]->toString(), $pair[1]), + ($values)($pair[0]->toString(), $pair[1]), ]) ->map(static fn($pair) => Level3\Parameters::named($pair[0])->expand( $pair[1], + $lists, + $keys, )); return Str::of('')->join($expanded)->toString(); diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 1e4d501..78c6477 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -76,9 +76,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index ddc7082..dbadba8 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -83,8 +83,16 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { + // todo break up + /** + * @psalm-suppress InvalidArgument + * @var Map|list> + */ + $variables = $values + ->merge($lists) + ->merge($keys); $variable = $variables->get($this->name->toString())->match( static fn($variable) => $variable, static fn() => null, @@ -95,14 +103,14 @@ public function expand(Map $variables): string } if (\is_array($variable)) { - return $this->expandList($variables, $variable); + return $this->expandList($values, $lists, $keys, $variable); } if ($this->explode) { - return $this->explodeList($variables, [$variable]); + return $this->explodeList($values, $lists, $keys, [$variable]); } - $value = Str::of($this->expression->expand($variables)); + $value = Str::of($this->expression->expand($values, $lists, $keys)); if ($this->mustLimit()) { return "?{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -158,13 +166,19 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string - { + private function expandList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($values, $lists, $keys, $variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -178,13 +192,13 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($variables): string { + $expanded = $flattenedVariables->map(function($variableToExpand) use ($values, $lists, $keys): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - $variables = ($variables)($this->name->toString(), $variableToExpand); + $values = ($values)($this->name->toString(), $variableToExpand); - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); }); return Str::of(',') @@ -194,11 +208,17 @@ static function($variableToExpand): Sequence { } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string - { + private function explodeList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -209,10 +229,12 @@ private function explodeList(Map $variables, array $variablesToExpand): string }) ->map(static fn($pair) => [ $pair[0], - ($variables)($pair[0]->toString(), $pair[1]), + ($values)($pair[0]->toString(), $pair[1]), ]) ->map(static fn($pair) => Level3\Query::named($pair[0])->expand( $pair[1], + $lists, + $keys, )) ->map(Str::of(...)) ->map(static fn($value) => $value->drop(1)->toString()); // to remove the '?' as it should be a '&' done below in the join diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index e4bfccb..e71c9a5 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -83,8 +83,16 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { + // todo break up + /** + * @psalm-suppress InvalidArgument + * @var Map|list> + */ + $variables = $values + ->merge($lists) + ->merge($keys); $variable = $variables->get($this->name->toString())->match( static fn($variable) => $variable, static fn() => null, @@ -95,14 +103,14 @@ public function expand(Map $variables): string } if (\is_array($variable)) { - return $this->expandList($variables, $variable); + return $this->expandList($values, $lists, $keys, $variable); } if ($this->explode) { - return $this->explodeList($variables, [$variable]); + return $this->explodeList($values, $lists, $keys, [$variable]); } - $value = Str::of($this->expression->expand($variables)); + $value = Str::of($this->expression->expand($values, $lists, $keys)); if ($this->mustLimit()) { return "&{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -158,13 +166,19 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string - { + private function expandList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($values, $lists, $keys, $variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -178,12 +192,12 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($variables): string { + $expanded = $flattenedVariables->map(function($variableToExpand) use ($values, $lists, $keys): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - $variables = ($variables)($this->name->toString(), $variableToExpand); + $values = ($values)($this->name->toString(), $variableToExpand); - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); }); return Str::of(',') @@ -193,11 +207,17 @@ static function($variableToExpand): Sequence { } /** - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string - { + private function explodeList( + Map $values, + Map $lists, + Map $keys, + array $variablesToExpand, + ): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -208,10 +228,12 @@ private function explodeList(Map $variables, array $variablesToExpand): string }) ->map(static fn($pair) => [ $pair[0], - ($variables)($pair[0]->toString(), $pair[1]), + ($values)($pair[0]->toString(), $pair[1]), ]) ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( $pair[1], + $lists, + $keys, )); return Str::of('')->join($expanded)->toString(); diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 646ee50..70a1cbe 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -90,9 +90,9 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/tests/Expression/Level1Test.php b/tests/Expression/Level1Test.php index 4c12ec9..6970f74 100644 --- a/tests/Expression/Level1Test.php +++ b/tests/Expression/Level1Test.php @@ -46,12 +46,18 @@ public function testExpand() $this->assertSame('value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('Hello%20World%21', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -94,7 +100,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level2/FragmentTest.php b/tests/Expression/Level2/FragmentTest.php index 0fb4c85..c5ccc66 100644 --- a/tests/Expression/Level2/FragmentTest.php +++ b/tests/Expression/Level2/FragmentTest.php @@ -46,15 +46,23 @@ public function testExpand() $this->assertSame('#value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('#Hello%20World!', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('#/foo/bar', $expression->expand( Map::of(['foo', '/foo/bar']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -97,7 +105,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level2/ReservedTest.php b/tests/Expression/Level2/ReservedTest.php index f894341..3bbec0c 100644 --- a/tests/Expression/Level2/ReservedTest.php +++ b/tests/Expression/Level2/ReservedTest.php @@ -46,15 +46,23 @@ public function testExpand() $this->assertSame('value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('Hello%20World!', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('/foo/bar', $expression->expand( Map::of(['foo', '/foo/bar']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -97,7 +105,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level3/FragmentTest.php b/tests/Expression/Level3/FragmentTest.php index fd0d27d..26032ac 100644 --- a/tests/Expression/Level3/FragmentTest.php +++ b/tests/Expression/Level3/FragmentTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '#1024,Hello%20World!,768', Fragment::of(Str::of('{#x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '#/foo/bar,1024', Fragment::of(Str::of('{#path,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/LabelTest.php b/tests/Expression/Level3/LabelTest.php index 45e886a..8103978 100644 --- a/tests/Expression/Level3/LabelTest.php +++ b/tests/Expression/Level3/LabelTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '.1024.768', Label::of(Str::of('{.x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '.value', Label::of(Str::of('{.var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ParametersTest.php b/tests/Expression/Level3/ParametersTest.php index 617675a..1faefd7 100644 --- a/tests/Expression/Level3/ParametersTest.php +++ b/tests/Expression/Level3/ParametersTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( ';x=1024;y=768', Parameters::of(Str::of('{;x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( ';x=1024;y=768;empty', Parameters::of(Str::of('{;x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/PathTest.php b/tests/Expression/Level3/PathTest.php index ad42b76..d9d2b02 100644 --- a/tests/Expression/Level3/PathTest.php +++ b/tests/Expression/Level3/PathTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '/value', Path::of(Str::of('{/var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '/value/1024', Path::of(Str::of('{/var,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryContinuationTest.php b/tests/Expression/Level3/QueryContinuationTest.php index 9ecf693..2491c2e 100644 --- a/tests/Expression/Level3/QueryContinuationTest.php +++ b/tests/Expression/Level3/QueryContinuationTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '&x=1024&y=768', QueryContinuation::of(Str::of('{&x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '&x=1024&y=768&empty=', QueryContinuation::of(Str::of('{&x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryTest.php b/tests/Expression/Level3/QueryTest.php index 37d487f..8c2b8a1 100644 --- a/tests/Expression/Level3/QueryTest.php +++ b/tests/Expression/Level3/QueryTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '?x=1024&y=768', Query::of(Str::of('{?x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '?x=1024&y=768&empty=', Query::of(Str::of('{?x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ReservedTest.php b/tests/Expression/Level3/ReservedTest.php index c65353d..450d869 100644 --- a/tests/Expression/Level3/ReservedTest.php +++ b/tests/Expression/Level3/ReservedTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '1024,Hello%20World!,768', Reserved::of(Str::of('{+x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '/foo/bar,1024', Reserved::of(Str::of('{+path,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level3Test.php b/tests/Expression/Level3Test.php index 4cf0344..b0722f8 100644 --- a/tests/Expression/Level3Test.php +++ b/tests/Expression/Level3Test.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '1024,768', Level3::of(Str::of('{x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '1024,Hello%20World%21,768', Level3::of(Str::of('{x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); diff --git a/tests/Expression/Level4/CompositeTest.php b/tests/Expression/Level4/CompositeTest.php index 6ff72f1..a258236 100644 --- a/tests/Expression/Level4/CompositeTest.php +++ b/tests/Expression/Level4/CompositeTest.php @@ -47,24 +47,26 @@ public function testStringCast() public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '/v/value', Composite::of(Str::of('{/var:1,var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/red/green/blue/%2Ffoo', Composite::of(Str::of('{/list*,path:4}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -73,11 +75,13 @@ public function testExpand() #[DataProvider('cases')] public function testOf($pattern, $expected) { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $expression = Composite::of(Str::of($pattern))->match( @@ -86,7 +90,7 @@ public function testOf($pattern, $expected) ); $this->assertSame($pattern, $expression->toString()); - $this->assertSame($expected, $expression->expand($variables)); + $this->assertSame($expected, $expression->expand($values, $lists, $keys)); } public function testReturnNothingWhenInvalidPattern() diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index fb752f5..6955511 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '#/foo/b', Fragment::of(Str::of('{#path:6}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#red,green,blue', Fragment::of(Str::of('{#list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#red,green,blue', Fragment::of(Str::of('{#list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#semi,;,dot,.,comma,,', Fragment::of(Str::of('{#keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#semi=;,dot=.,comma=,', Fragment::of(Str::of('{#keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index 9ed0645..b11b54b 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '.val', Label::of(Str::of('{.var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.red,green,blue', Label::of(Str::of('{.list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.red.green.blue', Label::of(Str::of('{.list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.semi,%3B,dot,.,comma,%2C', Label::of(Str::of('{.keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.semi=%3B.dot=..comma=%2C', Label::of(Str::of('{.keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index 3541c1b..335b21c 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( ';hello=Hello', Parameters::of(Str::of('{;hello:5}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';list=red,green,blue', Parameters::of(Str::of('{;list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';list=red;list=green;list=blue', Parameters::of(Str::of('{;list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';keys=semi,%3B,dot,.,comma,%2C', Parameters::of(Str::of('{;keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';semi=%3B;dot=.;comma=%2C', Parameters::of(Str::of('{;keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index 0aaf2dc..4301aa3 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -86,38 +86,40 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '/red,green,blue', Path::of(Str::of('{/list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/red/green/blue', Path::of(Str::of('{/list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/semi,%3B,dot,.,comma,%2C', Path::of(Str::of('{/keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/semi=%3B/dot=./comma=%2C', Path::of(Str::of('{/keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index 6300a0d..3357797 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '&var=val', QueryContinuation::of(Str::of('{&var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&list=red,green,blue', QueryContinuation::of(Str::of('{&list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&list=red&list=green&list=blue', QueryContinuation::of(Str::of('{&list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&keys=semi,%3B,dot,.,comma,%2C', QueryContinuation::of(Str::of('{&keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&semi=%3B&dot=.&comma=%2C', QueryContinuation::of(Str::of('{&keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index b95b0cf..3cc2242 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '?var=val', Query::of(Str::of('{?var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?list=red,green,blue', Query::of(Str::of('{?list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?list=red&list=green&list=blue', Query::of(Str::of('{?list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?keys=semi,%3B,dot,.,comma,%2C', Query::of(Str::of('{?keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?semi=%3B&dot=.&comma=%2C', Query::of(Str::of('{?keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index 2b13056..502c30d 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -86,45 +86,47 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '/foo/b', Reserved::of(Str::of('{+path:6}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Reserved::of(Str::of('{+list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Reserved::of(Str::of('{+list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi,;,dot,.,comma,,', Reserved::of(Str::of('{+keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi=;,dot=.,comma=,', Reserved::of(Str::of('{+keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 68b5571..20f0958 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -86,59 +86,61 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( 'val', Level4::of(Str::of('{var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'value', Level4::of(Str::of('{var:30}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '%2Ffoo', Level4::of(Str::of('{path:4}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Level4::of(Str::of('{list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Level4::of(Str::of('{list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi,%3B,dot,.,comma,%2C', Level4::of(Str::of('{keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi=%3B,dot=.,comma=%2C', Level4::of(Str::of('{keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); From 572e356f959f597ab586af1d257af7f94f89331a Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:07:44 +0100 Subject: [PATCH 11/33] remove unnecessary passing variables --- src/Expression/Level4.php | 54 ++++++++------------ src/Expression/Level4/Parameters.php | 54 ++++++++------------ src/Expression/Level4/Query.php | 55 ++++++++------------- src/Expression/Level4/QueryContinuation.php | 54 ++++++++------------ 4 files changed, 83 insertions(+), 134 deletions(-) diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 42f836c..abf661c 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -131,19 +131,19 @@ public function expand(Map $values, Map $lists, Map $keys): string } if (\is_array($variable)) { - return $this->expandList($values, $lists, $keys, $variable); + return $this->expandList($variable); } if ($this->explode) { - return $this->explodeList($values, $lists, $keys, [$variable]); + return $this->explodeList([$variable]); } if ($this->mustLimit()) { $value = Str::of($variable)->take($this->limit); $value = $this->expression->expand( - ($values)($this->name->toString(), $value->toString()), - $lists, - $keys, + Map::of([$this->name->toString(), $value->toString()]), + Map::of(), + Map::of(), ); } else { $value = $this->expression->expand($values, $lists, $keys); @@ -199,19 +199,12 @@ private function mustLimit(): bool } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function expandList(array $variablesToExpand): string + { if ($this->explode) { - return $this->explodeList($values, $lists, $keys, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -227,13 +220,13 @@ static function($variableToExpand): Sequence { ); $expanded = $flattenedVariables->map( - function($variableToExpand) use ($values, $lists, $keys): string { + function($variableToExpand): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation return $this->expression->expand( - ($values)($this->name->toString(), $variableToExpand), - $lists, - $keys, + Map::of([$this->name->toString(), $variableToExpand]), + Map::of(), + Map::of(), ); }, ); @@ -245,30 +238,23 @@ function($variableToExpand) use ($values, $lists, $keys): string { } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function explodeList(array $variablesToExpand): string + { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => $this->expression->expand( - ($values)($this->name->toString(), $value), - $lists, - $keys, + Map::of([$this->name->toString(), $value]), + Map::of(), + Map::of(), ), default => [ $value[0], $this->expression->expand( - ($values)($this->name->toString(), $value[1]), - $lists, - $keys, + Map::of([$this->name->toString(), $value[1]]), + Map::of(), + Map::of(), ), ], }) diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 8df7d9d..ff199d2 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -103,14 +103,18 @@ public function expand(Map $values, Map $lists, Map $keys): string } if (\is_array($variable)) { - return $this->expandList($values, $lists, $keys, $variable); + return $this->expandList($variable); } if ($this->explode) { - return $this->explodeList($values, $lists, $keys, [$variable]); + return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand($values, $lists, $keys)); + $value = Str::of($this->expression->expand( + $values, + Map::of(), + Map::of(), + )); if ($this->mustLimit()) { return ";{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -166,19 +170,12 @@ private function mustLimit(): bool } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function expandList(array $variablesToExpand): string + { if ($this->explode) { - return $this->explodeList($values, $lists, $keys, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -193,12 +190,14 @@ static function($variableToExpand): Sequence { }, ); $expanded = $flattenedVariables->map( - function($variableToExpand) use ($values, $lists, $keys): string { + function($variableToExpand): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - $values = ($values)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($values, $lists, $keys); + return $this->expression->expand( + Map::of([$this->name->toString(), $variableToExpand]), + Map::of(), + Map::of(), + ); }, ); @@ -209,17 +208,10 @@ function($variableToExpand) use ($values, $lists, $keys): string { } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function explodeList(array $variablesToExpand): string + { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -228,14 +220,10 @@ private function explodeList( $value[1], ], }) - ->map(static fn($pair) => [ - $pair[0], - ($values)($pair[0]->toString(), $pair[1]), - ]) ->map(static fn($pair) => Level3\Parameters::named($pair[0])->expand( - $pair[1], - $lists, - $keys, + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), )); return Str::of('')->join($expanded)->toString(); diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index dbadba8..0b6ad07 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -103,14 +103,18 @@ public function expand(Map $values, Map $lists, Map $keys): string } if (\is_array($variable)) { - return $this->expandList($values, $lists, $keys, $variable); + return $this->expandList($variable); } if ($this->explode) { - return $this->explodeList($values, $lists, $keys, [$variable]); + return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand($values, $lists, $keys)); + $value = Str::of($this->expression->expand( + $values, + Map::of(), + Map::of(), + )); if ($this->mustLimit()) { return "?{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -166,19 +170,12 @@ private function mustLimit(): bool } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function expandList(array $variablesToExpand): string + { if ($this->explode) { - return $this->explodeList($values, $lists, $keys, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -192,13 +189,14 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($values, $lists, $keys): string { + $expanded = $flattenedVariables->map(function($variableToExpand): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - - $values = ($values)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($values, $lists, $keys); + return $this->expression->expand( + Map::of([$this->name->toString(), $variableToExpand]), + Map::of(), + Map::of(), + ); }); return Str::of(',') @@ -208,17 +206,10 @@ static function($variableToExpand): Sequence { } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function explodeList(array $variablesToExpand): string + { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -227,14 +218,10 @@ private function explodeList( $value[1], ], }) - ->map(static fn($pair) => [ - $pair[0], - ($values)($pair[0]->toString(), $pair[1]), - ]) ->map(static fn($pair) => Level3\Query::named($pair[0])->expand( - $pair[1], - $lists, - $keys, + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), )) ->map(Str::of(...)) ->map(static fn($value) => $value->drop(1)->toString()); // to remove the '?' as it should be a '&' done below in the join diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index e71c9a5..de88224 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -103,14 +103,18 @@ public function expand(Map $values, Map $lists, Map $keys): string } if (\is_array($variable)) { - return $this->expandList($values, $lists, $keys, $variable); + return $this->expandList($variable); } if ($this->explode) { - return $this->explodeList($values, $lists, $keys, [$variable]); + return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand($values, $lists, $keys)); + $value = Str::of($this->expression->expand( + $values, + Map::of(), + Map::of(), + )); if ($this->mustLimit()) { return "&{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -166,19 +170,12 @@ private function mustLimit(): bool } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function expandList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function expandList(array $variablesToExpand): string + { if ($this->explode) { - return $this->explodeList($values, $lists, $keys, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -192,12 +189,14 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($values, $lists, $keys): string { + $expanded = $flattenedVariables->map(function($variableToExpand): string { // here we use the level1 expression to transform the variable to // be expanded to its string representation - $values = ($values)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($values, $lists, $keys); + return $this->expression->expand( + Map::of([$this->name->toString(), $variableToExpand]), + Map::of(), + Map::of(), + ); }); return Str::of(',') @@ -207,17 +206,10 @@ static function($variableToExpand): Sequence { } /** - * @param Map $values - * @param Map> $lists - * @param Map> $keys * @param list|list $variablesToExpand */ - private function explodeList( - Map $values, - Map $lists, - Map $keys, - array $variablesToExpand, - ): string { + private function explodeList(array $variablesToExpand): string + { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], @@ -226,14 +218,10 @@ private function explodeList( $value[1], ], }) - ->map(static fn($pair) => [ - $pair[0], - ($values)($pair[0]->toString(), $pair[1]), - ]) ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( - $pair[1], - $lists, - $keys, + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), )); return Str::of('')->join($expanded)->toString(); From 504c0cba068bfeb18010a372bf430d1b8b6538f7 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:31:50 +0100 Subject: [PATCH 12/33] explicitly define the kind of expressions used in properties --- src/Expression/Level3/Fragment.php | 4 ++-- src/Expression/Level3/Label.php | 4 ++-- src/Expression/Level3/NamedValues.php | 4 ++-- src/Expression/Level3/Parameters.php | 2 +- src/Expression/Level3/Path.php | 4 ++-- src/Expression/Level3/Query.php | 2 +- src/Expression/Level3/QueryContinuation.php | 2 +- src/Expression/Level3/Reserved.php | 4 ++-- src/Expression/Level4.php | 4 ++-- src/Expression/Level4/Fragment.php | 2 +- src/Expression/Level4/Label.php | 2 +- src/Expression/Level4/Parameters.php | 2 +- src/Expression/Level4/Path.php | 2 +- src/Expression/Level4/Query.php | 2 +- src/Expression/Level4/QueryContinuation.php | 2 +- src/Expression/Level4/Reserved.php | 2 +- 16 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 7edc984..6136d1e 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -24,7 +24,7 @@ final class Fragment implements Expression { /** @var Sequence */ private Sequence $names; - /** @var Sequence */ + /** @var Sequence */ private Sequence $expressions; /** @@ -33,7 +33,7 @@ final class Fragment implements Expression private function __construct(Sequence $names) { $this->names = $names; - /** @var Sequence */ + /** @var Sequence */ $this->expressions = $this->names->map(Level2\Reserved::named(...)); } diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 6791d8e..02d8805 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -24,7 +24,7 @@ final class Label implements Expression { /** @var Sequence */ private Sequence $names; - /** @var Sequence */ + /** @var Sequence */ private Sequence $expressions; /** @@ -33,7 +33,7 @@ final class Label implements Expression private function __construct(Sequence $names) { $this->names = $names; - /** @var Sequence */ + /** @var Sequence */ $this->expressions = $this->names->map(Level1::named(...)); } diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 709e427..ceaf732 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -24,7 +24,7 @@ final class NamedValues implements Expression private Expansion $expansion; /** @var Sequence */ private Sequence $names; - /** @var Map */ + /** @var Map */ private Map $expressions; private bool $keyOnlyWhenEmpty = false; @@ -35,7 +35,7 @@ public function __construct(Expansion $expansion, Sequence $names) { $this->expansion = $expansion; $this->names = $names; - /** @var Map */ + /** @var Map */ $this->expressions = Map::of( ...$this ->names diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index fcde649..e539d7d 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -21,7 +21,7 @@ */ final class Parameters implements Expression { - private Expression $expression; + private NamedValues $expression; /** * @param Sequence $names diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index da1c77b..8e06d1f 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -24,7 +24,7 @@ final class Path implements Expression { /** @var Sequence */ private Sequence $names; - /** @var Sequence */ + /** @var Sequence */ private Sequence $expressions; /** @@ -33,7 +33,7 @@ final class Path implements Expression private function __construct(Sequence $names) { $this->names = $names; - /** @var Sequence */ + /** @var Sequence */ $this->expressions = $this->names->map(Level1::named(...)); } diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index e33237c..99e18cf 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -21,7 +21,7 @@ */ final class Query implements Expression { - private Expression $expression; + private NamedValues $expression; /** * @param Sequence $names diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index e335db7..6bdce32 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -21,7 +21,7 @@ */ final class QueryContinuation implements Expression { - private Expression $expression; + private NamedValues $expression; /** * @param Sequence $names diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index c5be608..fe50cfb 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -24,7 +24,7 @@ final class Reserved implements Expression { /** @var Sequence */ private Sequence $names; - /** @var Sequence */ + /** @var Sequence */ private Sequence $expressions; /** @@ -33,7 +33,7 @@ final class Reserved implements Expression private function __construct(Sequence $names) { $this->names = $names; - /** @var Sequence */ + /** @var Sequence */ $this->expressions = $this->names->map(Level2\Reserved::named(...)); } diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index abf661c..1ac6dbf 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -21,7 +21,7 @@ final class Level4 implements Expression { private Name $name; - private Expression $expression; + private Level1|Level2\Reserved $expression; /** @var ?positive-int */ private ?int $limit = null; private bool $explode = false; @@ -100,7 +100,7 @@ public function withExpansion(Expansion $expansion): self * Not ideal technic but didn't find a better to reduce duplicated code * @internal * - * @param pure-callable(Name): Expression $expression + * @param pure-callable(Name): Level2\Reserved $expression */ public function withExpression(callable $expression): self { diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index b053b88..bd5184e 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -22,7 +22,7 @@ */ final class Fragment implements Expression { - private Expression $expression; + private Level4 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 37e9b7a..cafc040 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -21,7 +21,7 @@ */ final class Label implements Expression { - private Expression $expression; + private Level4 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index ff199d2..d6ebe58 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -28,7 +28,7 @@ final class Parameters implements Expression /** @var ?positive-int */ private ?int $limit = null; private bool $explode = false; - private Expression $expression; + private Level1 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 78c6477..3067fc0 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -21,7 +21,7 @@ */ final class Path implements Expression { - private Expression $expression; + private Level4 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 0b6ad07..aea25e4 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -28,7 +28,7 @@ final class Query implements Expression /** @var ?positive-int */ private ?int $limit = null; private bool $explode = false; - private Expression $expression; + private Level1 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index de88224..89b17d7 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -28,7 +28,7 @@ final class QueryContinuation implements Expression /** @var ?positive-int */ private ?int $limit = null; private bool $explode = false; - private Expression $expression; + private Level1 $expression; private function __construct(Name $name) { diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 70a1cbe..a1f1886 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -27,7 +27,7 @@ final class Reserved implements Expression /** @var ?positive-int */ private ?int $limit = null; private bool $explode = false; - private Expression $expression; + private Level4 $expression; private function __construct(Name $name) { From 3a58eba86dadc94e58cdf4145d327a9eac064361 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:33:36 +0100 Subject: [PATCH 13/33] no need for NamedValues to implements Expression --- src/Expression/Level3/NamedValues.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index ceaf732..4609e19 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -3,11 +3,10 @@ namespace Innmind\UrlTemplate\Expression\Level3; -use Innmind\UrlTemplate\{ - Expression, - Expression\Name, - Expression\Expansion, - Expression\Level1, +use Innmind\UrlTemplate\Expression\{ + Name, + Expansion, + Level1, }; use Innmind\Immutable\{ Map, @@ -19,7 +18,7 @@ * @psalm-immutable * @internal */ -final class NamedValues implements Expression +final class NamedValues { private Expansion $expansion; /** @var Sequence */ @@ -60,13 +59,16 @@ public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): return $self; } - #[\Override] public function expansion(): Expansion { return $this->expansion; } - #[\Override] + /** + * @param Map $values + * @param Map> $lists + * @param Map> $keys + */ public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this @@ -89,7 +91,6 @@ public function expand(Map $values, Map $lists, Map $keys): string ->toString(); } - #[\Override] public function regex(): string { return Str::of($this->expansion->continuation()->regex()) @@ -105,7 +106,6 @@ public function regex(): string ->toString(); } - #[\Override] public function toString(): string { /** @psalm-suppress InvalidArgument */ From b3e1058e0475ae0b2f09ec9c73b320f4d6813cfd Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:33:57 +0100 Subject: [PATCH 14/33] simplify encoding expanded values --- src/Expression/Level1.php | 5 ++++ src/Expression/Level2/Reserved.php | 5 ++++ src/Expression/Level4.php | 32 +++++---------------- src/Expression/Level4/Parameters.php | 18 +++--------- src/Expression/Level4/Query.php | 20 ++++--------- src/Expression/Level4/QueryContinuation.php | 22 +++++--------- 6 files changed, 34 insertions(+), 68 deletions(-) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 63d70c3..2d23346 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -64,6 +64,11 @@ public function expand(Map $values, Map $lists, Map $keys): string ); } + public function encode(string $string): string + { + return ($this->encode)($string); + } + #[\Override] public function regex(): string { diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 2d07a39..3dcc95d 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -66,6 +66,11 @@ public function expand(Map $values, Map $lists, Map $keys): string ); } + public function encode(string $string): string + { + return ($this->encode)($string); + } + #[\Override] public function regex(): string { diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 1ac6dbf..455f05a 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -140,13 +140,9 @@ public function expand(Map $values, Map $lists, Map $keys): string if ($this->mustLimit()) { $value = Str::of($variable)->take($this->limit); - $value = $this->expression->expand( - Map::of([$this->name->toString(), $value->toString()]), - Map::of(), - Map::of(), - ); + $value = $this->expression->encode($value->toString()); } else { - $value = $this->expression->expand($values, $lists, $keys); + $value = $this->expression->encode($variable); } return "{$this->expansion->toString()}$value"; @@ -219,16 +215,10 @@ static function($variableToExpand): Sequence { }, ); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation $expanded = $flattenedVariables->map( - function($variableToExpand): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - return $this->expression->expand( - Map::of([$this->name->toString(), $variableToExpand]), - Map::of(), - Map::of(), - ); - }, + $this->expression->encode(...), ); return $this->separator() @@ -244,18 +234,10 @@ private function explodeList(array $variablesToExpand): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { - \is_string($value) => $this->expression->expand( - Map::of([$this->name->toString(), $value]), - Map::of(), - Map::of(), - ), + \is_string($value) => $this->expression->encode($value), default => [ $value[0], - $this->expression->expand( - Map::of([$this->name->toString(), $value[1]]), - Map::of(), - Map::of(), - ), + $this->expression->encode($value[1]), ], }) ->map(static fn($value) => match (true) { diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index d6ebe58..1da8689 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -110,11 +110,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand( - $values, - Map::of(), - Map::of(), - )); + $value = Str::of($this->expression->encode($variable)); if ($this->mustLimit()) { return ";{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -189,16 +185,10 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation $expanded = $flattenedVariables->map( - function($variableToExpand): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - return $this->expression->expand( - Map::of([$this->name->toString(), $variableToExpand]), - Map::of(), - Map::of(), - ); - }, + $this->expression->encode(...), ); return Str::of(',') diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index aea25e4..74b2977 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -110,11 +110,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand( - $values, - Map::of(), - Map::of(), - )); + $value = Str::of($this->expression->encode($variable)); if ($this->mustLimit()) { return "?{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -189,15 +185,11 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - return $this->expression->expand( - Map::of([$this->name->toString(), $variableToExpand]), - Map::of(), - Map::of(), - ); - }); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation + $expanded = $flattenedVariables->map( + $this->expression->encode(...), + ); return Str::of(',') ->join($expanded) diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 89b17d7..26db618 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -110,11 +110,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$variable]); } - $value = Str::of($this->expression->expand( - $values, - Map::of(), - Map::of(), - )); + $value = Str::of($this->expression->encode($variable)); if ($this->mustLimit()) { return "&{$this->name->toString()}={$value->take($this->limit)->toString()}"; @@ -189,15 +185,11 @@ static function($variableToExpand): Sequence { return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - return $this->expression->expand( - Map::of([$this->name->toString(), $variableToExpand]), - Map::of(), - Map::of(), - ); - }); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation + $expanded = $flattenedVariables->map( + $this->expression->encode(...), + ); return Str::of(',') ->join($expanded) @@ -218,7 +210,7 @@ private function explodeList(array $variablesToExpand): string $value[1], ], }) - ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( + ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( // todo simplify Map::of([$pair[0]->toString(), $pair[1]]), Map::of(), Map::of(), From 3f80cf206ea99796b656ef01eba52e90c1bacf2c Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:44:46 +0100 Subject: [PATCH 15/33] remove dead code --- src/Expression/Level3/NamedValues.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 4609e19..70e2f2a 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -59,11 +59,6 @@ public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): return $self; } - public function expansion(): Expansion - { - return $this->expansion; - } - /** * @param Map $values * @param Map> $lists From de01669826c8fb37272219032338adbd3e627853 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:45:02 +0100 Subject: [PATCH 16/33] guarantee order of named values --- src/Expression/Level1.php | 5 +++++ src/Expression/Level3/NamedValues.php | 30 ++++++++++----------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 2d23346..752c4eb 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -47,6 +47,11 @@ public static function named(Name $name): self return new self($name); } + public function name(): Name + { + return $this->name; + } + #[\Override] public function expansion(): Expansion { diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 70e2f2a..7dd5e8c 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -23,8 +23,8 @@ final class NamedValues private Expansion $expansion; /** @var Sequence */ private Sequence $names; - /** @var Map */ - private Map $expressions; + /** @var Sequence */ + private Sequence $expressions; private bool $keyOnlyWhenEmpty = false; /** @@ -34,16 +34,7 @@ public function __construct(Expansion $expansion, Sequence $names) { $this->expansion = $expansion; $this->names = $names; - /** @var Map */ - $this->expressions = Map::of( - ...$this - ->names - ->map(static fn($name) => [ - $name->toString(), - Level1::named($name), - ]) - ->toList(), - ); + $this->expressions = $names->map(Level1::named(...)); } /** @@ -68,15 +59,16 @@ public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this ->expressions - ->map(static fn($_, $expression) => $expression->expand($values, $lists, $keys)) - ->map(static fn($_, $expression) => Str::of($expression)) - ->toSequence() - ->map(fn($pair) => match ([$pair->value()->empty(), $this->keyOnlyWhenEmpty]) { - [true, true] => $pair->key(), + ->map(static fn($expression) => [ + $expression->name()->toString(), + Str::of($expression->expand($values, $lists, $keys)), + ]) + ->map(fn($pair) => match ([$pair[1]->empty(), $this->keyOnlyWhenEmpty]) { + [true, true] => $pair[0], default => \sprintf( '%s=%s', - $pair->key(), - $pair->value()->toString(), + $pair[0], + $pair[1]->toString(), ), }); From cc6fcc13816ca8cca840af6760af94661dc70384 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:46:51 +0100 Subject: [PATCH 17/33] remove todo --- src/Expression/Level4/QueryContinuation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 26db618..35e57e5 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -210,7 +210,7 @@ private function explodeList(array $variablesToExpand): string $value[1], ], }) - ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( // todo simplify + ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( Map::of([$pair[0]->toString(), $pair[1]]), Map::of(), Map::of(), From 9fbb8f93555f83f62178f85262babc548d523df9 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 17:51:47 +0100 Subject: [PATCH 18/33] use more explicit int range --- src/Expression/Level4.php | 6 +++--- src/Expression/Level4/Fragment.php | 2 +- src/Expression/Level4/Label.php | 2 +- src/Expression/Level4/Parameters.php | 6 +++--- src/Expression/Level4/Parse.php | 4 ++-- src/Expression/Level4/Path.php | 2 +- src/Expression/Level4/Query.php | 6 +++--- src/Expression/Level4/QueryContinuation.php | 6 +++--- src/Expression/Level4/Reserved.php | 4 ++-- src/Expression/Name.php | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 455f05a..338b3b9 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -22,7 +22,7 @@ final class Level4 implements Expression { private Name $name; private Level1|Level2\Reserved $expression; - /** @var ?positive-int */ + /** @var ?int<1, max> */ private ?int $limit = null; private bool $explode = false; private Expansion $expansion; @@ -53,7 +53,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { @@ -187,7 +187,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index bd5184e..0fe6349 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -50,7 +50,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index cafc040..2427af6 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -47,7 +47,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 1da8689..ccfdb3a 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -25,7 +25,7 @@ final class Parameters implements Expression { private Name $name; - /** @var ?positive-int */ + /** @var ?int<1, max> */ private ?int $limit = null; private bool $explode = false; private Level1 $expression; @@ -55,7 +55,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { @@ -158,7 +158,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index 45ae3cb..aead883 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -25,7 +25,7 @@ final class Parse * * @param pure-callable(Name): T $standard * @param pure-callable(Name): T $explode - * @param pure-callable(Name, positive-int): T $limit + * @param pure-callable(Name, int<1, max>): T $limit * * @return Attempt */ @@ -62,7 +62,7 @@ private static function explode( * @psalm-pure * @template T of Expression * - * @param pure-callable(Name, positive-int): T $limit + * @param pure-callable(Name, int<1, max>): T $limit * * @return Attempt */ diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 3067fc0..7a71e3c 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -47,7 +47,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 74b2977..6f7d4a8 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -25,7 +25,7 @@ final class Query implements Expression { private Name $name; - /** @var ?positive-int */ + /** @var ?int<1, max> */ private ?int $limit = null; private bool $explode = false; private Level1 $expression; @@ -55,7 +55,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { @@ -158,7 +158,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 35e57e5..80aba9d 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -25,7 +25,7 @@ final class QueryContinuation implements Expression { private Name $name; - /** @var ?positive-int */ + /** @var ?int<1, max> */ private ?int $limit = null; private bool $explode = false; private Level1 $expression; @@ -55,7 +55,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { @@ -158,7 +158,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index a1f1886..601c822 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -24,7 +24,7 @@ final class Reserved implements Expression { private Name $name; - /** @var ?positive-int */ + /** @var ?int<1, max> */ private ?int $limit = null; private bool $explode = false; private Level4 $expression; @@ -56,7 +56,7 @@ public static function of(Str $string): Attempt /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { diff --git a/src/Expression/Name.php b/src/Expression/Name.php index e8bb5fc..60ca603 100644 --- a/src/Expression/Name.php +++ b/src/Expression/Name.php @@ -90,7 +90,7 @@ public static function explode( /** * @psalm-pure * - * @return Attempt + * @return Attempt}> */ public static function limit( Str $value, @@ -98,7 +98,7 @@ public static function limit( ): Attempt { /** * @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string - * @var Attempt + * @var Attempt}> */ return Maybe::just($value) ->filter($expansion->matchesLimit(...)) From 355bbf6a1790cb0b34bac53e94957bb130fa31d6 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 2 Nov 2025 18:00:46 +0100 Subject: [PATCH 19/33] use monadic style for level 4 expansion --- src/Expression/Level4.php | 60 +++++++++--------- src/Expression/Level4/Parameters.php | 67 +++++++++++---------- src/Expression/Level4/Query.php | 67 +++++++++++---------- src/Expression/Level4/QueryContinuation.php | 67 +++++++++++---------- 4 files changed, 132 insertions(+), 129 deletions(-) diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 338b3b9..6f4e6a9 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -113,39 +113,33 @@ public function withExpression(callable $expression): self #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - // todo break up - /** - * @psalm-suppress InvalidArgument - * @var Map|list> - */ - $variables = $values - ->merge($lists) - ->merge($keys); - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variable); - } - - if ($this->explode) { - return $this->explodeList([$variable]); - } - - if ($this->mustLimit()) { - $value = Str::of($variable)->take($this->limit); - $value = $this->expression->encode($value->toString()); - } else { - $value = $this->expression->encode($variable); - } - - return "{$this->expansion->toString()}$value"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + if ($this->mustLimit()) { + $value = Str::of($value)->take($this->limit)->toString(); + } + + $value = Str::of($this->expression->encode($value)); + + return "{$this->expansion->toString()}$value"; + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index ccfdb3a..4cb9965 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -85,38 +85,41 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - // todo break up - /** - * @psalm-suppress InvalidArgument - * @var Map|list> - */ - $variables = $values - ->merge($lists) - ->merge($keys); - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variable); - } - - if ($this->explode) { - return $this->explodeList([$variable]); - } - - $value = Str::of($this->expression->encode($variable)); - - if ($this->mustLimit()) { - return ";{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return ";{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of($this->expression->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + ';%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + ';%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 6f7d4a8..4d3171e 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -85,38 +85,41 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - // todo break up - /** - * @psalm-suppress InvalidArgument - * @var Map|list> - */ - $variables = $values - ->merge($lists) - ->merge($keys); - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variable); - } - - if ($this->explode) { - return $this->explodeList([$variable]); - } - - $value = Str::of($this->expression->encode($variable)); - - if ($this->mustLimit()) { - return "?{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return "?{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of($this->expression->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + '?%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + '?%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 80aba9d..976c70c 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -85,38 +85,41 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - // todo break up - /** - * @psalm-suppress InvalidArgument - * @var Map|list> - */ - $variables = $values - ->merge($lists) - ->merge($keys); - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variable); - } - - if ($this->explode) { - return $this->explodeList([$variable]); - } - - $value = Str::of($this->expression->encode($variable)); - - if ($this->mustLimit()) { - return "&{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return "&{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of($this->expression->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + '&%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + '&%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] From 5924e615e6ffb1d63bf6bb2049825eba7eab7db6 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 10:05:55 +0100 Subject: [PATCH 20/33] wrap keys early on in the expansion mechanism --- src/Expansion.php | 15 ++++++++++----- src/Expression.php | 3 ++- src/Expression/Level3/NamedValues.php | 2 +- src/Expression/Level4.php | 8 ++++---- src/Expression/Level4/Parameters.php | 11 ++++------- src/Expression/Level4/Query.php | 11 ++++------- src/Expression/Level4/QueryContinuation.php | 11 ++++------- tests/Expression/Level4/FragmentTest.php | 7 ++++++- tests/Expression/Level4/LabelTest.php | 7 ++++++- tests/Expression/Level4/ParametersTest.php | 7 ++++++- tests/Expression/Level4/PathTest.php | 7 ++++++- tests/Expression/Level4/QueryContinuationTest.php | 7 ++++++- tests/Expression/Level4/QueryTest.php | 7 ++++++- tests/Expression/Level4/ReservedTest.php | 7 ++++++- tests/Expression/Level4Test.php | 7 ++++++- 15 files changed, 77 insertions(+), 40 deletions(-) diff --git a/src/Expansion.php b/src/Expansion.php index b8436e0..16acedf 100644 --- a/src/Expansion.php +++ b/src/Expansion.php @@ -3,6 +3,7 @@ namespace Innmind\UrlTemplate; +use Innmind\UrlTemplate\Expression\Name; use Innmind\Url\Url; use Innmind\Immutable\{ Sequence, @@ -63,13 +64,17 @@ public function expand(): Url static fn($value) => \is_string($value), ), ); - /** @var Map> */ - $keys = $this->variables->filter( - static fn($_, $value) => \is_array($value) && \array_all( + /** @var Map> */ + $keys = $this + ->variables + ->filter(static fn($_, $value) => \is_array($value) && \array_all( $value, static fn($value) => \is_array($value), - ), - ); + )) + ->map(static fn($_, $keys) => \array_map( + static fn($pair) => [Name::of($pair[0]), $pair[1]], + $keys, + )); $url = $this->expressions->reduce( $this->template, diff --git a/src/Expression.php b/src/Expression.php index 7076536..ea094dc 100644 --- a/src/Expression.php +++ b/src/Expression.php @@ -3,6 +3,7 @@ namespace Innmind\UrlTemplate; +use Innmind\UrlTemplate\Expression\Name; use Innmind\Immutable\Map; /** @@ -16,7 +17,7 @@ public function expansion(): Expression\Expansion; /** * @param Map $values * @param Map> $lists - * @param Map> $keys + * @param Map> $keys */ public function expand(Map $values, Map $lists, Map $keys): string; public function regex(): string; diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 7dd5e8c..79cdebb 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -53,7 +53,7 @@ public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): /** * @param Map $values * @param Map> $lists - * @param Map> $keys + * @param Map> $keys */ public function expand(Map $values, Map $lists, Map $keys): string { diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 6f4e6a9..a257077 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -189,7 +189,7 @@ private function mustLimit(): bool } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function expandList(array $variablesToExpand): string { @@ -202,7 +202,7 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); @@ -222,7 +222,7 @@ static function($variableToExpand): Sequence { } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function explodeList(array $variablesToExpand): string { @@ -238,7 +238,7 @@ private function explodeList(array $variablesToExpand): string \is_string($value) => $value, default => \sprintf( '%s=%s', - Name::of($value[0])->toString(), // todo move verification earlier on + $value[0]->toString(), $value[1], ), }); diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 4cb9965..769a0f8 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -169,7 +169,7 @@ private function mustLimit(): bool } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function expandList(array $variablesToExpand): string { @@ -182,7 +182,7 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); @@ -201,17 +201,14 @@ static function($variableToExpand): Sequence { } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function explodeList(array $variablesToExpand): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], - default => [ - Name::of($value[0]), // todo move wrapping earlier on - $value[1], - ], + default => $value, }) ->map(static fn($pair) => Level3\Parameters::named($pair[0])->expand( Map::of([$pair[0]->toString(), $pair[1]]), diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 4d3171e..d9e9a95 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -169,7 +169,7 @@ private function mustLimit(): bool } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function expandList(array $variablesToExpand): string { @@ -182,7 +182,7 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); @@ -201,17 +201,14 @@ static function($variableToExpand): Sequence { } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function explodeList(array $variablesToExpand): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], - default => [ - Name::of($value[0]), // todo move wrapping earlier on - $value[1], - ], + default => $value, }) ->map(static fn($pair) => Level3\Query::named($pair[0])->expand( Map::of([$pair[0]->toString(), $pair[1]]), diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 976c70c..f13ef0c 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -169,7 +169,7 @@ private function mustLimit(): bool } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function expandList(array $variablesToExpand): string { @@ -182,7 +182,7 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); @@ -201,17 +201,14 @@ static function($variableToExpand): Sequence { } /** - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ private function explodeList(array $variablesToExpand): string { $expanded = Sequence::of(...$variablesToExpand) ->map(fn($value) => match (true) { \is_string($value) => [$this->name, $value], - default => [ - Name::of($value[0]), // todo move wrapping earlier on - $value[1], - ], + default => $value, }) ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( Map::of([$pair[0]->toString(), $pair[1]]), diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index 6955511..c0dfa61 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Fragment, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '#/foo/b', diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index b11b54b..589e5a1 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Label, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '.val', diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index 335b21c..d1d873d 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Parameters, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( ';hello=Hello', diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index 4301aa3..ebc1744 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Path, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '/red,green,blue', diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index 3357797..de2f037 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\QueryContinuation, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '&var=val', diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index 3cc2242..e5eeebe 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Query, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '?var=val', diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index 502c30d..ead4220 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4\Reserved, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '/foo/b', diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 20f0958..587a2a0 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -5,6 +5,7 @@ use Innmind\UrlTemplate\{ Expression\Level4, + Expression\Name, Expression, Exception\LogicException, }; @@ -93,7 +94,11 @@ public function testExpand() $lists = Map::of() ('list', ['red', 'green', 'blue']); $keys = Map::of() - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( 'val', From 69e887aa1280ab73162445ddc41840c82dd1f281 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 10:43:08 +0100 Subject: [PATCH 21/33] dispatch variables at declaration time instead of expand time --- src/Expansion.php | 82 +++++++++++++++++++++++++++--------------- tests/TemplateTest.php | 4 +-- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/Expansion.php b/src/Expansion.php index 16acedf..3165658 100644 --- a/src/Expansion.php +++ b/src/Expansion.php @@ -18,12 +18,16 @@ final class Expansion { /** * @param Sequence $expressions - * @param Map|list> $variables + * @param Map $values + * @param Map> $lists + * @param Map> $keys */ private function __construct( private Str $template, private Sequence $expressions, - private Map $variables, + private Map $values, + private Map $lists, + private Map $keys, ) { } @@ -35,52 +39,72 @@ private function __construct( */ public static function of(Str $template, Sequence $expressions): self { - return new self($template, $expressions, Map::of()); + return new self( + $template, + $expressions, + Map::of(), + Map::of(), + Map::of(), + ); } /** + * @no-named-arguments + * * @param non-empty-string $name Todo use literal strings - * @param string|list|list $value */ - public function with(string $name, string|array $value): self + public function with(string $name, string ...$values): self { + if (\count($values) === 1) { + return new self( + $this->template, + $this->expressions, + ($this->values)($name, $values[0]), + $this->lists->remove($name), + $this->keys->remove($name), + ); + } + return new self( $this->template, $this->expressions, - ($this->variables)($name, $value), + $this->values->remove($name), + ($this->lists)($name, $values), + $this->keys->remove($name), ); } - public function expand(): Url + /** + * @no-named-arguments + * + * @param non-empty-string $name Todo use literal strings + * @param array{string, string} ...$keys + */ + public function withKeys(string $name, array ...$keys): self { - /** @var Map */ - $values = $this->variables->filter( - static fn($_, $value) => \is_string($value), - ); - /** @var Map> */ - $lists = $this->variables->filter( - static fn($_, $value) => \is_array($value) && \array_all( - $value, - static fn($value) => \is_string($value), - ), - ); - /** @var Map> */ - $keys = $this - ->variables - ->filter(static fn($_, $value) => \is_array($value) && \array_all( - $value, - static fn($value) => \is_array($value), - )) - ->map(static fn($_, $keys) => \array_map( + return new self( + $this->template, + $this->expressions, + $this->values->remove($name), + $this->lists->remove($name), + ($this->keys)($name, \array_map( static fn($pair) => [Name::of($pair[0]), $pair[1]], $keys, - )); + )), + ); + } + public function expand(): Url + { $url = $this->expressions->reduce( $this->template, - static fn(Str $template, $expression) => $template->replace( + fn(Str $template, $expression) => $template->replace( $expression->toString(), - $expression->expand($values, $lists, $keys), + $expression->expand( + $this->values, + $this->lists, + $this->keys, + ), ), ); diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index cb02cfe..6e86449 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -39,8 +39,8 @@ public function testExpand($pattern, $expected) ->with('var', 'value') ->with('hello', 'Hello World!') ->with('path', '/foo/bar') - ->with('list', ['red', 'green', 'blue']) - ->with('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]) + ->with('list', 'red', 'green', 'blue') + ->withKeys('keys', ['semi', ';'], ['dot', '.'], ['comma', ',']) ->with('username', 'fred') ->with('term', 'dog') ->with('q', 'chien') From af5333c9a5676250f2323be0a5f3aba1dda6e903 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 10:44:48 +0100 Subject: [PATCH 22/33] fix the declared exception being thrown --- src/Template.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Template.php b/src/Template.php index 4e5f620..ed4f7c3 100644 --- a/src/Template.php +++ b/src/Template.php @@ -3,10 +3,7 @@ namespace Innmind\UrlTemplate; -use Innmind\UrlTemplate\Exception\{ - ExplodeExpressionCantBeMatched, - DomainException, -}; +use Innmind\UrlTemplate\Exception\ExplodeExpressionCantBeMatched; use Innmind\Url\Url; use Innmind\Immutable\{ Map, @@ -39,7 +36,7 @@ private function __construct(Str $template, Sequence $expressions) * * @param literal-string $template * - * @throws DomainException + * @throws \Exception */ public static function of(string $template): self { From 2068a84b94e91a17b58881d0361c080656d23741 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 10:58:22 +0100 Subject: [PATCH 23/33] wrap returns of Template methods that may throw an exception in an Attempt --- CHANGELOG.md | 2 + src/Template.php | 75 ++++++++++++++------------ tests/TemplateTest.php | 116 ++++++++++++++++++++++++++++++----------- 3 files changed, 130 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3f0eef..0a291a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - Requires `innmind/immutable:~5.18` - Requires PHP `8.4` +- `Innmind\UrlTemplate\Template::extract()` now returns an `Innmind\Immutable\Attempt` +- `Innmind\UrlTemplate\Template::matches()` now returns an `Innmind\Immutable\Attempt` ### Removed diff --git a/src/Template.php b/src/Template.php index ed4f7c3..4cc20a6 100644 --- a/src/Template.php +++ b/src/Template.php @@ -3,7 +3,6 @@ namespace Innmind\UrlTemplate; -use Innmind\UrlTemplate\Exception\ExplodeExpressionCantBeMatched; use Innmind\Url\Url; use Innmind\Immutable\{ Map, @@ -73,25 +72,35 @@ public function expansion(): Expansion } /** - * @throws ExplodeExpressionCantBeMatched + * The attempt will fail when trying to match on a template containing an + * explode expression. * - * @return Map + * @return Attempt> */ - public function extract(Url $url): Map + public function extract(Url $url): Attempt { - /** @var Map */ - return Str::of($url->toString()) - ->capture($this->regex()) - ->filter(static fn($key) => \is_string($key)) - ->map(static fn($_, $variable) => \rawurldecode($variable->toString())); + /** @var Attempt> */ + return $this + ->regex() + ->map(Str::of($url->toString())->capture(...)) + ->map( + static fn($captured) => $captured + ->filter(static fn($key) => \is_string($key)) + ->map(static fn($_, $variable) => \rawurldecode($variable->toString())), + ); } /** - * @throws ExplodeExpressionCantBeMatched + * The attempt will fail when trying to match on a template containing an + * explode expression. + * + * @return Attempt */ - public function matches(Url $url): bool + public function matches(Url $url): Attempt { - return Str::of($url->toString())->matches($this->regex()); + return $this->regex()->map( + Str::of($url->toString())->matches(...), + ); } public function toString(): string @@ -100,35 +109,37 @@ public function toString(): string } /** - * @throws ExplodeExpressionCantBeMatched + * @return Attempt */ - private function regex(): string + private function regex(): Attempt { - $template = $this - ->expressions - ->reduce( - $this->template->replace('~', '\~'), + return Attempt::of(function() { + $template = $this + ->expressions + ->reduce( + $this->template->replace('~', '\~'), + static fn(Str $template, $expression) => $template->replace( + $expression->toString(), + \sprintf( + '__innmind_expression_%s__', + \spl_object_hash($expression), + ), + ), + ) + ->pregQuote(); + $template = $this->expressions->reduce( + $template, static fn(Str $template, $expression) => $template->replace( - $expression->toString(), \sprintf( '__innmind_expression_%s__', \spl_object_hash($expression), ), + $expression->regex(), ), - ) - ->pregQuote(); - $template = $this->expressions->reduce( - $template, - static fn(Str $template, $expression) => $template->replace( - \sprintf( - '__innmind_expression_%s__', - \spl_object_hash($expression), - ), - $expression->regex(), - ), - ); + ); - return $template->prepend('~^')->append('$~')->toString(); + return $template->prepend('~^')->append('$~')->toString(); + }); } /** diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index 6e86449..81553ba 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -59,13 +59,16 @@ public function testReturnEmptyMapWhenUrlDoesntMatchTemplate() 0, Template::of('/{foo}') ->extract(Url::of('/hello%20world%21/foo')) + ->unwrap() ->size(), ); } public function testLevel1Extraction() { - $variables = Template::of('/{foo}/{bar}')->extract(Url::of('/hello%20world%21/foo')); + $variables = Template::of('/{foo}/{bar}') + ->extract(Url::of('/hello%20world%21/foo')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -81,7 +84,9 @@ public function testLevel1Extraction() public function testLevel2Extraction() { - $variables = Template::of('{+path}/here')->extract(Url::of('/foo/bar/here')); + $variables = Template::of('{+path}/here') + ->extract(Url::of('/foo/bar/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -90,7 +95,9 @@ public function testLevel2Extraction() static fn() => null, )); - $variables = Template::of('X{#hello}')->extract(Url::of('X#Hello%20World!')); + $variables = Template::of('X{#hello}') + ->extract(Url::of('X#Hello%20World!')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -102,7 +109,9 @@ public function testLevel2Extraction() public function testLevel3Extraction() { - $variables = Template::of('/map?{x,y}')->extract(Url::of('/map?1024,768')); + $variables = Template::of('/map?{x,y}') + ->extract(Url::of('/map?1024,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -115,7 +124,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('/{x,hello,y}')->extract(Url::of('/1024,Hello%20World%21,768')); + $variables = Template::of('/{x,hello,y}') + ->extract(Url::of('/1024,Hello%20World%21,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -132,7 +143,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('/{+x,hello,y}')->extract(Url::of('/1024,Hello%20World!,768')); + $variables = Template::of('/{+x,hello,y}') + ->extract(Url::of('/1024,Hello%20World!,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -149,7 +162,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{+path,x}/here')->extract(Url::of('/foo/bar,1024/here')); + $variables = Template::of('{+path,x}/here') + ->extract(Url::of('/foo/bar,1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -162,7 +177,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{#x,hello,y}')->extract(Url::of('#1024,Hello%20World!,768')); + $variables = Template::of('{#x,hello,y}') + ->extract(Url::of('#1024,Hello%20World!,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -179,7 +196,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{#path,x}/here')->extract(Url::of('#/foo/bar,1024/here')); + $variables = Template::of('{#path,x}/here') + ->extract(Url::of('#/foo/bar,1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -192,7 +211,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{.x,y}')->extract(Url::of('.1024.768')); + $variables = Template::of('{.x,y}') + ->extract(Url::of('.1024.768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -205,7 +226,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{/var,x}/here')->extract(Url::of('/value/1024/here')); + $variables = Template::of('{/var,x}/here') + ->extract(Url::of('/value/1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -218,7 +241,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{;x,y}')->extract(Url::of(';x=1024;y=768')); + $variables = Template::of('{;x,y}') + ->extract(Url::of(';x=1024;y=768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -231,7 +256,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{;x,y,empty}')->extract(Url::of(';x=1024;y=768;empty')); + $variables = Template::of('{;x,y,empty}') + ->extract(Url::of(';x=1024;y=768;empty')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -248,7 +275,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{?x,y}')->extract(Url::of('?x=1024&y=768')); + $variables = Template::of('{?x,y}') + ->extract(Url::of('?x=1024&y=768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -261,7 +290,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{?x,y,empty}')->extract(Url::of('?x=1024&y=768&empty=')); + $variables = Template::of('{?x,y,empty}') + ->extract(Url::of('?x=1024&y=768&empty=')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -278,7 +309,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('?fixed=yes{&x}')->extract(Url::of('?fixed=yes&x=1024')); + $variables = Template::of('?fixed=yes{&x}') + ->extract(Url::of('?fixed=yes&x=1024')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -287,7 +320,9 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{&x,y,empty}')->extract(Url::of('&x=1024&y=768&empty=')); + $variables = Template::of('{&x,y,empty}') + ->extract(Url::of('&x=1024&y=768&empty=')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(3, $variables->size()); @@ -307,7 +342,9 @@ public function testLevel3Extraction() public function testLevel4Extraction() { - $variables = Template::of('{var:3}')->extract(Url::of('val')); + $variables = Template::of('{var:3}') + ->extract(Url::of('val')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -316,7 +353,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{+path:6}/here')->extract(Url::of('/foo/b/here')); + $variables = Template::of('{+path:6}/here') + ->extract(Url::of('/foo/b/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -325,7 +364,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{#path:6}/here')->extract(Url::of('#/foo/b/here')); + $variables = Template::of('{#path:6}/here') + ->extract(Url::of('#/foo/b/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -334,7 +375,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{.var:3}')->extract(Url::of('.val')); + $variables = Template::of('{.var:3}') + ->extract(Url::of('.val')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -343,7 +386,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{/var:1}')->extract(Url::of('/v')); + $variables = Template::of('{/var:1}') + ->extract(Url::of('/v')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -352,7 +397,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{;var:5}')->extract(Url::of(';var=hello')); + $variables = Template::of('{;var:5}') + ->extract(Url::of(';var=hello')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -361,7 +408,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{?var:3}')->extract(Url::of('?var=hel')); + $variables = Template::of('{?var:3}') + ->extract(Url::of('?var=hel')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -370,7 +419,9 @@ public function testLevel4Extraction() static fn() => null, )); - $variables = Template::of('{&var:3}')->extract(Url::of('&var=hel')); + $variables = Template::of('{&var:3}') + ->extract(Url::of('&var=hel')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(1, $variables->size()); @@ -383,7 +434,8 @@ public function testLevel4Extraction() public function testExtraction() { $variables = Template::of('http://example.com/search{?q,lang:2}') - ->extract(Url::of('http://example.com/search?q=chien&lang=fr')); + ->extract(Url::of('http://example.com/search?q=chien&lang=fr')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); $this->assertSame(2, $variables->size()); @@ -401,23 +453,25 @@ public function testThrowWhenExtractionNotSupportedForTemplate() { $this->expectException(ExplodeExpressionCantBeMatched::class); - Template::of('{foo*}')->extract(Url::of('foo,bar,baz')); + Template::of('{foo*}') + ->extract(Url::of('foo,bar,baz')) + ->unwrap(); } public function testMatches() { $template = Template::of('{/foo}'); - $this->assertTrue($template->matches(Url::of('/bar'))); - $this->assertFalse($template->matches(Url::of('/bar/foo'))); + $this->assertTrue($template->matches(Url::of('/bar'))->unwrap()); + $this->assertFalse($template->matches(Url::of('/bar/foo'))->unwrap()); } public function testNoNeedToEscapeSpecialRegexCharactersInTheUrl() { $template = Template::of('/*'); - $this->assertTrue($template->matches(Url::of('/*'))); - $this->assertFalse($template->matches(Url::of('/f'))); + $this->assertTrue($template->matches(Url::of('/*'))->unwrap()); + $this->assertFalse($template->matches(Url::of('/f'))->unwrap()); } public static function cases(): array From 60ba783a7878e0b280d3bc53fca53c9374abaa6c Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 11:36:04 +0100 Subject: [PATCH 24/33] use Attempts instead of throwing to better track errors --- src/Expression.php | 11 ++++- src/Expression/Level1.php | 4 +- src/Expression/Level2/Fragment.php | 4 +- src/Expression/Level2/Reserved.php | 4 +- src/Expression/Level3.php | 14 +++--- src/Expression/Level3/Fragment.php | 15 ++++--- src/Expression/Level3/Label.php | 21 +++++---- src/Expression/Level3/NamedValues.php | 21 ++++++--- src/Expression/Level3/Parameters.php | 2 +- src/Expression/Level3/Path.php | 15 ++++--- src/Expression/Level3/Query.php | 2 +- src/Expression/Level3/QueryContinuation.php | 2 +- src/Expression/Level3/Reserved.php | 14 +++--- src/Expression/Level4.php | 22 ++++++---- src/Expression/Level4/Composite.php | 30 +++++++------ src/Expression/Level4/Fragment.php | 2 +- src/Expression/Level4/Label.php | 2 +- src/Expression/Level4/Parameters.php | 22 ++++++---- src/Expression/Level4/Path.php | 2 +- src/Expression/Level4/Query.php | 22 ++++++---- src/Expression/Level4/QueryContinuation.php | 22 ++++++---- src/Expression/Level4/Reserved.php | 6 +-- src/Template.php | 43 ++++++++++--------- tests/Expression/Level1Test.php | 2 +- tests/Expression/Level2/FragmentTest.php | 2 +- tests/Expression/Level2/ReservedTest.php | 2 +- tests/Expression/Level3/FragmentTest.php | 2 +- tests/Expression/Level3/LabelTest.php | 2 +- tests/Expression/Level3/ParametersTest.php | 2 +- tests/Expression/Level3/PathTest.php | 2 +- .../Level3/QueryContinuationTest.php | 2 +- tests/Expression/Level3/QueryTest.php | 2 +- tests/Expression/Level3/ReservedTest.php | 2 +- tests/Expression/Level3Test.php | 2 +- tests/Expression/Level4/CompositeTest.php | 18 ++++---- tests/Expression/Level4/FragmentTest.php | 6 +-- tests/Expression/Level4/LabelTest.php | 6 +-- tests/Expression/Level4/ParametersTest.php | 6 +-- tests/Expression/Level4/PathTest.php | 6 +-- .../Level4/QueryContinuationTest.php | 6 +-- tests/Expression/Level4/QueryTest.php | 6 +-- tests/Expression/Level4/ReservedTest.php | 6 +-- tests/Expression/Level4Test.php | 6 +-- 43 files changed, 224 insertions(+), 166 deletions(-) diff --git a/src/Expression.php b/src/Expression.php index ea094dc..9c6c6a2 100644 --- a/src/Expression.php +++ b/src/Expression.php @@ -4,7 +4,10 @@ namespace Innmind\UrlTemplate; use Innmind\UrlTemplate\Expression\Name; -use Innmind\Immutable\Map; +use Innmind\Immutable\{ + Map, + Attempt, +}; /** * @psalm-immutable @@ -20,6 +23,10 @@ public function expansion(): Expression\Expansion; * @param Map> $keys */ public function expand(Map $values, Map $lists, Map $keys): string; - public function regex(): string; + + /** + * @return Attempt + */ + public function regex(): Attempt; public function toString(): string; } diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 752c4eb..90f811c 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -75,9 +75,9 @@ public function encode(string $string): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 0bf824f..8d78f20 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -60,9 +60,9 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return "\#(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"; + return Attempt::result("\#(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 3dcc95d..7e77a66 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -72,9 +72,9 @@ public function encode(string $string): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 6dc9e61..215c03c 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -59,14 +59,16 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { /** @psalm-suppress InvalidArgument */ - return Str::of(',') - ->join($this->names->map( - static fn(Name $name) => "(?<{$name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)", - )) - ->toString(); + return Attempt::result( + Str::of(',') + ->join($this->names->map( + static fn(Name $name) => "(?<{$name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)", + )) + ->toString(), + ); } #[\Override] diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 6136d1e..6d2f4f8 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -68,14 +68,15 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of(',') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->prepend('\#') - ->toString(); + return $this + ->expressions + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of(',')->join(...)) + ->map(static fn($regex) => $regex->prepend('\#')->toString()); } #[\Override] diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 02d8805..08f9d02 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -68,15 +68,20 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of('.') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->replace('\.', '') - ->prepend('\.') - ->toString(); + return $this + ->expressions + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of('.')->join(...)) + ->map( + static fn($regex) => $regex + ->replace('\.', '') + ->prepend('\.') + ->toString(), + ); } #[\Override] diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index 79cdebb..f303c36 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -12,6 +12,7 @@ Map, Sequence, Str, + Attempt, }; /** @@ -78,19 +79,25 @@ public function expand(Map $values, Map $lists, Map $keys): string ->toString(); } - public function regex(): string + /** + * @return Attempt + */ + public function regex(): Attempt { - return Str::of($this->expansion->continuation()->regex()) - ->join($this->names->map( - fn($name) => \sprintf( + return $this + ->names + ->map(fn($name) => Level1::named($name)->regex()->map( + fn($regex) => \sprintf( '%s=%s%s', $name->toString(), $this->keyOnlyWhenEmpty ? '?' : '', - Level1::named($name)->regex(), + $regex, ), )) - ->prepend($this->expansion->regex()) - ->toString(); + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of($this->expansion->continuation()->regex())->join(...)) + ->map(fn($regex) => $regex->prepend($this->expansion->regex())->toString()); } public function toString(): string diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index e539d7d..72d12aa 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -63,7 +63,7 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 8e06d1f..31586a7 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -66,14 +66,15 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of('/') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->prepend('/') - ->toString(); + return $this + ->expressions + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of('/')->join(...)) + ->map(static fn($regex) => $regex->prepend('/')->toString()); } #[\Override] diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index 99e18cf..fa1eb1f 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -63,7 +63,7 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index 6bdce32..3a38aec 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -60,7 +60,7 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index fe50cfb..6da9de3 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -65,13 +65,15 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of(',') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->toString(); + return $this + ->expressions + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of(',')->join(...)) + ->map(static fn($regex) => $regex->toString()); } #[\Override] diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index a257077..db6e5f7 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -143,27 +143,33 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new ExplodeExpressionCantBeMatched); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = $this + ->expression + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { $regex = $this->expression->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '%s%s', $this->expansion->regex(), $regex, - ); + )); } #[\Override] diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index 5584a43..d78fb0b 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -95,28 +95,34 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { $remaining = $this ->expressions ->drop(1) - ->map(function(Expression $expression): string { + ->map(function($expression) { if ($this->removeLead()) { - return Str::of($expression->regex())->drop(2)->toString(); + return $expression + ->regex() + ->map(Str::of(...)) + ->map(static fn($regex) => $regex->drop(2)->toString()); } return $expression->regex(); }); - return Str::of($this->expansion()->separatorRegex()) - ->join( - $this - ->expressions - ->take(1) - ->map(static fn($expression) => $expression->regex()) - ->append($remaining), - ) - ->toString(); + return $this + ->expressions + ->take(1) + ->map(static fn($expression) => $expression->regex()) + ->append($remaining) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map( + fn($regexes) => Str::of($this->expansion()->separatorRegex()) + ->join($regexes) + ->toString(), + ); } #[\Override] diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index 0fe6349..00da4fd 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -82,7 +82,7 @@ public function expansion(): Expansion } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 2427af6..45052e3 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -82,7 +82,7 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 769a0f8..0ddc1da 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -123,27 +123,33 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new ExplodeExpressionCantBeMatched); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = $this + ->expression + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { $regex = $this->expression->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\;%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 7a71e3c..721aff2 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -82,7 +82,7 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index d9e9a95..f63e5d8 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -123,27 +123,33 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new ExplodeExpressionCantBeMatched); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = $this + ->expression + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { $regex = $this->expression->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\?%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index f13ef0c..513330a 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -123,27 +123,33 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new ExplodeExpressionCantBeMatched); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = $this + ->expression + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { $regex = $this->expression->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\&%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 601c822..f31a598 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -96,14 +96,14 @@ public function expand(Map $values, Map $lists, Map $keys): string } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new ExplodeExpressionCantBeMatched); } if (\is_int($this->limit)) { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]{{$this->limit}})"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]{{$this->limit}})"); } return $this->expression->regex(); diff --git a/src/Template.php b/src/Template.php index 4cc20a6..c349fc2 100644 --- a/src/Template.php +++ b/src/Template.php @@ -113,33 +113,36 @@ public function toString(): string */ private function regex(): Attempt { - return Attempt::of(function() { - $template = $this - ->expressions - ->reduce( - $this->template->replace('~', '\~'), - static fn(Str $template, $expression) => $template->replace( - $expression->toString(), - \sprintf( - '__innmind_expression_%s__', - \spl_object_hash($expression), - ), - ), - ) - ->pregQuote(); - $template = $this->expressions->reduce( - $template, + $template = $this + ->expressions + ->reduce( + $this->template->replace('~', '\~'), static fn(Str $template, $expression) => $template->replace( + $expression->toString(), \sprintf( '__innmind_expression_%s__', \spl_object_hash($expression), ), - $expression->regex(), ), - ); + ) + ->pregQuote(); - return $template->prepend('~^')->append('$~')->toString(); - }); + return $this + ->expressions + ->sink($template) + ->attempt( + static fn($template, $expression) => $expression + ->regex() + ->map(static fn($regex) => $template->replace( + \sprintf( + '__innmind_expression_%s__', + \spl_object_hash($expression), + ), + $regex, + )), + ) + ->map(static fn($regex) => $regex->prepend('~^')->append('$~')) + ->map(static fn($regex) => $regex->toString()); } /** diff --git a/tests/Expression/Level1Test.php b/tests/Expression/Level1Test.php index 6970f74..f6e7e10 100644 --- a/tests/Expression/Level1Test.php +++ b/tests/Expression/Level1Test.php @@ -86,7 +86,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)', Level1::of(Str::of('{foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level2/FragmentTest.php b/tests/Expression/Level2/FragmentTest.php index c5ccc66..0ed4d2b 100644 --- a/tests/Expression/Level2/FragmentTest.php +++ b/tests/Expression/Level2/FragmentTest.php @@ -91,7 +91,7 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level2/ReservedTest.php b/tests/Expression/Level2/ReservedTest.php index 3bbec0c..aaa4be6 100644 --- a/tests/Expression/Level2/ReservedTest.php +++ b/tests/Expression/Level2/ReservedTest.php @@ -91,7 +91,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/FragmentTest.php b/tests/Expression/Level3/FragmentTest.php index 26032ac..c7ce356 100644 --- a/tests/Expression/Level3/FragmentTest.php +++ b/tests/Expression/Level3/FragmentTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*),(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/LabelTest.php b/tests/Expression/Level3/LabelTest.php index 8103978..959ee3b 100644 --- a/tests/Expression/Level3/LabelTest.php +++ b/tests/Expression/Level3/LabelTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\.(?[a-zA-Z0-9\%\-\_\~]*).(?[a-zA-Z0-9\%\-\_\~]*)', Label::of(Str::of('{.foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ParametersTest.php b/tests/Expression/Level3/ParametersTest.php index 1faefd7..f1b6036 100644 --- a/tests/Expression/Level3/ParametersTest.php +++ b/tests/Expression/Level3/ParametersTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\;foo=?(?[a-zA-Z0-9\%\-\.\_\~]*)\;bar=?(?[a-zA-Z0-9\%\-\.\_\~]*)', Parameters::of(Str::of('{;foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/PathTest.php b/tests/Expression/Level3/PathTest.php index d9d2b02..014fbf8 100644 --- a/tests/Expression/Level3/PathTest.php +++ b/tests/Expression/Level3/PathTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '/(?[a-zA-Z0-9\%\-\.\_\~]*)/(?[a-zA-Z0-9\%\-\.\_\~]*)', Path::of(Str::of('{/foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryContinuationTest.php b/tests/Expression/Level3/QueryContinuationTest.php index 2491c2e..461ec75 100644 --- a/tests/Expression/Level3/QueryContinuationTest.php +++ b/tests/Expression/Level3/QueryContinuationTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]*)\&bar=(?[a-zA-Z0-9\%\-\.\_\~]*)', QueryContinuation::of(Str::of('{&foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryTest.php b/tests/Expression/Level3/QueryTest.php index 8c2b8a1..9cf1d51 100644 --- a/tests/Expression/Level3/QueryTest.php +++ b/tests/Expression/Level3/QueryTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]*)\&bar=(?[a-zA-Z0-9\%\-\.\_\~]*)', Query::of(Str::of('{?foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ReservedTest.php b/tests/Expression/Level3/ReservedTest.php index 450d869..a46bf09 100644 --- a/tests/Expression/Level3/ReservedTest.php +++ b/tests/Expression/Level3/ReservedTest.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*),(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3Test.php b/tests/Expression/Level3Test.php index b0722f8..bf2f5e3 100644 --- a/tests/Expression/Level3Test.php +++ b/tests/Expression/Level3Test.php @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*),(?[a-zA-Z0-9\%\-\.\_\~]*)', Level3::of(Str::of('{foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/CompositeTest.php b/tests/Expression/Level4/CompositeTest.php index a258236..495beb1 100644 --- a/tests/Expression/Level4/CompositeTest.php +++ b/tests/Expression/Level4/CompositeTest.php @@ -106,63 +106,63 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)\,(?[a-zA-Z0-9\%\-\.\_\~]*)', Composite::of(Str::of('{var,hello}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)\,(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)\,(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{5})', Composite::of(Str::of('{+var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)\,(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{5})', Composite::of(Str::of('{#var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]*)\.(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{.var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]*)\/(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{/var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\;var=(?[a-zA-Z0-9\%\-\.\_\~]*)\;hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{;var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\?var=(?[a-zA-Z0-9\%\-\.\_\~]*)\&hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{?var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\&var=(?[a-zA-Z0-9\%\-\.\_\~]*)\&hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{&var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index c0dfa61..d77a03a 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Fragment::of(Str::of('{#foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{2})', Fragment::of(Str::of('{#foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index 589e5a1..da7d804 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Label::of(Str::of('{.foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]*)', Label::of(Str::of('{.foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]{2})', Label::of(Str::of('{.foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index d1d873d..c051928 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Parameters::of(Str::of('{;foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '\;foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', Parameters::of(Str::of('{;foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\;foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', Parameters::of(Str::of('{;foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index ebc1744..cbc998c 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -171,7 +171,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Path::of(Str::of('{/foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +181,14 @@ public function testRegex() $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]*)', Path::of(Str::of('{/foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]{2})', Path::of(Str::of('{/foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index de2f037..d65a1db 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); QueryContinuation::of(Str::of('{&foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', QueryContinuation::of(Str::of('{&foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', QueryContinuation::of(Str::of('{&foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index e5eeebe..49b5d9f 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Query::of(Str::of('{?foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', Query::of(Str::of('{?foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', Query::of(Str::of('{?foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index ead4220..c6baae7 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -178,7 +178,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Reserved::of(Str::of('{+foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -188,14 +188,14 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{2})', Reserved::of(Str::of('{+foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 587a2a0..873b802 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -192,7 +192,7 @@ public function testThrowExplodeRegex() $this->expectException(LogicException::class); Level4::of(Str::of('{foo*}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -202,14 +202,14 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)', Level4::of(Str::of('{foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]{2})', Level4::of(Str::of('{foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); From fba6e08dc0491dbf0173dc546f202de4eeb0b7fd Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 11:44:35 +0100 Subject: [PATCH 25/33] remove custom exceptions --- CHANGELOG.md | 4 ++++ src/Exception/DomainException.php | 8 -------- src/Exception/Exception.php | 8 -------- src/Exception/ExplodeExpressionCantBeMatched.php | 8 -------- src/Exception/LogicException.php | 8 -------- src/Expression/Level4.php | 7 ++----- src/Expression/Level4/Parameters.php | 3 +-- src/Expression/Level4/Query.php | 3 +-- src/Expression/Level4/QueryContinuation.php | 3 +-- src/Expression/Level4/Reserved.php | 3 +-- src/Expression/Name.php | 5 +++-- tests/Expression/Level4/FragmentTest.php | 3 +-- tests/Expression/Level4/LabelTest.php | 3 +-- tests/Expression/Level4/ParametersTest.php | 3 +-- tests/Expression/Level4/PathTest.php | 3 +-- tests/Expression/Level4/QueryContinuationTest.php | 3 +-- tests/Expression/Level4/QueryTest.php | 3 +-- tests/Expression/Level4/ReservedTest.php | 3 +-- tests/Expression/Level4Test.php | 3 +-- tests/Expression/NameTest.php | 7 ++----- tests/TemplateTest.php | 7 ++----- 21 files changed, 25 insertions(+), 73 deletions(-) delete mode 100644 src/Exception/DomainException.php delete mode 100644 src/Exception/Exception.php delete mode 100644 src/Exception/ExplodeExpressionCantBeMatched.php delete mode 100644 src/Exception/LogicException.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a291a1..428c8dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ ### Removed - `Innmind\UrlTemplate\Template::expand()`, use `::expansion()` instead +- `Innmind\UrlTemplate\Exception\Exception` +- `Innmind\UrlTemplate\Exception\LogicException` +- `Innmind\UrlTemplate\Exception\DomainException` +- `Innmind\UrlTemplate\Exception\ExplodeExpressionCantBeMatched` ## 3.1.0 - 2023-09-16 diff --git a/src/Exception/DomainException.php b/src/Exception/DomainException.php deleted file mode 100644 index 3e994a0..0000000 --- a/src/Exception/DomainException.php +++ /dev/null @@ -1,8 +0,0 @@ -explode) { - return Attempt::error(new ExplodeExpressionCantBeMatched); + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 0ddc1da..e9245d8 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -9,7 +9,6 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, @@ -126,7 +125,7 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { if ($this->explode) { - return Attempt::error(new ExplodeExpressionCantBeMatched); + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index f63e5d8..8149abf 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -9,7 +9,6 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, @@ -126,7 +125,7 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { if ($this->explode) { - return Attempt::error(new ExplodeExpressionCantBeMatched); + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 513330a..2375317 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -9,7 +9,6 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, @@ -126,7 +125,7 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { if ($this->explode) { - return Attempt::error(new ExplodeExpressionCantBeMatched); + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index f31a598..e2ee552 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -9,7 +9,6 @@ Expression\Expansion, Expression\Level2, Expression\Level4, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, @@ -99,7 +98,7 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { if ($this->explode) { - return Attempt::error(new ExplodeExpressionCantBeMatched); + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if (\is_int($this->limit)) { diff --git a/src/Expression/Name.php b/src/Expression/Name.php index 60ca603..fd69e51 100644 --- a/src/Expression/Name.php +++ b/src/Expression/Name.php @@ -3,7 +3,6 @@ namespace Innmind\UrlTemplate\Expression; -use Innmind\UrlTemplate\Exception\DomainException; use Innmind\Immutable\{ Str, Maybe, @@ -30,13 +29,15 @@ private function __construct(string $value) /** * @psalm-pure + * + * @throws \DomainException */ public static function of(string $value): self { $characters = self::characters(); if (!Str::of($value)->matches("~^{$characters}\$~")) { - throw new DomainException($value); + throw new \DomainException($value); } /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index d77a03a..427a1ac 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -7,7 +7,6 @@ Expression\Level4\Fragment, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Fragment::of(Str::of('{#foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index da7d804..5888846 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -7,7 +7,6 @@ Expression\Level4\Label, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Label::of(Str::of('{.foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index c051928..4d419a4 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -7,7 +7,6 @@ Expression\Level4\Parameters, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Parameters::of(Str::of('{;foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index cbc998c..4472722 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -7,7 +7,6 @@ Expression\Level4\Path, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -168,7 +167,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Path::of(Str::of('{/foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index d65a1db..883e824 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -7,7 +7,6 @@ Expression\Level4\QueryContinuation, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); QueryContinuation::of(Str::of('{&foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index 49b5d9f..9c4ea1e 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -7,7 +7,6 @@ Expression\Level4\Query, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Query::of(Str::of('{?foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index c6baae7..725c8ae 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -7,7 +7,6 @@ Expression\Level4\Reserved, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -175,7 +174,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Reserved::of(Str::of('{+foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 873b802..35f2929 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -7,7 +7,6 @@ Expression\Level4, Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -189,7 +188,7 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); Level4::of(Str::of('{foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), diff --git a/tests/Expression/NameTest.php b/tests/Expression/NameTest.php index 9fa9081..b69c2c2 100644 --- a/tests/Expression/NameTest.php +++ b/tests/Expression/NameTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\UrlTemplate\Expression; -use Innmind\UrlTemplate\{ - Expression\Name, - Exception\DomainException, -}; +use Innmind\UrlTemplate\Expression\Name; use Innmind\BlackBox\{ PHPUnit\BlackBox, PHPUnit\Framework\TestCase, @@ -44,7 +41,7 @@ public function testThrowWhenInvalidName(): BlackBox\Proof }), ) ->prove(function(string $string): void { - $this->expectException(DomainException::class); + $this->expectException(\DomainException::class); $this->expectExceptionMessage($string); Name::of($string); diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index 81553ba..c3fef8d 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\UrlTemplate; -use Innmind\UrlTemplate\{ - Template, - Exception\ExplodeExpressionCantBeMatched, -}; +use Innmind\UrlTemplate\Template; use Innmind\Url\Url; use Innmind\Immutable\Map; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -451,7 +448,7 @@ public function testExtraction() public function testThrowWhenExtractionNotSupportedForTemplate() { - $this->expectException(ExplodeExpressionCantBeMatched::class); + $this->expectException(\LogicException::class); Template::of('{foo*}') ->extract(Url::of('foo,bar,baz')) From d6c0ce0f1bbc05f5629118005afe365d070bea40 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 12:04:49 +0100 Subject: [PATCH 26/33] use an enum to distinguish the different strategies to encode chars --- src/Expression/Level1.php | 6 ++--- src/Expression/Level2/Fragment.php | 4 ++-- src/Expression/Level2/Reserved.php | 6 ++--- src/UrlEncode.php | 37 +++++++++++------------------- tests/UrlEncodeTest.php | 14 +++++------ 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index 90f811c..dfbfa3e 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -25,7 +25,7 @@ final class Level1 implements Expression private function __construct(Name $name) { $this->name = $name; - $this->encode = new UrlEncode; + $this->encode = UrlEncode::everything; } /** @@ -64,14 +64,14 @@ public function expand(Map $values, Map $lists, Map $keys): string return $values ->get($this->name->toString()) ->match( - $this->encode, + $this->encode->encode(...), static fn() => '', ); } public function encode(string $string): string { - return ($this->encode)($string); + return $this->encode->encode($string); } #[\Override] diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 8d78f20..3ff4c02 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -27,7 +27,7 @@ final class Fragment implements Expression private function __construct(Name $name) { $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters(); + $this->encode = UrlEncode::allowReservedCharacters; } /** @@ -52,7 +52,7 @@ public function expand(Map $values, Map $lists, Map $keys): string { return $values ->get($this->name->toString()) - ->map($this->encode) + ->map($this->encode->encode(...)) ->match( static fn(string $variable) => '#'.$variable, static fn() => '', diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 7e77a66..65a4fe6 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -27,7 +27,7 @@ final class Reserved implements Expression private function __construct(Name $name) { $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters(); + $this->encode = UrlEncode::allowReservedCharacters; } /** @@ -61,14 +61,14 @@ public function expand(Map $values, Map $lists, Map $keys): string return $values ->get($this->name->toString()) ->match( - $this->encode, + $this->encode->encode(...), static fn() => '', ); } public function encode(string $string): string { - return ($this->encode)($string); + return $this->encode->encode($string); } #[\Override] diff --git a/src/UrlEncode.php b/src/UrlEncode.php index 3ed78f7..63b23f2 100644 --- a/src/UrlEncode.php +++ b/src/UrlEncode.php @@ -5,7 +5,6 @@ use Innmind\Immutable\{ Str, - Sequence, Monoid\Concat, }; @@ -13,26 +12,21 @@ * @psalm-immutable * @internal */ -final class UrlEncode +enum UrlEncode { - /** @var Sequence */ - private Sequence $safeCharacters; + case everything; + case allowReservedCharacters; - public function __construct() + public function encode(string $string): string { - $this->safeCharacters = Sequence::strings(); - } - - public function __invoke(string $string): string - { - if ($this->safeCharacters->empty()) { + if ($this === self::everything) { return \rawurlencode($string); } return Str::of($string) ->split() ->map(static fn($char) => $char->toString()) - ->map($this->encode(...)) + ->map(self::map(...)) ->map(Str::of(...)) ->fold(new Concat) ->toString(); @@ -41,10 +35,9 @@ public function __invoke(string $string): string /** * @psalm-pure */ - public static function allowReservedCharacters(): self + private static function map(string $char): string { - $self = new self; - $self->safeCharacters = Sequence::strings( + $allowed = [ ':', '/', '?', @@ -63,16 +56,12 @@ public static function allowReservedCharacters(): self ',', ';', '=', - ); + ]; - return $self; - } + if (\in_array($char, $allowed, true)) { + return $char; + } - private function encode(string $char): string - { - return match ($this->safeCharacters->contains($char)) { - true => $char, - false => \rawurlencode($char), - }; + return \rawurlencode($char); } } diff --git a/tests/UrlEncodeTest.php b/tests/UrlEncodeTest.php index 57c2df6..db47411 100644 --- a/tests/UrlEncodeTest.php +++ b/tests/UrlEncodeTest.php @@ -19,9 +19,9 @@ public function testStandardEncode(): BlackBox\Proof return $this ->forAll(Set::strings()) ->prove(function(string $string): void { - $encode = new UrlEncode; + $encode = UrlEncode::everything; - $this->assertSame(\rawurlencode($string), $encode($string)); + $this->assertSame(\rawurlencode($string), $encode->encode($string)); }); } @@ -49,24 +49,24 @@ public function testSafeCharactersAreNotEncoded(): BlackBox\Proof '=', )) ->prove(function(string $char): void { - $encode = UrlEncode::allowReservedCharacters(); + $encode = UrlEncode::allowReservedCharacters; - $this->assertSame($char, $encode($char)); + $this->assertSame($char, $encode->encode($char)); }); } public function testSafeCharactersAreNotEncodedEvenWhenInMiddleOfString() { - $encode = UrlEncode::allowReservedCharacters(); + $encode = UrlEncode::allowReservedCharacters; $this->assertSame( ':%20)', - $encode(': )'), + $encode->encode(': )'), ); } public function testDoesNothingOnEmptyString() { - $this->assertSame('', UrlEncode::allowReservedCharacters()('')); + $this->assertSame('', UrlEncode::allowReservedCharacters->encode('')); } } From c064bcd935d67e216168257d4ab2df3e0331710d Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 15:21:10 +0100 Subject: [PATCH 27/33] use promoted properties --- src/Expression/Level1.php | 15 ++-- src/Expression/Level2/Fragment.php | 16 ++--- src/Expression/Level2/Reserved.php | 15 ++-- src/Expression/Level3.php | 16 ++--- src/Expression/Level3/Fragment.php | 20 ++---- src/Expression/Level3/Label.php | 20 ++---- src/Expression/Level3/NamedValues.php | 41 +++++++---- src/Expression/Level3/Parameters.php | 14 ++-- src/Expression/Level3/Path.php | 22 +++--- src/Expression/Level3/Query.php | 16 ++--- src/Expression/Level3/QueryContinuation.php | 16 ++--- src/Expression/Level3/Reserved.php | 20 ++---- src/Expression/Level4.php | 76 +++++++++++++-------- src/Expression/Level4/Composite.php | 12 ++-- src/Expression/Level4/Fragment.php | 38 +++++------ src/Expression/Level4/Label.php | 27 ++++---- src/Expression/Level4/Parameters.php | 39 ++++------- src/Expression/Level4/Path.php | 27 ++++---- src/Expression/Level4/Query.php | 39 ++++------- src/Expression/Level4/QueryContinuation.php | 39 ++++------- src/Expression/Level4/Reserved.php | 56 ++++++++------- src/Expression/Name.php | 6 +- src/Template.php | 12 ++-- 23 files changed, 271 insertions(+), 331 deletions(-) diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index dfbfa3e..ca67c65 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -19,13 +19,10 @@ */ final class Level1 implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = UrlEncode::everything; + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** @@ -36,7 +33,7 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::simple) - ->map(static fn($name) => new self($name)); + ->map(self::named(...)); } /** @@ -44,7 +41,7 @@ public static function of(Str $string): Attempt */ public static function named(Name $name): self { - return new self($name); + return new self($name, UrlEncode::everything); } public function name(): Name diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index 3ff4c02..e03cb5d 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -21,13 +21,10 @@ */ final class Fragment implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters; + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** @@ -38,7 +35,10 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::fragment) - ->map(static fn($name) => new self($name)); + ->map(static fn($name) => new self( + $name, + UrlEncode::allowReservedCharacters, + )); } #[\Override] diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 65a4fe6..5816bc2 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -21,13 +21,10 @@ */ final class Reserved implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters; + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** @@ -38,7 +35,7 @@ private function __construct(Name $name) public static function of(Str $string): Attempt { return Name::one($string, Expansion::reserved) - ->map(static fn($name) => new self($name)); + ->map(self::named(...)); } /** @@ -46,7 +43,7 @@ public static function of(Str $string): Attempt */ public static function named(Name $name): self { - return new self($name); + return new self($name, UrlEncode::allowReservedCharacters); } #[\Override] diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 215c03c..a4f5cbe 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -17,18 +17,11 @@ */ final class Level3 implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - $this->expressions = $this->names->map(Level1::named(...)); } /** @@ -51,9 +44,10 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($values, $lists, $keys), - ); + $expanded = $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',')->join($expanded)->toString(); } diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 6d2f4f8..fbad1f6 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -22,19 +22,11 @@ */ final class Fragment implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level2\Reserved::named(...)); } /** @@ -57,9 +49,10 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($values, $lists, $keys), - ); + $expanded = $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',') ->join($expanded) @@ -71,7 +64,8 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { return $this - ->expressions + ->names + ->map(Level2\Reserved::named(...)) ->map(static fn($expression) => $expression->regex()) ->sink(Sequence::strings()) ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 08f9d02..3195814 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -22,19 +22,11 @@ */ final class Label implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level1::named(...)); } /** @@ -57,9 +49,10 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($values, $lists, $keys), - ); + $expanded = $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of('.') ->join($expanded) @@ -71,7 +64,8 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { return $this - ->expressions + ->names + ->map(Level1::named(...)) ->map(static fn($expression) => $expression->regex()) ->sink(Sequence::strings()) ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index f303c36..10f3b24 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -21,21 +21,24 @@ */ final class NamedValues { - private Expansion $expansion; - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - private bool $keyOnlyWhenEmpty = false; + /** + * @param Sequence $names + */ + private function __construct( + private Expansion $expansion, + private Sequence $names, + private bool $keyOnlyWhenEmpty = false, + ) { + } /** + * @psalm-pure + * * @param Sequence $names */ - public function __construct(Expansion $expansion, Sequence $names) + public static function parameters(Sequence $names): self { - $this->expansion = $expansion; - $this->names = $names; - $this->expressions = $names->map(Level1::named(...)); + return new self(Expansion::parameter, $names, true); } /** @@ -43,12 +46,19 @@ public function __construct(Expansion $expansion, Sequence $names) * * @param Sequence $names */ - public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): self + public static function query(Sequence $names): self { - $self = new self($expansion, $names); - $self->keyOnlyWhenEmpty = true; + return new self(Expansion::query, $names, false); + } - return $self; + /** + * @psalm-pure + * + * @param Sequence $names + */ + public static function queryContinuation(Sequence $names): self + { + return new self(Expansion::queryContinuation, $names, false); } /** @@ -59,7 +69,8 @@ public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this - ->expressions + ->names + ->map(Level1::named(...)) ->map(static fn($expression) => [ $expression->name()->toString(), Str::of($expression->expand($values, $lists, $keys)), diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index 72d12aa..b69d8c5 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -21,14 +21,9 @@ */ final class Parameters implements Expression { - private NamedValues $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = NamedValues::keyOnlyWhenEmpty(Expansion::parameter, $names); + private function __construct( + private NamedValues $expression, + ) { } /** @@ -39,6 +34,7 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::parameter) + ->map(NamedValues::parameters(...)) ->map(static fn($names) => new self($names)); } @@ -47,7 +43,7 @@ public static function of(Str $string): Attempt */ public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::parameters(Sequence::of($name))); } #[\Override] diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 31586a7..54423b6 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -22,19 +22,11 @@ */ final class Path implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level1::named(...)); } /** @@ -58,9 +50,12 @@ public function expansion(): Expansion public function expand(Map $values, Map $lists, Map $keys): string { return Str::of('/') - ->join($this->expressions->map( - static fn($expression) => $expression->expand($values, $lists, $keys), - )) + ->join( + $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)), + ) ->prepend('/') ->toString(); } @@ -69,7 +64,8 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { return $this - ->expressions + ->names + ->map(Level1::named(...)) ->map(static fn($expression) => $expression->regex()) ->sink(Sequence::strings()) ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index fa1eb1f..b1df6a2 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -21,14 +21,9 @@ */ final class Query implements Expression { - private NamedValues $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = new NamedValues(Expansion::query, $names); + private function __construct( + private NamedValues $expression, + ) { } /** @@ -39,7 +34,8 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::query) - ->map(static fn($names) => new self($names)); + ->map(NamedValues::query(...)) + ->map(static fn($expression) => new self($expression)); } /** @@ -47,7 +43,7 @@ public static function of(Str $string): Attempt */ public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::query(Sequence::of($name))); } #[\Override] diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index 3a38aec..76c6ef2 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -21,14 +21,9 @@ */ final class QueryContinuation implements Expression { - private NamedValues $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = new NamedValues(Expansion::queryContinuation, $names); + private function __construct( + private NamedValues $expression, + ) { } /** @@ -39,12 +34,13 @@ private function __construct(Sequence $names) public static function of(Str $string): Attempt { return Name::many($string, Expansion::queryContinuation) - ->map(static fn($names) => new self($names)); + ->map(NamedValues::queryContinuation(...)) + ->map(static fn($expression) => new self($expression)); } public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::queryContinuation(Sequence::of($name))); } #[\Override] diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 6da9de3..301a68c 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -22,19 +22,11 @@ */ final class Reserved implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level2\Reserved::named(...)); } /** @@ -57,9 +49,10 @@ public function expansion(): Expansion #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($values, $lists, $keys), - ); + $expanded = $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',')->join($expanded)->toString(); } @@ -68,7 +61,8 @@ public function expand(Map $values, Map $lists, Map $keys): string public function regex(): Attempt { return $this - ->expressions + ->names + ->map(Level2\Reserved::named(...)) ->map(static fn($expression) => $expression->regex()) ->sink(Sequence::strings()) ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index f9125a7..a40a650 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -17,18 +17,16 @@ */ final class Level4 implements Expression { - private Name $name; - private Level1|Level2\Reserved $expression; - /** @var ?int<1, max> */ - private ?int $limit = null; - private bool $explode = false; - private Expansion $expansion; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); - $this->expansion = Expansion::simple; + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private Level1|Level2\Reserved $expression, + private ?int $limit, + private bool $explode, + private Expansion $expansion, + ) { } /** @@ -40,7 +38,7 @@ public static function of(Str $string): Attempt { return Level4\Parse::of( $string, - static fn(Name $name) => new self($name), + self::named(...), self::explode(...), self::limit(...), Expansion::simple, @@ -54,10 +52,13 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self( + $name, + Level1::named($name), + $limit, + false, + Expansion::simple, + ); } /** @@ -65,10 +66,13 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self( + $name, + Level1::named($name), + null, + true, + Expansion::simple, + ); } /** @@ -76,7 +80,13 @@ public static function explode(Name $name): self */ public static function named(Name $name): self { - return new self($name); + return new self( + $name, + Level1::named($name), + null, + false, + Expansion::simple, + ); } #[\Override] @@ -87,10 +97,13 @@ public function expansion(): Expansion public function withExpansion(Expansion $expansion): self { - $self = clone $this; - $self->expansion = $expansion; - - return $self; + return new self( + $this->name, + $this->expression, + $this->limit, + $this->explode, + $expansion, + ); } /** @@ -101,10 +114,13 @@ public function withExpansion(Expansion $expansion): self */ public function withExpression(callable $expression): self { - $self = clone $this; - $self->expression = $expression($self->name); - - return $self; + return new self( + $this->name, + $expression($this->name), + $this->limit, + $this->explode, + $this->expansion, + ); } #[\Override] diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index d78fb0b..33fdba9 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -22,17 +22,13 @@ */ final class Composite implements Expression { - private Expansion $expansion; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $expressions */ - private function __construct(Expansion $expansion, Sequence $expressions) - { - $this->expansion = $expansion; - $this->expressions = $expressions; + private function __construct( + private Expansion $expansion, + private Sequence $expressions, + ) { } /** diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index 00da4fd..b5cb78b 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -22,13 +22,9 @@ */ final class Fragment implements Expression { - private Level4 $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); + private function __construct( + private Level4 $expression, + ) { } /** @@ -40,7 +36,11 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ), self::explode(...), self::limit(...), Expansion::fragment, @@ -54,12 +54,11 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); - - return $self; + return new self( + Level4::limit($name, $limit) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ); } /** @@ -67,12 +66,11 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); - - return $self; + return new self( + Level4::explode($name) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ); } #[\Override] diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index 45052e3..5a72f8e 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -21,11 +21,9 @@ */ final class Label implements Expression { - private Level4 $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name)->withExpansion(Expansion::label); + private function __construct( + private Level4 $expression, + ) { } /** @@ -37,7 +35,9 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name)->withExpansion(Expansion::label), + ), self::explode(...), self::limit(...), Expansion::label, @@ -51,10 +51,9 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit)->withExpansion(Expansion::label); - - return $self; + return new self( + Level4::limit($name, $limit)->withExpansion(Expansion::label), + ); } /** @@ -62,11 +61,9 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::label); - - return $self; + return new self( + Level4::explode($name)->withExpansion(Expansion::label), + ); } #[\Override] diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index e9245d8..2035340 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -23,16 +23,14 @@ */ final class Parameters implements Expression { - private Name $name; - /** @var ?int<1, max> */ - private ?int $limit = null; - private bool $explode = false; - private Level1 $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** @@ -44,7 +42,7 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::parameter, @@ -58,10 +56,7 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -69,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -98,7 +90,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$value]); } - $value = Str::of($this->expression->encode($value)); + $value = Str::of(Level1::named($this->name)->encode($value)); if ($this->mustLimit()) { return \sprintf( @@ -130,8 +122,7 @@ public function regex(): Attempt if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = $this - ->expression + $regex = Level1::named($this->name) ->regex() ->map(Str::of(...)) ->map( @@ -141,7 +132,7 @@ public function regex(): Attempt ->toString(), ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } return $regex->map(fn($regex) => \sprintf( @@ -196,7 +187,7 @@ static function($variableToExpand): Sequence { // here we use the level1 expression to transform the variable to // be expanded to its string representation $expanded = $flattenedVariables->map( - $this->expression->encode(...), + Level1::named($this->name)->encode(...), ); return Str::of(',') diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 721aff2..0e5fb0b 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -21,11 +21,9 @@ */ final class Path implements Expression { - private Level4 $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name)->withExpansion(Expansion::path); + private function __construct( + private Level4 $expression, + ) { } /** @@ -37,7 +35,9 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name)->withExpansion(Expansion::path), + ), self::explode(...), self::limit(...), Expansion::path, @@ -51,10 +51,9 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit)->withExpansion(Expansion::path); - - return $self; + return new self( + Level4::limit($name, $limit)->withExpansion(Expansion::path), + ); } /** @@ -62,11 +61,9 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::path); - - return $self; + return new self( + Level4::explode($name)->withExpansion(Expansion::path), + ); } #[\Override] diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 8149abf..a685778 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -23,16 +23,14 @@ */ final class Query implements Expression { - private Name $name; - /** @var ?int<1, max> */ - private ?int $limit = null; - private bool $explode = false; - private Level1 $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** @@ -44,7 +42,7 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::query, @@ -58,10 +56,7 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -69,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -98,7 +90,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$value]); } - $value = Str::of($this->expression->encode($value)); + $value = Str::of(Level1::named($this->name)->encode($value)); if ($this->mustLimit()) { return \sprintf( @@ -130,8 +122,7 @@ public function regex(): Attempt if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = $this - ->expression + $regex = Level1::named($this->name) ->regex() ->map(Str::of(...)) ->map( @@ -141,7 +132,7 @@ public function regex(): Attempt ->toString(), ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } return $regex->map(fn($regex) => \sprintf( @@ -196,7 +187,7 @@ static function($variableToExpand): Sequence { // here we use the level1 expression to transform the variable to // be expanded to its string representation $expanded = $flattenedVariables->map( - $this->expression->encode(...), + Level1::named($this->name)->encode(...), ); return Str::of(',') diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 2375317..ef446a4 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -23,16 +23,14 @@ */ final class QueryContinuation implements Expression { - private Name $name; - /** @var ?int<1, max> */ - private ?int $limit = null; - private bool $explode = false; - private Level1 $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** @@ -44,7 +42,7 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::queryContinuation, @@ -58,10 +56,7 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -69,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -98,7 +90,7 @@ public function expand(Map $values, Map $lists, Map $keys): string return $this->explodeList([$value]); } - $value = Str::of($this->expression->encode($value)); + $value = Str::of(Level1::named($this->name)->encode($value)); if ($this->mustLimit()) { return \sprintf( @@ -130,8 +122,7 @@ public function regex(): Attempt if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = $this - ->expression + $regex = Level1::named($this->name) ->regex() ->map(Str::of(...)) ->map( @@ -141,7 +132,7 @@ public function regex(): Attempt ->toString(), ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } return $regex->map(fn($regex) => \sprintf( @@ -196,7 +187,7 @@ static function($variableToExpand): Sequence { // here we use the level1 expression to transform the variable to // be expanded to its string representation $expanded = $flattenedVariables->map( - $this->expression->encode(...), + Level1::named($this->name)->encode(...), ); return Str::of(',') diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index e2ee552..78a6dec 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -22,18 +22,15 @@ */ final class Reserved implements Expression { - private Name $name; - /** @var ?int<1, max> */ - private ?int $limit = null; - private bool $explode = false; - private Level4 $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level4::named($name)->withExpression( - Level2\Reserved::named(...), - ); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + private Level4 $expression, + ) { } /** @@ -45,7 +42,14 @@ public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + $name, + null, + false, + Level4::named($name)->withExpression( + Level2\Reserved::named(...), + ), + ), self::explode(...), self::limit(...), Expansion::reserved, @@ -59,13 +63,14 @@ public static function of(Str $string): Attempt */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - $self->expression = Level4::limit($name, $limit)->withExpression( - Level2\Reserved::named(...), + return new self( + $name, + $limit, + false, + Level4::limit($name, $limit)->withExpression( + Level2\Reserved::named(...), + ), ); - - return $self; } /** @@ -73,13 +78,14 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - $self->expression = Level4::explode($name)->withExpression( - Level2\Reserved::named(...), + return new self( + $name, + null, + true, + Level4::explode($name)->withExpression( + Level2\Reserved::named(...), + ), ); - - return $self; } #[\Override] diff --git a/src/Expression/Name.php b/src/Expression/Name.php index fd69e51..932b7ad 100644 --- a/src/Expression/Name.php +++ b/src/Expression/Name.php @@ -16,15 +16,11 @@ */ final class Name { - /** @var non-empty-string */ - private string $value; - /** * @param non-empty-string $value */ - private function __construct(string $value) + private function __construct(private string $value) { - $this->value = $value; } /** diff --git a/src/Template.php b/src/Template.php index c349fc2..16b20a9 100644 --- a/src/Template.php +++ b/src/Template.php @@ -17,17 +17,13 @@ */ final class Template { - private Str $template; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $expressions */ - private function __construct(Str $template, Sequence $expressions) - { - $this->template = $template; - $this->expressions = $expressions; + private function __construct( + private Str $template, + private Sequence $expressions, + ) { } /** From 5086adc928c3f95955370a910135324c4ba9cc37 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 3 Nov 2025 15:23:52 +0100 Subject: [PATCH 28/33] avoid storing the name twice --- src/Expression/Level2/Reserved.php | 5 +++++ src/Expression/Level4.php | 21 ++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 5816bc2..c04696a 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -46,6 +46,11 @@ public static function named(Name $name): self return new self($name, UrlEncode::allowReservedCharacters); } + public function name(): Name + { + return $this->name; + } + #[\Override] public function expansion(): Expansion { diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index a40a650..e8dc235 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -21,7 +21,6 @@ final class Level4 implements Expression * @param ?int<1, max> $limit */ private function __construct( - private Name $name, private Level1|Level2\Reserved $expression, private ?int $limit, private bool $explode, @@ -53,7 +52,6 @@ public static function of(Str $string): Attempt public static function limit(Name $name, int $limit): self { return new self( - $name, Level1::named($name), $limit, false, @@ -67,7 +65,6 @@ public static function limit(Name $name, int $limit): self public static function explode(Name $name): self { return new self( - $name, Level1::named($name), null, true, @@ -81,7 +78,6 @@ public static function explode(Name $name): self public static function named(Name $name): self { return new self( - $name, Level1::named($name), null, false, @@ -89,6 +85,11 @@ public static function named(Name $name): self ); } + public function name(): Name + { + return $this->expression->name(); + } + #[\Override] public function expansion(): Expansion { @@ -98,7 +99,6 @@ public function expansion(): Expansion public function withExpansion(Expansion $expansion): self { return new self( - $this->name, $this->expression, $this->limit, $this->explode, @@ -115,8 +115,7 @@ public function withExpansion(Expansion $expansion): self public function withExpression(callable $expression): self { return new self( - $this->name, - $expression($this->name), + $expression($this->name()), $this->limit, $this->explode, $this->expansion, @@ -126,7 +125,7 @@ public function withExpression(callable $expression): self #[\Override] public function expand(Map $values, Map $lists, Map $keys): string { - $name = $this->name->toString(); + $name = $this->name()->toString(); return $lists ->get($name) @@ -189,14 +188,14 @@ public function regex(): Attempt public function toString(): string { if ($this->mustLimit()) { - return "{{$this->expansion->toString()}{$this->name->toString()}:{$this->limit}}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}:{$this->limit}}"; } if ($this->explode) { - return "{{$this->expansion->toString()}{$this->name->toString()}*}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}*}"; } - return "{{$this->expansion->toString()}{$this->name->toString()}}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}}"; } /** From ee938a4e560dddb5d221a314f89ba4b7a454f401 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 25 Jan 2026 14:13:29 +0100 Subject: [PATCH 29/33] tag dependencies --- .github/workflows/ci.yml | 8 ++++---- composer.json | 6 +++--- src/UrlEncode.php | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 779f162..2f3eecb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,11 @@ on: [push, pull_request] jobs: blackbox: - uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@next + uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main coverage: - uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@next + uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main secrets: inherit psalm: - uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@next + uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main cs: - uses: innmind/github-workflows/.github/workflows/cs.yml@next + uses: innmind/github-workflows/.github/workflows/cs.yml@main diff --git a/composer.json b/composer.json index c7004aa..b5a5c0d 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,8 @@ }, "require": { "php": "~8.4", - "innmind/url": "dev-next", - "innmind/immutable": "dev-next" + "innmind/url": "~5.0", + "innmind/immutable": "~6.0" }, "autoload": { "psr-4": { @@ -30,7 +30,7 @@ } }, "require-dev": { - "innmind/static-analysis": "^1.2.1", + "innmind/static-analysis": "~1.3", "innmind/black-box": "~6.5", "innmind/coding-standard": "~2.0" } diff --git a/src/UrlEncode.php b/src/UrlEncode.php index 63b23f2..9d2357d 100644 --- a/src/UrlEncode.php +++ b/src/UrlEncode.php @@ -28,7 +28,7 @@ public function encode(string $string): string ->map(static fn($char) => $char->toString()) ->map(self::map(...)) ->map(Str::of(...)) - ->fold(new Concat) + ->fold(Concat::monoid) ->toString(); } From a9a0657d300bfc5259a96e1894bd66ae7242ad54 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 25 Jan 2026 14:18:02 +0100 Subject: [PATCH 30/33] fix warnings --- tests/Expression/Level4/FragmentTest.php | 2 +- tests/Expression/Level4/LabelTest.php | 2 +- tests/Expression/Level4/ParametersTest.php | 2 +- tests/Expression/Level4/PathTest.php | 2 +- tests/Expression/Level4/QueryContinuationTest.php | 2 +- tests/Expression/Level4/QueryTest.php | 2 +- tests/Expression/Level4/ReservedTest.php | 2 +- tests/Expression/Level4Test.php | 2 +- tests/TemplateTest.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index 427a1ac..dff6292 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Fragment::of(Str::of('{#foo*}'))->match( + $_ = Fragment::of(Str::of('{#foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index 5888846..4f72d79 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Label::of(Str::of('{.foo*}'))->match( + $_ = Label::of(Str::of('{.foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index 4d419a4..13a036d 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Parameters::of(Str::of('{;foo*}'))->match( + $_ = Parameters::of(Str::of('{;foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index 4472722..9ff0abb 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -169,7 +169,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Path::of(Str::of('{/foo*}'))->match( + $_ = Path::of(Str::of('{/foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index 883e824..3aeae63 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - QueryContinuation::of(Str::of('{&foo*}'))->match( + $_ = QueryContinuation::of(Str::of('{&foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index 9c4ea1e..6044c35 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Query::of(Str::of('{?foo*}'))->match( + $_ = Query::of(Str::of('{?foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index 725c8ae..3fc8032 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -176,7 +176,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Reserved::of(Str::of('{+foo*}'))->match( + $_ = Reserved::of(Str::of('{+foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 35f2929..8b4cd65 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -190,7 +190,7 @@ public function testThrowExplodeRegex() { $this->expectException(\LogicException::class); - Level4::of(Str::of('{foo*}'))->match( + $_ = Level4::of(Str::of('{foo*}'))->match( static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index c3fef8d..b902e27 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -450,7 +450,7 @@ public function testThrowWhenExtractionNotSupportedForTemplate() { $this->expectException(\LogicException::class); - Template::of('{foo*}') + $_ = Template::of('{foo*}') ->extract(Url::of('foo,bar,baz')) ->unwrap(); } From 9d9dfa829909f2bca9761022ba61f369139e5919 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 25 Jan 2026 14:22:31 +0100 Subject: [PATCH 31/33] remove todos --- src/Expansion.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Expansion.php b/src/Expansion.php index 3165658..7c6f372 100644 --- a/src/Expansion.php +++ b/src/Expansion.php @@ -51,7 +51,7 @@ public static function of(Str $template, Sequence $expressions): self /** * @no-named-arguments * - * @param non-empty-string $name Todo use literal strings + * @param non-empty-string $name */ public function with(string $name, string ...$values): self { @@ -77,7 +77,7 @@ public function with(string $name, string ...$values): self /** * @no-named-arguments * - * @param non-empty-string $name Todo use literal strings + * @param non-empty-string $name * @param array{string, string} ...$keys */ public function withKeys(string $name, array ...$keys): self From 9d96710b4203d406f91a3a15a8e8cae32a1e4147 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 25 Jan 2026 14:29:59 +0100 Subject: [PATCH 32/33] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 428c8dd..baa209f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ ### Changed -- Requires `innmind/immutable:~5.18` +- Requires `innmind/immutable:~6.0` +- Requires `innmind/url:~5.0` - Requires PHP `8.4` - `Innmind\UrlTemplate\Template::extract()` now returns an `Innmind\Immutable\Attempt` - `Innmind\UrlTemplate\Template::matches()` now returns an `Innmind\Immutable\Attempt` From 24a9430a812291bd6f4c4eb4548fc924139eb102 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 25 Jan 2026 14:31:43 +0100 Subject: [PATCH 33/33] add extensive CI --- .github/workflows/extensive.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/extensive.yml diff --git a/.github/workflows/extensive.yml b/.github/workflows/extensive.yml new file mode 100644 index 0000000..257f139 --- /dev/null +++ b/.github/workflows/extensive.yml @@ -0,0 +1,12 @@ +name: Extensive CI + +on: + push: + tags: + - '*' + paths: + - '.github/workflows/extensive.yml' + +jobs: + blackbox: + uses: innmind/github-workflows/.github/workflows/extensive.yml@main