Skip to content

Commit 11ef7d9

Browse files
committed
[HttpKernel] Add the UidValueResolver argument value resolver
1 parent a14eb3f commit 11ef7d9

File tree

12 files changed

+351
-0
lines changed

12 files changed

+351
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
8181
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
8282
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver;
83+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver;
8384
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
8485
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
8586
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@@ -416,6 +417,8 @@ public function load(array $configs, ContainerBuilder $container)
416417
}
417418

418419
$this->registerUidConfiguration($config['uid'], $container, $loader);
420+
} else {
421+
$container->removeDefinition('argument_resolver.uid');
419422
}
420423

421424
// register cache before session so both can share the connection services
@@ -2577,6 +2580,10 @@ private function registerUidConfiguration(array $config, ContainerBuilder $conta
25772580
$container->getDefinition('name_based_uuid.factory')
25782581
->setArguments([$config['name_based_uuid_namespace']]);
25792582
}
2583+
2584+
if (!class_exists(UidValueResolver::class)) {
2585+
$container->removeDefinition('argument_resolver.uid');
2586+
}
25802587
}
25812588

25822589
private function resolveTrustedHeaders(array $headers): int

src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
2020
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver;
2121
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;
22+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver;
2223
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver;
2324
use Symfony\Component\HttpKernel\Controller\ErrorController;
2425
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
@@ -50,6 +51,11 @@
5051
'priority' => 105, // prior to the RequestAttributeValueResolver
5152
])
5253

54+
->set('argument_resolver.uid', UidValueResolver::class)
55+
->tag('controller.argument_value_resolver', [
56+
'priority' => 100, // same priority than RequestAttributeValueResolver, but registered before
57+
])
58+
5359
->set('argument_resolver.request_attribute', RequestAttributeValueResolver::class)
5460
->tag('controller.argument_value_resolver', ['priority' => 100])
5561

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;
13+
14+
use Symfony\Component\HttpFoundation\Response;
15+
use Symfony\Component\Routing\Annotation\Route;
16+
use Symfony\Component\Routing\Requirement;
17+
use Symfony\Component\Uid\Ulid;
18+
use Symfony\Component\Uid\UuidV1;
19+
20+
class UidController
21+
{
22+
#[Route(path: '/1/uuid-v1/{userId}')]
23+
public function anyFormat(UuidV1 $userId): Response
24+
{
25+
return new Response($userId);
26+
}
27+
28+
#[Route(path: '/2/ulid/{id}', requirements: ['id' => '[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}'])]
29+
public function specificFormatInAttribute(Ulid $id): Response
30+
{
31+
return new Response($id);
32+
}
33+
34+
#[Route(path: '/3/uuid-v1/{id<[0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]{26}>}')]
35+
public function specificFormatInPath(UuidV1 $id): Response
36+
{
37+
return new Response($id);
38+
}
39+
40+
#[Route(path: '/4/uuid-v1/{postId}/custom-uid/{commentId}')]
41+
public function manyUids(UuidV1 $postId, TestCommentIdentifier $commentId): Response
42+
{
43+
return new Response($postId."\n".$commentId);
44+
}
45+
}
46+
47+
class TestCommentIdentifier extends Ulid
48+
{
49+
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ array_controller:
6060
send_email:
6161
path: /send_email
6262
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\EmailController::indexAction }
63+
64+
uid:
65+
resource: "../../Controller/UidController.php"
66+
type: "annotation"
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\UidController;
15+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver;
16+
use Symfony\Component\Uid\Ulid;
17+
use Symfony\Component\Uid\UuidV1;
18+
use Symfony\Component\Uid\UuidV4;
19+
use Symfony\Component\Uid\UuidV6;
20+
21+
/**
22+
* @see UidController
23+
*/
24+
class UidTest extends AbstractWebTestCase
25+
{
26+
protected function setUp(): void
27+
{
28+
parent::setUp();
29+
30+
self::deleteTmpDir();
31+
}
32+
33+
public function testArgumentValueResolverDisabled()
34+
{
35+
$this->expectException(\TypeError::class);
36+
$this->expectExceptionMessage('Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\UidController::anyFormat(): Argument #1 ($userId) must be of type Symfony\Component\Uid\UuidV1, string given');
37+
38+
$client = $this->createClient(['test_case' => 'Uid', 'root_config' => 'config_disabled.yml']);
39+
40+
$client->request('GET', '/1/uuid-v1/'.new UuidV1());
41+
}
42+
43+
public function testArgumentValueResolverEnabled()
44+
{
45+
if (!class_exists(UidValueResolver::class)) {
46+
$this->markTestSkipped('Needs symfony/http-kernel >= 6.1');
47+
}
48+
49+
$client = $this->createClient(['test_case' => 'Uid', 'root_config' => 'config_enabled.yml']);
50+
51+
// Any format
52+
$client->request('GET', '/1/uuid-v1/'.$uuidV1 = new UuidV1());
53+
$this->assertSame((string) $uuidV1, $client->getResponse()->getContent());
54+
$client->request('GET', '/1/uuid-v1/'.$uuidV1->toBase58());
55+
$this->assertSame((string) $uuidV1, $client->getResponse()->getContent());
56+
$client->request('GET', '/1/uuid-v1/'.$uuidV1->toRfc4122());
57+
$this->assertSame((string) $uuidV1, $client->getResponse()->getContent());
58+
// Bad version
59+
$client->request('GET', '/1/uuid-v1/'.$uuidV4 = new UuidV4());
60+
$this->assertSame(404, $client->getResponse()->getStatusCode());
61+
62+
// Only base58 format
63+
$client->request('GET', '/2/ulid/'.($ulid = new Ulid())->toBase58());
64+
$this->assertSame((string) $ulid, $client->getResponse()->getContent());
65+
$client->request('GET', '/2/ulid/'.$ulid);
66+
$this->assertSame(404, $client->getResponse()->getStatusCode());
67+
$client->request('GET', '/2/ulid/'.$ulid->toRfc4122());
68+
$this->assertSame(404, $client->getResponse()->getStatusCode());
69+
70+
// Only base32 format
71+
$client->request('GET', '/3/uuid-v1/'.$uuidV1->toBase32());
72+
$this->assertSame((string) $uuidV1, $client->getResponse()->getContent());
73+
$client->request('GET', '/3/uuid-v1/'.$uuidV1);
74+
$this->assertSame(404, $client->getResponse()->getStatusCode());
75+
$client->request('GET', '/3/uuid-v1/'.$uuidV1->toBase58());
76+
$this->assertSame(404, $client->getResponse()->getStatusCode());
77+
// Bad version
78+
$client->request('GET', '/3/uuid-v1/'.(new UuidV6())->toBase32());
79+
$this->assertSame(404, $client->getResponse()->getStatusCode());
80+
81+
// Any format for both
82+
$client->request('GET', '/4/uuid-v1/'.$uuidV1.'/custom-uid/'.$ulid->toRfc4122());
83+
$this->assertSame($uuidV1."\n".$ulid, $client->getResponse()->getContent());
84+
$client->request('GET', '/4/uuid-v1/'.$uuidV1->toBase58().'/custom-uid/'.$ulid->toBase58());
85+
$this->assertSame($uuidV1."\n".$ulid, $client->getResponse()->getContent());
86+
// Bad version
87+
$client->request('GET', '/4/uuid-v1/'.$uuidV4.'/custom-uid/'.$ulid);
88+
$this->assertSame(404, $client->getResponse()->getStatusCode());
89+
}
90+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
13+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
14+
15+
return [
16+
new FrameworkBundle(),
17+
new TestBundle(),
18+
];
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imports:
2+
- { resource: "../config/default.yml" }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
imports:
2+
- { resource: "../config/default.yml" }
3+
4+
framework:
5+
uid: ~
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
uid:
2+
resource: "@TestBundle/Resources/config/routing.yml"

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add `BackedEnumValueResolver` to resolve backed enum cases from request attributes in controller arguments
88
* Deprecate StreamedResponseListener, it's not needed anymore
99
* Add `Profiler::isEnabled()` so collaborating collector services may elect to omit themselves.
10+
* Add the `UidValueResolver` argument value resolver
1011

1112
6.0
1213
---

0 commit comments

Comments
 (0)