diff --git a/UPGRADE-4.1.md b/UPGRADE-4.1.md
index 8d96b710ed9ec..807c028e5d0b6 100644
--- a/UPGRADE-4.1.md
+++ b/UPGRADE-4.1.md
@@ -81,6 +81,7 @@ Security
functionality, create a custom user-checker based on the
`Symfony\Component\Security\Core\User\UserChecker`.
* `AuthenticationUtils::getLastUsername()` now always returns a string.
+ * The `ExpressionVoter::addExpressionLanguageProvider()` method is deprecated. Register the provider directly on the injected ExpressionLanguage instance instead.
SecurityBundle
--------------
diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md
index 2141dbb1df13c..d178fcdc0dc11 100644
--- a/UPGRADE-5.0.md
+++ b/UPGRADE-5.0.md
@@ -76,6 +76,7 @@ Security
* The `ContextListener::setLogoutOnUserChange()` method has been removed.
* The `Symfony\Component\Security\Core\User\AdvancedUserInterface` has been removed.
+ * The `ExpressionVoter::addExpressionLanguageProvider()` method has been removed.
SecurityBundle
--------------
diff --git a/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php b/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php
new file mode 100644
index 0000000000000..de5a75b3b041f
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\CacheWarmer;
+
+use Symfony\Component\ExpressionLanguage\Expression;
+use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
+use Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
+
+class ExpressionCacheWarmer implements CacheWarmerInterface
+{
+ private $expressions;
+ private $expressionLanguage;
+
+ /**
+ * @param iterable|Expression[] $expressions
+ */
+ public function __construct(iterable $expressions, ExpressionLanguage $expressionLanguage)
+ {
+ $this->expressions = $expressions;
+ $this->expressionLanguage = $expressionLanguage;
+ }
+
+ public function isOptional()
+ {
+ return true;
+ }
+
+ public function warmUp($cacheDir)
+ {
+ foreach ($this->expressions as $expression) {
+ $this->expressionLanguage->parse($expression, array('token', 'user', 'object', 'subject', 'roles', 'request', 'trust_resolver'));
+ }
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index 3b56ac54dda35..88ab77577041e 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -26,7 +26,6 @@
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
-use Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Http\Controller\UserValueResolver;
@@ -45,7 +44,6 @@ class SecurityExtension extends Extension
private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me');
private $factories = array();
private $userProviderFactories = array();
- private $expressionLanguage;
public function __construct()
{
@@ -136,10 +134,6 @@ private function createRoleHierarchy(array $config, ContainerBuilder $container)
private function createAuthorization($config, ContainerBuilder $container)
{
- if (!$config['access_control']) {
- return;
- }
-
foreach ($config['access_control'] as $access) {
$matcher = $this->createRequestMatcher(
$container,
@@ -157,6 +151,14 @@ private function createAuthorization($config, ContainerBuilder $container)
$container->getDefinition('security.access_map')
->addMethodCall('add', array($matcher, $attributes, $access['requires_channel']));
}
+
+ // allow cache warm-up for expressions
+ if (count($this->expressions)) {
+ $container->getDefinition('security.cache_warmer.expression')
+ ->replaceArgument(0, new IteratorArgument(array_values($this->expressions)));
+ } else {
+ $container->removeDefinition('security.cache_warmer.expression');
+ }
}
private function createFirewalls($config, ContainerBuilder $container)
@@ -636,11 +638,14 @@ private function createExpression($container, $expression)
return $this->expressions[$id];
}
+ if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
+ throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
+ }
+
$container
- ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression')
+ ->register($id, 'Symfony\Component\ExpressionLanguage\Expression')
->setPublic(false)
->addArgument($expression)
- ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes()))
;
return $this->expressions[$id] = new Reference($id);
@@ -703,16 +708,4 @@ public function getConfiguration(array $config, ContainerBuilder $container)
// first assemble the factories
return new MainConfiguration($this->factories, $this->userProviderFactories);
}
-
- private function getExpressionLanguage()
- {
- if (null === $this->expressionLanguage) {
- if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
- throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
- }
- $this->expressionLanguage = new ExpressionLanguage();
- }
-
- return $this->expressionLanguage;
- }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
index 26db528aed550..2659619d49e89 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
@@ -80,7 +80,9 @@
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: