From 6303708a5ee6ed5bc9c051ef2b56991df5eb1080 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 28 Oct 2022 19:50:38 +0200 Subject: [PATCH 01/23] Bump Symfony version to 4.4.49 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 54ea465379ba1..ce28585fe154b 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.48'; - public const VERSION_ID = 40448; + public const VERSION = '4.4.49-DEV'; + public const VERSION_ID = 40449; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 48; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 49; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2022'; public const END_OF_LIFE = '11/2023'; From f56107c7d238156f9ab02951ba36acb43f9729fd Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Sat, 29 Oct 2022 14:00:53 +0200 Subject: [PATCH 02/23] Run tests with UTC to avoid daylight saving time messing with assertions --- .github/workflows/integration-tests.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index cf869eea173cd..cb2425d7da942 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -92,7 +92,7 @@ jobs: with: coverage: "none" extensions: "memcached,redis-5.3.4,xsl,ldap" - ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 + ini-values: date.timezone=UTC,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" - name: Load fixtures diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 42d2f02fbd24f..0030dab8466ea 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -48,7 +48,7 @@ jobs: uses: shivammathur/setup-php@v2 with: coverage: "none" - ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 + ini-values: date.timezone=UTC,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" extensions: "${{ env.extensions }}" tools: flex From 2b7ff1112a88a3d1153b522d7d2df0010c8519d5 Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Sun, 30 Oct 2022 11:27:32 +0000 Subject: [PATCH 03/23] [HttpFoundation] Check IPv6 is valid before comparing it --- src/Symfony/Component/HttpFoundation/IpUtils.php | 9 +++++++++ .../Component/HttpFoundation/Tests/IpUtilsTest.php | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 8f30ee099164f..de2112cfc7028 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -124,6 +124,15 @@ public static function checkIp6($requestIp, $ip) throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); } + // Check to see if we were given a IP4 $requestIp or $ip by mistake + if (str_contains($requestIp, '.') || str_contains($ip, '.')) { + return self::$checkedIps[$cacheKey] = false; + } + + if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + return self::$checkedIps[$cacheKey] = false; + } + if (str_contains($ip, '/')) { [$address, $netmask] = explode('/', $ip, 2); diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index 48509f9667cd7..8de4b4d7bd472 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -73,6 +73,10 @@ public function getIpv6Data() [false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'], [false, '', '::1'], [false, null, '::1'], + [false, '127.0.0.1', '::1'], + [false, '0.0.0.0/8', '::1'], + [false, '::1', '127.0.0.1'], + [false, '::1', '0.0.0.0/8'], ]; } From 4d5996d170c2b1e92554e6c49b7e7d7f774058ba Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 1 Nov 2022 07:46:18 +0100 Subject: [PATCH 04/23] Use 6.3 for new features --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 00a686580d01f..6d7bf0b140608 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | 6.2 for features / 4.4, 5.4, 6.0 or 6.1 for bug fixes +| Branch? | 6.3 for features / 4.4, 5.4, 6.0, 6.1, or 6.2 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no From 5cff81d0be3ee730bbc3faa7ac28e1fe44bed8b8 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 2 Nov 2022 16:49:01 +0100 Subject: [PATCH 05/23] Update actions in the CI to move away from the deprecated runtime --- .github/workflows/integration-tests.yml | 2 +- .github/workflows/intl-data-tests.yml | 2 +- .github/workflows/phpunit-bridge.yml | 2 +- .github/workflows/psalm.yml | 4 ++-- .github/workflows/unit-tests.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index cb2425d7da942..5cd8a425eb58a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -72,7 +72,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/intl-data-tests.yml b/.github/workflows/intl-data-tests.yml index 477278e416c4d..fef1dd1140374 100644 --- a/.github/workflows/intl-data-tests.yml +++ b/.github/workflows/intl-data-tests.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/phpunit-bridge.yml b/.github/workflows/phpunit-bridge.yml index d10098d119114..210029074f83e 100644 --- a/.github/workflows/phpunit-bridge.yml +++ b/.github/workflows/phpunit-bridge.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 96f34721f4a46..7b5fc5a4c8bbd 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -29,12 +29,12 @@ jobs: coverage: none - name: Checkout target branch - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.base_ref }} - name: Checkout PR - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install dependencies run: | diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 0030dab8466ea..767141eab5bfe 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 2 From 4b843d1eeae5e3b8b62affbcc08fa9e9acf72b0a Mon Sep 17 00:00:00 2001 From: Kris Buist Date: Thu, 3 Nov 2022 10:41:56 +0100 Subject: [PATCH 06/23] Fix the notification email theme for asynchronously dispatched emails When the `\Symfony\Component\Mailer\Messenger\SendEmailMessage` is dispatched asynchronously, the email message is serialised and unserialised. The theme that was set on the `NotificationEmail` was not included in the serialisation, causing the value to return back to the default after deserialisation. --- src/Symfony/Bridge/Twig/Mime/NotificationEmail.php | 9 +++++++-- .../Bridge/Twig/Tests/Mime/NotificationEmailTest.php | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php index 1a58aa5e5e5bc..382928a982da2 100644 --- a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php @@ -210,7 +210,7 @@ private function getExceptionAsString($exception): string */ public function __serialize(): array { - return [$this->context, parent::__serialize()]; + return [$this->context, $this->theme, parent::__serialize()]; } /** @@ -218,7 +218,12 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->context, $parentData] = $data; + if (3 === \count($data)) { + [$this->context, $this->theme, $parentData] = $data; + } else { + // Backwards compatibility for deserializing data structures that were serialized without the theme + [$this->context, $parentData] = $data; + } parent::__unserialize($parentData); } diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php index 6c5b4a4bf579e..0b55ca01d6b3e 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php @@ -46,6 +46,7 @@ public function testSerialize() ->importance(NotificationEmail::IMPORTANCE_HIGH) ->action('Bar', 'http://example.com/') ->context(['a' => 'b']) + ->theme('example') )); $this->assertEquals([ 'importance' => NotificationEmail::IMPORTANCE_HIGH, @@ -57,6 +58,8 @@ public function testSerialize() 'raw' => true, 'a' => 'b', ], $email->getContext()); + + $this->assertSame('@email/example/notification/body.html.twig', $email->getHtmlTemplate()); } public function testTheme() From 3c7dbb4f134de26c7913f3f50c6f9ff98503102b Mon Sep 17 00:00:00 2001 From: Robert Meijers Date: Thu, 3 Nov 2022 18:26:28 +0100 Subject: [PATCH 07/23] [DependencyInjection] don't move locator tag for service subscriber Decorators move tags applied to the decorated service to the decorating service. But this (sometimes) breaks when the decorated service is a service subscriber, which has the argument for the container explicitly set. This mostly works because the locator for the service subscriber is applied twice. The RegisterServiceSubscriberPass which creates the locator also sets a binding on the service. The ResolveServiceSubscriberPass replaces the arguments referencing the ContainerInterface or ServiceProviderInterface for those services tagged with the container.service_subscriber.locator tag. So when the argument isn't provided in the service definition it will automatically be set using the binding. And in case the argument is set, it will be replaced by the Resolver pass based on the tag. But this thus breaks in case a service explicitly sets the argument (which means the binding isn't applied) and the service being decorated (meaning the locator tag is "lost"). So add the locator tag to the list of tags to keep on the original service. --- .../Compiler/DecoratorServicePass.php | 2 +- .../Compiler/DecoratorServicePassTest.php | 4 +-- .../Tests/Compiler/IntegrationTest.php | 33 ++++++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index 3b8086d0931e6..185a097ebe20b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -42,7 +42,7 @@ public function process(ContainerBuilder $container) $tagsToKeep = $container->hasParameter('container.behavior_describing_tags') ? $container->getParameter('container.behavior_describing_tags') - : ['container.do_not_inline', 'container.service_locator', 'container.service_subscriber']; + : ['container.do_not_inline', 'container.service_locator', 'container.service_subscriber', 'container.service_subscriber.locator']; foreach ($definitions as [$id, $definition]) { $decoratedService = $definition->getDecoratedService(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index 9a456335569d4..2ec9b41ba9f53 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -249,7 +249,7 @@ public function testProcessLeavesServiceSubscriberTagOnOriginalDefinition() $container = new ContainerBuilder(); $container ->register('foo') - ->setTags(['container.service_subscriber' => [], 'bar' => ['attr' => 'baz']]) + ->setTags(['container.service_subscriber' => [], 'container.service_subscriber.locator' => [], 'bar' => ['attr' => 'baz']]) ; $container ->register('baz') @@ -259,7 +259,7 @@ public function testProcessLeavesServiceSubscriberTagOnOriginalDefinition() $this->process($container); - $this->assertEquals(['container.service_subscriber' => []], $container->getDefinition('baz.inner')->getTags()); + $this->assertEquals(['container.service_subscriber' => [], 'container.service_subscriber.locator' => []], $container->getDefinition('baz.inner')->getTags()); $this->assertEquals(['bar' => ['attr' => 'baz'], 'foobar' => ['attr' => 'bar']], $container->getDefinition('baz')->getTags()); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 5a2b603d41f56..713f1b859ebfe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; @@ -129,7 +130,7 @@ public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDe $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); } - public function testCanDecorateServiceSubscriber() + public function testCanDecorateServiceSubscriberUsingBinding() { $container = new ContainerBuilder(); $container->register(ServiceSubscriberStub::class) @@ -137,11 +138,33 @@ public function testCanDecorateServiceSubscriber() ->setPublic(true); $container->register(DecoratedServiceSubscriber::class) + ->setProperty('inner', new Reference(DecoratedServiceSubscriber::class.'.inner')) ->setDecoratedService(ServiceSubscriberStub::class); $container->compile(); $this->assertInstanceOf(DecoratedServiceSubscriber::class, $container->get(ServiceSubscriberStub::class)); + $this->assertInstanceOf(ServiceSubscriberStub::class, $container->get(ServiceSubscriberStub::class)->inner); + $this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceSubscriberStub::class)->inner->container); + } + + public function testCanDecorateServiceSubscriberReplacingArgument() + { + $container = new ContainerBuilder(); + $container->register(ServiceSubscriberStub::class) + ->setArguments([new Reference(ContainerInterface::class)]) + ->addTag('container.service_subscriber') + ->setPublic(true); + + $container->register(DecoratedServiceSubscriber::class) + ->setProperty('inner', new Reference(DecoratedServiceSubscriber::class.'.inner')) + ->setDecoratedService(ServiceSubscriberStub::class); + + $container->compile(); + + $this->assertInstanceOf(DecoratedServiceSubscriber::class, $container->get(ServiceSubscriberStub::class)); + $this->assertInstanceOf(ServiceSubscriberStub::class, $container->get(ServiceSubscriberStub::class)->inner); + $this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceSubscriberStub::class)->inner->container); } public function testCanDecorateServiceLocator() @@ -515,6 +538,13 @@ public function testTaggedServiceLocatorWithDefaultIndex() class ServiceSubscriberStub implements ServiceSubscriberInterface { + public $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + public static function getSubscribedServices(): array { return []; @@ -523,6 +553,7 @@ public static function getSubscribedServices(): array class DecoratedServiceSubscriber { + public $inner; } class DecoratedServiceLocator implements ServiceProviderInterface From 5f7004db6268815e42878008db70b22488446cc5 Mon Sep 17 00:00:00 2001 From: Lukas Mencl Date: Thu, 3 Nov 2022 20:03:45 +0100 Subject: [PATCH 08/23] don not set http_version instead of setting it to null --- src/Symfony/Component/HttpClient/HttplugClient.php | 11 ++++++++--- src/Symfony/Component/HttpClient/Psr18Client.php | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php index 86c72e4daf085..a91d738a9a8e7 100644 --- a/src/Symfony/Component/HttpClient/HttplugClient.php +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -246,12 +246,17 @@ private function sendPsr7Request(RequestInterface $request, bool $buffer = null) $body->seek(0); } - return $this->client->request($request->getMethod(), (string) $request->getUri(), [ + $options = [ 'headers' => $request->getHeaders(), 'body' => $body->getContents(), - 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null, 'buffer' => $buffer, - ]); + ]; + + if ('1.0' === $request->getProtocolVersion()) { + $options['http_version'] = '1.0'; + } + + return $this->client->request($request->getMethod(), (string) $request->getUri(), $options); } catch (\InvalidArgumentException $e) { throw new RequestException($e->getMessage(), $request, $e); } catch (TransportExceptionInterface $e) { diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index 7f79af16426a1..230b05aa0e987 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -90,11 +90,16 @@ public function sendRequest(RequestInterface $request): ResponseInterface $body->seek(0); } - $response = $this->client->request($request->getMethod(), (string) $request->getUri(), [ + $options = [ 'headers' => $request->getHeaders(), 'body' => $body->getContents(), - 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null, - ]); + ]; + + if ('1.0' === $request->getProtocolVersion()) { + $options['http_version'] = '1.0'; + } + + $response = $this->client->request($request->getMethod(), (string) $request->getUri(), $options); $psrResponse = $this->responseFactory->createResponse($response->getStatusCode()); From 486f2c5a2ba9e8de1fbac94b4ce2d814957765e0 Mon Sep 17 00:00:00 2001 From: Sezil <72402109+Sezil@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:25:27 +0100 Subject: [PATCH 09/23] [Mailer] Stream timeout not detected due to checking only string result of function fgets --- .../Component/Mailer/Transport/Smtp/Stream/AbstractStream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php index 2b8afd4c6fa67..804b339503161 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php @@ -75,7 +75,7 @@ public function readLine(): string } $line = fgets($this->out); - if ('' === $line) { + if ('' === $line || false === $line) { $metas = stream_get_meta_data($this->out); if ($metas['timed_out']) { throw new TransportException(sprintf('Connection to "%s" timed out.', $this->getReadConnectionDescription())); From 0cc736829a1d27058727df95138a90276275941a Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 4 Nov 2022 17:17:57 +0100 Subject: [PATCH 10/23] [HttpFoundation] Compare cookie with null value as empty string in ResponseCookieValueSame --- .../Test/Constraint/ResponseCookieValueSame.php | 2 +- .../Tests/Test/Constraint/ResponseCookieValueSameTest.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php b/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php index 554e1a1602dd6..eb9c26a3b7ee8 100644 --- a/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php +++ b/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php @@ -59,7 +59,7 @@ protected function matches($response): bool return false; } - return $this->value === $cookie->getValue(); + return $this->value === (string) $cookie->getValue(); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php b/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php index fc195309a4b29..1b68b20bddf59 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php @@ -41,4 +41,12 @@ public function testConstraint() $this->fail(); } + + public function testCookieWithNullValueIsComparedAsEmptyString() + { + $response = new Response(); + $response->headers->setCookie(Cookie::create('foo', null, 0, '/path')); + + $this->assertTrue((new ResponseCookieValueSame('foo', '', '/path'))->evaluate($response, '', true)); + } } From 0aad8c8892f04bac43004088e8e56d83df5307f3 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Sat, 5 Nov 2022 16:41:26 +0100 Subject: [PATCH 11/23] Allow to disable lock without defining a resource --- .../DependencyInjection/Configuration.php | 5 +++- .../DependencyInjection/ConfigurationTest.php | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 3875db646ff21..3cef369a44347 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1117,12 +1117,15 @@ private function addLockSection(ArrayNodeDefinition $rootNode) }) ->end() ->addDefaultsIfNotSet() + ->validate() + ->ifTrue(static function (array $config) { return $config['enabled'] && !$config['resources']; }) + ->thenInvalid('At least one resource must be defined.') + ->end() ->fixXmlConfig('resource') ->children() ->arrayNode('resources') ->normalizeKeys(false) ->useAttributeAsKey('name') - ->requiresAtLeastOneElement() ->defaultValue(['default' => [class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock']]) ->beforeNormalization() ->ifString()->then(function ($v) { return ['default' => $v]; }) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index e4d36c522fbf2..861e161f6792d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -378,6 +378,31 @@ public function testItErrorsWhenDefaultBusDoesNotExist() ]); } + public function testLockCanBeDisabled() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $config = $processor->processConfiguration($configuration, [ + ['lock' => ['enabled' => false]], + ]); + + $this->assertFalse($config['lock']['enabled']); + } + + public function testEnabledLockNeedsResources() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Invalid configuration for path "framework.lock": At least one resource must be defined.'); + + $processor->processConfiguration($configuration, [ + ['lock' => ['enabled' => true]], + ]); + } + protected static function getBundleDefaultConfig() { return [ From 0e4455b3cfd6183623fedd8e826e1a1c39655a6f Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 5 Nov 2022 17:57:55 +0100 Subject: [PATCH 12/23] [Messenger] Do not throw 'no handlers' exception when skipping due to duplicate handling --- .../Middleware/HandleMessageMiddleware.php | 4 +++- .../Middleware/HandleMessageMiddlewareTest.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php index eaf6b9508017b..f22a866322968 100644 --- a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php @@ -53,8 +53,10 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope ]; $exceptions = []; + $alreadyHandled = false; foreach ($this->handlersLocator->getHandlers($envelope) as $handlerDescriptor) { if ($this->messageHasAlreadyBeenHandled($envelope, $handlerDescriptor)) { + $alreadyHandled = true; continue; } @@ -68,7 +70,7 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope } } - if (null === $handler) { + if (null === $handler && !$alreadyHandled) { if (!$this->allowNoHandlers) { throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', $context['class'])); } diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php index c33bad5137d8c..f6e944199b947 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php @@ -123,6 +123,24 @@ public function testThrowsNoHandlerException() $middleware->handle(new Envelope(new DummyMessage('Hey')), new StackMiddleware()); } + public function testMessageAlreadyHandled() + { + $handler = $this->createPartialMock(HandleMessageMiddlewareTestCallable::class, ['__invoke']); + + $middleware = new HandleMessageMiddleware(new HandlersLocator([ + DummyMessage::class => [$handler], + ])); + + $envelope = new Envelope(new DummyMessage('Hey')); + + $envelope = $middleware->handle($envelope, $this->getStackMock()); + $handledStamp = $envelope->all(HandledStamp::class); + + $envelope = $middleware->handle($envelope, $this->getStackMock()); + + $this->assertSame($envelope->all(HandledStamp::class), $handledStamp); + } + public function testAllowNoHandlers() { $middleware = new HandleMessageMiddleware(new HandlersLocator([]), true); From cca8bcd4dd1c613d96cbfae0f6564f2cef02d59e Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Wed, 2 Nov 2022 18:15:44 +0100 Subject: [PATCH 13/23] Tell about messenger:consume invalid limit options --- .../Exception/InvalidOptionException.php | 2 +- .../Command/ConsumeMessagesCommand.php | 11 +++++- .../StopWorkerOnTimeLimitListener.php | 5 +++ .../Command/ConsumeMessagesCommandTest.php | 34 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Exception/InvalidOptionException.php b/src/Symfony/Component/Console/Exception/InvalidOptionException.php index b2eec61658d33..5cf62792e43c8 100644 --- a/src/Symfony/Component/Console/Exception/InvalidOptionException.php +++ b/src/Symfony/Component/Console/Exception/InvalidOptionException.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Console\Exception; /** - * Represents an incorrect option name typed in the console. + * Represents an incorrect option name or value typed in the console. * * @author Jérôme Tamarelle */ diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index defa1a4385b64..43babe5f96a3a 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -14,6 +14,7 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -163,7 +164,11 @@ protected function execute(InputInterface $input, OutputInterface $output) } $stopsWhen = []; - if ($limit = $input->getOption('limit')) { + if (null !== ($limit = $input->getOption('limit'))) { + if (!is_numeric($limit) || 0 >= $limit) { + throw new InvalidOptionException(sprintf('Option "limit" must be a positive integer, "%s" passed.', $limit)); + } + $stopsWhen[] = "processed {$limit} messages"; $this->eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener($limit, $this->logger)); } @@ -174,6 +179,10 @@ protected function execute(InputInterface $input, OutputInterface $output) } if (null !== ($timeLimit = $input->getOption('time-limit'))) { + if (!is_numeric($timeLimit) || 0 >= $limit) { + throw new InvalidOptionException(sprintf('Option "time-limit" must be a positive integer, "%s" passed.', $timeLimit)); + } + $stopsWhen[] = "been running for {$timeLimit}s"; $this->eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($timeLimit, $this->logger)); } diff --git a/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php b/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php index a3f982dff88d3..247982f8a8865 100644 --- a/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php +++ b/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php @@ -15,6 +15,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Messenger\Event\WorkerRunningEvent; use Symfony\Component\Messenger\Event\WorkerStartedEvent; +use Symfony\Component\Messenger\Exception\InvalidArgumentException; /** * @author Simon Delicata @@ -30,6 +31,10 @@ public function __construct(int $timeLimitInSeconds, LoggerInterface $logger = n { $this->timeLimitInSeconds = $timeLimitInSeconds; $this->logger = $logger; + + if ($timeLimitInSeconds <= 0) { + throw new InvalidArgumentException('Time limit must be greater than zero.'); + } } public function onWorkerStarted(): void diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index 7b56e74fb2984..a7b10edde3e3f 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ServiceLocator; @@ -172,4 +173,37 @@ public function testRunWithBusOptionAndBusLocator() $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); } + + /** + * @dataProvider getInvalidOptions + */ + public function testRunWithInvalidOption(string $option, string $value, string $expectedMessage) + { + $receiverLocator = $this->createMock(ContainerInterface::class); + $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); + + $busLocator = $this->createMock(ContainerInterface::class); + + $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); + + $application = new Application(); + $application->add($command); + $tester = new CommandTester($application->get('messenger:consume')); + + $this->expectException(InvalidOptionException::class); + $this->expectExceptionMessage($expectedMessage); + $tester->execute([ + 'receivers' => ['dummy-receiver'], + $option => $value, + ]); + } + + public function getInvalidOptions() + { + yield 'Zero message limit' => ['--limit', '0', 'Option "limit" must be a positive integer, "0" passed.']; + yield 'Non-numeric message limit' => ['--limit', 'whatever', 'Option "limit" must be a positive integer, "whatever" passed.']; + + yield 'Zero second time limit' => ['--time-limit', '0', 'Option "time-limit" must be a positive integer, "0" passed.']; + yield 'Non-numeric time limit' => ['--time-limit', 'whatever', 'Option "time-limit" must be a positive integer, "whatever" passed.']; + } } From d91121f7944f6760931e90f1b7d861c65e09092c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 6 Nov 2022 21:56:17 +0100 Subject: [PATCH 14/23] fix typo in PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 58caff2209f37..51e0d9902a771 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,7 @@ | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no -| Tickets | Fix #... +| Tickets | Fix #... | License | MIT | Doc PR | symfony/symfony-docs#...