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">
</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
BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including
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'))) {
$this->sendHeaders = false;
self::$sendHeaders = false;
$this->headers = [];
return;
@ -59,7 +59,7 @@ class ChromePhpHandler extends BaseChromePhpHandler
*/
protected function sendHeader($header, $content)
{
if (!$this->sendHeaders) {
if (!self::$sendHeaders) {
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\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\Form\Form;
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']; })
->thenInvalid('You must specify the "default_bus" if you define more than one bus.')
->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()
->arrayNode('routing')
->normalizeKeys(false)

View File

@ -304,7 +304,7 @@ class FrameworkExtension extends Extension
}
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 {
$container->removeDefinition('console.command.messenger_consume_messages');
$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)) {
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" />
</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">
<!-- Run before serializer.normalizer.object -->
<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()
{
return [

View File

@ -121,7 +121,7 @@ EOF
continue;
}
$this->displayLog($input, $output, $clientId, $record);
$this->displayLog($output, $clientId, $record);
}
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'])) {
$clientId = unpack('H*', $record['log_id'])[1];

View File

@ -797,7 +797,7 @@ class FilesystemTest extends FilesystemTestCase
$file = $this->workspace.\DIRECTORY_SEPARATOR.'file';
$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->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 (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])) {
@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
$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.
// 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()
{
$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('ETag', '"12345"');
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
{
@ -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
{
@ -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
{

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
{

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
{

View File

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

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Transport\AmqpExt;
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
use Symfony\Component\Messenger\Exception\LogicException;
/**
* An AMQP connection.
@ -58,6 +59,10 @@ class Connection
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([
'delay' => [
'exchange_name' => 'delays',

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Transport\Doctrine;
use Doctrine\Common\Persistence\ConnectionRegistry;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Messenger\Exception\TransportException;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
@ -24,8 +25,12 @@ class DoctrineTransportFactory implements TransportFactoryInterface
{
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;
}

View File

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

View File

@ -27,7 +27,7 @@ trait ClassResolverTrait
*
* @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
{

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>
<target>このパスワードは漏洩している為使用できません。</target>
</trans-unit>
<trans-unit id="94">
<source>This value should be between {{ min }} and {{ max }}.</source>
<target>{{ min }}以上{{ max }}以下でなければなりません。</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -179,7 +179,8 @@ abstract class ConstraintValidatorTestCase extends TestCase
->willReturn($validator);
$validator->expects($this->at(2 * $i + 1))
->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)
@ -191,7 +192,8 @@ abstract class ConstraintValidatorTestCase extends TestCase
->willReturn($contextualValidator);
$contextualValidator->expects($this->at(2 * $i + 1))
->method('validate')
->with($value, $constraints, $group);
->with($value, $constraints, $group)
->willReturn($contextualValidator);
}
protected function assertNoViolation()

View File

@ -51,7 +51,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
$method = 'get'.ucfirst($this->property);
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}();
@ -81,7 +81,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
$method = 'set'.ucfirst($this->property);
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);

View File

@ -5,6 +5,7 @@ namespace Symfony\Component\Workflow\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\StateMachine;
use Symfony\Component\Workflow\TransitionBlocker;
@ -84,27 +85,52 @@ class StateMachineTest extends TestCase
$subject = new Subject();
// 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
// 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
$subject->setMarking('a');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList);
$this->assertSame('Transition blocker of place a', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode());
// Test if when you are in place "d" trying transition "t1" then
// returned blocker list contains guard blocker instead blockedByMarking
// Test if when you are in place "d" and trying to apply "t1" then
// it returns blocker list contains guard blocker instead blockedByMarking
$subject->setMarking('d');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList);
$this->assertSame('Transition blocker of place d', $blockers[0]->getMessage());
$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);
$transitionBlockerList = null;
$applied = false;
$approvedTransitionQueue = [];
$transitionExist = false;
$approvedTransitions = [];
$bestTransitionBlockerList = null;
foreach ($this->definition->getTransitions() as $transition) {
if ($transition->getName() !== $transitionName) {
continue;
}
$transitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition);
if (!$transitionBlockerList->isEmpty()) {
$transitionExist = true;
$tmpTransitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition);
if ($tmpTransitionBlockerList->isEmpty()) {
$approvedTransitions[] = $transition;
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) {
$applied = true;
if (!$transitionExist) {
throw new UndefinedTransitionException($subject, $transitionName, $this);
}
if (!$approvedTransitions) {
throw new NotEnabledTransitionException($subject, $transitionName, $this, $bestTransitionBlockerList);
}
foreach ($approvedTransitions as $transition) {
$this->leave($subject, $transition, $marking);
$context = $this->transition($subject, $transition, $marking, $context);
@ -193,14 +215,6 @@ class Workflow implements WorkflowInterface
$this->announce($subject, $transition, $marking);
}
if (!$transitionBlockerList) {
throw new UndefinedTransitionException($subject, $transitionName, $this);
}
if (!$applied) {
throw new NotEnabledTransitionException($subject, $transitionName, $this, $transitionBlockerList);
}
return $marking;
}