[Routing][FrameworkBundle] Allow using env() in route conditions
This commit is contained in:
parent
8867f57a44
commit
b5744601bf
|
@ -12,6 +12,7 @@ CHANGELOG
|
||||||
* Deprecated passing a `RouteCollectionBuiler` to `MicroKernelTrait::configureRoutes()`, type-hint `RoutingConfigurator` instead
|
* Deprecated passing a `RouteCollectionBuiler` to `MicroKernelTrait::configureRoutes()`, type-hint `RoutingConfigurator` instead
|
||||||
* The `TemplateController` now accepts context argument
|
* The `TemplateController` now accepts context argument
|
||||||
* Deprecated *not* setting the "framework.router.utf8" configuration option as it will default to `true` in Symfony 6.0
|
* Deprecated *not* setting the "framework.router.utf8" configuration option as it will default to `true` in Symfony 6.0
|
||||||
|
* Added tag `routing.expression_language_function` to define functions available in route conditions
|
||||||
|
|
||||||
5.0.0
|
5.0.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -33,12 +33,14 @@ class RouterMatchCommand extends Command
|
||||||
protected static $defaultName = 'router:match';
|
protected static $defaultName = 'router:match';
|
||||||
|
|
||||||
private $router;
|
private $router;
|
||||||
|
private $expressionLanguageProviders;
|
||||||
|
|
||||||
public function __construct(RouterInterface $router)
|
public function __construct(RouterInterface $router, iterable $expressionLanguageProviders = [])
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->router = $router;
|
$this->router = $router;
|
||||||
|
$this->expressionLanguageProviders = $expressionLanguageProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,6 +89,9 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
$matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $context);
|
$matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $context);
|
||||||
|
foreach ($this->expressionLanguageProviders as $provider) {
|
||||||
|
$matcher->addExpressionLanguageProvider($provider);
|
||||||
|
}
|
||||||
|
|
||||||
$traces = $matcher->getTraces($input->getArgument('path_info'));
|
$traces = $matcher->getTraces($input->getArgument('path_info'));
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ class UnusedTagsPass implements CompilerPassInterface
|
||||||
'mime.mime_type_guesser',
|
'mime.mime_type_guesser',
|
||||||
'monolog.logger',
|
'monolog.logger',
|
||||||
'proxy',
|
'proxy',
|
||||||
|
'routing.expression_language_function',
|
||||||
'routing.expression_language_provider',
|
'routing.expression_language_provider',
|
||||||
'routing.loader',
|
'routing.loader',
|
||||||
'routing.route_loader',
|
'routing.route_loader',
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
|
|
||||||
<service id="console.command.router_match" class="Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand">
|
<service id="console.command.router_match" class="Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand">
|
||||||
<argument type="service" id="router" />
|
<argument type="service" id="router" />
|
||||||
|
<argument type="tagged_iterator" tag="routing.expression_language_provider" />
|
||||||
<tag name="console.command" command="router:match" />
|
<tag name="console.command" command="router:match" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
|
|
@ -86,9 +86,18 @@
|
||||||
<argument></argument> <!-- scheme -->
|
<argument></argument> <!-- scheme -->
|
||||||
<argument>%request_listener.http_port%</argument>
|
<argument>%request_listener.http_port%</argument>
|
||||||
<argument>%request_listener.https_port%</argument>
|
<argument>%request_listener.https_port%</argument>
|
||||||
|
<call method="setParameter">
|
||||||
|
<argument>_functions</argument>
|
||||||
|
<argument type="service" id="router.expression_language_provider" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
<service id="Symfony\Component\Routing\RequestContext" alias="router.request_context" />
|
<service id="Symfony\Component\Routing\RequestContext" alias="router.request_context" />
|
||||||
|
|
||||||
|
<service id="router.expression_language_provider" class="Symfony\Component\Routing\Matcher\ExpressionLanguageProvider">
|
||||||
|
<argument type="tagged_locator" tag="routing.expression_language_function" index-by="function" />
|
||||||
|
<tag name="routing.expression_language_provider" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="router.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer">
|
<service id="router.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer">
|
||||||
<tag name="container.service_subscriber" id="router" />
|
<tag name="container.service_subscriber" id="router" />
|
||||||
<tag name="kernel.cache_warmer" />
|
<tag name="kernel.cache_warmer" />
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<argument type="service" id="secrets.decryption_key" on-invalid="ignore" />
|
<argument type="service" id="secrets.decryption_key" on-invalid="ignore" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="secrets.decryption_key" parent="getenv">
|
<service id="secrets.decryption_key" parent="container.env">
|
||||||
<argument />
|
<argument /><!-- the name of the env var to read -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="secrets.local_vault" class="Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault">
|
<service id="secrets.local_vault" class="Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault">
|
||||||
|
|
|
@ -130,18 +130,19 @@
|
||||||
</service>
|
</service>
|
||||||
<service id="Symfony\Component\String\Slugger\SluggerInterface" alias="slugger" />
|
<service id="Symfony\Component\String\Slugger\SluggerInterface" alias="slugger" />
|
||||||
|
|
||||||
<!-- inherit from this service to lazily access env vars -->
|
<service id="container.getenv" class="Closure">
|
||||||
<service id="getenv" class="Symfony\Component\String\LazyString" abstract="true">
|
<factory class="Closure" method="fromCallable" />
|
||||||
<factory class="Symfony\Component\String\LazyString" method="fromCallable" />
|
<argument type="collection">
|
||||||
<argument type="service">
|
<argument type="service" id="service_container" />
|
||||||
<service class="Closure">
|
<argument>getEnv</argument>
|
||||||
<factory class="Closure" method="fromCallable" />
|
|
||||||
<argument type="collection">
|
|
||||||
<argument type="service" id="service_container" />
|
|
||||||
<argument>getEnv</argument>
|
|
||||||
</argument>
|
|
||||||
</service>
|
|
||||||
</argument>
|
</argument>
|
||||||
|
<tag name="routing.expression_language_function" function="env" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- inherit from this service to lazily access env vars -->
|
||||||
|
<service id="container.env" class="Symfony\Component\String\LazyString" abstract="true">
|
||||||
|
<factory class="Symfony\Component\String\LazyString" method="fromCallable" />
|
||||||
|
<argument type="service" id="container.getenv" />
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -9,6 +9,7 @@ CHANGELOG
|
||||||
* added "priority" option to annotated routes
|
* added "priority" option to annotated routes
|
||||||
* added argument `$priority` to `RouteCollection::add()`
|
* added argument `$priority` to `RouteCollection::add()`
|
||||||
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
|
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
|
||||||
|
* added `ExpressionLanguageProvider` to expose extra functions to route conditions
|
||||||
|
|
||||||
5.0.0
|
5.0.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Routing\Matcher;
|
||||||
|
|
||||||
|
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
|
||||||
|
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||||
|
use Symfony\Contracts\Service\ServiceProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes functions defined in the request context to route conditions.
|
||||||
|
*
|
||||||
|
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
|
||||||
|
*/
|
||||||
|
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
|
||||||
|
{
|
||||||
|
private $functions;
|
||||||
|
|
||||||
|
public function __construct(ServiceProviderInterface $functions)
|
||||||
|
{
|
||||||
|
$this->functions = $functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFunctions()
|
||||||
|
{
|
||||||
|
foreach ($this->functions->getProvidedServices() as $function => $type) {
|
||||||
|
yield new ExpressionFunction(
|
||||||
|
$function,
|
||||||
|
static function (...$args) use ($function) {
|
||||||
|
return sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args));
|
||||||
|
},
|
||||||
|
function ($values, ...$args) use ($function) {
|
||||||
|
return $values['context']->getParameter('_functions')->get($function)(...$args);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $function): callable
|
||||||
|
{
|
||||||
|
return $this->functions->get($function);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Routing\Tests\Matcher;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||||
|
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||||
|
use Symfony\Component\Routing\Matcher\ExpressionLanguageProvider;
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
|
||||||
|
class ExpressionLanguageProviderTest extends TestCase
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $expressionLanguage;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$functionProvider = new ServiceLocator([
|
||||||
|
'env' => function () {
|
||||||
|
// function with one arg
|
||||||
|
return function (string $arg) {
|
||||||
|
return [
|
||||||
|
'APP_ENV' => 'test',
|
||||||
|
'PHP_VERSION' => '7.2',
|
||||||
|
][$arg] ?? null;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'sum' => function () {
|
||||||
|
// function with multiple args
|
||||||
|
return function ($a, $b) { return $a + $b; };
|
||||||
|
},
|
||||||
|
'foo' => function () {
|
||||||
|
// function with no arg
|
||||||
|
return function () { return 'bar'; };
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->context = new RequestContext();
|
||||||
|
$this->context->setParameter('_functions', $functionProvider);
|
||||||
|
|
||||||
|
$this->expressionLanguage = new ExpressionLanguage();
|
||||||
|
$this->expressionLanguage->registerProvider(new ExpressionLanguageProvider($functionProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider compileProvider
|
||||||
|
*/
|
||||||
|
public function testCompile(string $expression, string $expected)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, $this->expressionLanguage->compile($expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function compileProvider(): iterable
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['env("APP_ENV")', '($context->getParameter(\'_functions\')->get(\'env\')("APP_ENV"))'],
|
||||||
|
['sum(1, 2)', '($context->getParameter(\'_functions\')->get(\'sum\')(1, 2))'],
|
||||||
|
['foo()', '($context->getParameter(\'_functions\')->get(\'foo\')())'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider evaluateProvider
|
||||||
|
*/
|
||||||
|
public function testEvaluate(string $expression, $expected)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, $this->expressionLanguage->evaluate($expression, ['context' => $this->context]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function evaluateProvider(): iterable
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['env("APP_ENV")', 'test'],
|
||||||
|
['env("PHP_VERSION")', '7.2'],
|
||||||
|
['env("unknown_env_variable")', null],
|
||||||
|
['sum(1, 2)', 3],
|
||||||
|
['foo()', 'bar'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue