Skip to content

Commit 3724576

Browse files
author
renanbr
committed
[FrameworkBundle] Allow to inject service to expression condition
Gather routing variables into a service locator Add service() function for route condition Add missing file Fix psalm remove routing variable concept Add tests and rename routing condition service make fabbot happy add entry in changelog
1 parent 6ecd17e commit 3724576

File tree

18 files changed

+394
-0
lines changed

18 files changed

+394
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CHANGELOG
1212
* Deprecate the `reset_on_message` config option. It can be set to `true` only and does nothing now
1313
* Add `trust_x_sendfile_type_header` option
1414
* Add support for first-class callable route controller in `MicroKernelTrait`
15+
* Add tag `routing.condition_service` to autoconfigure routing condition services.
1516

1617
6.0
1718
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class UnusedTagsPass implements CompilerPassInterface
7575
'routing.expression_language_provider',
7676
'routing.loader',
7777
'routing.route_loader',
78+
'routing.condition_service',
7879
'security.authenticator.login_linker',
7980
'security.expression_language_provider',
8081
'security.remember_me_aware',

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Symfony\Bridge\Twig\Extension\CsrfExtension;
2828
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
2929
use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader;
30+
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
3031
use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface;
3132
use Symfony\Bundle\FullStack;
3233
use Symfony\Bundle\MercureBundle\MercureBundle;
@@ -666,6 +667,10 @@ public function load(array $configs, ContainerBuilder $container)
666667
'kernel.locale_aware',
667668
'kernel.reset',
668669
]);
670+
671+
$container->registerAttributeForAutoconfiguration(AsRoutingConditionService::class, static function (ChildDefinition $definition, AsRoutingConditionService $attribute): void {
672+
$definition->addTag('routing.condition_service', (array) $attribute);
673+
});
669674
}
670675

671676
/**

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
200200
])
201201
->tag('routing.expression_language_function', ['function' => 'env'])
202202

203+
->set('container.get_routing_condition_service', \Closure::class)
204+
->public()
205+
->factory([\Closure::class, 'fromCallable'])
206+
->args([
207+
[tagged_locator('routing.condition_service', 'alias'), 'get'],
208+
])
209+
->tag('routing.expression_language_function', ['function' => 'service'])
210+
203211
// inherit from this service to lazily access env vars
204212
->set('container.env', LazyString::class)
205213
->abstract()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\Routing\Attribute;
13+
14+
/**
15+
* Service tag to autoconfigure routing condition services.
16+
*
17+
* You can tag a service:
18+
*
19+
* #[AsRoutingConditionService('foo')]
20+
* class SomeFooService
21+
* {
22+
* public function bar(): bool
23+
* {
24+
* // ...
25+
* }
26+
* }
27+
*
28+
* Then you can use the tagged service in the routing condition:
29+
*
30+
* class PageController
31+
* {
32+
* #[Route('/page', condition: "service('foo').bar()")]
33+
* public function page(): Response
34+
* {
35+
* // ...
36+
* }
37+
* }
38+
*/
39+
#[\Attribute(\Attribute::TARGET_CLASS)]
40+
class AsRoutingConditionService
41+
{
42+
public function __construct(
43+
public ?string $alias = null,
44+
public int $priority = 0,
45+
) {
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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\RoutingConditionServiceBundle\Controller;
13+
14+
use Symfony\Component\HttpFoundation\Response;
15+
use Symfony\Component\Routing\Annotation\Route;
16+
17+
class DefaultController
18+
{
19+
#[Route(
20+
path: '/allowed/manually-tagged',
21+
condition: 'service("manually_tagged").giveMeTrue()',
22+
)]
23+
public function allowedByManuallyTagged(): Response
24+
{
25+
return new Response();
26+
}
27+
28+
#[Route(
29+
path: '/allowed/auto-configured',
30+
condition: 'service("auto_configured").flip(false)',
31+
)]
32+
public function allowedByAutoConfigured(): Response
33+
{
34+
return new Response();
35+
}
36+
37+
#[Route(
38+
path: '/allowed/auto-configured-non-aliased',
39+
condition: 'service("auto_configured_non_aliased").alwaysTrue()',
40+
)]
41+
public function allowedByAutoConfiguredNonAliased(): Response
42+
{
43+
return new Response();
44+
}
45+
46+
#[Route(
47+
path: '/denied/manually-tagged',
48+
condition: 'service("manually_tagged").giveMeFalse()',
49+
)]
50+
public function deniedByManuallyTagged(): Response
51+
{
52+
return new Response();
53+
}
54+
55+
#[Route(
56+
path: '/denied/auto-configured',
57+
condition: 'service("auto_configured").flip(true)',
58+
)]
59+
public function deniedByAutoConfigured(): Response
60+
{
61+
return new Response();
62+
}
63+
64+
#[Route(
65+
path: '/denied/auto-configured-non-aliased',
66+
condition: 'service("auto_configured_non_aliased").alwaysFalse()',
67+
)]
68+
public function deniedByAutoConfiguredNonAliased(): Response
69+
{
70+
return new Response();
71+
}
72+
73+
#[Route(
74+
path: '/denied/overridden',
75+
condition: 'service("foo").isAllowed()',
76+
)]
77+
public function deniedByOverriddenAlias(): Response
78+
{
79+
return new Response();
80+
}
81+
}
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+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\RoutingConditionServiceBundle;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
class RoutingConditionServiceBundle extends Bundle
17+
{
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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\RoutingConditionServiceBundle\Service;
13+
14+
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
15+
16+
#[AsRoutingConditionService]
17+
class AutoConfiguredNonAliasedService
18+
{
19+
public function alwaysFalse(): bool
20+
{
21+
return false;
22+
}
23+
24+
public function alwaysTrue(): bool
25+
{
26+
return true;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\RoutingConditionServiceBundle\Service;
13+
14+
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
15+
16+
#[AsRoutingConditionService('auto_configured')]
17+
class AutoConfiguredService
18+
{
19+
public function flip(bool $value): bool
20+
{
21+
return !$value;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\RoutingConditionServiceBundle\Service;
13+
14+
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
15+
16+
#[AsRoutingConditionService('foo')]
17+
class FooOriginalService
18+
{
19+
public function isAllowed(): bool
20+
{
21+
return true;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\RoutingConditionServiceBundle\Service;
13+
14+
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
15+
16+
#[AsRoutingConditionService(alias: 'foo', priority: -1)]
17+
class FooReplacementService
18+
{
19+
public function isAllowed(): bool
20+
{
21+
return false;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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\RoutingConditionServiceBundle\Service;
13+
14+
class ManuallyTaggedService
15+
{
16+
public function giveMeTrue(): bool
17+
{
18+
return true;
19+
}
20+
21+
public function giveMeFalse(): bool
22+
{
23+
return false;
24+
}
25+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
class RoutingConditionServiceTest extends AbstractWebTestCase
15+
{
16+
/**
17+
* @dataProvider provideRoutes
18+
*/
19+
public function testCondition(int $code, string $path)
20+
{
21+
$client = static::createClient(['test_case' => 'RoutingConditionService']);
22+
23+
$client->request('GET', $path);
24+
$this->assertSame($code, $client->getResponse()->getStatusCode());
25+
}
26+
27+
public function provideRoutes(): iterable
28+
{
29+
yield 'allowed by an autoconfigured service' => [
30+
200,
31+
'/allowed/manually-tagged',
32+
];
33+
34+
yield 'allowed by a manually tagged service' => [
35+
200,
36+
'/allowed/auto-configured',
37+
];
38+
39+
yield 'allowed by a manually tagged non aliased service' => [
40+
200,
41+
'/allowed/auto-configured-non-aliased',
42+
];
43+
44+
yield 'denied by an autoconfigured service' => [
45+
404,
46+
'/denied/manually-tagged',
47+
];
48+
49+
yield 'denied by a manually tagged service' => [
50+
404,
51+
'/denied/auto-configured',
52+
];
53+
54+
yield 'denied by a manually tagged non aliased service' => [
55+
404,
56+
'/denied/auto-configured-non-aliased',
57+
];
58+
59+
yield 'denied by an overridden service' => [
60+
404,
61+
'/denied/overridden',
62+
];
63+
}
64+
}

0 commit comments

Comments
 (0)