Merge branch '4.3' into 4.4

* 4.3:
  [FWBundle] Remove unused parameter
  [Intl] [Workflow] fixes English grammar typos
  [Filesystem] [Serializer] fixes English grammar typo
  [Messenger] Adding exception to amqp transport in case amqp ext is not installed
  [Monolog Bridge] Fixed accessing static property as non static.
  Improve Symfony description
  Add DateTimeZoneNormalizer into Dependency Injection
  [Messenger] Error when specified default bus is not among the configured
  [Validator] Add Japanese translation
  [Workflow] Apply the same logic of precedence between the apply() and the buildTransitionBlockerList() method
  Remove some unused methods parameters
  Avoid empty \"If-Modified-Since\" header in validation request
  [Security] Fix SwitchUser is broken when the User Provider always returns a valid user
  Fix error message according to the new regex
  compatibility with DoctrineBundle 2
  [Validator] ConstraintValidatorTestCase: add missing return value to mocked validate method calls
This commit is contained in:
Robin Chalas 2019-11-27 00:16:41 +01:00
commit cde2538849
24 changed files with 136 additions and 44 deletions

View File

@ -2,7 +2,7 @@
<img src="https://symfony.com/logos/symfony_black_02.svg"> <img src="https://symfony.com/logos/symfony_black_02.svg">
</a></p> </a></p>
[Symfony][1] is a **PHP framework** for web applications and a set of reusable [Symfony][1] is a **PHP framework** for web and console applications and a set of reusable
**PHP components**. Symfony is used by thousands of web applications (including **PHP components**. Symfony is used by thousands of web applications (including
BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including
Drupal and Magento). Drupal and Magento).

View File

@ -41,7 +41,7 @@ class ChromePhpHandler extends BaseChromePhpHandler
} }
if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) {
$this->sendHeaders = false; self::$sendHeaders = false;
$this->headers = []; $this->headers = [];
return; return;
@ -59,7 +59,7 @@ class ChromePhpHandler extends BaseChromePhpHandler
*/ */
protected function sendHeader($header, $content) protected function sendHeader($header, $content)
{ {
if (!$this->sendHeaders) { if (!self::$sendHeaders) {
return; return;
} }

View File

@ -19,6 +19,7 @@ use Symfony\Component\Asset\Package;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpClient\HttpClient;
@ -1173,6 +1174,10 @@ class Configuration implements ConfigurationInterface
->ifTrue(function ($v) { return isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']; }) ->ifTrue(function ($v) { return isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']; })
->thenInvalid('You must specify the "default_bus" if you define more than one bus.') ->thenInvalid('You must specify the "default_bus" if you define more than one bus.')
->end() ->end()
->validate()
->ifTrue(static function ($v): bool { return isset($v['buses']) && null !== $v['default_bus'] && !isset($v['buses'][$v['default_bus']]); })
->then(static function (array $v): void { throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses'])))); })
->end()
->children() ->children()
->arrayNode('routing') ->arrayNode('routing')
->normalizeKeys(false) ->normalizeKeys(false)

View File

@ -304,7 +304,7 @@ class FrameworkExtension extends Extension
} }
if ($this->messengerConfigEnabled = $this->isConfigEnabled($container, $config['messenger'])) { if ($this->messengerConfigEnabled = $this->isConfigEnabled($container, $config['messenger'])) {
$this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['serializer'], $config['validation']); $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['validation']);
} else { } else {
$container->removeDefinition('console.command.messenger_consume_messages'); $container->removeDefinition('console.command.messenger_consume_messages');
$container->removeDefinition('console.command.messenger_debug'); $container->removeDefinition('console.command.messenger_debug');
@ -1696,7 +1696,7 @@ class FrameworkExtension extends Extension
} }
} }
private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $serializerConfig, array $validationConfig) private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $validationConfig)
{ {
if (!interface_exists(MessageBusInterface::class)) { if (!interface_exists(MessageBusInterface::class)) {
throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".');

View File

@ -40,6 +40,11 @@
<tag name="serializer.normalizer" priority="-915" /> <tag name="serializer.normalizer" priority="-915" />
</service> </service>
<service id="serializer.normalizer.datetimezone" class="Symfony\Component\Serializer\Normalizer\DateTimeZoneNormalizer">
<!-- Run before serializer.normalizer.object -->
<tag name="serializer.normalizer" priority="-915" />
</service>
<service id="serializer.normalizer.dateinterval" class="Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer"> <service id="serializer.normalizer.dateinterval" class="Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer">
<!-- Run before serializer.normalizer.object --> <!-- Run before serializer.normalizer.object -->
<tag name="serializer.normalizer" priority="-915" /> <tag name="serializer.normalizer" priority="-915" />

View File

@ -329,6 +329,27 @@ class ConfigurationTest extends TestCase
); );
} }
public function testItErrorsWhenDefaultBusDoesNotExist()
{
$processor = new Processor();
$configuration = new Configuration(true);
$this->expectException(InvalidConfigurationException::class);
$this->expectExceptionMessage('The specified default bus "foo" is not configured. Available buses are "bar", "baz".');
$processor->processConfiguration($configuration, [
[
'messenger' => [
'default_bus' => 'foo',
'buses' => [
'bar' => null,
'baz' => null,
],
],
],
]);
}
protected static function getBundleDefaultConfig() protected static function getBundleDefaultConfig()
{ {
return [ return [

View File

@ -121,7 +121,7 @@ EOF
continue; continue;
} }
$this->displayLog($input, $output, $clientId, $record); $this->displayLog($output, $clientId, $record);
} }
return 0; return 0;
@ -150,7 +150,7 @@ EOF
} }
} }
private function displayLog(InputInterface $input, OutputInterface $output, int $clientId, array $record) private function displayLog(OutputInterface $output, int $clientId, array $record)
{ {
if (isset($record['log_id'])) { if (isset($record['log_id'])) {
$clientId = unpack('H*', $record['log_id'])[1]; $clientId = unpack('H*', $record['log_id'])[1];

View File

@ -797,7 +797,7 @@ class FilesystemTest extends FilesystemTestCase
$file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; $file = $this->workspace.\DIRECTORY_SEPARATOR.'file';
$link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; $link = $this->workspace.\DIRECTORY_SEPARATOR.'link';
// $file does not exists right now: creating "broken" links is a wanted feature // $file does not exist right now: creating "broken" links is a wanted feature
$this->filesystem->symlink($file, $link); $this->filesystem->symlink($file, $link);
$this->assertTrue(is_link($link)); $this->assertTrue(is_link($link));

View File

@ -63,7 +63,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) { if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) {
if (isset($matches[1])) { if (isset($matches[1])) {
@trigger_error(sprintf('Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); @trigger_error(sprintf('Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED);
} }
if (isset($matches[2])) { if (isset($matches[2])) {
@trigger_error(sprintf('Using names for buttons that do not contain only letters, digits, underscores ("_"), hyphens ("-") and colons (":") ("%s" given) is deprecated since Symfony 4.3 and will throw an exception in 5.0.', $name), E_USER_DEPRECATED); @trigger_error(sprintf('Using names for buttons that do not contain only letters, digits, underscores ("_"), hyphens ("-") and colons (":") ("%s" given) is deprecated since Symfony 4.3 and will throw an exception in 5.0.', $name), E_USER_DEPRECATED);

View File

@ -377,7 +377,9 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
} }
// add our cached last-modified validator // add our cached last-modified validator
$subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); if ($entry->headers->has('Last-Modified')) {
$subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified'));
}
// Add our cached etag validator to the environment. // Add our cached etag validator to the environment.
// We keep the etags from the client to handle the case when the client // We keep the etags from the client to handle the case when the client

View File

@ -857,6 +857,7 @@ class HttpCacheTest extends HttpCacheTestCase
public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation()
{ {
$this->setNextResponse(200, [], 'Hello World', function ($request, $response) { $this->setNextResponse(200, [], 'Hello World', function ($request, $response) {
$this->assertFalse($request->headers->has('If-Modified-Since'));
$response->headers->set('Cache-Control', 'public'); $response->headers->set('Cache-Control', 'public');
$response->headers->set('ETag', '"12345"'); $response->headers->set('ETag', '"12345"');
if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) { if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) {

View File

@ -46,7 +46,7 @@ final class Currencies extends ResourceBundle
} }
/** /**
* @throws MissingResourceException if the currency code does not exists * @throws MissingResourceException if the currency code does not exist
*/ */
public static function getName(string $currency, string $displayLocale = null): string public static function getName(string $currency, string $displayLocale = null): string
{ {
@ -78,7 +78,7 @@ final class Currencies extends ResourceBundle
} }
/** /**
* @throws MissingResourceException if the currency code does not exists * @throws MissingResourceException if the currency code does not exist
*/ */
public static function getSymbol(string $currency, string $displayLocale = null): string public static function getSymbol(string $currency, string $displayLocale = null): string
{ {
@ -115,7 +115,7 @@ final class Currencies extends ResourceBundle
} }
/** /**
* @throws MissingResourceException if the numeric code does not exists * @throws MissingResourceException if the numeric code does not exist
*/ */
public static function forNumericCode(int $numericCode): array public static function forNumericCode(int $numericCode): array
{ {

View File

@ -49,7 +49,7 @@ final class Locales extends ResourceBundle
} }
/** /**
* @throws MissingResourceException if the locale does not exists * @throws MissingResourceException if the locale does not exist
*/ */
public static function getName(string $locale, string $displayLocale = null): string public static function getName(string $locale, string $displayLocale = null): string
{ {

View File

@ -41,7 +41,7 @@ final class Scripts extends ResourceBundle
} }
/** /**
* @throws MissingResourceException if the script code does not exists * @throws MissingResourceException if the script code does not exist
*/ */
public static function getName(string $script, string $displayLocale = null): string public static function getName(string $script, string $displayLocale = null): string
{ {

View File

@ -28,6 +28,9 @@ class AmqpTransportFactoryTest extends TestCase
$this->assertFalse($factory->supports('invalid-dsn', [])); $this->assertFalse($factory->supports('invalid-dsn', []));
} }
/**
* @requires extension amqp
*/
public function testItCreatesTheTransport() public function testItCreatesTheTransport()
{ {
$factory = new AmqpTransportFactory(); $factory = new AmqpTransportFactory();

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Transport\AmqpExt; namespace Symfony\Component\Messenger\Transport\AmqpExt;
use Symfony\Component\Messenger\Exception\InvalidArgumentException; use Symfony\Component\Messenger\Exception\InvalidArgumentException;
use Symfony\Component\Messenger\Exception\LogicException;
/** /**
* An AMQP connection. * An AMQP connection.
@ -58,6 +59,10 @@ class Connection
public function __construct(array $connectionOptions, array $exchangeOptions, array $queuesOptions, AmqpFactory $amqpFactory = null) public function __construct(array $connectionOptions, array $exchangeOptions, array $queuesOptions, AmqpFactory $amqpFactory = null)
{ {
if (!\extension_loaded('amqp')) {
throw new LogicException(sprintf('You cannot use the "%s" as the "amqp" extension is not installed.', __CLASS__));
}
$this->connectionOptions = array_replace_recursive([ $this->connectionOptions = array_replace_recursive([
'delay' => [ 'delay' => [
'exchange_name' => 'delays', 'exchange_name' => 'delays',

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Transport\Doctrine; namespace Symfony\Component\Messenger\Transport\Doctrine;
use Doctrine\Common\Persistence\ConnectionRegistry; use Doctrine\Common\Persistence\ConnectionRegistry;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Messenger\Exception\TransportException; use Symfony\Component\Messenger\Exception\TransportException;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
@ -24,8 +25,12 @@ class DoctrineTransportFactory implements TransportFactoryInterface
{ {
private $registry; private $registry;
public function __construct(ConnectionRegistry $registry) public function __construct($registry)
{ {
if (!$registry instanceof RegistryInterface && !$registry instanceof ConnectionRegistry) {
throw new \TypeError(sprintf('Expected an instance of %s or %s, but got %s.', RegistryInterface::class, ConnectionRegistry::class, \is_object($registry) ? \get_class($registry) : \gettype($registry)));
}
$this->registry = $registry; $this->registry = $registry;
} }

View File

@ -148,7 +148,6 @@ class SwitchUserListener implements ListenerInterface
try { try {
$this->provider->loadUserByUsername($nonExistentUsername); $this->provider->loadUserByUsername($nonExistentUsername);
throw new \LogicException('AuthenticationException expected');
} catch (AuthenticationException $e) { } catch (AuthenticationException $e) {
} }
} catch (AuthenticationException $e) { } catch (AuthenticationException $e) {

View File

@ -27,7 +27,7 @@ trait ClassResolverTrait
* *
* @param object|string $value * @param object|string $value
* *
* @throws InvalidArgumentException If the class does not exists * @throws InvalidArgumentException If the class does not exist
*/ */
private function getClass($value): string private function getClass($value): string
{ {

View File

@ -362,6 +362,10 @@
<source>This password has been leaked in a data breach, it must not be used. Please use another password.</source> <source>This password has been leaked in a data breach, it must not be used. Please use another password.</source>
<target>このパスワードは漏洩している為使用できません。</target> <target>このパスワードは漏洩している為使用できません。</target>
</trans-unit> </trans-unit>
<trans-unit id="94">
<source>This value should be between {{ min }} and {{ max }}.</source>
<target>{{ min }}以上{{ max }}以下でなければなりません。</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -179,7 +179,8 @@ abstract class ConstraintValidatorTestCase extends TestCase
->willReturn($validator); ->willReturn($validator);
$validator->expects($this->at(2 * $i + 1)) $validator->expects($this->at(2 * $i + 1))
->method('validate') ->method('validate')
->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group); ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group)
->willReturn($validator);
} }
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null) protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
@ -191,7 +192,8 @@ abstract class ConstraintValidatorTestCase extends TestCase
->willReturn($contextualValidator); ->willReturn($contextualValidator);
$contextualValidator->expects($this->at(2 * $i + 1)) $contextualValidator->expects($this->at(2 * $i + 1))
->method('validate') ->method('validate')
->with($value, $constraints, $group); ->with($value, $constraints, $group)
->willReturn($contextualValidator);
} }
protected function assertNoViolation() protected function assertNoViolation()

View File

@ -51,7 +51,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
$method = 'get'.ucfirst($this->property); $method = 'get'.ucfirst($this->property);
if (!method_exists($subject, $method)) { if (!method_exists($subject, $method)) {
throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method));
} }
$marking = $subject->{$method}(); $marking = $subject->{$method}();
@ -81,7 +81,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
$method = 'set'.ucfirst($this->property); $method = 'set'.ucfirst($this->property);
if (!method_exists($subject, $method)) { if (!method_exists($subject, $method)) {
throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method));
} }
$subject->{$method}($marking, $context); $subject->{$method}($marking, $context);

View File

@ -5,6 +5,7 @@ namespace Symfony\Component\Workflow\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Workflow\Event\GuardEvent; use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\StateMachine; use Symfony\Component\Workflow\StateMachine;
use Symfony\Component\Workflow\TransitionBlocker; use Symfony\Component\Workflow\TransitionBlocker;
@ -84,27 +85,52 @@ class StateMachineTest extends TestCase
$subject = new Subject(); $subject = new Subject();
// There may be multiple transitions with the same name. Make sure that transitions // There may be multiple transitions with the same name. Make sure that transitions
// that are not enabled by the marking are evaluated. // that are enabled by the marking are evaluated.
// see https://github.com/symfony/symfony/issues/28432 // see https://github.com/symfony/symfony/issues/28432
// Test if when you are in place "a"trying transition "t1" then returned // Test if when you are in place "a" and trying to apply "t1" then it returns
// blocker list contains guard blocker instead blockedByMarking // blocker list contains guard blocker instead blockedByMarking
$subject->setMarking('a'); $subject->setMarking('a');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList); $this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList);
$this->assertSame('Transition blocker of place a', $blockers[0]->getMessage()); $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode()); $this->assertSame('blocker', $blockers[0]->getCode());
// Test if when you are in place "d" trying transition "t1" then // Test if when you are in place "d" and trying to apply "t1" then
// returned blocker list contains guard blocker instead blockedByMarking // it returns blocker list contains guard blocker instead blockedByMarking
$subject->setMarking('d'); $subject->setMarking('d');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList); $this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList);
$this->assertSame('Transition blocker of place d', $blockers[0]->getMessage()); $this->assertSame('Transition blocker of place d', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode()); $this->assertSame('blocker', $blockers[0]->getCode());
} }
public function testApplyReturnsExpectedReasonOnBranchMerge()
{
$definition = $this->createComplexStateMachineDefinition();
$dispatcher = new EventDispatcher();
$net = new StateMachine($definition, null, $dispatcher);
$dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
$event->addTransitionBlocker(new TransitionBlocker(sprintf('Transition blocker of place %s', $event->getTransition()->getFroms()[0]), 'blocker'));
});
$subject = new Subject();
// There may be multiple transitions with the same name. Make sure that all transitions
// that are enabled by the marking are evaluated.
// see https://github.com/symfony/symfony/issues/34489
try {
$net->apply($subject, 't1');
$this->fail();
} catch (NotEnabledTransitionException $e) {
$blockers = iterator_to_array($e->getTransitionBlockerList());
$this->assertSame('Transition blocker of place a', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode());
}
}
} }

View File

@ -159,25 +159,47 @@ class Workflow implements WorkflowInterface
$marking = $this->getMarking($subject); $marking = $this->getMarking($subject);
$transitionBlockerList = null; $transitionExist = false;
$applied = false; $approvedTransitions = [];
$approvedTransitionQueue = []; $bestTransitionBlockerList = null;
foreach ($this->definition->getTransitions() as $transition) { foreach ($this->definition->getTransitions() as $transition) {
if ($transition->getName() !== $transitionName) { if ($transition->getName() !== $transitionName) {
continue; continue;
} }
$transitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition); $transitionExist = true;
if (!$transitionBlockerList->isEmpty()) {
$tmpTransitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition);
if ($tmpTransitionBlockerList->isEmpty()) {
$approvedTransitions[] = $transition;
continue; continue;
} }
$approvedTransitionQueue[] = $transition;
if (!$bestTransitionBlockerList) {
$bestTransitionBlockerList = $tmpTransitionBlockerList;
continue;
}
// We prefer to return transitions blocker by something else than
// marking. Because it means the marking was OK. Transitions are
// deterministic: it's not possible to have many transitions enabled
// at the same time that match the same marking with the same name
if (!$tmpTransitionBlockerList->has(TransitionBlocker::BLOCKED_BY_MARKING)) {
$bestTransitionBlockerList = $tmpTransitionBlockerList;
}
} }
foreach ($approvedTransitionQueue as $transition) { if (!$transitionExist) {
$applied = true; throw new UndefinedTransitionException($subject, $transitionName, $this);
}
if (!$approvedTransitions) {
throw new NotEnabledTransitionException($subject, $transitionName, $this, $bestTransitionBlockerList);
}
foreach ($approvedTransitions as $transition) {
$this->leave($subject, $transition, $marking); $this->leave($subject, $transition, $marking);
$context = $this->transition($subject, $transition, $marking, $context); $context = $this->transition($subject, $transition, $marking, $context);
@ -193,14 +215,6 @@ class Workflow implements WorkflowInterface
$this->announce($subject, $transition, $marking); $this->announce($subject, $transition, $marking);
} }
if (!$transitionBlockerList) {
throw new UndefinedTransitionException($subject, $transitionName, $this);
}
if (!$applied) {
throw new NotEnabledTransitionException($subject, $transitionName, $this, $transitionBlockerList);
}
return $marking; return $marking;
} }