Skip to content

Commit 692bfba

Browse files
committed
Return object from URI generation
This provides a richer API, allowing users to also retrieve the substitutions that were sent but aren't part of the route (either as a map or by generating a PSR-7 URI object). Signed-off-by: Luís Cobucci <[email protected]>
1 parent 0ec17fd commit 692bfba

File tree

7 files changed

+270
-20
lines changed

7 files changed

+270
-20
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
],
1515
"require": {
1616
"php": ">=8.1.0",
17+
"psr/http-message": "^2.0",
1718
"psr/simple-cache": "^2.0 || ^3.0"
1819
},
1920
"require-dev": {
2021
"lcobucci/coding-standard": "^11.1",
22+
"nyholm/psr7": "^1.8",
2123
"phpbench/phpbench": "^1.2",
2224
"phpstan/extension-installer": "^1.1",
2325
"phpstan/phpstan": "^1.10",

composer.lock

Lines changed: 187 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/GenerateUri.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace FastRoute;
55

6+
use FastRoute\GenerateUri\GeneratedUri;
67
use FastRoute\GenerateUri\UriCouldNotBeGenerated;
78

89
/**
@@ -17,5 +18,5 @@ interface GenerateUri
1718
*
1819
* @throws UriCouldNotBeGenerated
1920
*/
20-
public function forRoute(string $name, array $substitutions = []): string;
21+
public function forRoute(string $name, array $substitutions = []): GeneratedUri;
2122
}

src/GenerateUri/FromProcessedConfiguration.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function __construct(private readonly array $processedConfiguration)
2626
}
2727

2828
/** @inheritDoc */
29-
public function forRoute(string $name, array $substitutions = []): string
29+
public function forRoute(string $name, array $substitutions = []): GeneratedUri
3030
{
3131
if (! array_key_exists($name, $this->processedConfiguration)) {
3232
throw UriCouldNotBeGenerated::routeIsUndefined($name);
@@ -79,7 +79,7 @@ private function missingParameters(array $parts, array $substitutions): array
7979
* @param ParsedRoute $parsedRoute
8080
* @param UriSubstitutions $substitutions
8181
*/
82-
private function generatePath(string $route, array $parsedRoute, array $substitutions): string
82+
private function generatePath(string $route, array $parsedRoute, array $substitutions): GeneratedUri
8383
{
8484
$path = '';
8585

@@ -97,8 +97,11 @@ private function generatePath(string $route, array $parsedRoute, array $substitu
9797
}
9898

9999
$path .= $substitutions[$parameterName];
100+
unset($substitutions[$parameterName]);
100101
}
101102

102-
return $path;
103+
assert($path !== '');
104+
105+
return new GeneratedUri($path, $substitutions);
103106
}
104107
}

src/GenerateUri/GeneratedUri.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace FastRoute\GenerateUri;
5+
6+
use FastRoute\GenerateUri;
7+
use Psr\Http\Message\UriInterface;
8+
use Stringable;
9+
10+
use function http_build_query;
11+
12+
/** @phpstan-import-type UriSubstitutions from GenerateUri */
13+
final class GeneratedUri implements Stringable
14+
{
15+
/**
16+
* @param non-empty-string $path
17+
* @param UriSubstitutions $unmatchedSubstitutions
18+
*/
19+
public function __construct(
20+
public readonly string $path,
21+
public readonly array $unmatchedSubstitutions,
22+
) {
23+
}
24+
25+
public function asUri(UriInterface $baseUri): UriInterface
26+
{
27+
return $baseUri
28+
->withPath($this->path)
29+
->withQuery(http_build_query($this->unmatchedSubstitutions));
30+
}
31+
32+
public function __toString(): string
33+
{
34+
return $this->path;
35+
}
36+
}

test/FastRouteTest.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use FastRoute\Dispatcher;
99
use FastRoute\FastRoute;
1010
use FastRoute\GenerateUri;
11+
use Nyholm\Psr7\Uri;
1112
use PHPUnit\Framework\Attributes as PHPUnit;
1213
use PHPUnit\Framework\TestCase;
1314
use RuntimeException;
@@ -115,9 +116,9 @@ public function uriGeneratorCanBeOverridden(): void
115116
{
116117
$generator = new class () implements GenerateUri {
117118
/** @inheritDoc */
118-
public function forRoute(string $name, array $substitutions = []): string
119+
public function forRoute(string $name, array $substitutions = []): GenerateUri\GeneratedUri
119120
{
120-
return '';
121+
return new GenerateUri\GeneratedUri('/', $substitutions);
121122
}
122123
};
123124

@@ -159,10 +160,11 @@ public function processedDataShouldOnlyBeBuiltOnce(): void
159160
self::assertInstanceOf(Dispatcher\Result\Matched::class, $dispatcher->dispatch('POST', '/users/lcobucci'));
160161
self::assertInstanceOf(Dispatcher\Result\Matched::class, $dispatcher->dispatch('GET', '/posts/1234'));
161162

162-
self::assertSame('/users/lcobucci', $uriGenerator->forRoute('users', ['name' => 'lcobucci']));
163-
self::assertSame('/posts/1234', $uriGenerator->forRoute('posts.fetch', ['id' => '1234']));
164-
self::assertSame('/articles/2024', $uriGenerator->forRoute('articles.fetch', ['year' => '2024']));
165-
self::assertSame('/articles/2024/02', $uriGenerator->forRoute('articles.fetch', ['year' => '2024', 'month' => '02']));
166-
self::assertSame('/articles/2024/02/15', $uriGenerator->forRoute('articles.fetch', ['year' => '2024', 'month' => '02', 'day' => '15']));
163+
self::assertEquals('/users/lcobucci', $uriGenerator->forRoute('users', ['name' => 'lcobucci']));
164+
self::assertEquals('/posts/1234', $uriGenerator->forRoute('posts.fetch', ['id' => '1234']));
165+
self::assertEquals('/articles/2024', $uriGenerator->forRoute('articles.fetch', ['year' => '2024']));
166+
self::assertEquals('/articles/2024/02', $uriGenerator->forRoute('articles.fetch', ['year' => '2024', 'month' => '02']));
167+
self::assertEquals('/articles/2024/02/15', $uriGenerator->forRoute('articles.fetch', ['year' => '2024', 'month' => '02', 'day' => '15']));
168+
self::assertSame('/articles/2024/02/15?extra=value', (string) $uriGenerator->forRoute('articles.fetch', ['year' => '2024', 'month' => '02', 'day' => '15', 'extra' => 'value'])->asUri(new Uri()));
167169
}
168170
}

0 commit comments

Comments
 (0)