8000 [Routing][FrameworkBundle] Allow using env() in route conditions · symfony/symfony@ff17ac7 · GitHub
[go: up one dir, main page]

Skip to content
Sign in

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ff17ac7

Browse files
atailouloutenicolas-grekas
authored andcommitted
[Routing][FrameworkBundle] Allow using env() in route conditions
1 parent f46ab58 commit ff17ac7

File tree

9 files changed

+172
-14
lines changed

9 files changed

+172
-14
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ class RouterMatchCommand extends Command
3333
protected static $defaultName = 'router:match';
3434

3535
private $router;
36+
private $expressionLanguageProviders;
3637

37-
public function __construct(RouterInterface $router)
38+
public function __construct(RouterInterface $router, iterable $expressionLanguageProviders = [])
3839
{
3940
parent::__construct();
4041

4142
$this->router = $router;
43+
$this->expressionLanguageProviders = $expressionLanguageProviders;
4244
}
4345

4446
/**
@@ -87,6 +89,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8789 8000
}
8890

8991
$matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $context);
92+
foreach ($this->expressionLanguageProviders as $provider) {
93+
$matcher->addExpressionLanguageProvider($provider);
94+
}
9095

9196
$traces = $matcher->getTraces($input->getArgument('path_info'));
9297

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145

146146
<service id="console.command.router_match" class="Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand">
147147
<argument type="service" id="router" />
148+
<argument type="tagged_iterator" tag="routing.expression_language_provider"/>
148149
<tag name="console.command" command="router:match" />
149150
</service>
150151

src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@
8686
<argument></argument> <!-- scheme -->
8787
<argument>%request_listener.http_port%</argument>
8888
<argument>%request_listener.https_port%</argument>
89+
<call method="setParameter">
90+
<argument>_functions</argument>
91+
<argument type="collection">
92+
<argument type="service" key="env" id="container.getenv" />
93+
</argument>
94+
</call>
8995
</service>
9096
<service id="Symfony\Component\Routing\RequestContext" alias="router.request_context" />
9197

@@ -115,5 +121,12 @@
115121
<service id="Symfony\Bundle\FrameworkBundle\Controller\TemplateController" public="true">
116122
<argument type="service" id="twig" on-invalid="ignore" />
117123
</service>
124+
125+
<service id="Symfony\Component\Routing\Matcher\ExpressionLanguageProvider">
126+
<argument type="collection">
127+
<argument>env</argument>
128+
</argument>
129+
<tag name="routing.expression_language_provider" />
130+
</service>
118131
</services>
119132
</container>

src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
<argument type="service" id="secrets.decryption_key" on-invalid="ignore" />
1212
</service>
1313

14-
<service id="secrets.decryption_key" parent="getenv">
15-
<argument />
14+
<service id="secrets.decryption_key" parent="container.env">
15+
<argument /><!-- the name of the env var to retrieve -->
1616
</service>
1717

1818
<service id="secrets.local_vault" class="Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault">

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,18 @@
130130
</service>
131131
<service id="Symfony\Component\String\Slugger\SluggerInterface" alias="slugger" />
132132

133+
<service id="container.getenv" class="Closure">
134+
<factory class="Closure" method="fromCallable" />
135+
<argument type="collection">
136+
<argument type="service" id="service_container" />
137+
<argument>getEnv</argument>
138+
</argument>
139+
</service>
140+
133141
<!-- inherit from this service to lazily access env vars -->
134-
<service id="getenv" class="Symfony\Component\String\LazyString" abstract="true">
142+
<service id="container.env" class="Symfony\Component\String\LazyString" abstract="true">
135143
<factory class="Symfony\Component\String\LazyString" method="fromCallable" />
136-
<argument type="service">
137-
<service class="Closure">
138-
<factory class="Closure" method="fromCallable" />
139-
<argument type="collection">
140-
<argument type="service" id="service_container" />
141-
<argument>getEnv</argument>
142-
</argument>
143-
</service>
144-
</argument>
144+
<argument type="service" id="container.getenv" />
145145
</service>
146146
</services>
147147
</container>

src/Symfony/Component/Routing/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
99
* added "priority" option to annotated routes
1010
* added argument `$priority` to `RouteCollection::add()`
11+
* added `ExpressionLanguageProvider` to expose extra functions to route conditions
1112

1213
5.0.0
1314
-----
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\Component\Routing\Matcher;
13+
14+
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
15+
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
16+
17+
/**
18+
* Exposes functions defined in the request context to route conditions.
19+
*
20+
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
21+
*/
22+
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
23+
{
24+
private $functions;
25+
26+
/**
27+
* @param string[] $functions
28+
*/
29+
public function __construct(array $functions)
30+
{
31+
$this->functions = $functions;
32+
}
33+
34+
/**
35+
* {@inheritdoc}
36+
*/
37+
public function getFunctions()
38+
{
39+
foreach ($this->functions as $function) {
40+
yield new ExpressionFunction(
41+
$function,
42+
static function (...$args) use ($function) {
43+
return sprintf('(($context->getParameter(\'_functions\')[%s])(%s))', var_export($function, true), implode(', ', $args));
44+
},
45+
function ($values, ...$args) use ($function) {
46+
$context = $values['context'];
47+
48+
return ($context->getParameter('_functions')[$function])(...$args);
49+
}
50+
);
51+
}
52+
}
53+
}

src/Symfony/Component/Routing/Router.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,13 @@ class Router implements RouterInterface, RequestMatcherInterface
9797
/**
9898
* @param mixed $resource The main resource to load
9999
*/
100-
public function __construct(LoaderInterface $loader, $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null, string $defaultLocale = null)
100+
public function __construct(LoaderInterface $loader, $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null, string $defaultLocale = null, array $defaultParameters = [])
101101
{
102102
$this->loader = $loader;
103103
$this->resource = $resource;
104104
$this->logger = $logger;
105105
$this->context = $context ?: new RequestContext();
106+
$this->context->setParameters($defaultParameters);
106107
$this->setOptions($options);
107108
$this->defaultLocale = $defaultLocale;
108109
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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\Component\Routing\Tests\Matcher;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
16+
use Symfony\Component\Routing\Matcher\ExpressionLanguageProvider;
17+
use Symfony\Component\Routing\RequestContext;
18+
19+
class ExpressionLanguageTest extends TestCase
20+
{
21+
private $context;
22+
private $expressionLanguage;
23+
24+
protected function setUp(): void
25+
{
26+
$this->context = new RequestContext();
27+
$this->context->setParameter('_functions', [
28+
// function with one arg
29+
'env' => static function (string $arg) {
30+
return [
31+
'APP_ENV' => 'test',
32+
'PHP_VERSION' => '7.2',
33+
][$arg] ?? null;
34+
},
35+
// function with multiple args
36+
'sum' => static function ($a, $b) {
37+
return $a + $b;
38+
},
39+
// function with no arg
40+
'foo' => static function () {
41+
return 'bar';
42+
},
43+
]);
44+
45+
$this->expressionLanguage = new ExpressionLanguage();
46+
$this->expressionLanguage->registerProvider(new ExpressionLanguageProvider(['env', 'sum', 'foo']));
47+
}
48+
49+
/**
50+
* @dataProvider compileProvider
51+
*/
52+
public function testCompile(string $expression, string $expected)
53+
{
54+
$this->assertSame($expected, $this->expressionLanguage->compile($expression));
55+
}
56+
57+
public function compileProvider(): iterable
58+
{
59+
return [
60+
['env("APP_ENV")', '(($context->getParameter(\'_functions\')[\'env\'])("APP_ENV"))'],
61+
['sum(1, 2)', '(($context->getParameter(\'_functions\')[\'sum\'])(1, 2))'],
62+
['foo()', '(($context->getParameter(\'_functions\')[\'foo\'])())'],
63+
];
64+
}
65+
66+
/**
67+
* @dataProvider evaluateProvider
68+
*/
69+
public function testEvaluate(string $expression, $expected)
70+
{
71+
$this->assertSame($expected, $this->expressionLanguage->evaluate($expression, ['context' => $this->context]));
72+
}
73+
74+
public function evaluateProvider(): iterable
75+
{
76+
return [
77+
['env("APP_ENV")', 'test'],
78+
['env("PHP_VERSION")', '7.2'],
79+
['env("unknown_env_variable")', null],
80+
['sum(1, 2)', 3],
81+
['foo()', 'bar'],
82+
];
83+
}
84+
}

0 commit comments

Comments
 (0)
0