From 39e9158999cca0ad76f920fd4ff2c6a2078215e9 Mon Sep 17 00:00:00 2001 From: YaFou <33806646+YaFou@users.noreply.github.com> Date: Sun, 17 Jan 2021 14:43:11 +0100 Subject: [PATCH 01/72] [Mime] Escape commas in address names --- .../Amazon/Tests/Transport/SesApiTransportTest.php | 4 ++-- .../Tests/Transport/MailgunApiTransportTest.php | 4 ++-- .../Tests/Transport/PostmarkApiTransportTest.php | 4 ++-- src/Symfony/Component/Mime/Address.php | 11 ++++++++++- src/Symfony/Component/Mime/Tests/AddressTest.php | 8 +++++++- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiTransportTest.php index 254a1ff84eb09..d795457560c1e 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiTransportTest.php @@ -62,8 +62,8 @@ public function testSend() parse_str($options['body'], $content); $this->assertSame('Hello!', $content['Message_Subject_Data']); - $this->assertSame('Saif Eddin ', $content['Destination_ToAddresses_member'][0]); - $this->assertSame('Fabien ', $content['Source']); + $this->assertSame('"Saif Eddin" ', $content['Destination_ToAddresses_member'][0]); + $this->assertSame('"Fabien" ', $content['Source']); $this->assertSame('Hello There!', $content['Message_Body_Text_Data']); $xml = ' diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php index b4d235671ccda..2e4c53b83fc98 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php @@ -82,8 +82,8 @@ public function testSend() } $this->assertStringContainsString('Hello!', $content); - $this->assertStringContainsString('Saif Eddin ', $content); - $this->assertStringContainsString('Fabien ', $content); + $this->assertStringContainsString('"Saif Eddin" ', $content); + $this->assertStringContainsString('"Fabien" ', $content); $this->assertStringContainsString('Hello There!', $content); return new MockResponse(json_encode(['id' => 'foobar']), [ diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Tests/Transport/PostmarkApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Tests/Transport/PostmarkApiTransportTest.php index 356880bc20e63..a7c7259ce3426 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Tests/Transport/PostmarkApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Tests/Transport/PostmarkApiTransportTest.php @@ -74,8 +74,8 @@ public function testSend() $this->assertStringContainsStringIgnoringCase('X-Postmark-Server-Token: KEY', $options['headers'][1] ?? $options['request_headers'][1]); $body = json_decode($options['body'], true); - $this->assertSame('Fabien ', $body['From']); - $this->assertSame('Saif Eddin ', $body['To']); + $this->assertSame('"Fabien" ', $body['From']); + $this->assertSame('"Saif Eddin" ', $body['To']); $this->assertSame('Hello!', $body['Subject']); $this->assertSame('Hello There!', $body['TextBody']); diff --git a/src/Symfony/Component/Mime/Address.php b/src/Symfony/Component/Mime/Address.php index 53f682c9c2bb7..730b413e0c6db 100644 --- a/src/Symfony/Component/Mime/Address.php +++ b/src/Symfony/Component/Mime/Address.php @@ -77,7 +77,16 @@ public function getEncodedAddress(): string public function toString(): string { - return ($n = $this->getName()) ? $n.' <'.$this->getEncodedAddress().'>' : $this->getEncodedAddress(); + return ($n = $this->getEncodedName()) ? $n.' <'.$this->getEncodedAddress().'>' : $this->getEncodedAddress(); + } + + public function getEncodedName(): string + { + if ('' === $this->getName()) { + return ''; + } + + return sprintf('"%s"', preg_replace('/"/u', '\"', $this->getName())); } /** diff --git a/src/Symfony/Component/Mime/Tests/AddressTest.php b/src/Symfony/Component/Mime/Tests/AddressTest.php index 50d5780d82b34..476bb9c91acf4 100644 --- a/src/Symfony/Component/Mime/Tests/AddressTest.php +++ b/src/Symfony/Component/Mime/Tests/AddressTest.php @@ -27,7 +27,7 @@ public function testConstructor() $a = new Address('fabien@symfonï.com', 'Fabien'); $this->assertEquals('Fabien', $a->getName()); $this->assertEquals('fabien@symfonï.com', $a->getAddress()); - $this->assertEquals('Fabien ', $a->toString()); + $this->assertEquals('"Fabien" ', $a->toString()); $this->assertEquals('fabien@xn--symfon-nwa.com', $a->getEncodedAddress()); } @@ -153,4 +153,10 @@ public function fromStringProvider() ], ]; } + + public function testEncodeNameIfNameContainsCommas() + { + $address = new Address('fabien@symfony.com', 'Fabien, "Potencier'); + $this->assertSame('"Fabien, \"Potencier" ', $address->toString()); + } } From 116c54a554479d7780c6721fc1d24cfb75b8ff93 Mon Sep 17 00:00:00 2001 From: Pierre PLAZANET Date: Fri, 26 Feb 2021 10:07:24 +0100 Subject: [PATCH 02/72] Fix FrameworkBundle PropertyAccess definition when not in debug --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9d46e259fd263..d1a3cfbdd5cdf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1983,7 +1983,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con if (!$container->getParameter('kernel.debug')) { $propertyAccessDefinition->setFactory([PropertyAccessor::class, 'createCache']); - $propertyAccessDefinition->setArguments([null, 0, $version, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]); + $propertyAccessDefinition->setArguments(['', 0, $version, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]); $propertyAccessDefinition->addTag('cache.pool', ['clearer' => 'cache.system_clearer']); $propertyAccessDefinition->addTag('monolog.logger', ['channel' => 'cache']); } else { From 0f9434c1896dceb41274fce0f8e3993732b01877 Mon Sep 17 00:00:00 2001 From: fritzmg Date: Fri, 5 Mar 2021 11:13:54 +0000 Subject: [PATCH 03/72] check if templating engine supports view --- .../Bundle/FrameworkBundle/Controller/ControllerTrait.php | 4 ++-- .../FrameworkBundle/Tests/Controller/ControllerTraitTest.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index a5d00cdcec5b9..99516b5c3fac6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -207,7 +207,7 @@ protected function denyAccessUnlessGranted($attributes, $subject = null, string */ protected function renderView(string $view, array $parameters = []): string { - if ($this->container->has('templating')) { + if ($this->container->has('templating') && $this->container->get('templating')->supports($view)) { @trigger_error('Using the "templating" service is deprecated since version 4.3 and will be removed in 5.0; use Twig instead.', \E_USER_DEPRECATED); return $this->container->get('templating')->render($view, $parameters); @@ -227,7 +227,7 @@ protected function renderView(string $view, array $parameters = []): string */ protected function render(string $view, array $parameters = [], Response $response = null): Response { - if ($this->container->has('templating')) { + if ($this->container->has('templating') && $this->container->get('templating')->supports($view)) { @trigger_error('Using the "templating" service is deprecated since version 4.3 and will be removed in 5.0; use Twig instead.', \E_USER_DEPRECATED); $content = $this->container->get('templating')->render($view, $parameters); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php index a5392b0cc0e82..00007aee3af4a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -449,6 +449,7 @@ public function testRenderViewTemplating() { $templating = $this->createMock(EngineInterface::class); $templating->expects($this->once())->method('render')->willReturn('bar'); + $templating->expects($this->once())->method('supports')->willReturn(true); $container = new Container(); $container->set('templating', $templating); @@ -466,6 +467,7 @@ public function testRenderTemplating() { $templating = $this->createMock(EngineInterface::class); $templating->expects($this->once())->method('render')->willReturn('bar'); + $templating->expects($this->once())->method('supports')->willReturn(true); $container = new Container(); $container->set('templating', $templating); From 61b0be95640156e1ee4dbeedc179159ceff32008 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 7 Mar 2021 20:47:33 +0100 Subject: [PATCH 04/72] [TwigBridge] Allow version 3 of the Twig extra packages --- composer.json | 6 +++--- src/Symfony/Bridge/Twig/composer.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index a3430b3f8047e..737f767885da5 100644 --- a/composer.json +++ b/composer.json @@ -138,9 +138,9 @@ "symfony/phpunit-bridge": "^5.2", "symfony/security-acl": "~2.8|~3.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/cssinliner-extra": "^2.12", - "twig/inky-extra": "^2.12", - "twig/markdown-extra": "^2.12" + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" }, "conflict": { "egulias/email-validator": "~3.0.0", diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index e6e5533de0d1d..3fbb6b7eb4afd 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -45,9 +45,9 @@ "symfony/expression-language": "^3.4|^4.0|^5.0", "symfony/web-link": "^4.4|^5.0", "symfony/workflow": "^4.3|^5.0", - "twig/cssinliner-extra": "^2.12", - "twig/inky-extra": "^2.12", - "twig/markdown-extra": "^2.12" + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" }, "conflict": { "symfony/console": "<3.4", From d68832e1b92a772d488897283e629eb3bea4a887 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Fri, 5 Mar 2021 23:15:57 +0100 Subject: [PATCH 05/72] [ErrorHandler] Added missing type annotations to FlattenException --- .../Exception/FlattenException.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php index 0dcd72673e1e2..18e9b5ac1b7f4 100644 --- a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php +++ b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php @@ -26,17 +26,40 @@ */ class FlattenException extends LegacyFlattenException { + /** @var string */ private $message; + + /** @var int|string */ private $code; + + /** @var self|null */ private $previous; + + /** @var array */ private $trace; + + /** @var string */ private $traceAsString; + + /** @var string */ private $class; + + /** @var int */ private $statusCode; + + /** @var string */ private $statusText; + + /** @var array */ private $headers; + + /** @var string */ private $file; + + /** @var int */ private $line; + + /** @var string|null */ private $asString; public static function create(\Exception $exception, $statusCode = null, array $headers = []): self @@ -104,6 +127,8 @@ public function getStatusCode(): int } /** + * @param int $code + * * @return $this */ public function setStatusCode($code): self @@ -134,6 +159,8 @@ public function getClass(): string } /** + * @param string $class + * * @return $this */ public function setClass($class): self @@ -149,6 +176,8 @@ public function getFile(): string } /** + * @param string $file + * * @return $this */ public function setFile($file): self @@ -164,6 +193,8 @@ public function getLine(): int } /** + * @param int $line + * * @return $this */ public function setLine($line): self @@ -191,6 +222,8 @@ public function getMessage(): string } /** + * @param string $message + * * @return $this */ public function setMessage($message): self @@ -215,6 +248,8 @@ public function getCode() } /** + * @param int|string $code + * * @return $this */ public function setCode($code): self @@ -282,6 +317,10 @@ public function setTraceFromThrowable(\Throwable $throwable): self } /** + * @param array $trace + * @param string|null $file + * @param int|null $line + * * @return $this */ public function setTrace($trace, $file, $line): self From e7b4851ea04dfed6820a72a8c06b3899f2819b1b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Mar 2021 18:14:47 +0100 Subject: [PATCH 06/72] clear unchecked choice radio boxes even if clear missing is set to false --- .../Form/Extension/Core/Type/ChoiceType.php | 2 ++ .../Tests/Extension/Core/Type/ChoiceTypeTest.php | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index bd2985b313137..e4355084fe1e0 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -141,6 +141,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) $knownValues[$child->getName()] = $value; unset($unknownValues[$value]); continue; + } else { + $knownValues[$child->getName()] = null; } } } else { diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 691081aeb9ef5..c96ec62eed9bf 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1287,6 +1287,20 @@ public function testSubmitSingleExpandedObjectChoices() $this->assertNull($form[4]->getViewData()); } + public function testSubmitSingleExpandedClearMissingFalse() + { + $form = $this->factory->create(self::TESTED_TYPE, 'foo', [ + 'choices' => [ + 'foo label' => 'foo', + 'bar label' => 'bar', + ], + 'expanded' => true, + ]); + $form->submit('bar', false); + + $this->assertSame('bar', $form->getData()); + } + public function testSubmitMultipleExpanded() { $form = $this->factory->create(static::TESTED_TYPE, null, [ From b3ee29244f6408d7d59d7d865a5c0630a6d74fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 9 Mar 2021 17:11:27 +0100 Subject: [PATCH 07/72] Fix eventListener initialization when eventSubscriber constructor dispatch an event --- src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 9b3c1595a41f0..1ee4f54ded8e1 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -177,6 +177,7 @@ private function initializeSubscribers() if (!isset($this->listeners[$event])) { $this->listeners[$event] = []; } + unset($this->initialized[$event]); $this->listeners[$event] += $listeners; } $this->subscribers = []; From 72a464e449502060a7b6f4acfb6d53bea01142a9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 8 Mar 2021 16:43:01 +0100 Subject: [PATCH 08/72] Fix `ConstraintViolation#getMessageTemplate()` to always return `string` `ConstraintViolation#getMessageTemplate()`'s inherited signature states that `string` is to be returned by it at all times, yet the implementation returns `null` when no message template had been provided at instantiation. This patch obviates it, returning an empty string when the message template is `null`. Ref: https://github.com/symfony/symfony/pull/40415#issuecomment-792839512 --- .../Component/Validator/ConstraintViolation.php | 2 +- .../Validator/Tests/ConstraintViolationTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/ConstraintViolation.php b/src/Symfony/Component/Validator/ConstraintViolation.php index 2004745fe238d..e9aa1bd9f4400 100644 --- a/src/Symfony/Component/Validator/ConstraintViolation.php +++ b/src/Symfony/Component/Validator/ConstraintViolation.php @@ -100,7 +100,7 @@ public function __toString() */ public function getMessageTemplate() { - return $this->messageTemplate; + return (string) $this->messageTemplate; } /** diff --git a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php index a7734635aacff..dbac96a8aff4d 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php @@ -171,4 +171,19 @@ public function testRetrievedPropertyPathIsAStringEvenIfNotSet() ))->getPropertyPath() ); } + + public function testRetrievedMessageTemplateIsAStringEvenIfNotSet() + { + self::assertSame( + '', + (new ConstraintViolation( + 'irrelevant', + null, + [], + 'irrelevant', + 'irrelevant', + null + ))->getMessageTemplate() + ); + } } From 9a4a04664f74971c059b2600247ac88188e54380 Mon Sep 17 00:00:00 2001 From: Gunnstein Lye Date: Wed, 10 Mar 2021 11:29:51 +0000 Subject: [PATCH 09/72] Update translations for Norwegian Nynorsk (nn) #38756 --- .../Resources/translations/validators.nn.xlf | 122 +++++++++++++++++- .../Resources/translations/security.nn.xlf | 12 +- .../Resources/translations/validators.nn.xlf | 58 +++++---- 3 files changed, 162 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Form/Resources/translations/validators.nn.xlf b/src/Symfony/Component/Form/Resources/translations/validators.nn.xlf index 2f5da23444d0b..9fac1bf34e34f 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.nn.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.nn.xlf @@ -4,7 +4,7 @@ This form should not contain extra fields. - Feltgruppa må ikkje innehalde ekstra felt. + Feltgruppa kan ikkje innehalde ekstra felt. The uploaded file was too large. Please try to upload a smaller file. @@ -14,6 +14,126 @@ The CSRF token is invalid. CSRF-nøkkelen er ikkje gyldig. + + This value is not a valid HTML5 color. + Verdien er ikkje ein gyldig HTML5-farge. + + + Please enter a valid birthdate. + Gje opp ein gyldig fødselsdato. + + + The selected choice is invalid. + Valget du gjorde er ikkje gyldig. + + + The collection is invalid. + Samlinga er ikkje gyldig. + + + Please select a valid color. + Gje opp ein gyldig farge. + + + Please select a valid country. + Gje opp eit gyldig land. + + + Please select a valid currency. + Gje opp ein gyldig valuta. + + + Please choose a valid date interval. + Gje opp eit gyldig datointervall. + + + Please enter a valid date and time. + Gje opp ein gyldig dato og tid. + + + Please enter a valid date. + Gje opp ein gyldig dato. + + + Please select a valid file. + Velg ei gyldig fil. + + + The hidden field is invalid. + Det skjulte feltet er ikkje gyldig. + + + Please enter an integer. + Gje opp eit heiltal. + + + Please select a valid language. + Gje opp eit gyldig språk. + + + Please select a valid locale. + Gje opp eit gyldig locale. + + + Please enter a valid money amount. + Gje opp ein gyldig sum pengar. + + + Please enter a number. + Gje opp eit nummer. + + + The password is invalid. + Passordet er ikkje gyldig. + + + Please enter a percentage value. + Gje opp ein prosentverdi. + + + The values do not match. + Verdiane er ikkje eins. + + + Please enter a valid time. + Gje opp ei gyldig tid. + + + Please select a valid timezone. + Gje opp ei gyldig tidssone. + + + Please enter a valid URL. + Gje opp ein gyldig URL. + + + Please enter a valid search term. + Gje opp gyldige søkjeord. + + + Please provide a valid phone number. + Gje opp eit gyldig telefonnummer. + + + The checkbox has an invalid value. + Sjekkboksen har ein ugyldig verdi. + + + Please enter a valid email address. + Gje opp ei gyldig e-postadresse. + + + Please select a valid option. + Velg eit gyldig vilkår. + + + Please select a valid range. + Velg eit gyldig spenn. + + + Please enter a valid week. + Gje opp ei gyldig veke. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf index 27b55e5675cac..89ca44fa88f26 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf @@ -36,7 +36,7 @@ No session available, it either timed out or cookies are not enabled. - Ingen sesjon tilgjengeleg. Sesjonen er anten ikkje lenger gyldig, eller informasjonskapslar er ikke skrudd på i nettlesaren. + Ingen sesjon tilgjengeleg. Sesjonen er anten ikkje lenger gyldig, eller informasjonskapslar er ikkje skrudd på i nettlesaren. No token could be found. @@ -56,12 +56,20 @@ Account is disabled. - Brukarkontoen er deaktivert. + Brukarkontoen er sperra. Account is locked. Brukarkontoen er sperra. + + Too many failed login attempts, please try again later. + For mange innloggingsforsøk har feila, prøv igjen seinare. + + + Invalid or expired login link. + Innloggingslenka er ugyldig eller utgjengen. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf index db804d3b68eed..8963ba2d8c2c4 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf @@ -36,7 +36,7 @@ This field was not expected. - Dette feltet var ikke forventa. + Dette feltet var ikkje forventa. This field is missing. @@ -248,7 +248,7 @@ This value should be equal to {{ compared_value }}. - Verdien bør vera like med {{ compared_value }}. + Verdien bør vera eins med {{ compared_value }}. This value should be greater than {{ compared_value }}. @@ -256,11 +256,11 @@ This value should be greater than or equal to {{ compared_value }}. - Verdien bør vera større enn eller så med {{ compared_value }}. + Verdien bør vera større enn eller eins med {{ compared_value }}. This value should be identical to {{ compared_value_type }} {{ compared_value }}. - Verdien bør vera identisk med {{ compared_value_type }} {{ compared_value }}. + Verdien bør vera eins med {{ compared_value_type }} {{ compared_value }}. This value should be less than {{ compared_value }}. @@ -268,47 +268,47 @@ This value should be less than or equal to {{ compared_value }}. - Verdi bør vera mindre enn eller så med {{ compared_value }}. + Verdi bør vera mindre enn eller eins med {{ compared_value }}. This value should not be equal to {{ compared_value }}. - Verdi bør ikkje vera så med {{ compared_value }}. + Verdi bør ikkje vera eins med {{ compared_value }}. This value should not be identical to {{ compared_value_type }} {{ compared_value }}. - Dette verdi bør ikkje vera identisk med {{ compared_value_type }} {{ compared_value }}. + Denne verdien bør ikkje vera eins med {{ compared_value_type }} {{ compared_value }}. The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - Bildetilhøvet er for stort ({{ ratio }}). Det tillatne maksimale tilhøvet er {{ max_ratio }}. + Sideforholdet til biletet er for stort ({{ ratio }}). Sideforholdet kan ikkje vere større enn {{ max_ratio }}. The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - Bildetilhøvet er for lite ({{ ratio }}). Forventa minimikvot er {{ min_ratio }}. + Sideforholdet til biletet er for lite ({{ ratio }}). Sideforholdet kan ikkje vere mindre enn {{ min_ratio }}. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. - Bildet er firkanta ({{ width }}x{{ height }}px). Fyrkantiga bilde er ikkje tillatne. + Biletet er kvadratisk ({{ width }}x{{ height }}px). Kvadratiske bilete er ikkje tillatne. The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. - Bildet er liggande orientert ({{ width }}x{{ height }}px). Landskapsorienterade bilde er ikkje tillatne. + Biletet er landskapsorientert ({{ width }}x{{ height }}px). Landskapsorienterte bilete er ikkje tillatne. The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. - Bildet er porträttorienterad ({{ width }}x{{ height }}px). Porträttorienterade bilde er ikkje tillatne. + Biletet er portrettorientert ({{ width }}x{{ height }}px). Portrettorienterte bilete er ikkje tillatne. An empty file is not allowed. - Ein tom fil er ikkje tillaten. + Ei tom fil er ikkje tillate. The host could not be resolved. - Verdiar kunne ikkje løysast. + Verten kunne ikkje finnast. This value does not match the expected {{ charset }} charset. - Verdi stemmer ikkje med forventa {{ charset }} charset. + Verdien stemmer ikkje med forventa {{ charset }} charset. This is not a valid Business Identifier Code (BIC). @@ -324,7 +324,7 @@ This value should be a multiple of {{ compared_value }}. - Verdi bør vera eit multipel av {{ compared_value }}. + Verdien bør vera eit multipel av {{ compared_value }}. This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. @@ -332,7 +332,7 @@ This value should be valid JSON. - Verdi bør vera gyldig JSON. + Verdien bør vera gyldig JSON. This collection should contain only unique elements. @@ -340,31 +340,31 @@ This value should be positive. - Verdi bør vera positivt. + Verdien bør vera positiv. This value should be either positive or zero. - Verdi bør vera enten positivt eller noll. + Verdien bør vera anten positiv eller null. This value should be negative. - Verdi bør vera negativt. + Verdien bør vera negativ. This value should be either negative or zero. - Verdi bør vera negativt eller noll. + Verdien bør vera negativ eller null. This value is not a valid timezone. - Verdi er ikkje ei gyldig tidssone. + Verdien er ikkje ei gyldig tidssone. This password has been leaked in a data breach, it must not be used. Please use another password. - Det her passordet har lekt ut ved eit datainnbrot, det får ikkje nyttast. Nytt eit anna passord. + Dette passordet har lekt ut ved eit datainnbrot, det får ikkje nyttast. Gje opp eit anna passord. This value should be between {{ min }} and {{ max }}. - Dette verdi bør ligga mellom {{ min }} og {{ max }}. + Denne verdien bør liggje mellom {{ min }} og {{ max }}. This value is not a valid hostname. @@ -372,15 +372,19 @@ The number of elements in this collection should be a multiple of {{ compared_value }}. - Talet på element i denne samlinga bør vera eit multipel av {{ compared_value }}. + Talet på element i denne samlinga bør vera eit multippel av {{ compared_value }}. This value should satisfy at least one of the following constraints: - Verdien burde oppfylla minst ein av følgjande begränsningar: + Verdien burde oppfylla minst ein av følgjande avgrensingar: Each element of this collection should satisfy its own set of constraints. - Kvart element i denne samlinga bør oppfylla sine eigne begränsningar. + Kvart element i denne samlinga bør oppfylla sine eigne avgrensingar. + + + This value is not a valid International Securities Identification Number (ISIN). + Verdien er ikkje eit gyldig International Securities Identification Number (ISIN). From 74c3c5fcb3b41a28be878ee4c51a67dbc738e93e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 10 Mar 2021 18:11:15 +0100 Subject: [PATCH 10/72] Bump Symfony version to 5.2.6 --- 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 24573ec5aba7a..6f35643eb3e8c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -74,12 +74,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '5.2.5'; - public const VERSION_ID = 50205; + public const VERSION = '5.2.6-DEV'; + public const VERSION_ID = 50206; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 2; - public const RELEASE_VERSION = 5; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 6; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2021'; public const END_OF_LIFE = '07/2021'; From ef59c89dea7e21cea820d61a81d4f25550f1874d Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Tue, 9 Mar 2021 15:19:01 +0100 Subject: [PATCH 11/72] [DependencyInjection] Fix return type --- src/Symfony/Contracts/Service/ServiceSubscriberInterface.php | 2 +- src/Symfony/Contracts/Service/ServiceSubscriberTrait.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberInterface.php b/src/Symfony/Contracts/Service/ServiceSubscriberInterface.php index 8bb320f5b32d8..098ab908cdfc7 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberInterface.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberInterface.php @@ -47,7 +47,7 @@ interface ServiceSubscriberInterface * * ['?Psr\Log\LoggerInterface'] is a shortcut for * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] * - * @return array The required service types, optionally keyed by service names + * @return string[] The required service types, optionally keyed by service names */ public static function getSubscribedServices(); } diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php index 82fb5ab361559..81b2bae8a4499 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php @@ -24,6 +24,9 @@ trait ServiceSubscriberTrait /** @var ContainerInterface */ protected $container; + /** + * {@inheritdoc} + */ public static function getSubscribedServices(): array { static $services; From c3e30ebda24d4118dcf8ed931b0f45f76ae1b40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 11 Mar 2021 14:53:29 +0100 Subject: [PATCH 12/72] Fix fingerprint when context is not serializable --- src/Symfony/Bridge/Twig/Mime/BodyRenderer.php | 19 ++++++++++++++++- .../Twig/Tests/Mime/BodyRendererTest.php | 21 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php index dfd348937dff9..166b3c195ff17 100644 --- a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php +++ b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php @@ -49,7 +49,7 @@ public function render(Message $message): void $previousRenderingKey = $messageContext[__CLASS__] ?? null; unset($messageContext[__CLASS__]); - $currentRenderingKey = md5(serialize([$messageContext, $message->getTextTemplate(), $message->getHtmlTemplate()])); + $currentRenderingKey = $this->getFingerPrint($message); if ($previousRenderingKey === $currentRenderingKey) { return; } @@ -77,6 +77,23 @@ public function render(Message $message): void $message->context($message->getContext() + [__CLASS__ => $currentRenderingKey]); } + private function getFingerPrint(TemplatedEmail $message): string + { + $messageContext = $message->getContext(); + unset($messageContext[__CLASS__]); + + $payload = [$messageContext, $message->getTextTemplate(), $message->getHtmlTemplate()]; + try { + $serialized = serialize($payload); + } catch (\Exception $e) { + // Serialization of 'Closure' is not allowed + // Happens when context contain a closure, in that case, we assume that context always change. + $serialized = random_bytes(8); + } + + return md5($serialized); + } + private function convertHtmlToText(string $html): string { if (null !== $this->converter) { diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php index f13ab213c3c4f..8ff343b684b5e 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php @@ -100,6 +100,27 @@ public function testRenderedOnce() $this->assertEquals('reset', $email->getTextBody()); } + public function testRenderedOnceUnserializableContext() + { + $twig = new Environment(new ArrayLoader([ + 'text' => 'Text', + ])); + $renderer = new BodyRenderer($twig); + $email = (new TemplatedEmail()) + ->to('fabien@symfony.com') + ->from('helene@symfony.com') + ; + $email->textTemplate('text'); + $email->context([ + 'foo' => static function () { + return 'bar'; + }, + ]); + + $renderer->render($email); + $this->assertEquals('Text', $email->getTextBody()); + } + private function prepareEmail(?string $text, ?string $html, array $context = []): TemplatedEmail { $twig = new Environment(new ArrayLoader([ From 3a2d1b4a0a38ae838ed262deb6aaa9b96ba8ec96 Mon Sep 17 00:00:00 2001 From: Oliver Klee Date: Tue, 9 Mar 2021 17:59:23 +0100 Subject: [PATCH 13/72] bug #40427 [Console] Stop accepting ints as InputOption defaults The types accepted and provided by `InputInterface::getOption` and `setOption` do not include `int` (and passing something like `--option=123` will set the option to the string `"123"`, not to the integer `123`). The `InputOption` default types should match this. --- src/Symfony/Component/Console/Command/Command.php | 10 +++++----- .../Component/Console/Input/InputOption.php | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 71ad4a49e9e3e..d4ab2eb8df3da 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -394,11 +394,11 @@ public function addArgument($name, $mode = null, $description = '', $default = n /** * Adds an option. * - * @param string $name The option name - * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants - * @param string $description A description text - * @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE) + * @param string $name The option name + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param string|string[]|bool|null $default The default value (must be null for InputOption::VALUE_NONE) * * @throws InvalidArgumentException If option mode is invalid or incompatible * diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index 66f857a6c0d3b..a8e956db55b19 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -33,11 +33,11 @@ class InputOption private $description; /** - * @param string $name The option name - * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the VALUE_* constants - * @param string $description A description text - * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE) + * @param string $name The option name + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param string|string[]|bool|null $default The default value (must be null for self::VALUE_NONE) * * @throws InvalidArgumentException If option mode is invalid or incompatible */ @@ -149,7 +149,7 @@ public function isArray() /** * Sets the default value. * - * @param string|string[]|int|bool|null $default The default value + * @param string|string[]|bool|null $default The default value * * @throws LogicException When incorrect default value is given */ @@ -173,7 +173,7 @@ public function setDefault($default = null) /** * Returns the default value. * - * @return string|string[]|int|bool|null The default value + * @return string|string[]|bool|null The default value */ public function getDefault() { From 42453454c9b0d08939e5d8f0c142a6eef74d27b4 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Tue, 26 Jan 2021 22:12:06 -0600 Subject: [PATCH 14/72] Refresh original user in SwitchUserListener. Fixes #39991 --- .../Http/Firewall/SwitchUserListener.php | 1 + .../Tests/Firewall/SwitchUserListenerTest.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 8577d5c734629..5962bf856a6d5 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -217,6 +217,7 @@ private function attemptExitUser(Request $request): TokenInterface if (null !== $this->dispatcher && $original->getUser() instanceof UserInterface) { $user = $this->provider->refreshUser($original->getUser()); + $original->setUser($user); $switchEvent = new SwitchUserEvent($request, $user, $original); $this->dispatcher->dispatch($switchEvent, SecurityEvents::SWITCH_USER); $original = $switchEvent->getToken(); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 18e5c415d5ec4..8bc78df64a762 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -413,4 +413,34 @@ public function testSwitchUserStateless() $this->assertInstanceOf(UsernamePasswordToken::class, $this->tokenStorage->getToken()); $this->assertFalse($this->event->hasResponse()); } + + public function testSwitchUserRefreshesOriginalToken() + { + $originalUser = $this->createMock(UserInterface::class); + $refreshedOriginalUser = $this->createMock(UserInterface::class); + $this + ->userProvider + ->expects($this->any()) + ->method('refreshUser') + ->with($originalUser) + ->willReturn($refreshedOriginalUser); + $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); + $this->tokenStorage->setToken(new SwitchUserToken('username', '', 'key', ['ROLE_USER'], $originalToken)); + $this->request->query->set('_switch_user', SwitchUserListener::EXIT_VALUE); + + $dispatcher = $this->createMock(EventDispatcherInterface::class); + $dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with( + $this->callback(function (SwitchUserEvent $event) use ($refreshedOriginalUser) { + return $event->getToken()->getUser() === $refreshedOriginalUser; + }), + SecurityEvents::SWITCH_USER + ) + ; + + $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener($this->event); + } } From e9f2ef211e2247d4fc9dc6812b2f978ea649cede Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 12 Mar 2021 12:15:54 +0100 Subject: [PATCH 15/72] [Mailer] fix lowest allowed dependencies --- src/Symfony/Component/Mailer/Bridge/Amazon/composer.json | 2 +- src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json | 2 +- src/Symfony/Component/Mailer/Bridge/Postmark/composer.json | 2 +- src/Symfony/Component/Mailer/composer.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json index 5108ba33a92f1..3f66c4172ab71 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=7.1.3", - "symfony/mailer": "^4.4|^5.0" + "symfony/mailer": "^4.4.21|^5.2.6" }, "require-dev": { "symfony/http-client": "^4.3|^5.0" diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json index ac4051d7782f1..606d00281682f 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=7.1.3", - "symfony/mailer": "^4.4|^5.0" + "symfony/mailer": "^4.4.21|^5.2.6" }, "require-dev": { "symfony/http-client": "^4.3|^5.0" diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json index 2efbe5c3381ae..e70d4df085295 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=7.1.3", - "symfony/mailer": "^4.4|^5.0" + "symfony/mailer": "^4.4.21|^5.2.6" }, "require-dev": { "symfony/http-client": "^4.3|^5.0" diff --git a/src/Symfony/Component/Mailer/composer.json b/src/Symfony/Component/Mailer/composer.json index 915bac612143b..f8c416649d68f 100644 --- a/src/Symfony/Component/Mailer/composer.json +++ b/src/Symfony/Component/Mailer/composer.json @@ -20,7 +20,7 @@ "egulias/email-validator": "^2.1.10|^3", "psr/log": "~1.0", "symfony/event-dispatcher": "^4.3", - "symfony/mime": "^4.4|^5.0", + "symfony/mime": "^4.4.21|^5.2.6", "symfony/service-contracts": "^1.1|^2" }, "require-dev": { From 088fbf7948bea5731b67deb6b606ef2ea8ac51ac Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 12 Mar 2021 14:28:30 +0100 Subject: [PATCH 16/72] fix test --- .../Amazon/Tests/Transport/SesApiAsyncAwsTransportTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiAsyncAwsTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiAsyncAwsTransportTest.php index 52a7c29f41cb7..5de2bb0199e3e 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiAsyncAwsTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesApiAsyncAwsTransportTest.php @@ -64,8 +64,8 @@ public function testSend() $content = json_decode($options['body'], true); $this->assertSame('Hello!', $content['Content']['Simple']['Subject']['Data']); - $this->assertSame('Saif Eddin ', $content['Destination']['ToAddresses'][0]); - $this->assertSame('Fabien ', $content['FromEmailAddress']); + $this->assertSame('"Saif Eddin" ', $content['Destination']['ToAddresses'][0]); + $this->assertSame('"Fabien" ', $content['FromEmailAddress']); $this->assertSame('Hello There!', $content['Content']['Simple']['Body']['Text']['Data']); $this->assertSame('Hello There!', $content['Content']['Simple']['Body']['Html']['Data']); $this->assertSame(['replyto-1@example.com', 'replyto-2@example.com'], $content['ReplyToAddresses']); From 76ee9baf8eb5b457b538d22a0839ba036bc6b409 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 14 Mar 2021 13:58:25 +0100 Subject: [PATCH 17/72] [Routing] Remove unnecessary references to User class in test fixtures --- .../AttributesClassParamAfterCommaController.php | 3 +-- .../AttributesClassParamAfterParenthesisController.php | 3 +-- .../AttributesClassParamInlineAfterCommaController.php | 3 +-- .../AttributesClassParamInlineAfterParenthesisController.php | 3 +-- .../AttributesClassParamInlineQuotedAfterCommaController.php | 1 - ...ributesClassParamInlineQuotedAfterParenthesisController.php | 3 +-- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php index 3fdf55f30c373..6ca5aeec66772 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php @@ -3,14 +3,13 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; #[FooAttributes( foo: [ 'bar' => ['foo','bar'], 'foo' ], - class: User::class + class: \stdClass::class )] class AttributesClassParamAfterCommaController { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php index 06edcabf6d278..92a6759af65a9 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php @@ -3,10 +3,9 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; #[FooAttributes( - class: User::class, + class: \stdClass::class, foo: [ 'bar' => ['foo','bar'], 'foo' diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php index 4b47967f139a3..fc07e916d0c74 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php @@ -3,9 +3,8 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; -#[FooAttributes(foo: ['bar' => ['foo','bar'],'foo'],class: User::class)] +#[FooAttributes(foo: ['bar' => ['foo','bar'],'foo'],class: \stdClass::class)] class AttributesClassParamInlineAfterCommaController { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php index 876cdc33f73bc..13f2592edcbcf 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php @@ -3,9 +3,8 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; -#[FooAttributes(class: User::class,foo: ['bar' => ['foo','bar'],'foo'])] +#[FooAttributes(class: \stdClass::class,foo: ['bar' => ['foo','bar'],'foo'])] class AttributesClassParamInlineAfterParenthesisController { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php index 471efd305aa62..3bca2bc9669d0 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php @@ -3,7 +3,6 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; #[FooAttributes(foo: ['bar' => ['foo','bar'],'foo'],class: 'Symfony\Component\Security\Core\User\User')] class AttributesClassParamInlineQuotedAfterCommaController diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php index dc0ea7a82096a..31edf3cef98d6 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php @@ -3,9 +3,8 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures; use Symfony\Component\Routing\Tests\Fixtures\Attributes\FooAttributes; -use Symfony\Component\Security\Core\User\User; -#[FooAttributes(class: 'Symfony\Component\Security\Core\User\User',foo: ['bar' => ['foo','bar'],'foo'])] +#[FooAttributes(class: \stdClass::class,foo: ['bar' => ['foo','bar'],'foo'])] class AttributesClassParamInlineQuotedAfterParenthesisController { From b3759c2882007835dccc3696e4e96c0a295c0d42 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 14 Mar 2021 20:28:18 +0100 Subject: [PATCH 18/72] [Cache] Fix wrong namespace in test --- .../Cache/Tests/DataCollector/CacheDataCollectorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php index f704bbfe0e49f..6aa94c2c63383 100644 --- a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php +++ b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Tests\Marshaller; +namespace Symfony\Component\Cache\Tests\DataCollector; use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\TraceableAdapter; From c620f407babc1ba15a4daa851696a5b9724d70da Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 14 Mar 2021 20:33:15 +0100 Subject: [PATCH 19/72] [RateLimiter] Fix wrong namespace in tests --- .../Tests/{Strategy => Policy}/FixedWindowLimiterTest.php | 0 .../Tests/{Strategy => Policy}/SlidingWindowLimiterTest.php | 0 .../RateLimiter/Tests/{Strategy => Policy}/SlidingWindowTest.php | 0 .../Tests/{Strategy => Policy}/TokenBucketLimiterTest.php | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/Symfony/Component/RateLimiter/Tests/{Strategy => Policy}/FixedWindowLimiterTest.php (100%) rename src/Symfony/Component/RateLimiter/Tests/{Strategy => Policy}/SlidingWindowLimiterTest.php (100%) rename src/Symfony/Component/RateLimiter/Tests/{Strategy => Policy}/SlidingWindowTest.php (100%) rename src/Symfony/Component/RateLimiter/Tests/{Strategy => Policy}/TokenBucketLimiterTest.php (100%) diff --git a/src/Symfony/Component/RateLimiter/Tests/Strategy/FixedWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/FixedWindowLimiterTest.php similarity index 100% rename from src/Symfony/Component/RateLimiter/Tests/Strategy/FixedWindowLimiterTest.php rename to src/Symfony/Component/RateLimiter/Tests/Policy/FixedWindowLimiterTest.php diff --git a/src/Symfony/Component/RateLimiter/Tests/Strategy/SlidingWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php similarity index 100% rename from src/Symfony/Component/RateLimiter/Tests/Strategy/SlidingWindowLimiterTest.php rename to src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php diff --git a/src/Symfony/Component/RateLimiter/Tests/Strategy/SlidingWindowTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php similarity index 100% rename from src/Symfony/Component/RateLimiter/Tests/Strategy/SlidingWindowTest.php rename to src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php diff --git a/src/Symfony/Component/RateLimiter/Tests/Strategy/TokenBucketLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/TokenBucketLimiterTest.php similarity index 100% rename from src/Symfony/Component/RateLimiter/Tests/Strategy/TokenBucketLimiterTest.php rename to src/Symfony/Component/RateLimiter/Tests/Policy/TokenBucketLimiterTest.php From e2dffea49db454bebb309acfdf085c4ce9611db1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 15 Mar 2021 13:52:35 +0100 Subject: [PATCH 20/72] [Translation] fix test case name --- .../Tests/DependencyInjection/FrameworkExtensionTest.php | 2 +- .../{TranslationPassTest.php => TranslatorPassTest.php} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/Symfony/Component/Translation/Tests/DependencyInjection/{TranslationPassTest.php => TranslatorPassTest.php} (95%) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index db26edcfedebf..5e11db81026f4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1723,7 +1723,7 @@ protected function createContainerFromFile($file, $data = [], $resetCompilerPass $container->getCompilerPassConfig()->setAfterRemovingPasses([]); } $container->getCompilerPassConfig()->setBeforeOptimizationPasses([new LoggerPass()]); - $container->getCompilerPassConfig()->setBeforeRemovingPasses([new AddConstraintValidatorsPass(), new TranslatorPass('translator.default', 'translation.reader')]); + $container->getCompilerPassConfig()->setBeforeRemovingPasses([new AddConstraintValidatorsPass(), new TranslatorPass()]); $container->getCompilerPassConfig()->setAfterRemovingPasses([new AddAnnotationsCachedReaderPass()]); if (!$compile) { diff --git a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php similarity index 95% rename from src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php rename to src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php index f62fc85ebc97a..c0065e92fb859 100644 --- a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php +++ b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php @@ -18,7 +18,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Translation\DependencyInjection\TranslatorPass; -class TranslationPassTest extends TestCase +class TranslatorPassTest extends TestCase { public function testValidCollector() { @@ -35,7 +35,7 @@ public function testValidCollector() $container->setDefinition('translation.reader', $reader); $container->setDefinition('translation.xliff_loader', $loader); - $pass = new TranslatorPass('translator.default', 'translation.reader'); + $pass = new TranslatorPass(); $pass->process($container); $expectedReader = (new Definition()) @@ -72,7 +72,7 @@ public function testValidCommandsViewPathsArgument() ; $container->setParameter('twig.default_path', 'templates'); - $pass = new TranslatorPass('translator.default'); + $pass = new TranslatorPass(); $pass->process($container); $expectedViewPaths = ['other/templates', 'tpl']; @@ -113,7 +113,7 @@ public function testCommandsViewPathsArgumentsAreIgnoredWithOldServiceDefinition ; $container->setParameter('twig.default_path', 'templates'); - $pass = new TranslatorPass('translator.default'); + $pass = new TranslatorPass(); $pass->process($container); $this->assertSame('templates', $debugCommand->getArgument(4)); From f0cac47ae790cd880057e77be37d7cbedbd6a81c Mon Sep 17 00:00:00 2001 From: bezin Date: Mon, 15 Mar 2021 16:12:10 +0100 Subject: [PATCH 21/72] Be explicit about transparent background color of links in toolbar --- .../WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index d7508ec1448de..126bdd7458243 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -100,6 +100,7 @@ .sf-toolbar-block > a:hover { display: block; text-decoration: none; + background-color: transparent; color: inherit; } @@ -238,6 +239,7 @@ div.sf-toolbar .sf-toolbar-block a:hover { padding: 0 10px; } .sf-toolbar-block-request .sf-toolbar-info-piece a { + background-color: transparent; text-decoration: none; } .sf-toolbar-block-request .sf-toolbar-info-piece a:hover { From 7904d0896b601eb59aa86f17fe996c26fe23fc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 16 Mar 2021 08:45:57 +0100 Subject: [PATCH 22/72] Make async-ses required --- src/Symfony/Component/Mailer/Bridge/Amazon/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json index 7ca32dda10bd7..5d2ea6bcb5cdb 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=7.2.5", + "async-aws/ses": "^1.0", "symfony/deprecation-contracts": "^2.1", "symfony/mailer": "^4.4.21|^5.2.6" }, "require-dev": { - "async-aws/ses": "^1.0", "symfony/http-client": "^4.4|^5.0" }, "autoload": { From e73db2b1f1acaa1a9c2575bd5c2d646c4f3aeb6e Mon Sep 17 00:00:00 2001 From: Oviglo Date: Mon, 15 Mar 2021 14:29:15 +0100 Subject: [PATCH 23/72] [Bridge\Twig] Add 'form-control-range' for range input type --- .../views/Form/bootstrap_4_layout.html.twig | 20 +++++++---- .../AbstractBootstrap4LayoutTest.php | 36 +++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 8ac32978a0925..7db2a0dadd3e3 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -132,9 +132,15 @@ {% endblock %} {% block form_widget_simple -%} - {% if type is not defined or type != 'hidden' %} - {%- set attr = attr|merge({class: (attr.class|default('') ~ (type|default('') == 'file' ? ' custom-file-input' : ' form-control'))|trim}) -%} - {% endif %} + {%- if type is not defined or type != 'hidden' -%} + {%- set className = ' form-control' -%} + {%- if type|default('') == 'file' -%} + {%- set className = ' custom-file-input' -%} + {%- elseif type|default('') == 'range' -%} + {%- set className = ' form-control-range' -%} + {%- endif -%} + {%- set attr = attr|merge({class: (attr.class|default('') ~ className)|trim}) -%} + {%- endif -%} {%- if type is defined and (type == 'range' or type == 'color') %} {# Attribute "required" is not supported #} {%- set required = false -%} @@ -142,12 +148,12 @@ {{- parent() -}} {%- endblock form_widget_simple %} -{%- block widget_attributes -%} - {%- if not valid %} +{% block widget_attributes -%} + {%- if not valid -%} {% set attr = attr|merge({class: (attr.class|default('') ~ ' is-invalid')|trim}) %} - {% endif -%} + {%- endif -%} {{ parent() }} -{%- endblock widget_attributes -%} +{%- endblock widget_attributes %} {% block button_widget -%} {%- set attr = attr|merge({class: (attr.class|default('btn-secondary') ~ ' btn')|trim}) -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php index 2643274d47c3e..834de919edbb7 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\PercentType; use Symfony\Component\Form\Extension\Core\Type\RadioType; +use Symfony\Component\Form\Extension\Core\Type\RangeType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormError; @@ -1194,6 +1195,41 @@ public function testPercentCustomSymbol() [contains(.., "‱")] ] ] +' + ); + } + + public function testRange() + { + $form = $this->factory->createNamed('name', RangeType::class, 42, ['attr' => ['min' => 5]]); + + $this->assertWidgetMatchesXpath( + $form->createView(), + ['attr' => ['class' => 'my&class']], +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@class="my&class form-control-range"] +' + ); + } + + public function testRangeWithMinMaxValues() + { + $form = $this->factory->createNamed('name', RangeType::class, 42, ['attr' => ['min' => 5, 'max' => 57]]); + + $this->assertWidgetMatchesXpath( + $form->createView(), + ['attr' => ['class' => 'my&class']], +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@max="57"] + [@class="my&class form-control-range"] ' ); } From 25f503a8137a133096e91e851d176d01f4d53488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Jusi=C4=99ga?= Date: Sat, 13 Feb 2021 17:29:18 +0100 Subject: [PATCH 24/72] [FrameworkBundle] Exclude unreadable files when executing About command --- .../FrameworkBundle/Command/AboutCommand.php | 4 +- .../Command/AboutCommand/AboutCommandTest.php | 90 +++++++++++++++++++ .../AboutCommand/Fixture/TestAppKernel.php | 40 +++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/AboutCommandTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/Fixture/TestAppKernel.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index b6e5c74a798bf..36b3ae1f4a0d9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -116,7 +116,9 @@ private static function formatFileSize(string $path): string } else { $size = 0; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) { - $size += $file->getSize(); + if ($file->isReadable()) { + $size += $file->getSize(); + } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/AboutCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/AboutCommandTest.php new file mode 100644 index 0000000000000..8a1fcf93caabd --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/AboutCommandTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command\AboutCommand; + +use Symfony\Bundle\FrameworkBundle\Command\AboutCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\Command\AboutCommand\Fixture\TestAppKernel; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +class AboutCommandTest extends TestCase +{ + /** @var Filesystem */ + private $fs; + + protected function setUp(): void + { + $this->fs = new Filesystem(); + } + + public function testAboutWithReadableFiles() + { + $kernel = new TestAppKernel('test', true); + $this->fs->mkdir($kernel->getProjectDir()); + + $this->fs->dumpFile($kernel->getCacheDir().'/readable_file', 'The file content.'); + $this->fs->chmod($kernel->getCacheDir().'/readable_file', 0777); + + $tester = $this->createCommandTester($kernel); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret); + $this->assertStringContainsString('Cache directory', $tester->getDisplay()); + $this->assertStringContainsString('Log directory', $tester->getDisplay()); + + $this->fs->chmod($kernel->getCacheDir().'/readable_file', 0777); + + try { + $this->fs->remove($kernel->getProjectDir()); + } catch (IOException $e) { + } + } + + public function testAboutWithUnreadableFiles() + { + $kernel = new TestAppKernel('test', true); + $this->fs->mkdir($kernel->getProjectDir()); + + // skip test on Windows; PHP can't easily set file as unreadable on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test cannot run on Windows.'); + } + + $this->fs->dumpFile($kernel->getCacheDir().'/unreadable_file', 'The file content.'); + $this->fs->chmod($kernel->getCacheDir().'/unreadable_file', 0222); + + $tester = $this->createCommandTester($kernel); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret); + $this->assertStringContainsString('Cache directory', $tester->getDisplay()); + $this->assertStringContainsString('Log directory', $tester->getDisplay()); + + $this->fs->chmod($kernel->getCacheDir().'/unreadable_file', 0777); + + try { + $this->fs->remove($kernel->getProjectDir()); + } catch (IOException $e) { + } + } + + private function createCommandTester(TestAppKernel $kernel): CommandTester + { + $application = new Application($kernel); + $application->add(new AboutCommand()); + + return new CommandTester($application->find('about')); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/Fixture/TestAppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/Fixture/TestAppKernel.php new file mode 100644 index 0000000000000..c15bf83cb1cf8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/AboutCommand/Fixture/TestAppKernel.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command\AboutCommand\Fixture; + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel; + +class TestAppKernel extends Kernel +{ + public function registerBundles(): iterable + { + return [ + new FrameworkBundle(), + ]; + } + + public function getProjectDir(): string + { + return __DIR__.'/test'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + protected function build(ContainerBuilder $container) + { + } +} From 2aa3df0c74a79db531dcccaa577df301481b33b9 Mon Sep 17 00:00:00 2001 From: Dane Powell Date: Thu, 11 Mar 2021 12:15:07 -0800 Subject: [PATCH 25/72] [Console] ProgressBar clears too many lines on update --- .../Component/Console/Helper/ProgressBar.php | 2 +- .../Console/Tests/Helper/ProgressBarTest.php | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index 4690cffbdc92c..824c0f2963e5c 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -441,7 +441,7 @@ private function overwrite(string $message): void if ($this->overwrite) { if (null !== $this->previousMessage) { if ($this->output instanceof ConsoleSectionOutput) { - $lines = floor(Helper::strlen($message) / $this->terminal->getWidth()) + $this->formatLineCount + 1; + $lines = floor(Helper::strlenWithoutDecoration($this->output->getFormatter(), $message) / $this->terminal->getWidth()) + $this->formatLineCount + 1; $this->output->clear($lines); } else { // Erase previous lines diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index d927f4dfccb54..aabadef2cd2e2 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -343,6 +343,31 @@ public function testOverwriteWithSectionOutput() ); } + public function testOverwriteWithAnsiSectionOutput() + { + // output has 43 visible characters plus 2 invisible ANSI characters + putenv('COLUMNS=43'); + $sections = []; + $stream = $this->getOutputStream(true); + $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter()); + + $bar = new ProgressBar($output, 50, 0); + $bar->setFormat(" \033[44;37m%current%/%max%\033[0m [%bar%] %percent:3s%%"); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertSame( + " \033[44;37m 0/50\033[0m [>---------------------------] 0%".\PHP_EOL. + "\x1b[1A\x1b[0J"." \033[44;37m 1/50\033[0m [>---------------------------] 2%".\PHP_EOL. + "\x1b[1A\x1b[0J"." \033[44;37m 2/50\033[0m [=>--------------------------] 4%".\PHP_EOL, + stream_get_contents($output->getStream()) + ); + putenv('COLUMNS=120'); + } + public function testOverwriteMultipleProgressBarsWithSectionOutputs() { $sections = []; From a4958ae7ad9c8b066c089dc752ff50c847ce0bad Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Mar 2021 11:17:34 +0100 Subject: [PATCH 26/72] [FrameworkBundle] Make the TestBrowserToken interchangeable with other tokens --- src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php | 2 +- .../Bundle/FrameworkBundle/Test/TestBrowserToken.php | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php index 40381e34aa310..dff1eea251041 100644 --- a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php +++ b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php @@ -120,7 +120,7 @@ public function loginUser($user, string $firewallContext = 'main'): self throw new \LogicException(sprintf('The first argument of "%s" must be instance of "%s", "%s" provided.', __METHOD__, UserInterface::class, \is_object($user) ? \get_class($user) : \gettype($user))); } - $token = new TestBrowserToken($user->getRoles(), $user); + $token = new TestBrowserToken($user->getRoles(), $user, $firewallContext); $token->setAuthenticated(true); $session = $this->getContainer()->get('session'); $session->set('_security_'.$firewallContext, serialize($token)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php b/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php index 08f7b107d03a4..3343fbf0fb4cf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php @@ -21,13 +21,22 @@ */ class TestBrowserToken extends AbstractToken { - public function __construct(array $roles = [], UserInterface $user = null) + private $firewallName; + + public function __construct(array $roles = [], UserInterface $user = null, string $firewallName = 'main') { parent::__construct($roles); if (null !== $user) { $this->setUser($user); } + + $this->firewallName = $firewallName; + } + + public function getFirewallName(): string + { + return $this->firewallName; } public function getCredentials() From 9ad7832acd166aea2d918ea9f03d3d16778d2d96 Mon Sep 17 00:00:00 2001 From: stlrnz Date: Thu, 18 Feb 2021 15:27:58 +0100 Subject: [PATCH 27/72] [ErrorHandler] Fix error caused by `include` + open_basedir --- .../Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index 09643dcb70ab9..2850293096659 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -352,7 +352,7 @@ private function include(string $name, array $context = []): string extract($context, \EXTR_SKIP); ob_start(); - include file_exists($name) ? $name : __DIR__.'/../Resources/'.$name; + include is_file(\dirname(__DIR__).'/Resources/'.$name) ? \dirname(__DIR__).'/Resources/'.$name : $name; return trim(ob_get_clean()); } From 77fb0eb0a16eaaf7dcf469cf2d039249c12e1286 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Tue, 16 Mar 2021 14:36:31 +0100 Subject: [PATCH 28/72] [Security] Add XML support for authenticator manager --- .../Security/Factory/LoginLinkFactory.php | 6 +++- .../Resources/config/schema/security-1.0.xsd | 23 +++++++++++++ .../security_authenticator_login_link.php | 1 - .../CompleteConfigurationTest.php | 34 +++++++++++++++++++ .../Fixtures/php/authenticator_manager.php | 20 +++++++++++ .../Fixtures/xml/authenticator_manager.xml | 24 +++++++++++++ .../Fixtures/yml/authenticator_manager.yml | 13 +++++++ 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php index 3afddb3d35f3b..0b99f281e9a94 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php @@ -31,7 +31,7 @@ class LoginLinkFactory extends AbstractFactory implements AuthenticatorFactoryIn public function addConfiguration(NodeDefinition $node) { /** @var NodeBuilder $builder */ - $builder = $node->children(); + $builder = $node->fixXmlConfig('signature_property', 'signature_properties')->children(); $builder ->scalarNode('check_route') @@ -98,6 +98,10 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal if (null !== $config['max_uses'] && !isset($config['used_link_cache'])) { $config['used_link_cache'] = 'security.authenticator.cache.expired_links'; + $defaultCacheDefinition = $container->getDefinition($config['used_link_cache']); + if (!$defaultCacheDefinition->hasTag('cache.pool')) { + $defaultCacheDefinition->addTag('cache.pool'); + } } $expiredStorageId = null; diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index 8ff0d5e46da0d..509ac0b0534f7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -23,6 +23,7 @@ + @@ -141,6 +142,7 @@ + @@ -160,6 +162,7 @@ + @@ -231,6 +234,7 @@ + @@ -283,6 +287,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php index 2efb089262cfe..2248b5e8eeb7d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php @@ -51,7 +51,6 @@ ->set('security.authenticator.cache.expired_links') ->parent('cache.app') ->private() - ->tag('cache.pool') ->set('security.authenticator.firewall_aware_login_link_handler', FirewallAwareLoginLinkHandler::class) ->args([ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 072e33aca6f4d..47d3033a25582 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; +use Symfony\Component\Security\Http\Authentication\AuthenticatorManager; abstract class CompleteConfigurationTest extends TestCase { @@ -28,6 +29,38 @@ abstract protected function getLoader(ContainerBuilder $container); abstract protected function getFileExtension(); + public function testAuthenticatorManager() + { + $container = $this->getContainer('authenticator_manager'); + + $this->assertEquals(AuthenticatorManager::class, $container->getDefinition('security.authenticator.manager.main')->getClass()); + + // login link + $expiredStorage = $container->getDefinition($expiredStorageId = 'security.authenticator.expired_login_link_storage.main'); + $this->assertEquals('cache.redis', (string) $expiredStorage->getArgument(0)); + $this->assertEquals(3600, (string) $expiredStorage->getArgument(1)); + + $linker = $container->getDefinition($linkerId = 'security.authenticator.login_link_handler.main'); + $this->assertEquals(['id', 'email'], $linker->getArgument(3)); + $this->assertEquals([ + 'route_name' => 'login_check', + 'lifetime' => 3600, + 'max_uses' => 1, + ], $linker->getArgument(5)); + $this->assertEquals($expiredStorageId, (string) $linker->getArgument(6)); + + $authenticator = $container->getDefinition('security.authenticator.login_link.main'); + $this->assertEquals($linkerId, (string) $authenticator->getArgument(0)); + $this->assertEquals([ + 'check_route' => 'login_check', + 'check_post_only' => true, + ], $authenticator->getArgument(4)); + + // login throttling + $listener = $container->getDefinition('security.listener.login_throttling.main'); + $this->assertEquals('app.rate_limiter', (string) $listener->getArgument(1)); + } + public function testRolesHierarchy() { $container = $this->getContainer('container1'); @@ -648,6 +681,7 @@ protected function getContainer($file) $container->setParameter('kernel.debug', false); $container->setParameter('request_listener.http_port', 80); $container->setParameter('request_listener.https_port', 443); + $container->register('cache.app', \stdClass::class); $security = new SecurityExtension(); $container->registerExtension($security); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php new file mode 100644 index 0000000000000..31a37fe2103f9 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php @@ -0,0 +1,20 @@ +loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + 'firewalls' => [ + 'main' => [ + 'login_link' => [ + 'check_route' => 'login_check', + 'check_post_only' => true, + 'signature_properties' => ['id', 'email'], + 'max_uses' => 1, + 'lifetime' => 3600, + 'used_link_cache' => 'cache.redis', + ], + 'login_throttling' => [ + 'limiter' => 'app.rate_limiter', + ], + ], + ], +]); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml new file mode 100644 index 0000000000000..2a3b643a6e905 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml @@ -0,0 +1,24 @@ + + + + + + + id + email + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml new file mode 100644 index 0000000000000..8ff11698ae772 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml @@ -0,0 +1,13 @@ +security: + enable_authenticator_manager: true + firewalls: + main: + login_link: + check_route: login_check + check_post_only: true + signature_properties: [id, email] + max_uses: 1 + lifetime: 3600 + used_link_cache: 'cache.redis' + login_throttling: + limiter: 'app.rate_limiter' From 8ada55c07c44a399e48bba7f5f9289ebb934b3dd Mon Sep 17 00:00:00 2001 From: Matthew Grasmick Date: Tue, 16 Mar 2021 22:19:58 -0400 Subject: [PATCH 29/72] Correctly clear lines for multi-line progress bar messages. --- .../Component/Console/Helper/ProgressBar.php | 11 ++++++-- .../Console/Tests/Helper/ProgressBarTest.php | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index 824c0f2963e5c..5049c7dae0636 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -441,8 +441,15 @@ private function overwrite(string $message): void if ($this->overwrite) { if (null !== $this->previousMessage) { if ($this->output instanceof ConsoleSectionOutput) { - $lines = floor(Helper::strlenWithoutDecoration($this->output->getFormatter(), $message) / $this->terminal->getWidth()) + $this->formatLineCount + 1; - $this->output->clear($lines); + $messageLines = explode("\n", $message); + $lineCount = \count($messageLines); + foreach ($messageLines as $messageLine) { + $messageLineLength = Helper::strlenWithoutDecoration($this->output->getFormatter(), $messageLine); + if ($messageLineLength > $this->terminal->getWidth()) { + $lineCount += floor($messageLineLength / $this->terminal->getWidth()); + } + } + $this->output->clear($lineCount); } else { // Erase previous lines if ($this->formatLineCount > 0) { diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index aabadef2cd2e2..2d32e1c3d512a 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -397,6 +397,34 @@ public function testOverwriteMultipleProgressBarsWithSectionOutputs() ); } + public function testOverwriteWithSectionOutputWithNewlinesInMessage() + { + $sections = []; + $stream = $this->getOutputStream(true); + $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter()); + + ProgressBar::setFormatDefinition('test', '%current%/%max% [%bar%] %percent:3s%% %message% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'); + + $bar = new ProgressBar($output, 50, 0); + $bar->setFormat('test'); + $bar->start(); + $bar->display(); + $bar->setMessage("Twas brillig, and the slithy toves. Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe.\nBeware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!"); + $bar->advance(); + $bar->setMessage("He took his vorpal sword in hand; Long time the manxome foe he sought— So rested he by the Tumtum tree And stood awhile in thought.\nAnd, as in uffish thought he stood, The Jabberwock, with eyes of flame, Came whiffling through the tulgey wood, And burbled as it came!"); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>] 0% %message% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'.\PHP_EOL. + "\x1b[6A\x1b[0J 1/50 [>] 2% Twas brillig, and the slithy toves. Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. +Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch! Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.".\PHP_EOL. + "\x1b[6A\x1b[0J 2/50 [>] 4% He took his vorpal sword in hand; Long time the manxome foe he sought— So rested he by the Tumtum tree And stood awhile in thought. +And, as in uffish thought he stood, The Jabberwock, with eyes of flame, Came whiffling through the tulgey wood, And burbled as it came! Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.".\PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + public function testMultipleSectionsWithCustomFormat() { $sections = []; From 1a11491f6e25bef21d318900a8e107f917c5defd Mon Sep 17 00:00:00 2001 From: Michael Nelson Date: Fri, 12 Mar 2021 00:49:32 -0500 Subject: [PATCH 30/72] [VarDumper] Adds support for ReflectionUnionType to VarDumper --- .../VarDumper/Caster/ReflectionCaster.php | 21 +++- .../Tests/Caster/ReflectionCasterTest.php | 103 +++++++++++++++++- .../Fixtures/ExtendsReflectionTypeFixture.php | 21 ++++ .../Fixtures/ReflectionNamedTypeFixture.php | 8 ++ .../Fixtures/ReflectionUnionTypeFixture.php | 8 ++ 5 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Tests/Fixtures/ExtendsReflectionTypeFixture.php create mode 100644 src/Symfony/Component/VarDumper/Tests/Fixtures/ReflectionNamedTypeFixture.php create mode 100644 src/Symfony/Component/VarDumper/Tests/Fixtures/ReflectionUnionTypeFixture.php diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 8d5f428e51562..95c1dbf6ff1be 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -96,11 +96,20 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNes { $prefix = Caster::PREFIX_VIRTUAL; - $a += [ - $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, - $prefix.'allowsNull' => $c->allowsNull(), - $prefix.'isBuiltin' => $c->isBuiltin(), - ]; + if ($c instanceof \ReflectionNamedType || \PHP_VERSION_ID < 80000) { + $a += [ + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, + $prefix.'allowsNull' => $c->allowsNull(), + $prefix.'isBuiltin' => $c->isBuiltin(), + ]; + } elseif ($c instanceof \ReflectionUnionType) { + $a[$prefix.'allowsNull'] = $c->allowsNull(); + self::addMap($a, $c, [ + 'types' => 'getTypes', + ]); + } else { + $a[$prefix.'allowsNull'] = $c->allowsNull(); + } return $a; } @@ -377,7 +386,7 @@ private static function addExtra(array &$a, \Reflector $c) } } - private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) + private static function addMap(array &$a, $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) { foreach ($map as $k => $m) { if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) { diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index 9fc8dcaf0dc06..3f5497573afed 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -14,8 +14,11 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture; use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo; use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass; +use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture; +use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionUnionTypeFixture; /** * @author Nicolas Grekas @@ -75,7 +78,7 @@ public function testClosureCaster() $b: & 123 } file: "%sReflectionCasterTest.php" - line: "68 to 68" + line: "71 to 71" } EOTXT , $var @@ -211,6 +214,104 @@ public function testReflectionParameterNullableUnion() ); } + /** + * @requires PHP 7.4 + */ + public function testReflectionPropertyScalar() + { + $var = new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a'); + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionProperty { + +name: "a" + +class: "Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture" + modifiers: "public" +} +EOTXT + , $var + ); + } + + /** + * @requires PHP 7.4 + */ + public function testReflectionNamedType() + { + $var = (new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a'))->getType(); + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionNamedType { + name: "int" + allowsNull: false + isBuiltin: true +} +EOTXT + , $var + ); + } + + /** + * @requires PHP 8 + */ + public function testReflectionUnionType() + { + $var = (new \ReflectionProperty(ReflectionUnionTypeFixture::class, 'a'))->getType(); + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionUnionType { + allowsNull: false + types: array:2 [ + 0 => ReflectionNamedType { + name: "string" + allowsNull: false + isBuiltin: true + } + 1 => ReflectionNamedType { + name: "int" + allowsNull: false + isBuiltin: true + } + ] +} +EOTXT + , $var + ); + } + + /** + * @requires PHP 8 + */ + public function testExtendsReflectionType() + { + $var = new ExtendsReflectionTypeFixture(); + $this->assertDumpMatchesFormat( + <<<'EOTXT' +Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture { + allowsNull: false +} +EOTXT + , $var + ); + } + + /** + * @requires PHP < 8 + */ + public function testLegacyExtendsReflectionType() + { + $var = new ExtendsReflectionTypeFixture(); + $this->assertDumpMatchesFormat( + <<<'EOTXT' +Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture { + name: "fake" + allowsNull: false + isBuiltin: false +} +EOTXT + , $var + ); + } + public function testReturnType() { $f = eval('return function ():int {};'); diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/ExtendsReflectionTypeFixture.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/ExtendsReflectionTypeFixture.php new file mode 100644 index 0000000000000..1ee3d8c3844a7 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/ExtendsReflectionTypeFixture.php @@ -0,0 +1,21 @@ + Date: Wed, 17 Mar 2021 09:49:43 +0100 Subject: [PATCH 31/72] [PhpUnitBridge] fix compat with symfony/debug --- .../DeprecationErrorHandler/Deprecation.php | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index ac1a95ef41242..3ef1ee280a888 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -11,6 +11,8 @@ namespace Symfony\Bridge\PhpUnit\DeprecationErrorHandler; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; use PHPUnit\Util\Test; use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor; use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader; @@ -82,37 +84,41 @@ public function __construct($message, array $trace, $file) } } - if (isset($line['object']) || isset($line['class'])) { - if (!isset($line['class'], $trace[$i - 2]['function']) || 0 !== strpos($line['class'], SymfonyTestsListenerFor::class)) { - $this->originClass = isset($line['object']) ? \get_class($line['object']) : $line['class']; - $this->originMethod = $line['function']; + if (!isset($line['object']) && !isset($line['class'])) { + return; + } - return; - } + if (!isset($line['class'], $trace[$i - 2]['function']) || 0 !== strpos($line['class'], SymfonyTestsListenerFor::class)) { + $this->originClass = isset($line['object']) ? \get_class($line['object']) : $line['class']; + $this->originMethod = $line['function']; - if ('trigger_error' !== $trace[$i - 2]['function'] || isset($trace[$i - 2]['class'])) { - $this->originClass = \get_class($line['args'][0]); - $this->originMethod = $line['args'][0]->getName(); + return; + } - return; - } + $test = isset($line['args'][0]) ? $line['args'][0] : null; - set_error_handler(function () {}); - $parsedMsg = unserialize($this->message); - restore_error_handler(); - $this->message = $parsedMsg['deprecation']; - $this->originClass = $parsedMsg['class']; - $this->originMethod = $parsedMsg['method']; - if (isset($parsedMsg['files_stack'])) { - $this->originalFilesStack = $parsedMsg['files_stack']; - } - // If the deprecation has been triggered via - // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest() - // then we need to use the serialized information to determine - // if the error has been triggered from vendor code. - if (isset($parsedMsg['triggering_file'])) { - $this->triggeringFile = $parsedMsg['triggering_file']; - } + if (($test instanceof TestCase || $test instanceof TestSuite) && ('trigger_error' !== $trace[$i - 2]['function'] || isset($trace[$i - 2]['class']))) { + $this->originClass = \get_class($line['args'][0]); + $this->originMethod = $line['args'][0]->getName(); + + return; + } + + set_error_handler(function () {}); + $parsedMsg = unserialize($this->message); + restore_error_handler(); + $this->message = $parsedMsg['deprecation']; + $this->originClass = $parsedMsg['class']; + $this->originMethod = $parsedMsg['method']; + if (isset($parsedMsg['files_stack'])) { + $this->originalFilesStack = $parsedMsg['files_stack']; + } + // If the deprecation has been triggered via + // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest() + // then we need to use the serialized information to determine + // if the error has been triggered from vendor code. + if (isset($parsedMsg['triggering_file'])) { + $this->triggeringFile = $parsedMsg['triggering_file']; } } From d3412e919f42e04614028ec90635a5da05ace4a4 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Wed, 17 Mar 2021 16:36:05 +0100 Subject: [PATCH 32/72] [Inflector] Fixed pluralize "coupon" --- src/Symfony/Component/Inflector/Inflector.php | 3 +++ src/Symfony/Component/Inflector/Tests/InflectorTest.php | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Symfony/Component/Inflector/Inflector.php b/src/Symfony/Component/Inflector/Inflector.php index 8d2323cc4bd15..461c5f6090572 100644 --- a/src/Symfony/Component/Inflector/Inflector.php +++ b/src/Symfony/Component/Inflector/Inflector.php @@ -231,6 +231,9 @@ final class Inflector // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['noi', 3, true, true, 'ions'], + // coupon (coupons) + ['nop', 3, true, true, 'pons'], + // seasons (season), treasons (treason), poisons (poison), lessons (lesson) ['nos', 3, true, true, 'sons'], diff --git a/src/Symfony/Component/Inflector/Tests/InflectorTest.php b/src/Symfony/Component/Inflector/Tests/InflectorTest.php index a3c22ac0f5106..62b3790fd3e33 100644 --- a/src/Symfony/Component/Inflector/Tests/InflectorTest.php +++ b/src/Symfony/Component/Inflector/Tests/InflectorTest.php @@ -201,6 +201,7 @@ public function pluralizeProvider() ['crisis', 'crises'], ['criteria', 'criterion'], ['cup', 'cups'], + ['coupon', 'coupons'], ['data', 'data'], ['day', 'days'], ['disco', 'discos'], From 760be8831001282833fd7e1254a09ed089472874 Mon Sep 17 00:00:00 2001 From: Matthew Grasmick Date: Tue, 2 Mar 2021 16:31:24 -0500 Subject: [PATCH 33/72] [Console] Fix line wrapping for decorated text in block output --- src/Symfony/Component/Console/Style/SymfonyStyle.php | 11 +++++++++-- .../Fixtures/Style/SymfonyStyle/output/output_13.txt | 10 +++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 2056f04c99ad5..ba89fb4220e33 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -471,7 +471,12 @@ private function createBlock(iterable $messages, string $type = null, string $st $message = OutputFormatter::escape($message); } - $lines = array_merge($lines, explode(\PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, \PHP_EOL, true))); + $decorationLength = Helper::strlen($message) - Helper::strlenWithoutDecoration($this->getFormatter(), $message); + $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); + $messageLines = explode(\PHP_EOL, wordwrap($message, $messageLineLength, \PHP_EOL, true)); + foreach ($messageLines as $messageLine) { + $lines[] = $messageLine; + } if (\count($messages) > 1 && $key < \count($messages) - 1) { $lines[] = ''; @@ -491,7 +496,9 @@ private function createBlock(iterable $messages, string $type = null, string $st } $line = $prefix.$line; - $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + $decorationLength = Helper::strlen($line) - Helper::strlenWithoutDecoration($this->getFormatter(), $line); + $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); + $line .= str_repeat(' ', max($this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line), 0)); if ($style) { $line = sprintf('<%s>%s', $style, $line); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt index 0f3704b7482ea..ea8e4351eafa5 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt @@ -1,7 +1,7 @@ - // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et  - // dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea  - // commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla  - // pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim - // id est laborum + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore  + // magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo  + // consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla  + // pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + // est laborum From 6a6274ca894c12c09106c33c85d017204fbecd11 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Mar 2021 18:01:41 +0100 Subject: [PATCH 34/72] CS fix --- .../Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index 3ef1ee280a888..a8d3061a84296 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -98,8 +98,8 @@ public function __construct($message, array $trace, $file) $test = isset($line['args'][0]) ? $line['args'][0] : null; if (($test instanceof TestCase || $test instanceof TestSuite) && ('trigger_error' !== $trace[$i - 2]['function'] || isset($trace[$i - 2]['class']))) { - $this->originClass = \get_class($line['args'][0]); - $this->originMethod = $line['args'][0]->getName(); + $this->originClass = \get_class($test); + $this->originMethod = $test->getName(); return; } From 763edf9c9233745ef4a5c9f14efa7d18e01e2cca Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Mar 2021 18:12:15 +0100 Subject: [PATCH 35/72] Fix test --- .../Component/String/Tests/Inflector/EnglishInflectorTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index 259661744b6c4..e1ce3284aea81 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -58,7 +58,7 @@ public function singularizeProvider() ['crises', ['cris', 'crise', 'crisis']], ['criteria', ['criterion', 'criterium']], ['cups', 'cup'], - ['coupon', 'coupons'], + ['coupons', 'coupon'], ['data', 'data'], ['days', 'day'], ['discos', 'disco'], @@ -202,6 +202,7 @@ public function pluralizeProvider() ['crisis', 'crises'], ['criteria', 'criterion'], ['cup', 'cups'], + ['coupon', 'coupons'], ['data', 'data'], ['day', 'days'], ['disco', 'discos'], From e3788b68be307b037eb49024a8c0996e87fa44a6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 17 Mar 2021 21:31:43 +0100 Subject: [PATCH 36/72] fix version constraint --- src/Symfony/Component/Inflector/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json index 3f8d1eced1493..1162fa4167f86 100644 --- a/src/Symfony/Component/Inflector/composer.json +++ b/src/Symfony/Component/Inflector/composer.json @@ -25,7 +25,7 @@ "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", - "symfony/string": "~5.1.10|^5.2.1" + "symfony/string": "^5.2.6" }, "autoload": { "psr-4": { "Symfony\\Component\\Inflector\\": "" }, From 93e9337382e9983d724c501357fc2622df071e5a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 17 Mar 2021 21:16:29 +0100 Subject: [PATCH 37/72] enable HTTP method overrides as early as possible with the HTTP cache --- .../Bundle/FrameworkBundle/HttpCache/HttpCache.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php index 35ea73c235771..45b2ca785603e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -18,6 +18,7 @@ use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpKernel\HttpCache\StoreInterface; use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelInterface; /** @@ -62,6 +63,15 @@ public function __construct(KernelInterface $kernel, $cache = null, SurrogateInt parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge($this->options, $this->getOptions())); } + public function handle(Request $request, int $type = HttpKernelInterface::MASTER_REQUEST, bool $catch = true) + { + if ($this->kernel->getContainer()->getParameter('kernel.http_method_override')) { + Request::enableHttpMethodParameterOverride(); + } + + return parent::handle($request, $type, $catch); + } + /** * {@inheritdoc} */ From 5dd56a61a326a909d7734d70ed73cd444c4d09c3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Mar 2021 10:22:03 +0100 Subject: [PATCH 38/72] [FrameworkBundle] skip deprecation in integration tests --- .../FrameworkBundle/Tests/Functional/app/CachePools/bundles.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php index 15ff182c6fed5..2e46e896bca7c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php @@ -14,5 +14,4 @@ return [ new FrameworkBundle(), - new TestBundle(), ]; From cb68aeab381b254a575b0ba05fb17c8bc095c204 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Mar 2021 13:39:50 +0100 Subject: [PATCH 39/72] [PhpUnitBridge] fix reporting deprecations from DebugClassLoader --- .../DeprecationErrorHandler/Deprecation.php | 5 +++ .../debug_class_loader_deprecation.phpt | 41 +++++++++++++++++++ .../fake_app/ExtendsDeprecatedFromVendor.php | 9 ++++ 3 files changed, 55 insertions(+) create mode 100644 src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt create mode 100644 src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index ac1a95ef41242..80ec8ed79bfb5 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -76,6 +76,11 @@ public function __construct($message, array $trace, $file) $this->triggeringFile = isset($trace[1 + $j]['args'][1]) ? realpath($trace[1 + $j]['args'][1]) : (new \ReflectionClass($class))->getFileName(); $this->getOriginalFilesStack(); array_splice($this->originalFilesStack, 0, $j, [$this->triggeringFile]); + + if (preg_match('/(?|"([^"]++)" that is deprecated|should implement method "([^:]++))/', $message, $m) || preg_match('/^(?:The|Method) "([^":]++)/', $message, $m)) { + $this->triggeringFile = (new \ReflectionClass($m[1]))->getFileName(); + array_unshift($this->originalFilesStack, $this->triggeringFile); + } } break; diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt new file mode 100644 index 0000000000000..a6b0133af93ed --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test that a deprecation from the DebugClassLoader triggered by an app class extending a vendor one is considered direct. +--FILE-- + +--EXPECTF-- +Remaining direct deprecation notices (1) + + 1x: The "App\Services\ExtendsDeprecatedFromVendor" class extends "fcy\lib\DeprecatedClass" that is deprecated. + 1x in DebugClassLoader::loadClass from Symfony\Component\ErrorHandler diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php new file mode 100644 index 0000000000000..b4305e0d08a55 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php @@ -0,0 +1,9 @@ + Date: Fri, 19 Mar 2021 08:57:35 +0100 Subject: [PATCH 40/72] [HttpKernel] do is_file check before include Trying to include a file that doesn't exist issues a warning. Doing an is_file check beforehand should prevent those warnings. --- src/Symfony/Component/HttpKernel/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index cd185b14416fd..b8c93dbd8c6dc 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -533,7 +533,7 @@ protected function initializeContainer() if (!flock($lock, $wouldBlock ? \LOCK_SH : \LOCK_EX)) { fclose($lock); $lock = null; - } elseif (!\is_object($this->container = include $cachePath)) { + } elseif (!is_file($cachePath) || !\is_object($this->container = include $cachePath)) { $this->container = null; } elseif (!$oldContainer || \get_class($this->container) !== $oldContainer->name) { flock($lock, \LOCK_UN); From abe4ee5b56a0387babf1dea8466fb0979291d5c2 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sun, 21 Mar 2021 17:12:13 +0100 Subject: [PATCH 41/72] [UID] refer to AbstractUid instead of "parent" --- src/Symfony/Component/Uid/Uuid.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Uid/Uuid.php b/src/Symfony/Component/Uid/Uuid.php index 0a1327eedd445..4ed48b5735b9d 100644 --- a/src/Symfony/Component/Uid/Uuid.php +++ b/src/Symfony/Component/Uid/Uuid.php @@ -121,7 +121,7 @@ public function toRfc4122(): string return $this->uid; } - public function compare(parent $other): int + public function compare(AbstractUid $other): int { if (false !== $cmp = uuid_compare($this->uid, $other->uid)) { return $cmp; From 2bcf69c07196e83fec65e804e528165ec1f63ffb Mon Sep 17 00:00:00 2001 From: flies Date: Mon, 22 Mar 2021 09:32:13 +0100 Subject: [PATCH 42/72] [Security] Handle properly 'auto' option for remember me cookie security --- .../Security/Factory/RememberMeFactory.php | 7 +++- .../Tests/Functional/RememberMeCookieTest.php | 33 +++++++++++++++++++ .../app/RememberMeCookie/bundles.php | 9 +++++ .../app/RememberMeCookie/config.yml | 25 ++++++++++++++ .../app/RememberMeCookie/routing.yml | 2 ++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/config.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/routing.yml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 70fcd16b96c44..153028165c987 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -69,7 +69,12 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, } // remember-me options - $rememberMeServices->replaceArgument(3, array_intersect_key($config, $this->options)); + $mergedOptions = array_intersect_key($config, $this->options); + if ('auto' === $mergedOptions['secure']) { + $mergedOptions['secure'] = null; + } + + $rememberMeServices->replaceArgument(3, $mergedOptions); // attach to remember-me aware listeners $userProviders = []; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php new file mode 100644 index 0000000000000..6bfa1ed438732 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php @@ -0,0 +1,33 @@ +createClient(['test_case' => 'RememberMeCookie', 'root_config' => 'config.yml']); + + $client->request('POST', '/login', [ + '_username' => 'test', + '_password' => 'test', + ], [], [ + 'HTTPS' => (int) $https, + ]); + + $cookies = $client->getResponse()->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + + $this->assertEquals($expectedSecureFlag, $cookies['']['/']['REMEMBERME']->isSecure()); + } + + public function getSessionRememberMeSecureCookieFlagAutoHttpsMap() + { + return [ + [true, true], + [false, false], + ]; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/bundles.php new file mode 100644 index 0000000000000..8d4a02497947a --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/bundles.php @@ -0,0 +1,9 @@ + Date: Mon, 22 Mar 2021 10:43:50 +0100 Subject: [PATCH 43/72] [HttpClient] remove using $http_response_header --- src/Symfony/Component/HttpClient/Response/NativeResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/NativeResponse.php b/src/Symfony/Component/HttpClient/Response/NativeResponse.php index a3566aea326e4..e87402f0ad8dc 100644 --- a/src/Symfony/Component/HttpClient/Response/NativeResponse.php +++ b/src/Symfony/Component/HttpClient/Response/NativeResponse.php @@ -137,7 +137,7 @@ private function open(): void // Send request and follow redirects when needed $this->handle = $h = fopen($url, 'r', false, $this->context); - self::addResponseHeaders($http_response_header, $this->info, $this->headers, $this->info['debug']); + self::addResponseHeaders(stream_get_meta_data($h)['wrapper_data'], $this->info, $this->headers, $this->info['debug']); $url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context); if (null === $url) { From 6958bdc0bef08b91e557aaeda69e8e57aef65ac8 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 22 Mar 2021 06:04:25 -0400 Subject: [PATCH 44/72] improve login throttling rate limiter requirement message --- .../Security/Factory/LoginThrottlingFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php index c0aa37ec88712..1ff09a48ac5b9 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php @@ -64,7 +64,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal } if (!class_exists(RateLimiterFactory::class)) { - throw new \LogicException('Login throttling requires symfony/rate-limiter to be installed and enabled.'); + throw new \LogicException('Login throttling requires the Rate Limiter component. Try running "composer require symfony/rate-limiter".'); } if (!isset($config['limiter'])) { From d585b64953794a88ce7f4f9d36bbd4a0bd5386a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3js?= Date: Mon, 22 Mar 2021 11:31:20 +0100 Subject: [PATCH 45/72] Fixed parsing deprecated definitions without message key --- .../Loader/YamlFileLoader.php | 4 ++-- .../deprecated_definition_without_message.yml | 12 +++++++++++ .../Tests/Loader/YamlFileLoaderTest.php | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_definition_without_message.yml diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index a941b2d3eb77c..4fe4839179af6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -416,7 +416,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); } - $alias->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message']); + $alias->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } } @@ -485,7 +485,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); } - $definition->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message']); + $definition->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } if (isset($service['factory'])) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_definition_without_message.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_definition_without_message.yml new file mode 100644 index 0000000000000..7fe725fa608ff --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_definition_without_message.yml @@ -0,0 +1,12 @@ +services: + service_without_deprecation_message: + class: Foo + deprecated: + package: vendor/package + version: 1.1 + + alias_without_deprecation_message: + alias: foobar + deprecated: + package: vendor/package + version: 1.1 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 1a1dc9444a5f7..8591b9f12891a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -220,6 +220,27 @@ public function testLoadShortSyntax() $this->assertSame(['$a' => 'a', 'App\Foo' => 'foo'], $services['bar_foo']->getArguments()); } + public function testLoadDeprecatedDefinitionWithoutMessageKey() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('deprecated_definition_without_message.yml'); + + $this->assertTrue($container->getDefinition('service_without_deprecation_message')->isDeprecated()); + $deprecation = $container->getDefinition('service_without_deprecation_message')->getDeprecation('service_without_deprecation_message'); + $message = 'The "service_without_deprecation_message" service is deprecated. You should stop using it, as it will be removed in the future.'; + $this->assertSame($message, $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); + + $this->assertTrue($container->getAlias('alias_without_deprecation_message')->isDeprecated()); + $deprecation = $container->getAlias('alias_without_deprecation_message')->getDeprecation('alias_without_deprecation_message'); + $message = 'The "alias_without_deprecation_message" service alias is deprecated. You should stop using it, as it will be removed in the future.'; + $this->assertSame($message, $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); + } + public function testDeprecatedAliases() { $container = new ContainerBuilder(); From 8ba12ece57ad4e7c5a4de6cd4ae3d4f7b91e17d2 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Mon, 22 Mar 2021 10:41:09 -0400 Subject: [PATCH 46/72] [FrameworkBundle] ensure TestBrowserToken::$firewallName is serialized --- .../FrameworkBundle/Test/TestBrowserToken.php | 12 ++++++++++++ .../Tests/Test/TestBrowserTokenTest.php | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Test/TestBrowserTokenTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php b/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php index 3343fbf0fb4cf..7580743f6d5cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/TestBrowserToken.php @@ -43,4 +43,16 @@ public function getCredentials() { return null; } + + public function __serialize(): array + { + return [$this->firewallName, parent::__serialize()]; + } + + public function __unserialize(array $data): void + { + [$this->firewallName, $parentData] = $data; + + parent::__unserialize($parentData); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Test/TestBrowserTokenTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Test/TestBrowserTokenTest.php new file mode 100644 index 0000000000000..8c5387590a43a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Test/TestBrowserTokenTest.php @@ -0,0 +1,16 @@ +assertSame('main', $token->getFirewallName()); + } +} From e61553af4b4c57446dbc1f0937b995c7f6282dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 22 Mar 2021 20:26:15 +0100 Subject: [PATCH 47/72] Security hardening - Rate limiter --- src/Symfony/Component/RateLimiter/RateLimiterFactory.php | 2 +- .../Security/Http/RateLimiter/DefaultLoginRateLimiter.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/RateLimiter/RateLimiterFactory.php b/src/Symfony/Component/RateLimiter/RateLimiterFactory.php index 9fdbe474bf3ef..08b588b62cbb6 100644 --- a/src/Symfony/Component/RateLimiter/RateLimiterFactory.php +++ b/src/Symfony/Component/RateLimiter/RateLimiterFactory.php @@ -46,7 +46,7 @@ public function __construct(array $config, StorageInterface $storage, ?LockFacto public function create(?string $key = null): LimiterInterface { - $id = $this->config['id'].$key; + $id = $this->config['id'].'-'.$key; $lock = $this->lockFactory ? $this->lockFactory->createLock($id) : new NoLock(); switch ($this->config['policy']) { diff --git a/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php b/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php index cdf7109cf3ad4..783732e7d8e28 100644 --- a/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php +++ b/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php @@ -41,7 +41,7 @@ protected function getLimiters(Request $request): array { return [ $this->globalFactory->create($request->getClientIp()), - $this->localFactory->create($request->attributes->get(Security::LAST_USERNAME).$request->getClientIp()), + $this->localFactory->create($request->attributes->get(Security::LAST_USERNAME).'-'.$request->getClientIp()), ]; } } From 214dbfec5128ce2caadd3e2bdcecd24893ae2663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 22 Mar 2021 20:15:23 +0100 Subject: [PATCH 48/72] Hardening Security - Unserialize DumpDataCollector --- .../HttpKernel/DataCollector/DumpDataCollector.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index ae751c4f2e011..a3cf2147cb576 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -183,6 +183,11 @@ public function __wakeup() $charset = array_pop($this->data); $fileLinkFormat = array_pop($this->data); $this->dataCount = \count($this->data); + foreach ($this->data as $dump) { + if (!\is_string($dump['name']) || !\is_string($dump['file']) || !\is_int($dump['line'])) { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + } self::__construct($this->stopwatch, \is_string($fileLinkFormat) || $fileLinkFormat instanceof FileLinkFormatter ? $fileLinkFormat : null, \is_string($charset) ? $charset : null); } @@ -257,7 +262,7 @@ public function __destruct() } } - private function doDump(DataDumperInterface $dumper, $data, string $name, string $file, int $line) + private function doDump(DataDumperInterface $dumper, Data $data, string $name, string $file, int $line) { if ($dumper instanceof CliDumper) { $contextDumper = function ($name, $file, $line, $fmt) { From e45eb23ba2a74036bfd6f14bbd9b371a0a430fc3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 18 Mar 2021 08:44:57 +0100 Subject: [PATCH 49/72] [Validator] Avoid triggering the autoloader for user-input values --- .../Validator/Validator/RecursiveContextualValidator.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 9e743f44a7538..c816317fb5efd 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -656,8 +656,10 @@ private function validateGenericNode($value, $object, ?string $cacheKey, ?Metada return; } - // If the value is a scalar, pass it anyway, because we want - // a NoSuchMetadataException to be thrown in that case + if (!\is_object($value)) { + throw new NoSuchMetadataException(sprintf('Cannot create metadata for non-objects. Got: "%s".', \gettype($value))); + } + $this->validateObject( $value, $propertyPath, From 36b36dcecc8223b8f3d02c6c463a9a2dbf48ff90 Mon Sep 17 00:00:00 2001 From: Marion Hurteau Date: Fri, 19 Mar 2021 20:16:44 +0100 Subject: [PATCH 50/72] [Command] fix emojis messing up the line width add tests + removed irrelevant method --- .../Component/Console/Helper/Helper.php | 25 +++++++++++++------ .../Console/Helper/QuestionHelper.php | 2 +- .../Component/Console/Style/SymfonyStyle.php | 2 -- .../Style/SymfonyStyle/command/command_21.php | 13 ++++++++++ .../Style/SymfonyStyle/output/output_21.txt | 7 ++++++ 5 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_21.php create mode 100644 src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_21.txt diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php index f82dd286fc3b5..acec994db83c4 100644 --- a/src/Symfony/Component/Console/Helper/Helper.php +++ b/src/Symfony/Component/Console/Helper/Helper.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Helper; use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\String\UnicodeString; /** * Helper is the base class for all helper classes. @@ -45,7 +46,11 @@ public function getHelperSet() */ public static function strlen(?string $string) { - $string = (string) $string; + $string ?? $string = ''; + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->width(false); + } if (false === $encoding = mb_detect_encoding($string, null, true)) { return \strlen($string); @@ -59,9 +64,9 @@ public static function strlen(?string $string) * * @return string The string subset */ - public static function substr(string $string, int $from, int $length = null) + public static function substr(?string $string, int $from, int $length = null) { - $string = (string) $string; + $string ?? $string = ''; if (false === $encoding = mb_detect_encoding($string, null, true)) { return substr($string, $from, $length); @@ -116,17 +121,23 @@ public static function formatMemory(int $memory) return sprintf('%d B', $memory); } - public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, ?string $string) { - return self::strlen(self::removeDecoration($formatter, $string)); + $string = self::removeDecoration($formatter, $string); + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->width(true); + } + + return self::strlen($string); } - public static function removeDecoration(OutputFormatterInterface $formatter, $string) + public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string) { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); // remove <...> formatting - $string = $formatter->format($string); + $string = $formatter->format($string ?? ''); // remove already formatted characters $string = preg_replace("/\033\[[^m]*m/", '', $string); $formatter->setDecorated($isDecorated); diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index c0fb5461fc660..5bf8186b8fbf8 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -311,7 +311,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); $output->write($remainingCharacters); $fullChoice .= $remainingCharacters; - $i = self::strlen($fullChoice); + $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding); $matches = array_filter( $autocomplete($ret), diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 187bceed2a75f..075fe6621cc1f 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -501,8 +501,6 @@ private function createBlock(iterable $messages, string $type = null, string $st } $line = $prefix.$line; - $decorationLength = Helper::strlen($line) - Helper::strlenWithoutDecoration($this->getFormatter(), $line); - $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); $line .= str_repeat(' ', max($this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line), 0)); if ($style) { diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_21.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_21.php new file mode 100644 index 0000000000000..8460e7ececf37 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_21.php @@ -0,0 +1,13 @@ +success('Lorem ipsum dolor sit amet'); + $output->success('Lorem ipsum dolor sit amet with one emoji 🎉'); + $output->success('Lorem ipsum dolor sit amet with so many of them 👩‍🌾👩‍🌾👩‍🌾👩‍🌾👩‍🌾'); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_21.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_21.txt new file mode 100644 index 0000000000000..aee3c4a89c2e7 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_21.txt @@ -0,0 +1,7 @@ + + [OK] Lorem ipsum dolor sit amet + + [OK] Lorem ipsum dolor sit amet with one emoji 🎉 + + [OK] Lorem ipsum dolor sit amet with so many of them 👩‍🌾👩‍🌾👩‍🌾👩‍🌾👩‍🌾 + From 7eb4db6d9dc3a12ed2176d417bcb5fdbf4f610c7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Mar 2021 13:24:21 +0100 Subject: [PATCH 51/72] [Console] minor fix --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 2 +- src/Symfony/Component/Console/Style/SymfonyStyle.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index d211fcfd1e6ec..6dde580cf6214 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -309,7 +309,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); $output->write($remainingCharacters); $fullChoice .= $remainingCharacters; - $i = self::strlen($fullChoice); + $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding); $matches = array_filter( $autocomplete($ret), diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index ba89fb4220e33..ecdf9b1a3b376 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -496,8 +496,6 @@ private function createBlock(iterable $messages, string $type = null, string $st } $line = $prefix.$line; - $decorationLength = Helper::strlen($line) - Helper::strlenWithoutDecoration($this->getFormatter(), $line); - $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); $line .= str_repeat(' ', max($this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line), 0)); if ($style) { From c5c47f11a4798dab98b6f005d97d291aee2e4203 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Tue, 23 Mar 2021 16:25:38 +0100 Subject: [PATCH 52/72] [Contracts] Fix branch name in README.md links --- src/Symfony/Contracts/Cache/README.md | 2 +- src/Symfony/Contracts/EventDispatcher/README.md | 2 +- src/Symfony/Contracts/HttpClient/README.md | 2 +- src/Symfony/Contracts/Service/README.md | 2 +- src/Symfony/Contracts/Translation/README.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Contracts/Cache/README.md b/src/Symfony/Contracts/Cache/README.md index 58c589e9ea6ed..7085a6996e4d7 100644 --- a/src/Symfony/Contracts/Cache/README.md +++ b/src/Symfony/Contracts/Cache/README.md @@ -6,4 +6,4 @@ A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. -See https://github.com/symfony/contracts/blob/master/README.md for more information. +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/src/Symfony/Contracts/EventDispatcher/README.md b/src/Symfony/Contracts/EventDispatcher/README.md index fb051c73fc74e..b1ab4c00ceae2 100644 --- a/src/Symfony/Contracts/EventDispatcher/README.md +++ b/src/Symfony/Contracts/EventDispatcher/README.md @@ -6,4 +6,4 @@ A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. -See https://github.com/symfony/contracts/blob/master/README.md for more information. +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/src/Symfony/Contracts/HttpClient/README.md b/src/Symfony/Contracts/HttpClient/README.md index 01f8118949bd8..03b3a69b70208 100644 --- a/src/Symfony/Contracts/HttpClient/README.md +++ b/src/Symfony/Contracts/HttpClient/README.md @@ -6,4 +6,4 @@ A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. -See https://github.com/symfony/contracts/blob/master/README.md for more information. +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/src/Symfony/Contracts/Service/README.md b/src/Symfony/Contracts/Service/README.md index d033a439b9a96..41e054a101cf4 100644 --- a/src/Symfony/Contracts/Service/README.md +++ b/src/Symfony/Contracts/Service/README.md @@ -6,4 +6,4 @@ A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. -See https://github.com/symfony/contracts/blob/master/README.md for more information. +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/src/Symfony/Contracts/Translation/README.md b/src/Symfony/Contracts/Translation/README.md index 6c693ce0b35ff..42e5c51754ed6 100644 --- a/src/Symfony/Contracts/Translation/README.md +++ b/src/Symfony/Contracts/Translation/README.md @@ -6,4 +6,4 @@ A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. -See https://github.com/symfony/contracts/blob/master/README.md for more information. +See https://github.com/symfony/contracts/blob/main/README.md for more information. From 2a196ca0dc91de4497031cc93bfbaa59c7c7a4e1 Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 23 Mar 2021 15:52:10 +0100 Subject: [PATCH 53/72] [translation] Fix update existing key with existing +int-icu domain --- .../Component/Translation/MessageCatalogue.php | 2 +- .../Translation/Tests/MessageCatalogueTest.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Translation/MessageCatalogue.php b/src/Symfony/Component/Translation/MessageCatalogue.php index f2fc9a92652a2..cc8d3ceef366c 100644 --- a/src/Symfony/Component/Translation/MessageCatalogue.php +++ b/src/Symfony/Component/Translation/MessageCatalogue.php @@ -167,7 +167,7 @@ public function add($messages, $domain = 'messages') } $intlDomain = $domain; $suffixLength = \strlen(self::INTL_DOMAIN_SUFFIX); - if (\strlen($domain) > $suffixLength && false !== strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength)) { + if (\strlen($domain) < $suffixLength || false === strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength)) { $intlDomain .= self::INTL_DOMAIN_SUFFIX; } foreach ($messages as $id => $message) { diff --git a/src/Symfony/Component/Translation/Tests/MessageCatalogueTest.php b/src/Symfony/Component/Translation/Tests/MessageCatalogueTest.php index 89784fe7f417b..db0785ddfa66d 100644 --- a/src/Symfony/Component/Translation/Tests/MessageCatalogueTest.php +++ b/src/Symfony/Component/Translation/Tests/MessageCatalogueTest.php @@ -69,7 +69,7 @@ public function testAll() $this->assertEquals($messages, $catalogue->all()); } - public function testAllIntICU() + public function testAllIntlIcu() { $messages = [ 'domain1+intl-icu' => ['foo' => 'bar'], @@ -129,6 +129,16 @@ public function testAdd() $this->assertEquals('bar', $catalogue->get('foo', 'domain88')); } + public function testAddIntlIcu() + { + $catalogue = new MessageCatalogue('en', ['domain1+intl-icu' => ['foo' => 'foo']]); + $catalogue->add(['foo1' => 'foo1'], 'domain1'); + $catalogue->add(['foo' => 'bar'], 'domain1'); + + $this->assertSame('bar', $catalogue->get('foo', 'domain1')); + $this->assertSame('foo1', $catalogue->get('foo1', 'domain1')); + } + public function testReplace() { $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain1+intl-icu' => ['bar' => 'bar']]); From af43ca080b0d86dca565051aa05182cbf5ff60f0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Mar 2021 20:17:54 +0100 Subject: [PATCH 54/72] [travis] use packagist API v2 --- .github/rm-invalid-lowest-lock-files.php | 28 ++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 80cf81b9340ce..3e47b861e63f8 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -67,7 +67,7 @@ function getContentHash(array $composerJson) $referencedCommits = []; -foreach ($composerJsons as list($dir, $lockedPackages)) { +foreach ($composerJsons as [$dir, $lockedPackages]) { foreach ($lockedPackages as $lockedJson) { if (0 !== strpos($version = $lockedJson['version'], 'dev-') && '-dev' !== substr($version, -4)) { continue; @@ -119,11 +119,13 @@ function getContentHash(array $composerJson) $chs = []; foreach ($referencedCommits as $name => $dirsByCommit) { - $chs[] = $ch = [curl_init(), fopen($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '$').'.json', 'wb')]; - curl_setopt($ch[0], CURLOPT_URL, 'https://repo.packagist.org/p/'.$name.'.json'); - curl_setopt($ch[0], CURLOPT_FILE, $ch[1]); - curl_setopt($ch[0], CURLOPT_SHARE, $sh); - curl_multi_add_handle($mh, $ch[0]); + foreach (['', '~dev'] as $suffix) { + $chs[] = $ch = [curl_init(), fopen($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '~').$suffix.'.json', 'w')]; + curl_setopt($ch[0], CURLOPT_URL, 'https://repo.packagist.org/p2/'.$name.$suffix.'.json'); + curl_setopt($ch[0], CURLOPT_FILE, $ch[1]); + curl_setopt($ch[0], CURLOPT_SHARE, $sh); + curl_multi_add_handle($mh, $ch[0]); + } } do { @@ -131,18 +133,22 @@ function getContentHash(array $composerJson) curl_multi_select($mh); } while ($active); -foreach ($chs as list($ch, $fd)) { +foreach ($chs as [$ch, $fd]) { curl_multi_remove_handle($mh, $ch); curl_close($ch); fclose($fd); } foreach ($referencedCommits as $name => $dirsByCommit) { - $repo = file_get_contents($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '$').'.json'); - $repo = json_decode($repo, true); + foreach (['', '~dev'] as $suffix) { + $repo = file_get_contents($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '~').$suffix.'.json'); + $repo = json_decode($repo, true); - foreach ($repo['packages'][$name] as $version) { - unset($referencedCommits[$name][$version['source']['reference']]); + foreach ($repo['packages'][$name] as $version) { + if (isset($version['source']['reference'])) { + unset($referencedCommits[$name][$version['source']['reference']]); + } + } } } From aa80df609f9f8b7e2948bf63dad1fb2ae18b94c3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 23 Mar 2021 21:33:06 +0100 Subject: [PATCH 55/72] fix reported class when deprecated method is static --- .../Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index 8ea535e0ece14..fdc898a9316f5 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -79,7 +79,7 @@ public function __construct($message, array $trace, $file) $this->getOriginalFilesStack(); array_splice($this->originalFilesStack, 0, $j, [$this->triggeringFile]); - if (preg_match('/(?|"([^"]++)" that is deprecated|should implement method "([^:]++))/', $message, $m) || preg_match('/^(?:The|Method) "([^":]++)/', $message, $m)) { + if (preg_match('/(?|"([^"]++)" that is deprecated|should implement method "(?:static )?([^:]++))/', $message, $m) || preg_match('/^(?:The|Method) "([^":]++)/', $message, $m)) { $this->triggeringFile = (new \ReflectionClass($m[1]))->getFileName(); array_unshift($this->originalFilesStack, $this->triggeringFile); } From 96a05daf7e6f5968013e1ba2febb64ffdcbf5dc0 Mon Sep 17 00:00:00 2001 From: sebpacz Date: Tue, 23 Mar 2021 21:55:00 +0100 Subject: [PATCH 56/72] Fix comment with typo --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index c5f1674c4e37e..2af04a0cf2e2a 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1752,7 +1752,7 @@ public function testCopyShouldKeepExecutionPermission() } /** - * Normalize the given path (transform each blackslash into a real directory separator). + * Normalize the given path (transform each forward slash into a real directory separator). */ private function normalize(string $path): string { From 9ba015d2369d10481f85d94d6ae2d73d34d455c3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Mar 2021 21:57:14 +0100 Subject: [PATCH 57/72] [travis] remove cache of composer.lock for deps=low --- .github/rm-invalid-lowest-lock-files.php | 164 ----------------------- .travis.yml | 6 +- 2 files changed, 1 insertion(+), 169 deletions(-) delete mode 100644 .github/rm-invalid-lowest-lock-files.php diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php deleted file mode 100644 index 3e47b861e63f8..0000000000000 --- a/.github/rm-invalid-lowest-lock-files.php +++ /dev/null @@ -1,164 +0,0 @@ - [], 'packages-dev' => []]; - $composerJsons[$composerJson['name']] = [$dir, $composerLock['packages'] + $composerLock['packages-dev'], getRelevantContent($composerJson)]; -} - -$referencedCommits = []; - -foreach ($composerJsons as [$dir, $lockedPackages]) { - foreach ($lockedPackages as $lockedJson) { - if (0 !== strpos($version = $lockedJson['version'], 'dev-') && '-dev' !== substr($version, -4)) { - continue; - } - - if (!isset($composerJsons[$name = $lockedJson['name']])) { - echo "$dir/composer.lock references missing $name.\n"; - @unlink($dir.'/composer.lock'); - continue 2; - } - - if (isset($composerJsons[$name][2]['repositories']) && !isset($lockedJson['repositories'])) { - // the locked package has been patched locally but the lock references a commit, - // which means the referencing package itself is not modified - continue; - } - - foreach (['minimum-stability', 'prefer-stable'] as $key) { - if (array_key_exists($key, $composerJsons[$name][2])) { - $lockedJson[$key] = $composerJsons[$name][2][$key]; - } - } - - // use weak comparison to ignore ordering - if (getRelevantContent($lockedJson) != $composerJsons[$name][2]) { - echo "$dir/composer.lock is not in sync with $name.\n"; - @unlink($dir.'/composer.lock'); - continue 2; - } - - if ($lockedJson['dist']['reference']) { - $referencedCommits[$name][$lockedJson['dist']['reference']][] = $dir; - } - } -} - -if (!$referencedCommits) { - return; -} - -@mkdir($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org', 0777, true); - -$ch = null; -$mh = curl_multi_init(); -$sh = curl_share_init(); -curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); -curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); -curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); -$chs = []; - -foreach ($referencedCommits as $name => $dirsByCommit) { - foreach (['', '~dev'] as $suffix) { - $chs[] = $ch = [curl_init(), fopen($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '~').$suffix.'.json', 'w')]; - curl_setopt($ch[0], CURLOPT_URL, 'https://repo.packagist.org/p2/'.$name.$suffix.'.json'); - curl_setopt($ch[0], CURLOPT_FILE, $ch[1]); - curl_setopt($ch[0], CURLOPT_SHARE, $sh); - curl_multi_add_handle($mh, $ch[0]); - } -} - -do { - curl_multi_exec($mh, $active); - curl_multi_select($mh); -} while ($active); - -foreach ($chs as [$ch, $fd]) { - curl_multi_remove_handle($mh, $ch); - curl_close($ch); - fclose($fd); -} - -foreach ($referencedCommits as $name => $dirsByCommit) { - foreach (['', '~dev'] as $suffix) { - $repo = file_get_contents($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '~').$suffix.'.json'); - $repo = json_decode($repo, true); - - foreach ($repo['packages'][$name] as $version) { - if (isset($version['source']['reference'])) { - unset($referencedCommits[$name][$version['source']['reference']]); - } - } - } -} - -foreach ($referencedCommits as $name => $dirsByCommit) { - foreach ($dirsByCommit as $dirs) { - foreach ($dirs as $dir) { - if (file_exists($dir.'/composer.lock')) { - echo "$dir/composer.lock references old commit for $name.\n"; - @unlink($dir.'/composer.lock'); - } - } - } -} diff --git a/.travis.yml b/.travis.yml index ee0c4c815974d..336224a23bbbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -279,11 +279,7 @@ install: [[ ! $X ]] || (exit 1) elif [[ $deps = low ]]; then - [[ -e ~/php-ext/composer-lowest.lock.tar ]] && tar -xf ~/php-ext/composer-lowest.lock.tar - tar -cf ~/php-ext/composer-lowest.lock.tar --files-from /dev/null - php .github/rm-invalid-lowest-lock-files.php $COMPONENTS - echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && ([ -e composer.lock ] && ${COMPOSER_UP/update/install} || $COMPOSER_UP --prefer-lowest --prefer-stable) && $PHPUNIT_X'" - echo "$COMPONENTS" | xargs -n1 -I{} tar --append -f ~/php-ext/composer-lowest.lock.tar {}/composer.lock + echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && $COMPOSER_UP --prefer-lowest --prefer-stable && $PHPUNIT_X'" else if [[ $PHP = 8.0* ]]; then # add return types before running the test suite From f67dab95e2582d24eee84212fabf19a032679299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 24 Mar 2021 08:42:23 +0100 Subject: [PATCH 58/72] FIx Trying to clone an uncloneable object of class --- src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php b/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php index 06bdd57b6cd83..31abcdf5894c4 100644 --- a/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php @@ -40,7 +40,7 @@ public function testCreateStore($connection, string $expectedStoreClass) public function validConnections() { if (class_exists(\Redis::class)) { - yield [$this->createMock(\Redis::class), RedisStore::class]; + yield [new \Redis(), RedisStore::class]; } if (class_exists(RedisProxy::class)) { yield [$this->createMock(RedisProxy::class), RedisStore::class]; From cf1404a30b3f7b11607f72a5c1f7ff8d72f84d3b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 25 Mar 2021 18:52:07 +0100 Subject: [PATCH 59/72] [HttpClient] fix using stream_copy_to_stream() with responses cast to php streams --- .../Component/HttpClient/Response/StreamWrapper.php | 2 +- .../HttpClient/Tests/HttpClientTestCase.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php index 961681e4c445a..83f1562e84e35 100644 --- a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php +++ b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php @@ -281,7 +281,7 @@ public function stream_stat(): array 'uid' => 0, 'gid' => 0, 'rdev' => 0, - 'size' => (int) ($headers['content-length'][0] ?? 0), + 'size' => (int) ($headers['content-length'][0] ?? -1), 'atime' => 0, 'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0, 'ctime' => 0, diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index d429934b8bdfa..a3782df49e5ed 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -63,6 +63,19 @@ public function testToStream() $this->assertTrue(feof($stream)); } + public function testStreamCopyToStream() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://localhost:8057'); + $h = fopen('php://temp', 'w+'); + stream_copy_to_stream($response->toStream(), $h); + + $this->assertTrue(rewind($h)); + $this->assertSame("{\n \"SER", fread($h, 10)); + $this->assertSame('VER_PROTOCOL', fread($h, 12)); + $this->assertFalse(feof($h)); + } + public function testToStream404() { $client = $this->getHttpClient(__FUNCTION__); From d919f2ce83ad24288284ebf7c78d6df51bf6414a Mon Sep 17 00:00:00 2001 From: Roberto Nygaard Date: Sun, 21 Mar 2021 22:12:55 +0000 Subject: [PATCH 60/72] [HttpKernel] ConfigDataCollector to return known data without the need of a Kernel --- .../DataCollector/ConfigDataCollector.php | 17 +++++---- .../DataCollector/ConfigDataCollectorTest.php | 35 +++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index d4b298c594a14..c32c71b7477b4 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -59,12 +59,19 @@ public function setKernel(KernelInterface $kernel = null) */ public function collect(Request $request, Response $response/*, \Throwable $exception = null*/) { + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE); + $this->data = [ 'app_name' => $this->name, 'app_version' => $this->version, 'token' => $response->headers->get('X-Debug-Token'), 'symfony_version' => Kernel::VERSION, - 'symfony_state' => 'unknown', + 'symfony_minor_version' => sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), + 'symfony_lts' => 4 === Kernel::MINOR_VERSION, + 'symfony_state' => $this->determineSymfonyState(), + 'symfony_eom' => $eom->format('F Y'), + 'symfony_eol' => $eol->format('F Y'), 'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a', 'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a', 'php_version' => \PHP_VERSION, @@ -82,14 +89,6 @@ public function collect(Request $request, Response $response/*, \Throwable $exce foreach ($this->kernel->getBundles() as $name => $bundle) { $this->data['bundles'][$name] = new ClassStub(\get_class($bundle)); } - - $this->data['symfony_state'] = $this->determineSymfonyState(); - $this->data['symfony_minor_version'] = sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION); - $this->data['symfony_lts'] = 4 === Kernel::MINOR_VERSION; - $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE); - $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE); - $this->data['symfony_eom'] = $eom->format('F Y'); - $this->data['symfony_eol'] = $eol->format('F Y'); } if (preg_match('~^(\d+(?:\.\d+)*)(.+)?$~', $this->data['php_version'], $matches) && isset($matches[2])) { diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php index df6af954f3a7b..86bd394f56087 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php @@ -41,6 +41,13 @@ public function testCollect() $this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); $this->assertSame(\extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN), $c->hasZendOpcache()); $this->assertSame(\extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN), $c->hasApcu()); + $this->assertSame(sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), $c->getSymfonyMinorVersion()); + $this->assertContains($c->getSymfonyState(), ['eol', 'eom', 'dev', 'stable']); + + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE)->format('F Y'); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE)->format('F Y'); + $this->assertSame($eom, $c->getSymfonyEom()); + $this->assertSame($eol, $c->getSymfonyEol()); } /** @@ -58,6 +65,34 @@ public function testLegacy() $this->assertSame('name', $c->getApplicationName()); $this->assertNull($c->getApplicationVersion()); } + + public function testCollectWithoutKernel() + { + $c = new ConfigDataCollector(); + $c->collect(new Request(), new Response()); + + $this->assertSame('n/a', $c->getEnv()); + $this->assertSame('n/a', $c->isDebug()); + $this->assertSame('config', $c->getName()); + $this->assertMatchesRegularExpression('~^'.preg_quote($c->getPhpVersion(), '~').'~', \PHP_VERSION); + $this->assertMatchesRegularExpression('~'.preg_quote((string) $c->getPhpVersionExtra(), '~').'$~', \PHP_VERSION); + $this->assertSame(\PHP_INT_SIZE * 8, $c->getPhpArchitecture()); + $this->assertSame(class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a', $c->getPhpIntlLocale()); + $this->assertSame(date_default_timezone_get(), $c->getPhpTimezone()); + $this->assertSame(Kernel::VERSION, $c->getSymfonyVersion()); + $this->assertSame(4 === Kernel::MINOR_VERSION, $c->isSymfonyLts()); + $this->assertNull($c->getToken()); + $this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); + $this->assertSame(\extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN), $c->hasZendOpcache()); + $this->assertSame(\extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN), $c->hasApcu()); + $this->assertSame(sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), $c->getSymfonyMinorVersion()); + $this->assertContains($c->getSymfonyState(), ['eol', 'eom', 'dev', 'stable']); + + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE)->format('F Y'); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE)->format('F Y'); + $this->assertSame($eom, $c->getSymfonyEom()); + $this->assertSame($eol, $c->getSymfonyEol()); + } } class KernelForTest extends Kernel From d0a3c538f474fe467296f19280b1ba1bd897f2f6 Mon Sep 17 00:00:00 2001 From: Roberto Nygaard Date: Thu, 25 Mar 2021 22:22:40 +0000 Subject: [PATCH 61/72] Uses the correct assignment action for console options depending if they are short or long --- src/Symfony/Component/Console/Input/ArrayInput.php | 5 +++-- src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index 25d2b750b4aae..bf9a8a455a4c5 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -108,12 +108,13 @@ public function __toString() $params = []; foreach ($this->parameters as $param => $val) { if ($param && \is_string($param) && '-' === $param[0]) { + $glue = ('-' === $param[1]) ? '=' : ' '; if (\is_array($val)) { foreach ($val as $v) { - $params[] = $param.('' != $v ? '='.$this->escapeToken($v) : ''); + $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : ''); } } else { - $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : ''); } } else { $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); diff --git a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php index f3eedb3a2d57f..5777c44b7269a 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php @@ -162,10 +162,10 @@ public function provideInvalidInput() public function testToString() { $input = new ArrayInput(['-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"]); - $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + $this->assertEquals('-f -b bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); $input = new ArrayInput(['-b' => ['bval_1', 'bval_2'], '--f' => ['fval_1', 'fval_2']]); - $this->assertSame('-b=bval_1 -b=bval_2 --f=fval_1 --f=fval_2', (string) $input); + $this->assertSame('-b bval_1 -b bval_2 --f=fval_1 --f=fval_2', (string) $input); $input = new ArrayInput(['array_arg' => ['val_1', 'val_2']]); $this->assertSame('val_1 val_2', (string) $input); From 83b836dbc90e68e7b088c1c737d393b827baaded Mon Sep 17 00:00:00 2001 From: Warxcell Date: Thu, 18 Mar 2021 15:13:44 +0200 Subject: [PATCH 62/72] IntegerType: Always use en for IntegerToLocalizedStringTransformer Fixes #40456 --- .../IntegerToLocalizedStringTransformer.php | 14 +++++---- .../Form/Extension/Core/Type/IntegerType.php | 2 +- .../Extension/Core/Type/IntegerTypeTest.php | 31 ++++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php index 76e3eea8ede9a..7e0eeea2a50b0 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -24,19 +24,21 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo /** * Constructs a transformer. * - * @param bool $grouping Whether thousands should be grouped - * @param int $roundingMode One of the ROUND_ constants in this class + * @param bool $grouping Whether thousands should be grouped + * @param int $roundingMode One of the ROUND_ constants in this class + * @param string|null $locale locale used for transforming */ - public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN) + public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN, $locale = null) { - if (\is_int($grouping) || \is_bool($roundingMode) || 2 < \func_num_args()) { + if (\is_int($grouping) || \is_bool($roundingMode) || \is_int($locale)) { @trigger_error(sprintf('Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.', __CLASS__), \E_USER_DEPRECATED); $grouping = $roundingMode; - $roundingMode = 2 < \func_num_args() ? func_get_arg(2) : self::ROUND_DOWN; + $roundingMode = null !== $locale ? $locale : self::ROUND_DOWN; + $locale = null; } - parent::__construct(0, $grouping, $roundingMode); + parent::__construct(0, $grouping, $roundingMode, $locale); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php index 540cdb6f79db3..2fa7c9c642316 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php @@ -25,7 +25,7 @@ class IntegerType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode'])); + $builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode'], !$options['grouping'] ? 'en' : null)); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index a88eb70c5fe7a..0156b21905c5c 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -17,13 +17,42 @@ class IntegerTypeTest extends BaseTypeTest { public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\IntegerType'; + private $previousLocale; + protected function setUp(): void { IntlTestHelper::requireIntl($this, false); - + $this->previousLocale = \Locale::getDefault(); parent::setUp(); } + protected function tearDown(): void + { + \Locale::setDefault($this->previousLocale); + } + + public function testArabicLocale() + { + \Locale::setDefault('ar'); + + $form = $this->factory->create(static::TESTED_TYPE); + $form->submit('123456'); + + $this->assertSame(123456, $form->getData()); + $this->assertSame('123456', $form->getViewData()); + } + + public function testArabicLocaleNonHtml5() + { + \Locale::setDefault('ar'); + + $form = $this->factory->create(static::TESTED_TYPE, null, ['grouping' => true]); + $form->submit('123456'); + + $this->assertSame(123456, $form->getData()); + $this->assertSame('١٢٣٬٤٥٦', $form->getViewData()); + } + public function testSubmitRejectsFloats() { $form = $this->factory->create(static::TESTED_TYPE); From 5ce5300da335ca0c0b15b1e5c6524c2f94ab72c8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 27 Mar 2021 18:02:59 +0100 Subject: [PATCH 63/72] error if the input string couldn't be parsed as a date When the Intl polyfill is used instead of the PHP intl extension, the intl_get_error_code() function always returns 0 no matter if the input string could be parsed. --- .../DataTransformer/DateTimeToLocalizedStringTransformer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 8d73404b5d1b0..a5ef4f6f067bc 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -130,6 +130,10 @@ public function reverseTransform($value) } elseif ($timestamp > 253402214400) { // This timestamp represents UTC midnight of 9999-12-31 to prevent 5+ digit years throw new TransformationFailedException('Years beyond 9999 are not supported.'); + } elseif (false === $timestamp) { + // the value couldn't be parsed but the Intl extension didn't report an error code, this + // could be the case when the Intl polyfill is used which always returns 0 as the error code + throw new TransformationFailedException(sprintf('"%s" could not be parsed as a date.', $value)); } try { From bfef4546d313a12af364a1b9680f18f536f0be4f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 27 Mar 2021 20:49:03 +0100 Subject: [PATCH 64/72] make DateCaster tests timezone-agnostic --- src/Symfony/Component/VarDumper/Caster/DateCaster.php | 2 +- src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php index 6b264c7958729..171fbde55c3bc 100644 --- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php @@ -92,7 +92,7 @@ public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNeste if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/74639 foreach (clone $p as $i => $d) { if (self::PERIOD_LIMIT === $i) { - $now = new \DateTimeImmutable(); + $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php index 7f36800d8562c..63be1681d0649 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php @@ -353,7 +353,7 @@ public function testDumpPeriod($start, $interval, $end, $options, $expected) */ public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates) { - $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options); + $p = new \DatePeriod(new \DateTime($start, new \DateTimeZone('UTC')), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end, new \DateTimeZone('UTC')), $options); $stub = new Stub(); $cast = DateCaster::castPeriod($p, [], $stub, false, 0); From 71da904de056b5bf5b4e5f1cec2948c558f2e0b8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Mar 2021 10:05:52 +0200 Subject: [PATCH 65/72] skip intl dependent tests if the extension is missing --- .../Form/Tests/Extension/Core/Type/IntegerTypeTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index 0156b21905c5c..15cf308d990dd 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -31,6 +31,9 @@ protected function tearDown(): void \Locale::setDefault($this->previousLocale); } + /** + * @requires extension intl + */ public function testArabicLocale() { \Locale::setDefault('ar'); @@ -42,6 +45,9 @@ public function testArabicLocale() $this->assertSame('123456', $form->getViewData()); } + /** + * @requires extension intl + */ public function testArabicLocaleNonHtml5() { \Locale::setDefault('ar'); From d33973bbbe37188ed1a3b846c31c39e54dbdb00c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Mar 2021 11:59:03 +0200 Subject: [PATCH 66/72] fix merge --- .../DataTransformer/IntegerToLocalizedStringTransformer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php index 3bd5519fe7dca..3af9809ed1f97 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -28,7 +28,7 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo * @param int $roundingMode One of the ROUND_ constants in this class * @param string|null $locale locale used for transforming */ - public function __construct(?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_DOWN, ?string $locale) + public function __construct(?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_DOWN, ?string $locale = null) { parent::__construct(0, $grouping, $roundingMode, $locale); } From 2b33d4af653738ee31a1882b951401f09144e041 Mon Sep 17 00:00:00 2001 From: sebpacz Date: Sun, 28 Mar 2021 11:59:32 +0200 Subject: [PATCH 67/72] [Filesystem] Fix dumpFile() method call --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 2af04a0cf2e2a..77f780127e050 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1731,7 +1731,7 @@ public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() file_put_contents($filename, 'FOO BAR'); chmod($filename, 0745); - $this->filesystem->dumpFile($filename, 'bar', null); + $this->filesystem->dumpFile($filename, 'bar'); $this->assertFilePermissions(745, $filename); } From 73999b8a3a88c48ddc00073920196faaac566bc6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Mar 2021 15:54:53 +0200 Subject: [PATCH 68/72] fix docblock --- .../Component/HttpKernel/DataCollector/ConfigDataCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index 37f8fb73cd217..12c38153a26f4 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -219,7 +219,7 @@ public function getEnv() /** * Returns true if the debug is enabled. * - * @return bool true if debug is enabled, false otherwise + * @return bool|string true if debug is enabled, false otherwise or a string if no kernel was set */ public function isDebug() { From 89d9de20778e1cde79032aae28aed58c041d2371 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 28 Mar 2021 15:14:31 +0200 Subject: [PATCH 69/72] Use concrete user related classes in the tests This allows the tests to automatically adapt for changes in the UserInterface and UserProviderInterface in 5.x --- .../Fixtures/PasswordUpgraderProvider.php | 7 +- .../FormLoginAuthenticatorTest.php | 9 +-- .../HttpBasicAuthenticatorTest.php | 15 ++-- .../RemoteUserAuthenticatorTest.php | 19 ++--- .../Authenticator/X509AuthenticatorTest.php | 24 ++---- .../PasswordMigratingListenerTest.php | 16 ++-- .../UserProviderListenerTest.php | 8 +- .../Tests/Firewall/SwitchUserListenerTest.php | 13 ++-- .../Tests/LoginLink/LoginLinkHandlerTest.php | 60 +++++++++------ ...istentTokenBasedRememberMeServicesTest.php | 37 ++-------- .../TokenBasedRememberMeServicesTest.php | 73 +++---------------- 11 files changed, 100 insertions(+), 181 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/Fixtures/PasswordUpgraderProvider.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/Fixtures/PasswordUpgraderProvider.php index 5836d8893ef7f..d50dbf1be28a9 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/Fixtures/PasswordUpgraderProvider.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/Fixtures/PasswordUpgraderProvider.php @@ -11,9 +11,14 @@ namespace Symfony\Component\Security\Http\Tests\Authenticator\Fixtures; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; +use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; -abstract class PasswordUpgraderProvider implements UserProviderInterface, PasswordUpgraderInterface +class PasswordUpgraderProvider extends InMemoryUserProvider implements PasswordUpgraderInterface { + public function upgradePassword(UserInterface $user, string $newEncodedPassword): void + { + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php index 71169d988477b..2acb3ed52fa48 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -17,8 +17,7 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator; @@ -37,8 +36,7 @@ class FormLoginAuthenticatorTest extends TestCase protected function setUp(): void { - $this->userProvider = $this->createMock(UserProviderInterface::class); - $this->userProvider->expects($this->any())->method('loadUserByUsername')->willReturn(new User('test', 's$cr$t')); + $this->userProvider = new InMemoryUserProvider(['test' => ['password' => 's$cr$t']]); $this->successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class); $this->failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class); } @@ -149,8 +147,7 @@ public function testUpgradePassword() $request = Request::create('/login_check', 'POST', ['_username' => 'wouter', '_password' => 's$cr$t']); $request->setSession($this->createSession()); - $this->userProvider = $this->createMock(PasswordUpgraderProvider::class); - $this->userProvider->expects($this->any())->method('loadUserByUsername')->willReturn(new User('test', 's$cr$t')); + $this->userProvider = new PasswordUpgraderProvider(['test' => ['password' => 's$cr$t']]); $this->setUpAuthenticator(); $passport = $this->authenticator->authenticate($request); diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/HttpBasicAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/HttpBasicAuthenticatorTest.php index 79e914965ab9e..0093dabeeee16 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/HttpBasicAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/HttpBasicAuthenticatorTest.php @@ -6,8 +6,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\HttpBasicAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; @@ -22,7 +22,7 @@ class HttpBasicAuthenticatorTest extends TestCase protected function setUp(): void { - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new InMemoryUserProvider(); $this->encoderFactory = $this->createMock(EncoderFactoryInterface::class); $this->encoder = $this->createMock(PasswordEncoderInterface::class); $this->encoderFactory @@ -40,16 +40,12 @@ public function testExtractCredentialsAndUserFromRequest() 'PHP_AUTH_PW' => 'ThePassword', ]); - $this->userProvider - ->expects($this->any()) - ->method('loadUserByUsername') - ->with('TheUsername') - ->willReturn($user = new User('TheUsername', 'ThePassword')); + $this->userProvider->createUser($user = new User('TheUsername', 'ThePassword')); $passport = $this->authenticator->authenticate($request); $this->assertEquals('ThePassword', $passport->getBadge(PasswordCredentials::class)->getPassword()); - $this->assertSame($user, $passport->getUser()); + $this->assertTrue($user->isEqualTo($passport->getUser())); } /** @@ -77,8 +73,7 @@ public function testUpgradePassword() 'PHP_AUTH_PW' => 'ThePassword', ]); - $this->userProvider = $this->createMock(PasswordUpgraderProvider::class); - $this->userProvider->expects($this->any())->method('loadUserByUsername')->willReturn(new User('test', 's$cr$t')); + $this->userProvider = new PasswordUpgraderProvider(['test' => ['password' => 's$cr$t']]); $authenticator = new HttpBasicAuthenticator('test', $this->userProvider); $passport = $authenticator->authenticate($request); diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/RemoteUserAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/RemoteUserAuthenticatorTest.php index f55c72abff5e5..b51a9fe643158 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/RemoteUserAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/RemoteUserAuthenticatorTest.php @@ -14,8 +14,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\RemoteUserAuthenticator; class RemoteUserAuthenticatorTest extends TestCase @@ -23,7 +23,7 @@ class RemoteUserAuthenticatorTest extends TestCase /** * @dataProvider provideAuthenticators */ - public function testSupport(UserProviderInterface $userProvider, RemoteUserAuthenticator $authenticator, $parameterName) + public function testSupport(InMemoryUserProvider $userProvider, RemoteUserAuthenticator $authenticator, $parameterName) { $request = $this->createRequest([$parameterName => 'TheUsername']); @@ -32,7 +32,7 @@ public function testSupport(UserProviderInterface $userProvider, RemoteUserAuthe public function testSupportNoUser() { - $authenticator = new RemoteUserAuthenticator($this->createMock(UserProviderInterface::class), new TokenStorage(), 'main'); + $authenticator = new RemoteUserAuthenticator(new InMemoryUserProvider(), new TokenStorage(), 'main'); $this->assertFalse($authenticator->supports($this->createRequest([]))); } @@ -40,27 +40,24 @@ public function testSupportNoUser() /** * @dataProvider provideAuthenticators */ - public function testAuthenticate(UserProviderInterface $userProvider, RemoteUserAuthenticator $authenticator, $parameterName) + public function testAuthenticate(InMemoryUserProvider $userProvider, RemoteUserAuthenticator $authenticator, $parameterName) { $request = $this->createRequest([$parameterName => 'TheUsername']); $authenticator->supports($request); - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('TheUsername') - ->willReturn($user = new User('TheUsername', null)); + $userProvider->createUser($user = new User('TheUsername', null)); $passport = $authenticator->authenticate($request); - $this->assertEquals($user, $passport->getUser()); + $this->assertTrue($user->isEqualTo($passport->getUser())); } public function provideAuthenticators() { - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = new InMemoryUserProvider(); yield [$userProvider, new RemoteUserAuthenticator($userProvider, new TokenStorage(), 'main'), 'REMOTE_USER']; - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = new InMemoryUserProvider(); yield [$userProvider, new RemoteUserAuthenticator($userProvider, new TokenStorage(), 'main', 'CUSTOM_USER_PARAMETER'), 'CUSTOM_USER_PARAMETER']; } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php index 9f620efd2cfa9..d0322d4759038 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php @@ -14,8 +14,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\X509Authenticator; class X509AuthenticatorTest extends TestCase @@ -25,7 +25,7 @@ class X509AuthenticatorTest extends TestCase protected function setUp(): void { - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new InMemoryUserProvider(); $this->authenticator = new X509Authenticator($this->userProvider, new TokenStorage(), 'main'); } @@ -45,10 +45,7 @@ public function testAuthentication($username, $credentials) $request = $this->createRequest($serverVars); $this->assertTrue($this->authenticator->supports($request)); - $this->userProvider->expects($this->any()) - ->method('loadUserByUsername') - ->with($username) - ->willReturn(new User($username, null)); + $this->userProvider->createUser(new User($username, null)); $passport = $this->authenticator->authenticate($request); $this->assertEquals($username, $passport->getUser()->getUsername()); @@ -69,10 +66,7 @@ public function testAuthenticationNoUser($emailAddress, $credentials) $this->assertTrue($this->authenticator->supports($request)); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with($emailAddress) - ->willReturn(new User($emailAddress, null)); + $this->userProvider->createUser(new User($emailAddress, null)); $passport = $this->authenticator->authenticate($request); $this->assertEquals($emailAddress, $passport->getUser()->getUsername()); @@ -105,10 +99,7 @@ public function testAuthenticationCustomUserKey() ]); $this->assertTrue($authenticator->supports($request)); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('TheUser') - ->willReturn(new User('TheUser', null)); + $this->userProvider->createUser(new User('TheUser', null)); $passport = $this->authenticator->authenticate($request); $this->assertEquals('TheUser', $passport->getUser()->getUsername()); @@ -123,10 +114,7 @@ public function testAuthenticationCustomCredentialsKey() ]); $this->assertTrue($authenticator->supports($request)); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('cert@example.com') - ->willReturn(new User('cert@example.com', null)); + $this->userProvider->createUser(new User('cert@example.com', null)); $passport = $authenticator->authenticate($request); $this->assertEquals('cert@example.com', $passport->getUser()->getUsername()); diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php index dea08f0186023..285472f037137 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php @@ -16,9 +16,10 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; +use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; @@ -36,13 +37,12 @@ class PasswordMigratingListenerTest extends TestCase protected function setUp(): void { - $this->user = $this->createMock(UserInterface::class); - $this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password'); + $this->user = new User('test', 'old-encoded-password'); $encoder = $this->createMock(PasswordEncoderInterface::class); $encoder->expects($this->any())->method('needsRehash')->willReturn(true); $encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password'); $this->encoderFactory = $this->createMock(EncoderFactoryInterface::class); - $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder); + $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->callback(function ($user) { return $this->user->isEqualTo($user); }))->willReturn($encoder); $this->listener = new PasswordMigratingListener($this->encoderFactory); } @@ -96,12 +96,12 @@ public function testUpgradeWithUpgrader() public function testUpgradeWithoutUpgrader() { - $userLoader = $this->createMock(MigratingUserProvider::class); - $userLoader->expects($this->any())->method('loadUserByUsername')->willReturn($this->user); + $userLoader = $this->getMockBuilder(MigratingUserProvider::class)->setMethods(['upgradePassword'])->getMock(); + $userLoader->createUser($this->user); $userLoader->expects($this->once()) ->method('upgradePassword') - ->with($this->user, 'new-encoded-password') + ->with($this->callback(function ($user) { return $this->user->isEqualTo($user); }), 'new-encoded-password') ; $event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', [$userLoader, 'loadUserByUsername']), [new PasswordUpgradeBadge('pa$$word')])); @@ -119,7 +119,7 @@ private function createEvent(PassportInterface $passport) } } -abstract class MigratingUserProvider implements UserProviderInterface, PasswordUpgraderInterface +class MigratingUserProvider extends InMemoryUserProvider implements PasswordUpgraderInterface { public function upgradePassword(UserInterface $user, string $newEncodedPassword): void { diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php index 95f99de8d0fde..2f7113e05e15d 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php @@ -12,9 +12,9 @@ namespace Symfony\Component\Security\Http\Tests\EventListener; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; @@ -28,7 +28,7 @@ class UserProviderListenerTest extends TestCase protected function setUp(): void { - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new InMemoryUserProvider(); $this->listener = new UserProviderListener($this->userProvider); } @@ -42,8 +42,8 @@ public function testSetUserProvider() $this->assertEquals([$this->userProvider, 'loadUserByUsername'], $badge->getUserLoader()); $user = new User('wouter', null); - $this->userProvider->expects($this->once())->method('loadUserByUsername')->with('wouter')->willReturn($user); - $this->assertSame($user, $passport->getUser()); + $this->userProvider->createUser($user); + $this->assertTrue($user->isEqualTo($passport->getUser())); } /** diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 6e48cc5e63dbb..cc333cd7e9f44 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -25,7 +25,6 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Event\SwitchUserEvent; use Symfony\Component\Security\Http\Firewall\SwitchUserListener; @@ -112,13 +111,13 @@ public function testExitUserUpdatesToken() public function testExitUserDispatchesEventWithRefreshedUser() { - $originalUser = $this->createMock(UserInterface::class); - $refreshedUser = $this->createMock(UserInterface::class); + $originalUser = new User('username', null); + $refreshedUser = new User('username', null); $this ->userProvider ->expects($this->any()) ->method('refreshUser') - ->with($originalUser) + ->with($this->identicalTo($originalUser)) ->willReturn($refreshedUser); $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); $this->tokenStorage->setToken(new SwitchUserToken('username', '', 'key', ['ROLE_USER'], $originalToken)); @@ -399,13 +398,13 @@ public function testSwitchUserStateless() public function testSwitchUserRefreshesOriginalToken() { - $originalUser = $this->createMock(UserInterface::class); - $refreshedOriginalUser = $this->createMock(UserInterface::class); + $originalUser = new User('username', null); + $refreshedOriginalUser = new User('username', null); $this ->userProvider ->expects($this->any()) ->method('refreshUser') - ->with($originalUser) + ->with($this->identicalTo($originalUser)) ->willReturn($refreshedOriginalUser); $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); $this->tokenStorage->setToken(new SwitchUserToken('username', '', 'key', ['ROLE_USER'], $originalToken)); diff --git a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php index 05c6340b6aaa5..42c8094ba10b7 100644 --- a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php @@ -29,7 +29,7 @@ class LoginLinkHandlerTest extends TestCase { /** @var MockObject|UrlGeneratorInterface */ private $router; - /** @var MockObject|UserProviderInterface */ + /** @var TestLoginLinkHandlerUserProvider */ private $userProvider; /** @var PropertyAccessorInterface */ private $propertyAccessor; @@ -39,7 +39,7 @@ class LoginLinkHandlerTest extends TestCase protected function setUp(): void { $this->router = $this->createMock(UrlGeneratorInterface::class); - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new TestLoginLinkHandlerUserProvider(); $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); $this->expiredLinkStorage = $this->createMock(ExpiredLoginLinkStorage::class); } @@ -94,10 +94,7 @@ public function testConsumeLoginLink() $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('weaverryan') - ->willReturn($user); + $this->userProvider->createUser($user); $this->expiredLinkStorage->expects($this->once()) ->method('incrementUsages') @@ -105,7 +102,7 @@ public function testConsumeLoginLink() $linker = $this->createLinker(['max_uses' => 3]); $actualUser = $linker->consumeLoginLink($request); - $this->assertSame($user, $actualUser); + $this->assertEquals($user, $actualUser); } public function testConsumeLoginLinkWithExpired() @@ -116,10 +113,7 @@ public function testConsumeLoginLinkWithExpired() $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('weaverryan') - ->willReturn($user); + $this->userProvider->createUser($user); $linker = $this->createLinker(['max_uses' => 3]); $linker->consumeLoginLink($request); @@ -130,11 +124,6 @@ public function testConsumeLoginLinkWithUserNotFound() $this->expectException(InvalidLoginLinkException::class); $request = Request::create('/login/verify?user=weaverryan&hash=thehash&expires=10000'); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('weaverryan') - ->willThrowException(new UsernameNotFoundException()); - $linker = $this->createLinker(); $linker->consumeLoginLink($request); } @@ -145,10 +134,7 @@ public function testConsumeLoginLinkWithDifferentSignature() $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=fake_hash&expires=%d', time() + 500)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('weaverryan') - ->willReturn($user); + $this->userProvider->createUser($user); $linker = $this->createLinker(); $linker->consumeLoginLink($request); @@ -162,10 +148,7 @@ public function testConsumeLoginLinkExceedsMaxUsage() $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->with('weaverryan') - ->willReturn($user); + $this->userProvider->createUser($user); $this->expiredLinkStorage->expects($this->once()) ->method('countUsages') @@ -198,6 +181,35 @@ private function createLinker(array $options = [], array $extraProperties = ['em } } +class TestLoginLinkHandlerUserProvider implements UserProviderInterface +{ + private $users = []; + + public function createUser(TestLoginLinkHandlerUser $user): void + { + $this->users[$user->getUsername()] = $user; + } + + public function loadUserByUsername(string $username): TestLoginLinkHandlerUser + { + if (!isset($this->users[$username])) { + throw new UsernameNotFoundException(); + } + + return clone $this->users[$username]; + } + + public function refreshUser(UserInterface $user) + { + return $this->users[$username]; + } + + public function supportsClass(string $class) + { + return TestLoginLinkHandlerUser::class === $class; + } +} + class TestLoginLinkHandlerUser implements UserInterface { public $username; diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index f85422fe60eb5..50b16916ae37c 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -22,9 +22,9 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; +use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; @@ -94,12 +94,6 @@ public function testAutoLoginReturnsNullOnNonExistentUser() ; $service->setTokenProvider($tokenProvider); - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->willThrowException(new UsernameNotFoundException('user not found')) - ; - $this->assertNull($service->autoLogin($request)); $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } @@ -161,20 +155,10 @@ public function testAutoLoginDoesNotAcceptAnExpiredCookie() */ public function testAutoLogin(bool $hashTokenValue) { - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getRoles') - ->willReturn(['ROLE_FOO']) - ; + $user = new User('foouser', null, ['ROLE_FOO']); $userProvider = $this->getProvider(); - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->with($this->equalTo('foouser')) - ->willReturn($user) - ; + $userProvider->createUser($user); $service = $this->getService($userProvider, ['name' => 'foo', 'path' => null, 'domain' => null, 'secure' => false, 'httponly' => false, 'always_remember_me' => true, 'lifetime' => 3600]); $request = new Request(); @@ -186,14 +170,14 @@ public function testAutoLogin(bool $hashTokenValue) ->expects($this->once()) ->method('loadTokenBySeries') ->with($this->equalTo('fooseries')) - ->willReturn(new PersistentToken('fooclass', 'foouser', 'fooseries', $tokenValue, new \DateTime())) + ->willReturn(new PersistentToken(User::class, 'foouser', 'fooseries', $tokenValue, new \DateTime())) ; $service->setTokenProvider($tokenProvider); $returnedToken = $service->autoLogin($request); $this->assertInstanceOf(RememberMeToken::class, $returnedToken); - $this->assertSame($user, $returnedToken->getUser()); + $this->assertTrue($user->isEqualTo($returnedToken->getUser())); $this->assertEquals('foosecret', $returnedToken->getSecret()); $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } @@ -339,14 +323,7 @@ protected function getService($userProvider = null, $options = [], $logger = nul protected function getProvider() { - $provider = $this->createMock(UserProviderInterface::class); - $provider - ->expects($this->any()) - ->method('supportsClass') - ->willReturn(true) - ; - - return $provider; + return new InMemoryUserProvider(); } protected function generateHash(string $tokenValue): string diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php index 55cfb678f3014..ea4f6dc89bb48 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php @@ -18,9 +18,9 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; +use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices; @@ -51,12 +51,6 @@ public function testAutoLoginThrowsExceptionOnNonExistentUser() $request = new Request(); $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() + 3600, 'foopass')); - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->willThrowException(new UsernameNotFoundException('user not found')) - ; - $this->assertNull($service->autoLogin($request)); $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); } @@ -68,19 +62,8 @@ public function testAutoLoginDoesNotAcceptCookieWithInvalidHash() $request = new Request(); $request->cookies->set('foo', base64_encode('class:'.base64_encode('foouser').':123456789:fooHash')); - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getPassword') - ->willReturn('foopass') - ; - - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->with($this->equalTo('foouser')) - ->willReturn($user) - ; + $user = new User('foouser', 'foopass'); + $userProvider->createUser($user); $this->assertNull($service->autoLogin($request)); $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); @@ -93,19 +76,8 @@ public function testAutoLoginDoesNotAcceptAnExpiredCookie() $request = new Request(); $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() - 1, 'foopass')); - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getPassword') - ->willReturn('foopass') - ; - - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->with($this->equalTo('foouser')) - ->willReturn($user) - ; + $user = new User('foouser', 'foopass'); + $userProvider->createUser($user); $this->assertNull($service->autoLogin($request)); $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); @@ -118,34 +90,18 @@ public function testAutoLoginDoesNotAcceptAnExpiredCookie() */ public function testAutoLogin($username) { - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getRoles') - ->willReturn(['ROLE_FOO']) - ; - $user - ->expects($this->once()) - ->method('getPassword') - ->willReturn('foopass') - ; - $userProvider = $this->getProvider(); - $userProvider - ->expects($this->once()) - ->method('loadUserByUsername') - ->with($this->equalTo($username)) - ->willReturn($user) - ; + $user = new User($username, 'foopass', ['ROLE_FOO']); + $userProvider->createUser($user); $service = $this->getService($userProvider, ['name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600]); $request = new Request(); - $request->cookies->set('foo', $this->getCookie('fooclass', $username, time() + 3600, 'foopass')); + $request->cookies->set('foo', $this->getCookie(User::class, $username, time() + 3600, 'foopass')); $returnedToken = $service->autoLogin($request); $this->assertInstanceOf(RememberMeToken::class, $returnedToken); - $this->assertSame($user, $returnedToken->getUser()); + $this->assertTrue($user->isEqualTo($returnedToken->getUser())); $this->assertEquals('foosecret', $returnedToken->getSecret()); } @@ -279,13 +235,6 @@ protected function getService($userProvider = null, $options = [], $logger = nul protected function getProvider() { - $provider = $this->createMock(UserProviderInterface::class); - $provider - ->expects($this->any()) - ->method('supportsClass') - ->willReturn(true) - ; - - return $provider; + return new InMemoryUserProvider(); } } From 631408b75780228955a95c9b2d842afa7ac80d38 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 28 Mar 2021 19:08:45 +0200 Subject: [PATCH 70/72] [Security] Use more concrete user classes in tests --- .../Tests/Firewall/SwitchUserListenerTest.php | 97 ++++++------------- ...istentTokenBasedRememberMeServicesTest.php | 8 +- .../TokenBasedRememberMeServicesTest.php | 13 +-- 3 files changed, 32 insertions(+), 86 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index cc333cd7e9f44..559ef3e0ef1a2 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -22,10 +22,9 @@ use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Event\SwitchUserEvent; use Symfony\Component\Security\Http\Firewall\SwitchUserListener; use Symfony\Component\Security\Http\SecurityEvents; @@ -48,7 +47,7 @@ class SwitchUserListenerTest extends TestCase protected function setUp(): void { $this->tokenStorage = new TokenStorage(); - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new InMemoryUserProvider(['kuba' => []]); $this->userChecker = $this->createMock(UserCheckerInterface::class); $this->accessDecisionManager = $this->createMock(AccessDecisionManagerInterface::class); $this->request = new Request(); @@ -113,8 +112,8 @@ public function testExitUserDispatchesEventWithRefreshedUser() { $originalUser = new User('username', null); $refreshedUser = new User('username', null); - $this - ->userProvider + $userProvider = $this->createMock(InMemoryUserProvider::class); + $userProvider ->expects($this->any()) ->method('refreshUser') ->with($this->identicalTo($originalUser)) @@ -135,15 +134,15 @@ public function testExitUserDispatchesEventWithRefreshedUser() ) ; - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener = new SwitchUserListener($this->tokenStorage, $userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); $listener($this->event); } public function testExitUserDoesNotDispatchEventWithStringUser() { $originalUser = 'anon.'; - $this - ->userProvider + $userProvider = $this->createMock(InMemoryUserProvider::class); + $userProvider ->expects($this->never()) ->method('refreshUser'); $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); @@ -156,7 +155,7 @@ public function testExitUserDoesNotDispatchEventWithStringUser() ->method('dispatch') ; - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener = new SwitchUserListener($this->tokenStorage, $userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); $listener($this->event); } @@ -173,11 +172,6 @@ public function testSwitchUserIsDisallowed() ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH']) ->willReturn(false); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); } @@ -188,16 +182,11 @@ public function testSwitchUserTurnsAuthenticationExceptionTo403() $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_ALLOWED_TO_SWITCH']); $this->tokenStorage->setToken($token); - $this->request->query->set('_switch_user', 'kuba'); + $this->request->query->set('_switch_user', 'not-existing'); $this->accessDecisionManager->expects($this->never()) ->method('decide'); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba'], ['username']) - ->will($this->onConsecutiveCalls($this->throwException(new UsernameNotFoundException()))); - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); } @@ -205,21 +194,16 @@ public function testSwitchUserTurnsAuthenticationExceptionTo403() public function testSwitchUser() { $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']); - $user = new User('username', 'password', []); $this->tokenStorage->setToken($token); $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $user) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUsername(); })) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($user); + ->method('checkPostAuth')->with($this->callback(function ($user) { return 'kuba' === $user->getUsername(); })); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); @@ -237,20 +221,15 @@ public function testSwitchUserAlreadySwitched() $tokenStorage = new TokenStorage(); $tokenStorage->setToken($alreadySwitchedToken); - $targetUser = new User('kuba', 'password', ['ROLE_FOO', 'ROLE_BAR']); - $this->request->query->set('_switch_user', 'kuba'); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($originalToken, ['ROLE_ALLOWED_TO_SWITCH'], $targetUser) + ->method('decide')->with($originalToken, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($targetUser, $this->throwException(new UsernameNotFoundException()))); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($targetUser); + ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', null, false); $listener($this->event); @@ -264,22 +243,19 @@ public function testSwitchUserAlreadySwitched() public function testSwitchUserWorksWithFalsyUsernames() { - $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']); - $user = new User('username', 'password', []); + $token = new UsernamePasswordToken('kuba', '', 'key', ['ROLE_FOO']); $this->tokenStorage->setToken($token); $this->request->query->set('_switch_user', '0'); + $this->userProvider->createUser($user = new User('0', null)); + $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH']) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['0']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($user); + ->method('checkPostAuth')->with($this->callback(function ($argUser) use ($user) { return $user->isEqualTo($argUser); })); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); @@ -292,7 +268,6 @@ public function testSwitchUserWorksWithFalsyUsernames() public function testSwitchUserKeepsOtherQueryStringParameters() { $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']); - $user = new User('username', 'password', []); $this->tokenStorage->setToken($token); $this->request->query->replace([ @@ -301,16 +276,13 @@ public function testSwitchUserKeepsOtherQueryStringParameters() 'section' => 2, ]); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $user) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($user); + ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); @@ -331,21 +303,16 @@ public function testSwitchUserWithReplacedToken() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->any()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $user) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUsername(); })) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); - $dispatcher = $this->createMock(EventDispatcherInterface::class); $dispatcher ->expects($this->once()) ->method('dispatch') ->with( - $this->callback(function (SwitchUserEvent $event) use ($replacedToken, $user) { - if ($user !== $event->getTargetUser()) { + $this->callback(function (SwitchUserEvent $event) use ($replacedToken) { + if ('kuba' !== $event->getTargetUser()->getUsername()) { return false; } $event->setToken($replacedToken); @@ -373,21 +340,17 @@ public function testSwitchUserThrowsAuthenticationExceptionIfNoCurrentToken() public function testSwitchUserStateless() { $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']); - $user = new User('username', 'password', []); $this->tokenStorage->setToken($token); $this->request->query->set('_switch_user', 'kuba'); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $user) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); - $this->userProvider->expects($this->exactly(2)) - ->method('loadUserByUsername') - ->withConsecutive(['kuba']) - ->will($this->onConsecutiveCalls($user, $this->throwException(new UsernameNotFoundException()))); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($user); + ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', null, true); $listener($this->event); @@ -400,8 +363,8 @@ public function testSwitchUserRefreshesOriginalToken() { $originalUser = new User('username', null); $refreshedOriginalUser = new User('username', null); - $this - ->userProvider + $userProvider = $this->createMock(InMemoryUserProvider::class); + $userProvider ->expects($this->any()) ->method('refreshUser') ->with($this->identicalTo($originalUser)) @@ -422,7 +385,7 @@ public function testSwitchUserRefreshesOriginalToken() ) ; - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener = new SwitchUserListener($this->tokenStorage, $userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); $listener($this->event); } } diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 50b16916ae37c..0efda325d3552 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -24,7 +24,6 @@ use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; @@ -267,12 +266,7 @@ public function testLoginSuccessSetsCookieWhenLoggedInWithNonRememberMeTokenInte $request = new Request(); $response = new Response(); - $account = $this->createMock(UserInterface::class); - $account - ->expects($this->once()) - ->method('getUsername') - ->willReturn('foo') - ; + $account = new User('foo', null); $token = $this->createMock(TokenInterface::class); $token ->expects($this->any()) diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php index ea4f6dc89bb48..cc8152047359a 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php @@ -20,7 +20,6 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices; @@ -170,18 +169,8 @@ public function testLoginSuccess() $request = new Request(); $response = new Response(); + $user = new User('foouser', 'foopass'); $token = $this->createMock(TokenInterface::class); - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getPassword') - ->willReturn('foopass') - ; - $user - ->expects($this->once()) - ->method('getUsername') - ->willReturn('foouser') - ; $token ->expects($this->atLeastOnce()) ->method('getUser') From 361206abe0a6f8b365a9c67080aaeb03e98a25d6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 29 Mar 2021 07:16:50 +0200 Subject: [PATCH 71/72] Update CHANGELOG for 5.2.6 --- CHANGELOG-5.2.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG-5.2.md b/CHANGELOG-5.2.md index 68a57b4be09f1..5e5f455254f7a 100644 --- a/CHANGELOG-5.2.md +++ b/CHANGELOG-5.2.md @@ -7,6 +7,46 @@ in 5.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.2.0...v5.2.1 +* 5.2.6 (2021-03-29) + + * bug #40598 [Form] error if the input string couldn't be parsed as a date (xabbuh) + * bug #40587 [HttpClient] fix using stream_copy_to_stream() with responses cast to php streams (nicolas-grekas) + * bug #40510 [Form] IntegerType: Always use en for IntegerToLocalizedStringTransformer (Warxcell) + * bug #40593 Uses the correct assignment action for console options depending if they are short or long (topikito) + * bug #40535 [HttpKernel] ConfigDataCollector to return known data without the need of a Kernel (topikito) + * bug #40552 [Translation] Fix update existing key with existing +int-icu domain (Alexis) + * bug #40541 Fixed parsing deprecated definitions without message key (adamwojs) + * bug #40537 [Security] Handle properly 'auto' option for remember me cookie security (fliespl) + * bug #40524 [Console] fix emojis messing up the line width (MarionLeHerisson) + * bug #40506 [Validator] Avoid triggering the autoloader for user-input values (Seldaek) + * bug #40544 [FrameworkBundle] ensure TestBrowserToken::$firewallName is serialized (kbond) + * bug #40547 [RateLimiter] Security hardening - Rate limiter (jderusse) + * bug #40538 [HttpClient] remove using $http_response_header (nicolas-grekas) + * bug #40508 [PhpUnitBridge] fix reporting deprecations from DebugClassLoader (nicolas-grekas) + * bug #40497 [HttpFoundation] enable HTTP method overrides as early as possible with the HTTP cache (xabbuh) + * bug #40348 [Console] Fix line wrapping for decorated text in block output (grasmash) + * bug #40499 [Inflector][String] Fixed pluralize "coupon" (Nyholm) + * bug #40494 [PhpUnitBridge] fix compat with symfony/debug (nicolas-grekas) + * bug #40453 [VarDumper] Adds support for ReflectionUnionType to VarDumper (Michael Nelson, michaeldnelson) + * bug #40460 Correctly clear lines for multi-line progress bar messages (grasmash) + * bug #40490 [Security] Add XML support for authenticator manager (wouterj) + * bug #40242 [ErrorHandler] Fix error caused by `include` + open_basedir (stlrnz) + * bug #40368 [FrameworkBundle] Make the TestBrowserToken interchangeable with other tokens (Seldaek) + * bug #40450 [Console] ProgressBar clears too many lines on update (danepowell) + * bug #40178 [FrameworkBundle] Exclude unreadable files when executing About command (michaljusiega) + * bug #40472 [Bridge\Twig] Add 'form-control-range' for range input type (Oviglo) + * bug #40481 make async-ses required (jderusse) + * bug #39866 [Mime] Escape commas in address names (YaFou) + * bug #40373 Check if templating engine supports given view (fritzmg) + * bug #39992 [Security] Refresh original user in SwitchUserListener (AndrolGenhald) + * bug #40446 [TwigBridge] Fix "Serialization of 'Closure'" error when rendering an TemplatedEmail (jderusse) + * bug #40416 Fix `ConstraintViolation#getMessageTemplate()` to always return `string` (Ocramius) + * bug #40425 [DoctrineBridge] Fix eventListener initialization when eventSubscriber constructor dispatch an event (jderusse) + * bug #40313 [FrameworkBundle] Fix PropertyAccess definition when not in debug (PedroTroller) + * bug #40417 [Form] clear unchecked choice radio boxes even if clear missing is set to false (xabbuh) + * bug #40388 [ErrorHandler] Added missing type annotations to FlattenException (derrabus) + * bug #40407 [TwigBridge] Allow version 3 of the Twig extra packages (derrabus) + * 5.2.5 (2021-03-10) * bug #40415 Fix `ConstraintViolation#getPropertyPath()` to always return `string` (Ocramius) From 4217e4c4cc8ec8a40b10c621310f73f264253997 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 29 Mar 2021 07:16:58 +0200 Subject: [PATCH 72/72] Update VERSION for 5.2.6 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f36f4bed4a1b1..7549ae9b21ac6 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -74,12 +74,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '5.2.6-DEV'; + public const VERSION = '5.2.6'; public const VERSION_ID = 50206; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 2; public const RELEASE_VERSION = 6; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2021'; public const END_OF_LIFE = '07/2021';