diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 5b6c8b447e..9ec02f0722 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -602,15 +602,44 @@ class FrameworkExtension extends Extension
@trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED);
}
$type = $workflow['type'];
+ $workflowId = sprintf('%s.%s', $type, $name);
+ // Create transitions
$transitions = array();
+ $guardsConfiguration = array();
+ // Global transition counter per workflow
+ $transitionCounter = 0;
foreach ($workflow['transitions'] as $transition) {
if ('workflow' === $type) {
- $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to']));
+ $transitionDefinition = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to']));
+ $transitionDefinition->setPublic(false);
+ $transitionId = sprintf('%s.transition.%s', $workflowId, $transitionCounter++);
+ $container->setDefinition($transitionId, $transitionDefinition);
+ $transitions[] = new Reference($transitionId);
+ if (isset($transition['guard'])) {
+ $configuration = new Definition(Workflow\EventListener\GuardExpression::class);
+ $configuration->addArgument(new Reference($transitionId));
+ $configuration->addArgument($transition['guard']);
+ $configuration->setPublic(false);
+ $eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']);
+ $guardsConfiguration[$eventName][] = $configuration;
+ }
} elseif ('state_machine' === $type) {
foreach ($transition['from'] as $from) {
foreach ($transition['to'] as $to) {
- $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to));
+ $transitionDefinition = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to));
+ $transitionDefinition->setPublic(false);
+ $transitionId = sprintf('%s.transition.%s', $workflowId, $transitionCounter++);
+ $container->setDefinition($transitionId, $transitionDefinition);
+ $transitions[] = new Reference($transitionId);
+ if (isset($transition['guard'])) {
+ $configuration = new Definition(Workflow\EventListener\GuardExpression::class);
+ $configuration->addArgument(new Reference($transitionId));
+ $configuration->addArgument($transition['guard']);
+ $configuration->setPublic(false);
+ $eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']);
+ $guardsConfiguration[$eventName][] = $configuration;
+ }
}
}
}
@@ -641,7 +670,6 @@ class FrameworkExtension extends Extension
}
// Create Workflow
- $workflowId = sprintf('%s.%s', $type, $name);
$workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type));
$workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId)));
if (isset($markingStoreDefinition)) {
@@ -677,16 +705,7 @@ class FrameworkExtension extends Extension
}
// Add Guard Listener
- $guard = new Definition(Workflow\EventListener\GuardListener::class);
- $guard->setPrivate(true);
- $configuration = array();
- foreach ($workflow['transitions'] as $config) {
- $transitionName = $config['name'];
-
- if (!isset($config['guard'])) {
- continue;
- }
-
+ if ($guardsConfiguration) {
if (!class_exists(ExpressionLanguage::class)) {
throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
}
@@ -695,13 +714,11 @@ class FrameworkExtension extends Extension
throw new LogicException('Cannot guard workflows as the Security component is not installed. Try running "composer require symfony/security".');
}
- $eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName);
- $guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
- $configuration[$eventName] = $config['guard'];
- }
- if ($configuration) {
+ $guard = new Definition(Workflow\EventListener\GuardListener::class);
+ $guard->setPrivate(true);
+
$guard->setArguments(array(
- $configuration,
+ $guardsConfiguration,
new Reference('workflow.security.expression_language'),
new Reference('security.token_storage'),
new Reference('security.authorization_checker'),
@@ -709,6 +726,9 @@ class FrameworkExtension extends Extension
new Reference('security.role_hierarchy'),
new Reference('validator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
));
+ foreach ($guardsConfiguration as $eventName => $config) {
+ $guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
+ }
$container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard);
$container->setParameter('workflow.has_guard_listeners', true);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml
index cf129e45c3..a5124d1fe7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml
@@ -13,12 +13,12 @@
a
Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
-
-
-
-
-
-
+ draft
+ wait_for_journalist
+ approved_by_journalist
+ wait_for_spellchecker
+ approved_by_spellchecker
+ published
draft
wait_for_journalist
diff --git a/src/Symfony/Component/Workflow/EventListener/GuardExpression.php b/src/Symfony/Component/Workflow/EventListener/GuardExpression.php
index cf3b8c7e18..09ab15086b 100644
--- a/src/Symfony/Component/Workflow/EventListener/GuardExpression.php
+++ b/src/Symfony/Component/Workflow/EventListener/GuardExpression.php
@@ -19,19 +19,22 @@ class GuardExpression
private $expression;
- public function getTransition(): Transition
- {
- return $this->transition;
- }
-
- public function getExpression(): string
- {
- return $this->expression;
- }
-
- public function __construct(Transition $transition, string $expression)
+ /**
+ * @param string $expression
+ */
+ public function __construct(Transition $transition, $expression)
{
$this->transition = $transition;
$this->expression = $expression;
}
+
+ public function getTransition()
+ {
+ return $this->transition;
+ }
+
+ public function getExpression()
+ {
+ return $this->expression;
+ }
}
diff --git a/src/Symfony/Component/Workflow/EventListener/GuardListener.php b/src/Symfony/Component/Workflow/EventListener/GuardListener.php
index 4f1c229e51..4d3cfac57e 100644
--- a/src/Symfony/Component/Workflow/EventListener/GuardListener.php
+++ b/src/Symfony/Component/Workflow/EventListener/GuardListener.php
@@ -18,7 +18,6 @@ use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Exception\InvalidTokenConfigurationException;
-use Symfony\Component\Workflow\TransitionBlocker;
/**
* @author Grégoire Pineau
@@ -63,16 +62,15 @@ class GuardListener
}
}
- private function validateGuardExpression(GuardEvent $event, string $expression)
+ private function validateGuardExpression(GuardEvent $event, $expression)
{
if (!$this->expressionLanguage->evaluate($expression, $this->getVariables($event))) {
- $blocker = TransitionBlocker::createBlockedByExpressionGuardListener($expression);
- $event->addTransitionBlocker($blocker);
+ $event->setBlocked(true);
}
}
// code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
- private function getVariables(GuardEvent $event): array
+ private function getVariables(GuardEvent $event)
{
$token = $this->tokenStorage->getToken();
diff --git a/src/Symfony/Component/Workflow/Tests/EventListener/GuardListenerTest.php b/src/Symfony/Component/Workflow/Tests/EventListener/GuardListenerTest.php
index f4c646ade3..8686d74cf6 100644
--- a/src/Symfony/Component/Workflow/Tests/EventListener/GuardListenerTest.php
+++ b/src/Symfony/Component/Workflow/Tests/EventListener/GuardListenerTest.php
@@ -21,16 +21,16 @@ class GuardListenerTest extends TestCase
private $authenticationChecker;
private $validator;
private $listener;
- private $transition;
+ private $configuration;
protected function setUp()
{
- $configuration = array(
+ $this->configuration = array(
'test_is_granted' => 'is_granted("something")',
'test_is_valid' => 'is_valid(subject)',
'test_expression' => array(
- new GuardExpression($this->getTransition(true), '!is_valid(subject)'),
- new GuardExpression($this->getTransition(true), 'is_valid(subject)'),
+ new GuardExpression(new Transition('name', 'from', 'to'), '!is_valid(subject)'),
+ new GuardExpression(new Transition('name', 'from', 'to'), 'is_valid(subject)'),
),
);
$expressionLanguage = new ExpressionLanguage();
@@ -41,7 +41,7 @@ class GuardListenerTest extends TestCase
$this->authenticationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
$trustResolver = $this->getMockBuilder(AuthenticationTrustResolverInterface::class)->getMock();
$this->validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
- $this->listener = new GuardListener($configuration, $expressionLanguage, $tokenStorage, $this->authenticationChecker, $trustResolver, null, $this->validator);
+ $this->listener = new GuardListener($this->configuration, $expressionLanguage, $tokenStorage, $this->authenticationChecker, $trustResolver, null, $this->validator);
}
protected function tearDown()
@@ -104,8 +104,8 @@ class GuardListenerTest extends TestCase
public function testWithGuardExpressionWithNotSupportedTransition()
{
- $event = $this->createEvent(true);
- $this->configureValidator(false, false);
+ $event = $this->createEvent();
+ $this->configureValidator(false);
$this->listener->onTransition($event, 'test_expression');
$this->assertFalse($event->isBlocked());
@@ -113,7 +113,7 @@ class GuardListenerTest extends TestCase
public function testWithGuardExpressionWithSupportedTransition()
{
- $event = $this->createEvent();
+ $event = $this->createEvent($this->configuration['test_expression'][1]->getTransition());
$this->configureValidator(true, true);
$this->listener->onTransition($event, 'test_expression');
@@ -122,18 +122,18 @@ class GuardListenerTest extends TestCase
public function testGuardExpressionBlocks()
{
- $event = $this->createEvent();
+ $event = $this->createEvent($this->configuration['test_expression'][1]->getTransition());
$this->configureValidator(true, false);
$this->listener->onTransition($event, 'test_expression');
$this->assertTrue($event->isBlocked());
}
- private function createEvent($newTransition = false)
+ private function createEvent(Transition $transition = null)
{
$subject = new \stdClass();
$subject->marking = new Marking();
- $transition = $this->getTransition($newTransition);
+ $transition = $transition ?: new Transition('name', 'from', 'to');
return new GuardEvent($subject, $subject->marking, $transition);
}
@@ -173,13 +173,4 @@ class GuardListenerTest extends TestCase
->willReturn($valid ? array() : array('a violation'))
;
}
-
- private function getTransition($new = false)
- {
- if ($new || !$this->transition) {
- $this->transition = new Transition('name', 'from', 'to');
- }
-
- return $this->transition;
- }
}