From a0bce33815df3ed01280fb7ab551c57c071a39c7 Mon Sep 17 00:00:00 2001 From: Eric COURTIAL Date: Sat, 5 Mar 2022 16:39:30 +0100 Subject: [PATCH 01/49] Remove blocking test for adding support for placeholders in EmumNode in 6.1 --- .../Compiler/ValidateEnvPlaceholdersPassTest.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php index dd88c71227c52..3258328993650 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php @@ -156,19 +156,6 @@ public function testConcatenatedEnvInConfig() $this->assertSame(['scalar_node' => $expected], $container->resolveEnvPlaceholders($ext->getConfig())); } - public function testEnvIsIncompatibleWithEnumNode() - { - $this->expectException(InvalidConfigurationException::class); - $this->expectExceptionMessage('A dynamic value is not compatible with a "Symfony\Component\Config\Definition\EnumNode" node type at path "env_extension.enum_node".'); - $container = new ContainerBuilder(); - $container->registerExtension(new EnvExtension()); - $container->prependExtensionConfig('env_extension', [ - 'enum_node' => '%env(FOO)%', - ]); - - $this->doProcess($container); - } - public function testEnvIsIncompatibleWithArrayNode() { $this->expectException(InvalidConfigurationException::class); From c3ba272a9d7a125f55b147f88387907a3017d4bc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 5 Mar 2022 22:14:05 +0100 Subject: [PATCH 02/49] Bump Symfony version to 4.4.40 --- 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 e4903bfff60aa..233c80f10b55d 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.39'; - public const VERSION_ID = 40439; + public const VERSION = '4.4.40-DEV'; + public const VERSION_ID = 40440; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 39; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 40; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2022'; public const END_OF_LIFE = '11/2023'; From 698cc970fd3dcf5b92633c527d6b9f1d36e53a3d Mon Sep 17 00:00:00 2001 From: RTUnreal <22859658+RTUnreal@users.noreply.github.com> Date: Mon, 7 Mar 2022 14:29:34 +0100 Subject: [PATCH 03/49] Center icons vertically in trace list Make icons centered in trace list and not on the bottom trace-line-header div --- .../Component/ErrorHandler/Resources/assets/css/exception.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css index 952c66d2fc936..3f629a4104fef 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css +++ b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css @@ -184,7 +184,7 @@ header .container { display: flex; justify-content: space-between; } .trace-line + .trace-line { border-top: var(--border); } .trace-line:hover { background: var(--base-1); } .trace-line a { color: var(--base-6); } -.trace-line .icon { opacity: .4; position: absolute; left: 10px; top: 11px; } +.trace-line .icon { opacity: .4; position: absolute; left: 10px; } .trace-line .icon svg { height: 16px; width: 16px; } .trace-line-header { padding-left: 36px; padding-right: 10px; } From 0d99a547ad107f43ac9c84687420bf933e062e38 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Mar 2022 19:38:28 +0100 Subject: [PATCH 04/49] [Asset] Update the error message when assets are not built --- .../Asset/VersionStrategy/JsonManifestVersionStrategy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php index 72688a4926444..998c636e6134a 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php @@ -54,7 +54,7 @@ private function getManifestPath(string $path): ?string { if (null === $this->manifestData) { if (!file_exists($this->manifestPath)) { - throw new \RuntimeException(sprintf('Asset manifest file "%s" does not exist.', $this->manifestPath)); + throw new \RuntimeException(sprintf('Asset manifest file "%s" does not exist. Did you forget to build the assets with npm or yarn?', $this->manifestPath)); } $this->manifestData = json_decode(file_get_contents($this->manifestPath), true); From 608b1ddf6c74a4a300a8c06e8a34af78a2bdb019 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 8 Mar 2022 12:26:23 +0100 Subject: [PATCH 05/49] [FrameworkBundle] Ensure container is reset between tests --- src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index a65a17b4c53e2..1ec6548be2738 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Contracts\Service\ResetInterface; /** * KernelTestCase is the base class for tests needing a Kernel. @@ -131,8 +132,13 @@ protected static function ensureKernelShutdown() { if (null !== static::$kernel) { static::$kernel->boot(); + $container = static::$kernel->getContainer(); static::$kernel->shutdown(); static::$booted = false; + + if ($container instanceof ResetInterface) { + $container->reset(); + } } static::$container = null; From 37ca066c6fb3aac241ccfb12b2531675798528d7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 8 Mar 2022 09:52:50 +0100 Subject: [PATCH 06/49] Stand with Ukraine --- src/Symfony/Bundle/FrameworkBundle/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index dddde43dda4a1..02c38c2a2c1bc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -148,7 +148,7 @@ public function all($namespace = null) */ public function getLongVersion() { - return parent::getLongVersion().sprintf(' (env: %s, debug: %s)', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); + return parent::getLongVersion().sprintf(' (env: %s, debug: %s) #StandWithUkraine https://sf.to/ukraine', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); } public function add(Command $command) From 0558ea37191d01108f411f50d38a7bc6a9356291 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 8 Mar 2022 16:15:48 +0100 Subject: [PATCH 07/49] Fix colors for 4.4 --- src/Symfony/Bundle/FrameworkBundle/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index 02c38c2a2c1bc..20fb6e05f73de 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -148,7 +148,7 @@ public function all($namespace = null) */ public function getLongVersion() { - return parent::getLongVersion().sprintf(' (env: %s, debug: %s) #StandWithUkraine https://sf.to/ukraine', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); + return parent::getLongVersion().sprintf(' (env: %s, debug: %s) #StandWithUkraine https://sf.to/ukraine', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); } public function add(Command $command) From 947152b2ff849df9d7510654ae5b99beb3f9685e Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 8 Mar 2022 18:16:11 +0100 Subject: [PATCH 08/49] [Process] Don't return executable directories in PhpExecutableFinder --- .../Component/Process/PhpExecutableFinder.php | 10 +++++-- .../Process/Tests/PhpExecutableFinderTest.php | 30 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index 3d5eabd997cbe..92e0262ad7c95 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -47,6 +47,10 @@ public function find($includeArgs = true) } } + if (@is_dir($php)) { + return false; + } + return $php; } @@ -59,7 +63,7 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PATH')) { - if (!@is_executable($php)) { + if (!@is_executable($php) || @is_dir($php)) { return false; } @@ -67,12 +71,12 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PEAR_PHP_BIN')) { - if (@is_executable($php)) { + if (@is_executable($php) && !@is_dir($php)) { return $php; } } - if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { + if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php')) && !@is_dir($php)) { return $php; } diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index cf3ffb55efb78..0ef4cf092b3ba 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -50,12 +50,32 @@ public function testFindArguments() public function testNotExitsBinaryFile() { $f = new PhpExecutableFinder(); - $phpBinaryEnv = \PHP_BINARY; - putenv('PHP_BINARY=/usr/local/php/bin/php-invalid'); - $this->assertFalse($f->find(), '::find() returns false because of not exist file'); - $this->assertFalse($f->find(false), '::find(false) returns false because of not exist file'); + $originalPhpBinary = getenv('PHP_BINARY'); - putenv('PHP_BINARY='.$phpBinaryEnv); + try { + putenv('PHP_BINARY=/usr/local/php/bin/php-invalid'); + + $this->assertFalse($f->find(), '::find() returns false because of not exist file'); + $this->assertFalse($f->find(false), '::find(false) returns false because of not exist file'); + } finally { + putenv('PHP_BINARY='.$originalPhpBinary); + } + } + + public function testFindWithExecutableDirectory() + { + $originalPhpBinary = getenv('PHP_BINARY'); + + try { + $executableDirectoryPath = sys_get_temp_dir().'/PhpExecutableFinderTest_testFindWithExecutableDirectory'; + @mkdir($executableDirectoryPath); + $this->assertTrue(is_executable($executableDirectoryPath)); + putenv('PHP_BINARY='.$executableDirectoryPath); + + $this->assertFalse((new PhpExecutableFinder())->find()); + } finally { + putenv('PHP_BINARY='.$originalPhpBinary); + } } } From 9e5305e68aaf022ba24a7da10318716335205a46 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 8 Mar 2022 19:37:06 +0100 Subject: [PATCH 09/49] [HttpClient] Fix reading proxy settings from dotenv when curl is used --- .../Component/HttpClient/CurlHttpClient.php | 15 +++++++++++++-- .../HttpClient/Test/HttpClientTestCase.php | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index e77eb86c4c452..660ba61ee9045 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -92,6 +92,10 @@ public function request(string $method, string $url, array $options = []): Respo $scheme = $url['scheme']; $authority = $url['authority']; $host = parse_url($authority, \PHP_URL_HOST); + $proxy = $options['proxy'] + ?? ('https:' === $url['scheme'] ? $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? null : null) + // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities + ?? $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null; $url = implode('', $url); if (!isset($options['normalized_headers']['user-agent'])) { @@ -107,7 +111,7 @@ public function request(string $method, string $url, array $options = []): Respo \CURLOPT_MAXREDIRS => 0 < $options['max_redirects'] ? $options['max_redirects'] : 0, \CURLOPT_COOKIEFILE => '', // Keep track of cookies during redirects \CURLOPT_TIMEOUT => 0, - \CURLOPT_PROXY => $options['proxy'], + \CURLOPT_PROXY => $proxy, \CURLOPT_NOPROXY => $options['no_proxy'] ?? $_SERVER['no_proxy'] ?? $_SERVER['NO_PROXY'] ?? '', \CURLOPT_SSL_VERIFYPEER => $options['verify_peer'], \CURLOPT_SSL_VERIFYHOST => $options['verify_host'] ? 2 : 0, @@ -404,8 +408,15 @@ private static function createRedirectResolver(array $options, string $host): \C } $url = self::parseUrl(curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)); + $url = self::resolveUrl($location, $url); - return implode('', self::resolveUrl($location, $url)); + curl_setopt($ch, \CURLOPT_PROXY, $options['proxy'] + ?? ('https:' === $url['scheme'] ? $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? null : null) + // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities + ?? $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null + ); + + return implode('', $url); }; } } diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index 7ebf055d75701..ddc5324492c86 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -929,6 +929,16 @@ public function testProxy() $body = $response->toArray(); $this->assertSame('Basic Zm9vOmI9YXI=', $body['HTTP_PROXY_AUTHORIZATION']); + + $_SERVER['http_proxy'] = 'http://localhost:8057'; + try { + $response = $client->request('GET', 'http://localhost:8057/'); + $body = $response->toArray(); + $this->assertSame('localhost:8057', $body['HTTP_HOST']); + $this->assertMatchesRegularExpression('#^http://(localhost|127\.0\.0\.1):8057/$#', $body['REQUEST_URI']); + } finally { + unset($_SERVER['http_proxy']); + } } public function testNoProxy() From 4b8cd5205eb40eb063c781cb8ffd7abbd28514d0 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 8 Mar 2022 13:07:55 -0500 Subject: [PATCH 10/49] [DI] fix `ServiceSubscriberTrait` bug where parent has `__call()` --- .../Service/ServiceSubscriberTrait.php | 4 +- .../Service/ServiceSubscriberTraitTest.php | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php index 0c085303748d9..020887b3f5736 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php @@ -35,7 +35,7 @@ public static function getSubscribedServices(): array return $services; } - $services = \is_callable(['parent', __FUNCTION__]) ? parent::getSubscribedServices() : []; + $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { @@ -69,7 +69,7 @@ public function setContainer(ContainerInterface $container) { $this->container = $container; - if (\is_callable(['parent', __FUNCTION__])) { + if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { return parent::setContainer($container); } diff --git a/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php b/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php index 17fd108dbd08c..1d95824baf1e1 100644 --- a/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php +++ b/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php @@ -36,6 +36,32 @@ public function testSetContainerIsCalledOnParent() $this->assertSame($container, (new TestService())->setContainer($container)); } + public function testParentNotCalledIfHasMagicCall() + { + $container = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + $service = new class() extends ParentWithMagicCall { + use ServiceSubscriberTrait; + }; + + $this->assertNull($service->setContainer($container)); + $this->assertSame([], $service::getSubscribedServices()); + } + + public function testParentNotCalledIfNoParent() + { + $container = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + $service = new class() { + use ServiceSubscriberTrait; + }; + + $this->assertNull($service->setContainer($container)); + $this->assertSame([], $service::getSubscribedServices()); + } + /** * @requires PHP 8 */ @@ -77,3 +103,16 @@ public function aChildService(): Service3 { } } + +class ParentWithMagicCall +{ + public function __call($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } + + public static function __callStatic($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } +} From 542c2fbc2ee8857ea6c64e3db6de3540851006c1 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Thu, 10 Mar 2022 12:22:15 +0100 Subject: [PATCH 11/49] Fix the usage of the Valid constraints in array-based forms --- .../Validator/Constraints/FormValidator.php | 2 +- .../FormValidatorFunctionalTest.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index b0deef7f04e50..fc50af4d01d94 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -110,7 +110,7 @@ public function validate($form, Constraint $formConstraint) foreach ($constraints as $constraint) { // For the "Valid" constraint, validate the data in all groups if ($constraint instanceof Valid) { - if (\is_object($data)) { + if (\is_object($data) || \is_array($data)) { $validator->atPath('data')->validate($data, $constraint, $groups); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php index e73947ada3081..35712707b1aca 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; @@ -328,6 +329,35 @@ public function testCascadeValidationToChildFormsWithTwoValidConstraints2() $this->assertSame('children[author].data.email', $violations[1]->getPropertyPath()); } + public function testCascadeValidationToArrayChildForm() + { + $form = $this->formFactory->create(FormType::class, null, [ + 'data_class' => Review::class, + ]) + ->add('title') + ->add('customers', CollectionType::class, [ + 'mapped' => false, + 'entry_type' => CustomerType::class, + 'allow_add' => true, + 'constraints' => [new Valid()], + ]); + + $form->submit([ + 'title' => 'Sample Title', + 'customers' => [ + ['email' => null], + ], + ]); + + $violations = $this->validator->validate($form); + + $this->assertCount(2, $violations); + $this->assertSame('This value should not be blank.', $violations[0]->getMessage()); + $this->assertSame('data.rating', $violations[0]->getPropertyPath()); + $this->assertSame('This value should not be blank.', $violations[1]->getMessage()); + $this->assertSame('children[customers].data[0].email', $violations[1]->getPropertyPath()); + } + public function testCascadeValidationToChildFormsUsingPropertyPathsValidatedInSequence() { $form = $this->formFactory->create(FormType::class, null, [ From b370b322909964ca7195b07d5f7a9a40539f15b8 Mon Sep 17 00:00:00 2001 From: jmsche Date: Fri, 11 Mar 2022 15:23:30 +0100 Subject: [PATCH 12/49] Remove extra space in NotificationEmail --- src/Symfony/Bridge/Twig/Mime/NotificationEmail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php index e6b28c4db43d2..1a58aa5e5e5bc 100644 --- a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php @@ -43,7 +43,7 @@ public function __construct(Headers $headers = null, AbstractPart $body = null) { $missingPackages = []; if (!class_exists(CssInlinerExtension::class)) { - $missingPackages['twig/cssinliner-extra'] = ' CSS Inliner'; + $missingPackages['twig/cssinliner-extra'] = 'CSS Inliner'; } if (!class_exists(InkyExtension::class)) { From 7134e46638bfc11be5f8fbf4d5a10d6cc10a9956 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Wed, 9 Mar 2022 23:15:29 +0100 Subject: [PATCH 13/49] [Mailer] Use recipients in sendmail transport --- .../Transport/Fixtures/fake-sendmail.php | 5 ++ .../Tests/Transport/SendmailTransportTest.php | 68 +++++++++++++++++++ .../Mailer/Transport/SendmailTransport.php | 9 +++ 3 files changed, 82 insertions(+) create mode 100755 src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-sendmail.php diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-sendmail.php b/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-sendmail.php new file mode 100755 index 0000000000000..5a4bafd20f1d1 --- /dev/null +++ b/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-sendmail.php @@ -0,0 +1,5 @@ +#!/usr/bin/env php +argsPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'sendmail_args'; + } + + protected function tearDown(): void + { + if (file_exists($this->argsPath)) { + @unlink($this->argsPath); + } + unset($this->argsPath); + } + public function testToString() { $t = new SendmailTransport(); $this->assertEquals('smtp://sendmail', (string) $t); } + + public function testToIsUsedWhenRecipientsAreNotSet() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams'); + } + + $mail = new Email(); + $mail + ->from('from@mail.com') + ->to('to@mail.com') + ->subject('Subject') + ->text('Some text') + ; + + $envelope = new DelayedEnvelope($mail); + + $sendmailTransport = new SendmailTransport(self::FAKE_SENDMAIL); + $sendmailTransport->send($mail, $envelope); + + $this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-sendmail.php -ffrom@mail.com to@mail.com'); + } + + public function testRecipientsAreUsedWhenSet() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams'); + } + + $mail = new Email(); + $mail + ->from('from@mail.com') + ->to('to@mail.com') + ->subject('Subject') + ->text('Some text') + ; + + $envelope = new DelayedEnvelope($mail); + $envelope->setRecipients([new Address('recipient@mail.com')]); + + $sendmailTransport = new SendmailTransport(self::FAKE_SENDMAIL); + $sendmailTransport->send($mail, $envelope); + + $this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-sendmail.php -ffrom@mail.com recipient@mail.com'); + } } diff --git a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php index c53d72f6974d6..e272d05e0e31c 100644 --- a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php +++ b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php @@ -86,6 +86,11 @@ protected function doSend(SentMessage $message): void $this->getLogger()->debug(sprintf('Email transport "%s" starting', __CLASS__)); $command = $this->command; + + if (!empty($recipients = $message->getEnvelope()->getRecipients())) { + $command = str_replace(' -t', '', $command); + } + if (!str_contains($command, ' -f')) { $command .= ' -f'.escapeshellarg($message->getEnvelope()->getSender()->getEncodedAddress()); } @@ -96,6 +101,10 @@ protected function doSend(SentMessage $message): void $chunks = AbstractStream::replace("\n.", "\n..", $chunks); } + foreach ($recipients as $recipient) { + $command .= ' '.escapeshellarg($recipient->getEncodedAddress()); + } + $this->stream->setCommand($command); $this->stream->initialize(); foreach ($chunks as $chunk) { From 5ffedf5036f00b156ed1d071822ef1bbe1592063 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 5 Jan 2022 13:54:51 +0100 Subject: [PATCH 14/49] [Console] Fix compact table style to avoid outputting a leading space --- src/Symfony/Component/Console/Helper/Table.php | 4 ++-- .../Component/Console/Tests/Helper/TableTest.php | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 1d0a22baa3245..868374ab3832c 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -804,9 +804,9 @@ private static function initStyles(): array $compact = new TableStyle(); $compact ->setHorizontalBorderChars('') - ->setVerticalBorderChars(' ') + ->setVerticalBorderChars('') ->setDefaultCrossingChar('') - ->setCellRowContentFormat('%s') + ->setCellRowContentFormat('%s ') ; $styleGuide = new TableStyle(); diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index eed0b166237fe..d69e817422056 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -118,11 +118,11 @@ public function renderProvider() $books, 'compact', <<<'TABLE' - ISBN Title Author - 99921-58-10-7 Divine Comedy Dante Alighieri - 9971-5-0210-0 A Tale of Two Cities Charles Dickens - 960-425-059-0 The Lord of the Rings J. R. R. Tolkien - 80-902734-1-6 And Then There Were None Agatha Christie +ISBN Title Author +99921-58-10-7 Divine Comedy Dante Alighieri +9971-5-0210-0 A Tale of Two Cities Charles Dickens +960-425-059-0 The Lord of the Rings J. R. R. Tolkien +80-902734-1-6 And Then There Were None Agatha Christie TABLE ], From 53f473fb0718dbe14d650128de9ffba3587a672e Mon Sep 17 00:00:00 2001 From: VojtaB Date: Thu, 17 Mar 2022 18:57:09 +0100 Subject: [PATCH 15/49] ignoring exception from sem_get in SemaphoreStore Lock component, preventing of error: identifier removed --- src/Symfony/Component/Lock/Store/SemaphoreStore.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Lock/Store/SemaphoreStore.php b/src/Symfony/Component/Lock/Store/SemaphoreStore.php index 6fd48a4c2807e..b85a63c6b4b38 100644 --- a/src/Symfony/Component/Lock/Store/SemaphoreStore.php +++ b/src/Symfony/Component/Lock/Store/SemaphoreStore.php @@ -64,12 +64,12 @@ private function lock(Key $key, bool $blocking) } $keyId = unpack('i', md5($key, true))[1]; - $resource = sem_get($keyId); - $acquired = @sem_acquire($resource, !$blocking); + $resource = @sem_get($keyId); + $acquired = $resource && @sem_acquire($resource, !$blocking); while ($blocking && !$acquired) { - $resource = sem_get($keyId); - $acquired = @sem_acquire($resource); + $resource = @sem_get($keyId); + $acquired = $resource && @sem_acquire($resource); } if (!$acquired) { From 723cd0919d69b04b8cec823c0afe09736eaf0e8e Mon Sep 17 00:00:00 2001 From: Emil Masiakowski Date: Fri, 11 Mar 2022 18:25:39 +0100 Subject: [PATCH 16/49] [PropertyInfo] strip only leading `\` when unknown docType --- .../Tests/Extractor/PhpDocExtractorTest.php | 6 ++++++ .../PropertyInfo/Tests/Fixtures/PseudoTypeDummy.php | 11 +++++++++++ .../Component/PropertyInfo/Util/PhpDocTypeHelper.php | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/PseudoTypeDummy.php diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index 4a141642d0009..5737fb02cd5ef 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy; +use Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypeDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait; use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait; use Symfony\Component\PropertyInfo\Type; @@ -350,6 +351,11 @@ public function propertiesParentTypeProvider(): array ]; } + public function testUnknownPseudoType() + { + $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType')); + } + protected function isPhpDocumentorV5() { if (class_exists(InvalidTag::class)) { diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/PseudoTypeDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/PseudoTypeDummy.php new file mode 100644 index 0000000000000..71756044fafc9 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/PseudoTypeDummy.php @@ -0,0 +1,11 @@ + Date: Fri, 18 Mar 2022 09:12:19 +0100 Subject: [PATCH 17/49] Fix CS --- src/Symfony/Component/Mailer/Transport/SendmailTransport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php index e272d05e0e31c..df3dd6b43cfe0 100644 --- a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php +++ b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php @@ -87,7 +87,7 @@ protected function doSend(SentMessage $message): void $command = $this->command; - if (!empty($recipients = $message->getEnvelope()->getRecipients())) { + if ($recipients = $message->getEnvelope()->getRecipients()) { $command = str_replace(' -t', '', $command); } From 074ff42e983298dc0e2263ccc7776b1b07b54ec3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Mar 2022 11:44:25 +0100 Subject: [PATCH 18/49] [GHA] Fix psalm job --- .github/psalm/.gitignore | 2 + .../psalm/stubs/ForwardCompatTestTrait.php | 38 +++++++++++++++++++ .github/psalm/stubs/SetUpTearDownTrait.php | 19 ++++++++++ .github/workflows/psalm.yml | 17 ++++----- .../Test/ForwardCompatTestTrait.php | 2 +- .../Transport/Doctrine/Connection.php | 4 +- 6 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 .github/psalm/stubs/ForwardCompatTestTrait.php create mode 100644 .github/psalm/stubs/SetUpTearDownTrait.php diff --git a/.github/psalm/.gitignore b/.github/psalm/.gitignore index d6b7ef32c8478..53021ab087be4 100644 --- a/.github/psalm/.gitignore +++ b/.github/psalm/.gitignore @@ -1,2 +1,4 @@ * !.gitignore +!stubs +!stubs/* diff --git a/.github/psalm/stubs/ForwardCompatTestTrait.php b/.github/psalm/stubs/ForwardCompatTestTrait.php new file mode 100644 index 0000000000000..e3ddf4da3d431 --- /dev/null +++ b/.github/psalm/stubs/ForwardCompatTestTrait.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Test; + +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +trait ForwardCompatTestTrait +{ + private function doSetUp(): void + { + } + + private function doTearDown(): void + { + } + + protected function setUp(): void + { + $this->doSetUp(); + } + + protected function tearDown(): void + { + $this->doTearDown(); + } +} diff --git a/.github/psalm/stubs/SetUpTearDownTrait.php b/.github/psalm/stubs/SetUpTearDownTrait.php new file mode 100644 index 0000000000000..20dbe6fe73e0f --- /dev/null +++ b/.github/psalm/stubs/SetUpTearDownTrait.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +use PHPUnit\Framework\TestCase; + +trait SetUpTearDownTrait +{ + use Legacy\SetUpTearDownTraitForV8; +} diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index f607bf8d5eb05..8631d6c6a2b3d 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -20,7 +20,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' extensions: "json,memcached,mongodb,redis,xsl,ldap,dom" ini-values: "memory_limit=-1" coverage: none @@ -39,18 +39,17 @@ jobs: ([ -d "$COMPOSER_HOME" ] || mkdir "$COMPOSER_HOME") && cp .github/composer-config.json "$COMPOSER_HOME/config.json" export COMPOSER_ROOT_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | grep -P -o '[0-9]+\.[0-9]+').x-dev composer remove --dev --no-update --no-interaction symfony/phpunit-bridge - composer require --no-update psalm/phar phpunit/phpunit:^9.5 php-http/discovery psr/event-dispatcher mongodb/mongodb - - echo "::group::composer update" - composer update --no-progress --ansi - git checkout composer.json - echo "::endgroup::" - - ./vendor/bin/psalm.phar --version + composer require --no-progress --ansi psalm/phar phpunit/phpunit:^9.5 php-http/discovery psr/event-dispatcher mongodb/mongodb - name: Generate Psalm baseline run: | + git checkout composer.json git checkout -m ${{ github.base_ref }} + cat .github/psalm/stubs/SetUpTearDownTrait.php > src/Symfony/Bridge/PhpUnit/SetUpTearDownTrait.php + cat .github/psalm/stubs/ForwardCompatTestTrait.php | sed 's/Component/Bundle\\FrameworkBundle/' > src/Symfony/Bundle/FrameworkBundle/Test/ForwardCompatTestTrait.php + cat .github/psalm/stubs/ForwardCompatTestTrait.php | sed 's/Component/Component\\Form/' > src/Symfony/Component/Form/Test/ForwardCompatTestTrait.php + cat .github/psalm/stubs/ForwardCompatTestTrait.php | sed 's/Component/Component\\Validator/' > src/Symfony/Component/Validator/Test/ForwardCompatTestTrait.php + ./vendor/bin/psalm.phar --set-baseline=.github/psalm/psalm.baseline.xml --no-progress git checkout -m FETCH_HEAD diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/ForwardCompatTestTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/ForwardCompatTestTrait.php index 7dd933858088d..9a0dad403063c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/ForwardCompatTestTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/ForwardCompatTestTrait.php @@ -15,7 +15,7 @@ // Auto-adapt to PHPUnit 8 that added a `void` return-type to the setUp/tearDown methods -if (method_exists(\ReflectionMethod::class, 'hasReturnType') && (new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) { +if ((new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) { /** * @internal */ diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php index 48d00c4be5ede..47ab0824a2e1b 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php @@ -17,7 +17,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\LockMode; -use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\AbstractSchemaManager; @@ -397,7 +397,7 @@ private function getSchema(): Schema ->setNotnull(false); $table->setPrimaryKey(['id']); // No indices on queue_name and available_at on MySQL to prevent deadlock issues when running multiple consumers. - if (!$this->driverConnection->getDatabasePlatform() instanceof MySqlPlatform) { + if (!$this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) { $table->addIndex(['queue_name']); $table->addIndex(['available_at']); } From 79ee63639d0e001b5ca23c6e1c443a1d68e4318e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Mar 2022 11:33:36 +0100 Subject: [PATCH 19/49] [VarExporter] fix tests on Windows --- .../VarExporter/Tests/Fixtures/datetime-legacy.php | 4 ++-- .../Component/VarExporter/Tests/Fixtures/datetime.php | 4 ++-- src/Symfony/Component/VarExporter/Tests/VarExporterTest.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php index 7b217c5fb21b0..64c39f75faa8b 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php @@ -7,7 +7,7 @@ clone ($p['DateTimeZone'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('DateTimeZone')), clone ($p['DateInterval'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('DateInterval')), ], [ - 4 => 'O:10:"DatePeriod":6:{s:5:"start";O:8:"DateTime":3:{s:4:"date";s:26:"2012-07-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";b:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;}', + 4 => 'O:10:"DatePeriod":6:{s:5:"start";O:8:"DateTime":3:{s:4:"date";s:26:"2009-10-11 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Europe/Paris";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";i:7;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;}', ]), null, [ @@ -60,7 +60,7 @@ 3 => 0, ], 'days' => [ - 3 => false, + 3 => 7, ], 'special_type' => [ 3 => 0, diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php index 1de8fa03f0919..e9f41f9ade34c 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php @@ -5,8 +5,8 @@ 'O:8:"DateTime":3:{s:4:"date";s:26:"1970-01-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}', 'O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"1970-01-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}', 'O:12:"DateTimeZone":2:{s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Europe/Paris";}', - 'O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";b:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}', - 'O:10:"DatePeriod":6:{s:5:"start";O:8:"DateTime":3:{s:4:"date";s:26:"2012-07-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";b:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;}', + 'O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";i:7;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}', + 'O:10:"DatePeriod":6:{s:5:"start";O:8:"DateTime":3:{s:4:"date";s:26:"2009-10-11 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Europe/Paris";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";i:7;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;}', ]), null, [], diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index f87e4e9b01d1e..407f25af1891f 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -132,9 +132,9 @@ public function provideExport() yield ['datetime', [ \DateTime::createFromFormat('U', 0), \DateTimeImmutable::createFromFormat('U', 0), - new \DateTimeZone('Europe/Paris'), - new \DateInterval('P7D'), - new \DatePeriod('R4/2012-07-01T00:00:00Z/P7D'), + $tz = new \DateTimeZone('Europe/Paris'), + $interval = ($start = new \DateTime('2009-10-11', $tz))->diff(new \DateTime('2009-10-18', $tz)), + new \DatePeriod($start, $interval, 4), ]]; $value = \PHP_VERSION_ID >= 70406 ? new ArrayObject() : new \ArrayObject(); From 06b3cef1be3e39f893dd878e1933e922af68e65a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Mar 2022 17:18:39 +0100 Subject: [PATCH 20/49] [Finder] fix tests on Windows --- .../Component/Process/Tests/PhpExecutableFinderTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index 0ef4cf092b3ba..23de6d42eb5fb 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -65,6 +65,10 @@ public function testNotExitsBinaryFile() public function testFindWithExecutableDirectory() { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Directories are not executable on Windows'); + } + $originalPhpBinary = getenv('PHP_BINARY'); try { From 01f674975a2dd27340aab9b6e7402bc0aee8423f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Mar 2022 10:50:51 +0100 Subject: [PATCH 21/49] [HttpClient] minor cs fix --- src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 189788cce8ca9..989c6f32af2e6 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -205,9 +205,9 @@ public function testNegativeTimeout() public function testNullBody() { - $httpClient = $this->getHttpClient(__FUNCTION__); + $client = $this->getHttpClient(__FUNCTION__); - $httpClient->request('POST', 'http://localhost:8057/post', [ + $client->request('POST', 'http://localhost:8057/post', [ 'body' => null, ]); From b87868a6e7d9daf90d2cfd664b6ef28cbe837e58 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Mar 2022 11:25:55 +0100 Subject: [PATCH 22/49] [HttpClient] Move Content-Type after Content-Length --- .../Component/HttpClient/HttpClientTrait.php | 17 ++++++++++++----- .../HttpClient/Tests/ScopingHttpClientTest.php | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index 57d48aade8db2..20522255e9a97 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -76,12 +76,12 @@ private static function prepareRequest(?string $method, ?string $url, array $opt unset($options['json']); if (!isset($options['normalized_headers']['content-type'])) { - $options['normalized_headers']['content-type'] = [$options['headers'][] = 'Content-Type: application/json']; + $options['normalized_headers']['content-type'] = ['Content-Type: application/json']; } } if (!isset($options['normalized_headers']['accept'])) { - $options['normalized_headers']['accept'] = [$options['headers'][] = 'Accept: */*']; + $options['normalized_headers']['accept'] = ['Accept: */*']; } if (isset($options['body'])) { @@ -92,7 +92,6 @@ private static function prepareRequest(?string $method, ?string $url, array $opt && ('' !== $h || ('' !== $options['body'] && !isset($options['normalized_headers']['transfer-encoding']))) ) { $options['normalized_headers']['content-length'] = [substr_replace($h ?: 'Content-Length: ', \strlen($options['body']), 16)]; - $options['headers'] = array_merge(...array_values($options['normalized_headers'])); } } @@ -134,11 +133,11 @@ private static function prepareRequest(?string $method, ?string $url, array $opt if (null !== $url) { // Merge auth with headers if (($options['auth_basic'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) { - $options['normalized_headers']['authorization'] = [$options['headers'][] = 'Authorization: Basic '.base64_encode($options['auth_basic'])]; + $options['normalized_headers']['authorization'] = ['Authorization: Basic '.base64_encode($options['auth_basic'])]; } // Merge bearer with headers if (($options['auth_bearer'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) { - $options['normalized_headers']['authorization'] = [$options['headers'][] = 'Authorization: Bearer '.$options['auth_bearer']]; + $options['normalized_headers']['authorization'] = ['Authorization: Bearer '.$options['auth_bearer']]; } unset($options['auth_basic'], $options['auth_bearer']); @@ -161,6 +160,14 @@ private static function prepareRequest(?string $method, ?string $url, array $opt $options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0; + if (isset($options['normalized_headers']['content-length']) && $contentType = $options['normalized_headers']['content-type'] ?? null) { + // Move Content-Type after Content-Length, see https://bugs.php.net/44603 + unset($options['normalized_headers']['content-type']); + $options['normalized_headers']['content-type'] = $contentType; + } + + $options['headers'] = array_merge(...array_values($options['normalized_headers'])); + return [$url, $options]; } diff --git a/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php index bfca02b3581aa..a3e3099953ffe 100644 --- a/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php @@ -73,7 +73,7 @@ public function testMatchingUrlsAndOptions() $response = $client->request('GET', 'http://example.com/foo-bar', ['json' => ['url' => 'http://example.com']]); $requestOptions = $response->getRequestOptions(); - $this->assertSame('Content-Type: application/json', $requestOptions['headers'][1]); + $this->assertSame('Content-Type: application/json', $requestOptions['headers'][3]); $requestJson = json_decode($requestOptions['body'], true); $this->assertSame('http://example.com', $requestJson['url']); $this->assertSame('X-FooBar: '.$defaultOptions['.*/foo-bar']['headers']['X-FooBar'], $requestOptions['headers'][0]); From 99c69e1f1c1219b3c5c6e2fb3de76c92c210d747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Vu=C5=A1ak?= Date: Fri, 18 Mar 2022 21:43:27 +0100 Subject: [PATCH 23/49] Improve testsuite --- .github/workflows/unit-tests.yml | 2 +- src/Symfony/Component/Lock/Tests/Store/CombinedStoreTest.php | 4 +++- src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php | 1 + src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 49806815338d4..6be35796056d5 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -66,7 +66,7 @@ jobs: ([ -d "$COMPOSER_HOME" ] || mkdir "$COMPOSER_HOME") && cp .github/composer-config.json "$COMPOSER_HOME/config.json" echo COLUMNS=120 >> $GITHUB_ENV - echo PHPUNIT="$(pwd)/phpunit --exclude-group tty,benchmark,intl-data" >> $GITHUB_ENV + echo PHPUNIT="$(pwd)/phpunit --exclude-group tty,benchmark,intl-data,integration" >> $GITHUB_ENV echo COMPOSER_UP='composer update --no-progress --ansi' >> $GITHUB_ENV SYMFONY_VERSIONS=$(git ls-remote -q --heads | cut -f2 | grep -o '/[1-9][0-9]*\.[0-9].*' | sort -V) diff --git a/src/Symfony/Component/Lock/Tests/Store/CombinedStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/CombinedStoreTest.php index 991208f8b204e..ebb8199382d77 100644 --- a/src/Symfony/Component/Lock/Tests/Store/CombinedStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/CombinedStoreTest.php @@ -23,6 +23,7 @@ /** * @author Jérémy Derussé + * @group integration */ class CombinedStoreTest extends AbstractStoreTest { @@ -41,7 +42,8 @@ protected function getClockDelay() */ public function getStore(): PersistingStoreInterface { - $redis = new \Predis\Client(array_combine(['host', 'port'], explode(':', getenv('REDIS_HOST')) + [1 => null])); + $redis = new \Predis\Client(array_combine(['host', 'port'], explode(':', getenv('REDIS_HOST')) + [1 => 6379])); + try { $redis->connect(); } catch (\Exception $e) { diff --git a/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php index 082f50aa75e63..0562800f28cc2 100644 --- a/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php @@ -20,6 +20,7 @@ * @author Jérémy Derussé * * @requires extension pdo_sqlite + * @group integration */ class PdoStoreTest extends AbstractStoreTest { diff --git a/src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php index b3cd685a23160..2e63b08eb5ad3 100644 --- a/src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php @@ -20,6 +20,7 @@ * @author Ganesh Chandrasekaran * * @requires extension zookeeper + * @group integration */ class ZookeeperStoreTest extends AbstractStoreTest { From 63f8f1edb609e18ed8b8e71b3ce618236fe6eaf7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Mar 2022 15:33:05 +0100 Subject: [PATCH 24/49] [HttpClient] Let curl handle Content-Length headers --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 7 ++++++- .../Component/HttpClient/Tests/HttpClientTestCase.php | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 660ba61ee9045..e02f2c8b17d50 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -202,7 +202,12 @@ public function request(string $method, string $url, array $options = []): Respo $options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided } + $hasContentLength = isset($options['normalized_headers']['content-length'][0]); + foreach ($options['headers'] as $header) { + if ($hasContentLength && 0 === stripos($header, 'Content-Length:')) { + continue; // Let curl handle Content-Length headers + } if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) { // curl requires a special syntax to send empty headers $curlopts[\CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2); @@ -229,7 +234,7 @@ public function request(string $method, string $url, array $options = []): Respo }; } - if (isset($options['normalized_headers']['content-length'][0])) { + if ($hasContentLength) { $curlopts[\CURLOPT_INFILESIZE] = substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: ')); } elseif (!isset($options['normalized_headers']['transfer-encoding'])) { $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding: chunked'; // Enable chunked request bodies diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 989c6f32af2e6..148d3e762cd7b 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -203,6 +203,17 @@ public function testNegativeTimeout() ])->getStatusCode()); } + public function testRedirectAfterPost() + { + $client = $this->getHttpClient(__FUNCTION__); + + $response = $client->request('POST', 'http://localhost:8057/302/relative', [ + 'body' => 'abc', + ]); + + $this->assertSame(200, $response->getStatusCode()); + } + public function testNullBody() { $client = $this->getHttpClient(__FUNCTION__); From a64c6e6836b6caf781992d1a8976d27ef0dd6ea5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Mar 2022 16:23:06 +0100 Subject: [PATCH 25/49] [Mailer] Preserve case of headers --- .../Bridge/Mailchimp/Transport/MandrillApiTransport.php | 2 +- .../Mailgun/Tests/Transport/MailgunApiTransportTest.php | 4 ++-- .../Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php | 2 +- .../Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php | 2 +- .../Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php index 068da4d7eb7a0..469354d67bbe7 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php @@ -121,7 +121,7 @@ private function getPayload(Email $email, Envelope $envelope): array continue; } - $payload['message']['headers'][$name] = $header->getBodyAsString(); + $payload['message']['headers'][$header->getName()] = $header->getBodyAsString(); } return $payload; 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 2e4c53b83fc98..5f5b8f32362a1 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php @@ -65,8 +65,8 @@ public function testCustomHeader() $method->setAccessible(true); $payload = $method->invoke($transport, $email, $envelope); - $this->assertArrayHasKey('h:x-mailgun-variables', $payload); - $this->assertEquals($json, $payload['h:x-mailgun-variables']); + $this->assertArrayHasKey('h:X-Mailgun-Variables', $payload); + $this->assertEquals($json, $payload['h:X-Mailgun-Variables']); } public function testSend() diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php index 329b6eeaef449..4607a07037d16 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php @@ -120,7 +120,7 @@ private function getPayload(Email $email, Envelope $envelope): array continue; } - $payload['h:'.$name] = $header->getBodyAsString(); + $payload['h:'.$header->getName()] = $header->getBodyAsString(); } return $payload; diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php index 4ccf8762b7a85..e1b4cd3dda8d4 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php @@ -93,7 +93,7 @@ private function getPayload(Email $email, Envelope $envelope): array } $payload['Headers'][] = [ - 'Name' => $name, + 'Name' => $header->getName(), 'Value' => $header->getBodyAsString(), ]; } diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php index 07108f19aab3c..d198be82d7b35 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php @@ -121,7 +121,7 @@ private function getPayload(Email $email, Envelope $envelope): array continue; } - $payload['headers'][$name] = $header->getBodyAsString(); + $payload['headers'][$header->getName()] = $header->getBodyAsString(); } return $payload; From 1643e250a83cac714c05ba69a5281a4a5d43b0d9 Mon Sep 17 00:00:00 2001 From: Alessandro Lai Date: Thu, 10 Mar 2022 13:18:09 +0100 Subject: [PATCH 26/49] [Cache] Declaratively declare/hide DoctrineProvider to avoid breaking static analysis --- src/Symfony/Component/Cache/DoctrineProvider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Cache/DoctrineProvider.php b/src/Symfony/Component/Cache/DoctrineProvider.php index e4255f0177b37..f6ac5745dfd12 100644 --- a/src/Symfony/Component/Cache/DoctrineProvider.php +++ b/src/Symfony/Component/Cache/DoctrineProvider.php @@ -15,6 +15,10 @@ use Psr\Cache\CacheItemPoolInterface; use Symfony\Contracts\Service\ResetInterface; +if (!class_exists(CacheProvider::class)) { + return; +} + /** * @author Nicolas Grekas */ From 67dd69b29da8b2b01620d083efa8b689279713cf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Mar 2022 17:01:27 +0100 Subject: [PATCH 27/49] Revert "bug #45813 [HttpClient] Move Content-Type after Content-Length (nicolas-grekas)" This reverts commit 13e0671ff9222d6797c5a44593c1a09f65e8a738, reversing changes made to 01f674975a2dd27340aab9b6e7402bc0aee8423f. --- src/Symfony/Component/HttpClient/HttpClientTrait.php | 7 ------- .../Component/HttpClient/Tests/ScopingHttpClientTest.php | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index 20522255e9a97..8c9bd8d35526e 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -159,13 +159,6 @@ private static function prepareRequest(?string $method, ?string $url, array $opt } $options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0; - - if (isset($options['normalized_headers']['content-length']) && $contentType = $options['normalized_headers']['content-type'] ?? null) { - // Move Content-Type after Content-Length, see https://bugs.php.net/44603 - unset($options['normalized_headers']['content-type']); - $options['normalized_headers']['content-type'] = $contentType; - } - $options['headers'] = array_merge(...array_values($options['normalized_headers'])); return [$url, $options]; diff --git a/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php index a3e3099953ffe..bfca02b3581aa 100644 --- a/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/ScopingHttpClientTest.php @@ -73,7 +73,7 @@ public function testMatchingUrlsAndOptions() $response = $client->request('GET', 'http://example.com/foo-bar', ['json' => ['url' => 'http://example.com']]); $requestOptions = $response->getRequestOptions(); - $this->assertSame('Content-Type: application/json', $requestOptions['headers'][3]); + $this->assertSame('Content-Type: application/json', $requestOptions['headers'][1]); $requestJson = json_decode($requestOptions['body'], true); $this->assertSame('http://example.com', $requestJson['url']); $this->assertSame('X-FooBar: '.$defaultOptions['.*/foo-bar']['headers']['X-FooBar'], $requestOptions['headers'][0]); From 98acd62d25c5c8217ab269088c90537f7bfa4b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Nagy=20=28T-bond=29?= Date: Thu, 24 Mar 2022 15:51:32 +0100 Subject: [PATCH 28/49] [Serializer] Fix denormalizing union types Fixes symfony#45818 --- .../Normalizer/AbstractObjectNormalizer.php | 52 +++++++++++------- .../Serializer/Tests/SerializerTest.php | 54 +++++++++++++++++++ 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 5a6d3b78e61d9..e29a7cd18e6b6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -404,6 +404,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, } $expectedTypes = []; + $isUnionType = \count($types) > 1; foreach ($types as $type) { if (null === $data && $type->isNullable()) { return null; @@ -455,29 +456,40 @@ private function validateAndDenormalize(string $currentClass, string $attribute, $expectedTypes[Type::BUILTIN_TYPE_OBJECT === $builtinType && $class ? $class : $builtinType] = true; - if (Type::BUILTIN_TYPE_OBJECT === $builtinType) { - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.', $attribute, $class)); + // This try-catch should cover all NotNormalizableValueException (and all return branches after the first + // exception) so we could try denormalizing all types of an union type. If the target type is not an union + // type, we will just re-throw the catched exception. + // In the case of no denormalization succeeds with an union type, it will fall back to the default exception + // with the acceptable types list. + try { + if (Type::BUILTIN_TYPE_OBJECT === $builtinType) { + if (!$this->serializer instanceof DenormalizerInterface) { + throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.', $attribute, $class)); + } + + $childContext = $this->createChildContext($context, $attribute, $format); + if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { + return $this->serializer->denormalize($data, $class, $format, $childContext); + } } - $childContext = $this->createChildContext($context, $attribute, $format); - if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { - return $this->serializer->denormalize($data, $class, $format, $childContext); + // JSON only has a Number type corresponding to both int and float PHP types. + // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert + // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible). + // PHP's json_decode automatically converts Numbers without a decimal part to integers. + // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when + // a float is expected. + if (Type::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && null !== $format && str_contains($format, JsonEncoder::FORMAT)) { + return (float) $data; } - } - - // JSON only has a Number type corresponding to both int and float PHP types. - // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert - // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible). - // PHP's json_decode automatically converts Numbers without a decimal part to integers. - // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when - // a float is expected. - if (Type::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && null !== $format && str_contains($format, JsonEncoder::FORMAT)) { - return (float) $data; - } - if (('is_'.$builtinType)($data)) { - return $data; + if (('is_'.$builtinType)($data)) { + return $data; + } + } catch (NotNormalizableValueException $e) { + if (!$isUnionType) { + throw $e; + } } } @@ -639,7 +651,7 @@ private function getCacheKey(?string $format, array $context) 'ignored' => $this->ignoredAttributes, 'camelized' => $this->camelizedAttributes, ])); - } catch (\Exception $exception) { + } catch (\Exception $e) { // The context cannot be serialized, skip the cache return false; } diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index e5a8acbf03543..a95811a46f8d9 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Serializer\Encoder\JsonEncoder; @@ -33,6 +34,7 @@ use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; @@ -538,6 +540,38 @@ public function testNormalizePreserveEmptyArrayObject() $this->assertEquals('{"foo":{},"bar":["notempty"],"baz":{"nested":{}}}', $serializer->serialize($object, 'json', [AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true])); } + public function testUnionTypeDeserializable() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); + $serializer = new Serializer( + [ + new DateTimeNormalizer(), + new ObjectNormalizer($classMetadataFactory, null, null, $extractor, new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), + ], + ['json' => new JsonEncoder()] + ); + + $actual = $serializer->deserialize('{ "changed": null }', DummyUnionType::class, 'json', [ + DateTimeNormalizer::FORMAT_KEY => \DateTime::ISO8601, + ]); + + $this->assertEquals((new DummyUnionType())->setChanged(null), $actual, 'Union type denormalization first case failed.'); + + $actual = $serializer->deserialize('{ "changed": "2022-03-22T16:15:05+0000" }', DummyUnionType::class, 'json', [ + DateTimeNormalizer::FORMAT_KEY => \DateTime::ISO8601, + ]); + + $expectedDateTime = \DateTime::createFromFormat(\DateTime::ISO8601, '2022-03-22T16:15:05+0000'); + $this->assertEquals((new DummyUnionType())->setChanged($expectedDateTime), $actual, 'Union type denormalization second case failed.'); + + $actual = $serializer->deserialize('{ "changed": false }', DummyUnionType::class, 'json', [ + DateTimeNormalizer::FORMAT_KEY => \DateTime::ISO8601, + ]); + + $this->assertEquals(new DummyUnionType(), $actual, 'Union type denormalization third case failed.'); + } + private function serializerWithClassDiscriminator() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); @@ -610,6 +644,26 @@ public function __construct($value) } } +class DummyUnionType +{ + /** + * @var \DateTime|bool|null + */ + public $changed = false; + + /** + * @param \DateTime|bool|null + * + * @return $this + */ + public function setChanged($changed): self + { + $this->changed = $changed; + + return $this; + } +} + interface NormalizerAwareNormalizer extends NormalizerInterface, NormalizerAwareInterface { } From bc0e2fc8b097ca3630488b0dd309e97c662b5be9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Mar 2022 13:55:41 +0100 Subject: [PATCH 29/49] [HttpClient] fix sending PUT requests with curl --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 6 +++++- .../Component/HttpClient/NativeHttpClient.php | 2 +- .../HttpClient/Tests/HttpClientTestCase.php | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index e02f2c8b17d50..4a764414b7604 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -243,8 +243,12 @@ public function request(string $method, string $url, array $options = []): Respo if ('POST' !== $method) { $curlopts[\CURLOPT_UPLOAD] = true; } - } elseif ('' !== $body || 'POST' === $method) { + } elseif ('' !== $body || 'POST' === $method || $hasContentLength) { $curlopts[\CURLOPT_POSTFIELDS] = $body; + + if ('' === $body && !isset($options['normalized_headers']['content-type'])) { + $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } } if ($options['peer_fingerprint']) { diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 4394e51400cec..a955bbb98026e 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -82,7 +82,7 @@ public function request(string $method, string $url, array $options = []): Respo $options['body'] = self::getBodyAsString($options['body']); - if ('' !== $options['body'] && 'POST' === $method && !isset($options['normalized_headers']['content-type'])) { + if ('' !== $options['body'] && !isset($options['normalized_headers']['content-type'])) { $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded'; } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 148d3e762cd7b..faed27aed24a7 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -214,6 +214,18 @@ public function testRedirectAfterPost() $this->assertSame(200, $response->getStatusCode()); } + public function testEmptyPut() + { + $client = $this->getHttpClient(__FUNCTION__); + + $response = $client->request('PUT', 'http://localhost:8057/post', [ + 'headers' => ['Content-Length' => '0'], + ]); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertStringContainsString("\r\nContent-Length: ", $response->getInfo('debug')); + } + public function testNullBody() { $client = $this->getHttpClient(__FUNCTION__); From ee6717595bec2922e877ff3ade338064561a49cb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Mar 2022 15:31:06 +0100 Subject: [PATCH 30/49] [HttpClient] fix sending Content-Length/Type for POST --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 2 +- src/Symfony/Component/HttpClient/NativeHttpClient.php | 5 ++++- .../Component/HttpClient/Tests/HttpClientTestCase.php | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 4a764414b7604..bb0b68918bfd1 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -246,7 +246,7 @@ public function request(string $method, string $url, array $options = []): Respo } elseif ('' !== $body || 'POST' === $method || $hasContentLength) { $curlopts[\CURLOPT_POSTFIELDS] = $body; - if ('' === $body && !isset($options['normalized_headers']['content-type'])) { + if ('' === $body && 'POST' !== $method && !isset($options['normalized_headers']['content-type'])) { $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index a955bbb98026e..54fd512570487 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -82,7 +82,10 @@ public function request(string $method, string $url, array $options = []): Respo $options['body'] = self::getBodyAsString($options['body']); - if ('' !== $options['body'] && !isset($options['normalized_headers']['content-type'])) { + if ('' === $options['body'] && 'POST' === $method && !isset($options['normalized_headers']['content-length'])) { + $options['headers'][] = 'Content-Length: 0'; + } + if (('' !== $options['body'] || 'POST' === $method) && !isset($options['normalized_headers']['content-type'])) { $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded'; } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index faed27aed24a7..cf33bd9816e86 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -208,10 +208,11 @@ public function testRedirectAfterPost() $client = $this->getHttpClient(__FUNCTION__); $response = $client->request('POST', 'http://localhost:8057/302/relative', [ - 'body' => 'abc', + 'body' => '', ]); $this->assertSame(200, $response->getStatusCode()); + $this->assertStringContainsString("\r\nContent-Length: 0", $response->getInfo('debug')); } public function testEmptyPut() From 66f39b0fe61c51f824c3fa816a62a782be1173d3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Mar 2022 15:52:11 +0100 Subject: [PATCH 31/49] [HttpClient] always send Content-Length when a body is passed --- src/Symfony/Component/HttpClient/NativeHttpClient.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 54fd512570487..0ecc471202dfe 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -80,9 +80,11 @@ public function request(string $method, string $url, array $options = []): Respo } } + $sendContentLength = !\is_string($options['body']) || 'POST' === $method; + $options['body'] = self::getBodyAsString($options['body']); - if ('' === $options['body'] && 'POST' === $method && !isset($options['normalized_headers']['content-length'])) { + if ('' === $options['body'] && $sendContentLength && !isset($options['normalized_headers']['content-length'])) { $options['headers'][] = 'Content-Length: 0'; } if (('' !== $options['body'] || 'POST' === $method) && !isset($options['normalized_headers']['content-type'])) { From 31a1a2b61f2bf6d7efc0aff9716769484d5beeea Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Mar 2022 16:07:14 +0100 Subject: [PATCH 32/49] [HttpClient] always send Content-Type when a body is passed --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 8 ++++---- src/Symfony/Component/HttpClient/NativeHttpClient.php | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index bb0b68918bfd1..2830c5025063e 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -242,13 +242,13 @@ public function request(string $method, string $url, array $options = []): Respo if ('POST' !== $method) { $curlopts[\CURLOPT_UPLOAD] = true; + + if (!isset($options['normalized_headers']['content-type'])) { + $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type: application/x-www-form-urlencoded'; + } } } elseif ('' !== $body || 'POST' === $method || $hasContentLength) { $curlopts[\CURLOPT_POSTFIELDS] = $body; - - if ('' === $body && 'POST' !== $method && !isset($options['normalized_headers']['content-type'])) { - $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; - } } if ($options['peer_fingerprint']) { diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 0ecc471202dfe..9f3737efb3b84 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -80,14 +80,15 @@ public function request(string $method, string $url, array $options = []): Respo } } - $sendContentLength = !\is_string($options['body']) || 'POST' === $method; + $hasContentLength = isset($options['normalized_headers']['content-length']); + $hasBody = '' !== $options['body'] || 'POST' === $method || $hasContentLength; $options['body'] = self::getBodyAsString($options['body']); - if ('' === $options['body'] && $sendContentLength && !isset($options['normalized_headers']['content-length'])) { + if ('' === $options['body'] && $hasBody && !$hasContentLength) { $options['headers'][] = 'Content-Length: 0'; } - if (('' !== $options['body'] || 'POST' === $method) && !isset($options['normalized_headers']['content-type'])) { + if ($hasBody && !isset($options['normalized_headers']['content-type'])) { $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded'; } From 56b428f1f89894b6c1163105e60c134d5f8d45ba Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Mar 2022 17:56:10 +0100 Subject: [PATCH 33/49] [HttpClient] fix 303 after PUT and sending chunked requests --- .../Component/HttpClient/HttpClientTrait.php | 24 ++++++++++++++++++- .../Component/HttpClient/NativeHttpClient.php | 5 ++++ .../HttpClient/Response/CurlResponse.php | 1 + .../HttpClient/Tests/MockHttpClientTest.php | 4 ++-- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index 8c9bd8d35526e..e616ca1f9c01b 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpClient; use Symfony\Component\HttpClient\Exception\InvalidArgumentException; +use Symfony\Component\HttpClient\Exception\TransportException; /** * Provides the common logic from writing HttpClientInterface implementations. @@ -89,8 +90,13 @@ private static function prepareRequest(?string $method, ?string $url, array $opt if (\is_string($options['body']) && (string) \strlen($options['body']) !== substr($h = $options['normalized_headers']['content-length'][0] ?? '', 16) - && ('' !== $h || ('' !== $options['body'] && !isset($options['normalized_headers']['transfer-encoding']))) + && ('' !== $h || '' !== $options['body']) ) { + if (isset($options['normalized_headers']['transfer-encoding'])) { + unset($options['normalized_headers']['transfer-encoding']); + $options['body'] = self::dechunk($options['body']); + } + $options['normalized_headers']['content-length'] = [substr_replace($h ?: 'Content-Length: ', \strlen($options['body']), 16)]; } } @@ -329,6 +335,22 @@ private static function normalizeBody($body) return $body; } + private static function dechunk(string $body): string + { + $h = fopen('php://temp', 'w+'); + stream_filter_append($h, 'dechunk', \STREAM_FILTER_WRITE); + fwrite($h, $body); + $body = stream_get_contents($h, -1, 0); + rewind($h); + ftruncate($h, 0); + + if (fwrite($h, '-') && '' !== stream_get_contents($h, -1, 0)) { + throw new TransportException('Request body has broken chunked encoding.'); + } + + return $body; + } + /** * @param string|string[] $fingerprint * diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 9f3737efb3b84..939eb425c7672 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -85,6 +85,11 @@ public function request(string $method, string $url, array $options = []): Respo $options['body'] = self::getBodyAsString($options['body']); + if (isset($options['normalized_headers']['transfer-encoding'])) { + unset($options['normalized_headers']['transfer-encoding']); + $options['headers'] = array_merge(...array_values($options['normalized_headers'])); + $options['body'] = self::dechunk($options['body']); + } if ('' === $options['body'] && $hasBody && !$hasContentLength) { $options['headers'][] = 'Content-Length: 0'; } diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index ee0d3f655a6fb..e065c4aa17f0e 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -363,6 +363,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & } elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) { $info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET'; curl_setopt($ch, \CURLOPT_POSTFIELDS, ''); + curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']); } } diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php index e324dc107be3f..4e8dbf26d7e29 100644 --- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php @@ -96,12 +96,12 @@ public function testFixContentLength() $this->assertSame(['Content-Length: 7'], $requestOptions['normalized_headers']['content-length']); $response = $client->request('POST', 'http://localhost:8057/post', [ - 'body' => 'abc=def', + 'body' => "8\r\nSymfony \r\n5\r\nis aw\r\n6\r\nesome!\r\n0\r\n\r\n", 'headers' => ['Transfer-Encoding: chunked'], ]); $requestOptions = $response->getRequestOptions(); - $this->assertFalse(isset($requestOptions['normalized_headers']['content-length'])); + $this->assertSame(['Content-Length: 19'], $requestOptions['normalized_headers']['content-length']); $response = $client->request('POST', 'http://localhost:8057/post', [ 'body' => '', From e7072aa5cc5be00fcbdcbfce524b4da3e9beaa50 Mon Sep 17 00:00:00 2001 From: acoulton Date: Sat, 26 Mar 2022 00:17:43 +0000 Subject: [PATCH 34/49] [Console] Fix exit status on uncaught exception with negative code As described in #45850, if an application threw an exception with the `code` property set to a negative number this could in some cases cause the process to appear to exit 'successfully' with a zero exit status. Exiting with any negative number produces potentially unexpected results - the reported exit status will not appear to match the value that was set. This is due to the binary handling / truncation of exit codes. This may theoretically break BC for applications that were intentionally throwing exceptions with negative codes and performing some action based on that status. However, given they would have had to implement an algorithm to map e.g. `-10` in PHP to `246` as the actual exit status, it seems unlikely this is a common usage. It is certainly outside the defined behaviour of POSIX exit codes. Therefore I believe it is essentially safe to assume that exceptions with negative codes are e.g. being thrown by lower-level components, and are not intended to set a shell exit status. Coalescing all negative numbers to 1 matches the existing behaviour with other 'invalid' exception codes e.g. empty / zero / non-numeric. This therefore feels the most robust fix and eliminates any potential for confusion. Fixes #45850 --- src/Symfony/Component/Console/Application.php | 2 +- .../Console/Tests/ApplicationTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 15d537dacb3a6..1021a900f0972 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -157,7 +157,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null $exitCode = $e->getCode(); if (is_numeric($exitCode)) { $exitCode = (int) $exitCode; - if (0 === $exitCode) { + if ($exitCode <= 0) { $exitCode = 1; } } else { diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 6e9953dd27a7b..fae8d2dcf743b 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1175,6 +1175,25 @@ public function testRunDispatchesExitCodeOneForExceptionCodeZero() $this->assertTrue($passedRightValue, '-> exit code 1 was passed in the console.terminate event'); } + /** + * @testWith [-1] + * [-32000] + */ + public function testRunReturnsExitCodeOneForNegativeExceptionCode($exceptionCode) + { + $exception = new \Exception('', $exceptionCode); + + $application = $this->getMockBuilder(Application::class)->setMethods(['doRun'])->getMock(); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->willThrowException($exception); + + $exitCode = $application->run(new ArrayInput([]), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is '.$exceptionCode); + } + public function testAddingOptionWithDuplicateShortcut() { $this->expectException(\LogicException::class); From a9e781feb1f38608811df5b4978bab88b7886fdd Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 28 Mar 2022 10:53:28 +0200 Subject: [PATCH 35/49] [Validator] Fix File constraint invalid max size exception message --- src/Symfony/Component/Validator/Constraints/File.php | 2 +- .../Validator/Tests/Constraints/FileValidatorTest.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index 29844a4c95476..377be1f2ec979 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -117,7 +117,7 @@ private function normalizeBinaryFormat($maxSize) $this->maxSize = $matches[1] * $factors[$unit = strtolower($matches[2])]; $this->binaryFormat = $this->binaryFormat ?? (2 === \strlen($unit)); } else { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size.', $this->maxSize)); + throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size.', $maxSize)); } } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index 72bdc950343b9..5e98ef64313a4 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -479,5 +479,14 @@ public function uploadedFileErrorProvider() return $tests; } + public function testNegativeMaxSize() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('"-1" is not a valid maximum size.'); + + $file = new File(); + $file->maxSize = -1; + } + abstract protected function getFile($filename); } From 12271a44cfe0b4ea7fdcbfc20a1ebf588aa54410 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Mar 2022 12:19:22 +0200 Subject: [PATCH 36/49] [Messenger] Add mysql indexes back and work around deadlocks using soft-delete --- .../Transport/Doctrine/ConnectionTest.php | 58 ------------------- .../Transport/Doctrine/Connection.php | 25 ++++++-- 2 files changed, 20 insertions(+), 63 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php index 5bc7dddcd2934..9d9ad3c4d2c4c 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Messenger\Tests\Transport\Doctrine; use Doctrine\DBAL\Abstraction\Result as AbstractionResult; -use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection as DBALConnection; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\Result as DriverResult; @@ -24,11 +23,8 @@ use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\AbstractSchemaManager; -use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaConfig; -use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Statement; -use Doctrine\DBAL\Types\Types; use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Exception\InvalidArgumentException; use Symfony\Component\Messenger\Exception\TransportException; @@ -410,58 +406,4 @@ public function providePlatformSql(): iterable 'SELECT m.* FROM messenger_messages m WITH (UPDLOCK, ROWLOCK) WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY available_at ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ', ]; } - - /** - * @dataProvider setupIndicesProvider - */ - public function testSetupIndices(string $platformClass, array $expectedIndices) - { - $driverConnection = $this->createMock(DBALConnection::class); - $driverConnection->method('getConfiguration')->willReturn(new Configuration()); - - $schemaManager = $this->createMock(AbstractSchemaManager::class); - $schema = new Schema(); - $expectedTable = $schema->createTable('messenger_messages'); - $expectedTable->addColumn('id', Types::BIGINT); - $expectedTable->setPrimaryKey(['id']); - // Make sure columns for indices exists so addIndex() will not throw - foreach (array_unique(array_merge(...$expectedIndices)) as $columnName) { - $expectedTable->addColumn($columnName, Types::STRING); - } - foreach ($expectedIndices as $indexColumns) { - $expectedTable->addIndex($indexColumns); - } - $schemaManager->method('createSchema')->willReturn($schema); - if (method_exists(DBALConnection::class, 'createSchemaManager')) { - $driverConnection->method('createSchemaManager')->willReturn($schemaManager); - } else { - $driverConnection->method('getSchemaManager')->willReturn($schemaManager); - } - - $platformMock = $this->createMock($platformClass); - $platformMock - ->expects(self::once()) - ->method('getAlterTableSQL') - ->with(self::callback(static function (TableDiff $tableDiff): bool { - return 0 === \count($tableDiff->addedIndexes) && 0 === \count($tableDiff->changedIndexes) && 0 === \count($tableDiff->removedIndexes); - })) - ->willReturn([]); - $driverConnection->method('getDatabasePlatform')->willReturn($platformMock); - - $connection = new Connection([], $driverConnection); - $connection->setup(); - } - - public function setupIndicesProvider(): iterable - { - yield 'MySQL' => [ - MySQL57Platform::class, - [['delivered_at']], - ]; - - yield 'Other platforms' => [ - AbstractPlatform::class, - [['queue_name'], ['available_at'], ['delivered_at']], - ]; - } } diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php index 47ab0824a2e1b..7ed376f61ce36 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Connection as DBALConnection; use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\Exception as DriverException; use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\TableNotFoundException; @@ -157,6 +158,14 @@ public function send(string $body, array $headers, int $delay = 0): string public function get(): ?array { + if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) { + try { + $this->driverConnection->delete($this->configuration['table_name'], ['delivered_at' => '9999-12-31']); + } catch (DriverException $e) { + // Ignore the exception + } + } + get: $this->driverConnection->beginTransaction(); try { @@ -224,6 +233,10 @@ public function get(): ?array public function ack(string $id): bool { try { + if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) { + return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31'], ['id' => $id]) > 0; + } + return $this->driverConnection->delete($this->configuration['table_name'], ['id' => $id]) > 0; } catch (DBALException|Exception $exception) { throw new TransportException($exception->getMessage(), 0, $exception); @@ -233,6 +246,10 @@ public function ack(string $id): bool public function reject(string $id): bool { try { + if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) { + return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31'], ['id' => $id]) > 0; + } + return $this->driverConnection->delete($this->configuration['table_name'], ['id' => $id]) > 0; } catch (DBALException|Exception $exception) { throw new TransportException($exception->getMessage(), 0, $exception); @@ -388,6 +405,7 @@ private function getSchema(): Schema $table->addColumn('headers', self::$useDeprecatedConstants ? Type::TEXT : Types::TEXT) ->setNotnull(true); $table->addColumn('queue_name', self::$useDeprecatedConstants ? Type::STRING : Types::STRING) + ->setLength(190) // MySQL 5.6 only supports 191 characters on an indexed column in utf8mb4 mode ->setNotnull(true); $table->addColumn('created_at', self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE) ->setNotnull(true); @@ -396,11 +414,8 @@ private function getSchema(): Schema $table->addColumn('delivered_at', self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE) ->setNotnull(false); $table->setPrimaryKey(['id']); - // No indices on queue_name and available_at on MySQL to prevent deadlock issues when running multiple consumers. - if (!$this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) { - $table->addIndex(['queue_name']); - $table->addIndex(['available_at']); - } + $table->addIndex(['queue_name']); + $table->addIndex(['available_at']); $table->addIndex(['delivered_at']); return $schema; From f09da16f31f8bc75b80e04c6dae8e695c8e1e3c8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 28 Mar 2022 22:04:49 +0200 Subject: [PATCH 37/49] [ExpressionLanguage] Fix matches when the regexp is not valid --- .../ExpressionLanguage/Node/BinaryNode.php | 21 +++++++++- .../Tests/Node/BinaryNodeTest.php | 42 ++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php index 3820f880e7f31..2f49bd90c6cae 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php @@ -12,6 +12,7 @@ namespace Symfony\Component\ExpressionLanguage\Node; use Symfony\Component\ExpressionLanguage\Compiler; +use Symfony\Component\ExpressionLanguage\SyntaxError; /** * @author Fabien Potencier @@ -46,8 +47,12 @@ public function compile(Compiler $compiler) $operator = $this->attributes['operator']; if ('matches' == $operator) { + if ($this->nodes['right'] instanceof ConstantNode) { + $this->evaluateMatches($this->nodes['right']->evaluate([], []), ''); + } + $compiler - ->raw('preg_match(') + ->raw('(static function ($regexp, $str) { set_error_handler(function ($t, $m) use ($regexp, $str) { throw new \Symfony\Component\ExpressionLanguage\SyntaxError(sprintf(\'Regexp "%s" passed to "matches" is not valid\', $regexp).substr($m, 12)); }); try { return preg_match($regexp, $str); } finally { restore_error_handler(); } })(') ->compile($this->nodes['right']) ->raw(', ') ->compile($this->nodes['left']) @@ -159,7 +164,7 @@ public function evaluate($functions, $values) return $left % $right; case 'matches': - return preg_match($right, $left); + return $this->evaluateMatches($right, $left); } } @@ -167,4 +172,16 @@ public function toArray() { return ['(', $this->nodes['left'], ' '.$this->attributes['operator'].' ', $this->nodes['right'], ')']; } + + private function evaluateMatches(string $regexp, string $str): int + { + set_error_handler(function ($t, $m) use ($regexp) { + throw new SyntaxError(sprintf('Regexp "%s" passed to "matches" is not valid', $regexp).substr($m, 12)); + }); + try { + return preg_match($regexp, $str); + } finally { + restore_error_handler(); + } + } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php index b45a1e57b9b17..fccc04abce4b8 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php @@ -11,9 +11,12 @@ namespace Symfony\Component\ExpressionLanguage\Tests\Node; +use Symfony\Component\ExpressionLanguage\Compiler; use Symfony\Component\ExpressionLanguage\Node\ArrayNode; use Symfony\Component\ExpressionLanguage\Node\BinaryNode; use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\Node\NameNode; +use Symfony\Component\ExpressionLanguage\SyntaxError; class BinaryNodeTest extends AbstractNodeTest { @@ -111,7 +114,7 @@ public function getCompileData() ['range(1, 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))], - ['preg_match("/^[a-z]+/i\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))], + ['(static function ($regexp, $str) { set_error_handler(function ($t, $m) use ($regexp, $str) { throw new \Symfony\Component\ExpressionLanguage\SyntaxError(sprintf(\'Regexp "%s" passed to "matches" is not valid\', $regexp).substr($m, 12)); }); try { return preg_match($regexp, $str); } finally { restore_error_handler(); } })("/^[a-z]+\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+$/'))], ]; } @@ -160,7 +163,42 @@ public function getDumpData() ['(1 .. 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))], - ['("abc" matches "/^[a-z]+/i$/")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))], + ['("abc" matches "/^[a-z]+$/")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+$/'))], ]; } + + public function testEvaluateMatchesWithInvalidRegexp() + { + $node = new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('this is not a regexp')); + + $this->expectExceptionObject(new SyntaxError('Regexp "this is not a regexp" passed to "matches" is not valid: Delimiter must not be alphanumeric or backslash')); + $node->evaluate([], []); + } + + public function testEvaluateMatchesWithInvalidRegexpAsExpression() + { + $node = new BinaryNode('matches', new ConstantNode('abc'), new NameNode('regexp')); + + $this->expectExceptionObject(new SyntaxError('Regexp "this is not a regexp" passed to "matches" is not valid: Delimiter must not be alphanumeric or backslash')); + $node->evaluate([], ['regexp' => 'this is not a regexp']); + } + + public function testCompileMatchesWithInvalidRegexp() + { + $node = new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('this is not a regexp')); + + $this->expectExceptionObject(new SyntaxError('Regexp "this is not a regexp" passed to "matches" is not valid: Delimiter must not be alphanumeric or backslash')); + $compiler = new Compiler([]); + $node->compile($compiler); + } + + public function testCompileMatchesWithInvalidRegexpAsExpression() + { + $node = new BinaryNode('matches', new ConstantNode('abc'), new NameNode('regexp')); + + $this->expectExceptionObject(new SyntaxError('Regexp "this is not a regexp" passed to "matches" is not valid: Delimiter must not be alphanumeric or backslash')); + $compiler = new Compiler([]); + $node->compile($compiler); + eval('$regexp = "this is not a regexp"; '.$compiler->getSource().';'); + } } From 8a9b841985e2535b842b8f23a666b855f1de6da2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Mar 2022 14:43:12 +0200 Subject: [PATCH 38/49] [VarExporter] Fix exporting objects with readonly properties --- .../VarExporter/Internal/Exporter.php | 2 +- .../Tests/Fixtures/FooReadonly.php | 21 +++++++++++++++++++ .../VarExporter/Tests/Fixtures/readonly.php | 20 ++++++++++++++++++ .../VarExporter/Tests/VarExporterTest.php | 8 +++++-- 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/FooReadonly.php create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/readonly.php diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index c46eb50aa988d..7141b4e6d96c1 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -140,7 +140,7 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount $i = 0; $n = (string) $name; if ('' === $n || "\0" !== $n[0]) { - $c = 'stdClass'; + $c = \PHP_VERSION_ID >= 80100 && $reflector->hasProperty($n) && ($p = $reflector->getProperty($n))->isReadOnly() ? $p->class : 'stdClass'; } elseif ('*' === $n[1]) { $n = substr($n, 3); $c = $reflector->getProperty($n)->class; diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/FooReadonly.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/FooReadonly.php new file mode 100644 index 0000000000000..8e41de95958bc --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/FooReadonly.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Tests\Fixtures; + +class FooReadonly +{ + public function __construct( + public readonly string $name, + public readonly string $value, + ) { + } +} diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/readonly.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/readonly.php new file mode 100644 index 0000000000000..3b3db27305859 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/readonly.php @@ -0,0 +1,20 @@ + [ + 'name' => [ + 'k', + ], + 'value' => [ + 'v', + ], + ], + ], + $o[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index 407f25af1891f..f90737da2e8cf 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -16,6 +16,7 @@ use Symfony\Component\VarExporter\Exception\ClassNotFoundException; use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; use Symfony\Component\VarExporter\Internal\Registry; +use Symfony\Component\VarExporter\Tests\Fixtures\FooReadonly; use Symfony\Component\VarExporter\Tests\Fixtures\FooSerializable; use Symfony\Component\VarExporter\Tests\Fixtures\FooUnitEnum; use Symfony\Component\VarExporter\Tests\Fixtures\MySerializable; @@ -244,9 +245,12 @@ public function provideExport() yield ['php74-serializable', new Php74Serializable()]; - if (\PHP_VERSION_ID >= 80100) { - yield ['unit-enum', [FooUnitEnum::Bar], true]; + if (\PHP_VERSION_ID < 80100) { + return; } + + yield ['unit-enum', [FooUnitEnum::Bar], true]; + yield ['readonly', new FooReadonly('k', 'v')]; } public function testUnicodeDirectionality() From 6782fd48fa49b0772ce716554aeacf2ce919f728 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Wed, 30 Mar 2022 18:27:26 +0200 Subject: [PATCH 39/49] [PropertyAccess] Fix typo in PropertyAccessor::readProperty() DocBlock --- src/Symfony/Component/PropertyAccess/PropertyAccessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index f73e148a89b74..607c8fad571ac 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -384,7 +384,7 @@ private function readIndex(array $zval, $index): array } /** - * Reads the a property from an object. + * Reads the value of a property from an object. * * @throws NoSuchPropertyException If $ignoreInvalidProperty is false and the property does not exist or is not public */ From 80162936b65006abc053ce670a86692c3369ad7d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 31 Mar 2022 18:54:39 +0200 Subject: [PATCH 40/49] Fix composer on appveyor --- .appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 889aafe26929b..494dce0423fe3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -47,8 +47,7 @@ install: - echo extension=php_curl.dll >> php.ini-max - copy /Y php.ini-max php.ini - cd c:\projects\symfony - - IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/2.0.0/composer.phar) - - php composer.phar self-update --2 + - IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/2.2.10/composer.phar) - copy /Y .github\composer-config.json %APPDATA%\Composer\config.json - git config --global user.email "" - git config --global user.name "Symfony" From 98279474183701aaa965bcd2cefbceae78429b9e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 1 Apr 2022 09:35:56 +0200 Subject: [PATCH 41/49] Fix appveyor --- .appveyor.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 494dce0423fe3..e60c3e2632184 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,10 +2,6 @@ build: false clone_depth: 2 clone_folder: c:\projects\symfony -cache: - - composer.phar - - .phpunit -> phpunit - init: - SET PATH=c:\php;%PATH% - SET COMPOSER_NO_INTERACTION=1 @@ -47,8 +43,8 @@ install: - echo extension=php_curl.dll >> php.ini-max - copy /Y php.ini-max php.ini - cd c:\projects\symfony - - IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/2.2.10/composer.phar) - - copy /Y .github\composer-config.json %APPDATA%\Composer\config.json + - appveyor DownloadFile https://github.com/composer/composer/releases/download/2.2.10/composer.phar + - mkdir %APPDATA%\Composer && copy /Y .github\composer-config.json %APPDATA%\Composer\config.json - git config --global user.email "" - git config --global user.name "Symfony" - FOR /F "tokens=* USEBACKQ" %%F IN (`bash -c "grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | grep -o '[0-9][0-9]*\.[0-9]'"`) DO (SET SYMFONY_VERSION=%%F) From f55927a06663c9025c8fd3be86e1ba0c3c191d6a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 1 Apr 2022 09:54:10 +0200 Subject: [PATCH 42/49] Fix URL to get composer.phar --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index e60c3e2632184..dfc01ccce97de 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -43,7 +43,7 @@ install: - echo extension=php_curl.dll >> php.ini-max - copy /Y php.ini-max php.ini - cd c:\projects\symfony - - appveyor DownloadFile https://github.com/composer/composer/releases/download/2.2.10/composer.phar + - appveyor DownloadFile https://getcomposer.org/download/latest-2.2.x/composer.phar - mkdir %APPDATA%\Composer && copy /Y .github\composer-config.json %APPDATA%\Composer\config.json - git config --global user.email "" - git config --global user.name "Symfony" From d4a695fe143864b949faa26b088ba4f809c30a81 Mon Sep 17 00:00:00 2001 From: Robert-Jan de Dreu Date: Fri, 11 Mar 2022 14:46:27 +0100 Subject: [PATCH 43/49] [Messenger] Fix cannot select FOR UPDATE from view on Oracle --- .../Messenger/Transport/Doctrine/Connection.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php index 7ed376f61ce36..379132c9c98da 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php @@ -19,6 +19,7 @@ use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\AbstractSchemaManager; @@ -187,6 +188,18 @@ public function get(): ?array ); } + // Wrap the rownum query in a sub-query to allow writelocks without ORA-02014 error + if ($this->driverConnection->getDatabasePlatform() instanceof OraclePlatform) { + $sql = str_replace('SELECT a.* FROM', 'SELECT a.id FROM', $sql); + + $wrappedQuery = $this->driverConnection->createQueryBuilder() + ->select('w.*') + ->from($this->configuration['table_name'], 'w') + ->where('w.id IN('.$sql.')'); + + $sql = $wrappedQuery->getSQL(); + } + // use SELECT ... FOR UPDATE to lock table $stmt = $this->executeQuery( $sql.' '.$this->driverConnection->getDatabasePlatform()->getWriteLockSQL(), From d7c13610acc586f31a38e559c1a144a10ab914b3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 21 Mar 2022 10:27:48 +0100 Subject: [PATCH 44/49] [HttpClient] On redirections don't send content-related request headers --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 6 ++++-- .../Component/HttpClient/NativeHttpClient.php | 7 +++++-- .../HttpClient/Tests/HttpClientTestCase.php | 12 ++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 2830c5025063e..3b63addec8865 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -204,9 +204,11 @@ public function request(string $method, string $url, array $options = []): Respo $hasContentLength = isset($options['normalized_headers']['content-length'][0]); - foreach ($options['headers'] as $header) { + foreach ($options['headers'] as $i => $header) { if ($hasContentLength && 0 === stripos($header, 'Content-Length:')) { - continue; // Let curl handle Content-Length headers + // Let curl handle Content-Length headers + unset($options['headers'][$i]); + continue; } if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) { // curl requires a special syntax to send empty headers diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 939eb425c7672..f52d93d5eb7e0 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -430,9 +430,12 @@ private static function createRedirectResolver(array $options, string $host, ?ar if ('POST' === $options['method'] || 303 === $info['http_code']) { $info['http_method'] = $options['method'] = 'HEAD' === $options['method'] ? 'HEAD' : 'GET'; $options['content'] = ''; - $options['header'] = array_filter($options['header'], static function ($h) { + $filterContentHeaders = static function ($h) { return 0 !== stripos($h, 'Content-Length:') && 0 !== stripos($h, 'Content-Type:'); - }); + }; + $options['header'] = array_filter($options['header'], $filterContentHeaders); + $redirectHeaders['no_auth'] = array_filter($redirectHeaders['no_auth'], $filterContentHeaders); + $redirectHeaders['with_auth'] = array_filter($redirectHeaders['with_auth'], $filterContentHeaders); stream_context_set_option($context, ['http' => $options]); } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index cf33bd9816e86..d36e7f70b72ca 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -194,6 +194,18 @@ public function testFixContentLength() $this->assertSame(['abc' => 'def', 'REQUEST_METHOD' => 'POST'], $body); } + public function testDropContentRelatedHeadersWhenFollowingRequestIsUsingGet() + { + $client = $this->getHttpClient(__FUNCTION__); + + $response = $client->request('POST', 'http://localhost:8057/302', [ + 'body' => 'foo', + 'headers' => ['Content-Length: 3'], + ]); + + $this->assertSame(200, $response->getStatusCode()); + } + public function testNegativeTimeout() { $client = $this->getHttpClient(__FUNCTION__); From 873a86e26eb31299a37a5ac8765b294350268f95 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 1 Apr 2022 14:58:49 +0200 Subject: [PATCH 45/49] reset Twig form theme resources between requests --- .../TwigBundle/DependencyInjection/TwigExtension.php | 8 ++++++++ .../Component/Form/AbstractRendererEngine.php | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 234a845edadd6..1c0e2ff327409 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -18,9 +18,11 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Form\AbstractRendererEngine; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Translation\Translator; +use Symfony\Contracts\Service\ResetInterface; use Twig\Extension\ExtensionInterface; use Twig\Extension\RuntimeExtensionInterface; use Twig\Loader\LoaderInterface; @@ -40,6 +42,12 @@ public function load(array $configs, ContainerBuilder $container) if (class_exists(\Symfony\Component\Form\Form::class)) { $loader->load('form.xml'); + + if (is_subclass_of(AbstractRendererEngine::class, ResetInterface::class)) { + $container->getDefinition('twig.form.engine')->addTag('kernel.reset', [ + 'method' => 'reset', + ]); + } } if (interface_exists(\Symfony\Component\Templating\EngineInterface::class)) { diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php index 116892d415777..7c4822bf14767 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Form; +use Symfony\Contracts\Service\ResetInterface; + /** * Default implementation of {@link FormRendererEngineInterface}. * * @author Bernhard Schussek */ -abstract class AbstractRendererEngine implements FormRendererEngineInterface +abstract class AbstractRendererEngine implements FormRendererEngineInterface, ResetInterface { /** * The variable in {@link FormView} used as cache key. @@ -197,4 +199,12 @@ private function loadResourceForBlockNameHierarchy(string $cacheKey, FormView $v return false; } + + public function reset(): void + { + $this->themes = []; + $this->useDefaultThemes = []; + $this->resources = []; + $this->resourceHierarchyLevels = []; + } } From c4863053f1aa0344d220184fdd541e90eb62f3e6 Mon Sep 17 00:00:00 2001 From: Sander Hagen <28562438+SanderHagen@users.noreply.github.com> Date: Fri, 1 Apr 2022 15:29:23 +0200 Subject: [PATCH 46/49] [Messenger] reset connection on worker shutdown --- .../Component/Messenger/Tests/WorkerTest.php | 29 +++++++++++++++++++ src/Symfony/Component/Messenger/Worker.php | 11 +++++++ 2 files changed, 40 insertions(+) diff --git a/src/Symfony/Component/Messenger/Tests/WorkerTest.php b/src/Symfony/Component/Messenger/Tests/WorkerTest.php index b5406b59ea3f6..24c281c364d39 100644 --- a/src/Symfony/Component/Messenger/Tests/WorkerTest.php +++ b/src/Symfony/Component/Messenger/Tests/WorkerTest.php @@ -29,6 +29,7 @@ use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; use Symfony\Component\Messenger\Worker; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @group time-sensitive @@ -85,6 +86,19 @@ public function testHandlingErrorCausesReject() $this->assertSame(0, $receiver->getAcknowledgeCount()); } + public function testWorkerResetsConnectionIfReceiverIsResettable() + { + $resettableReceiver = new ResettableDummyReceiver([]); + + $bus = $this->createMock(MessageBusInterface::class); + $dispatcher = new EventDispatcher(); + + $worker = new Worker([$resettableReceiver], $bus, $dispatcher); + $worker->stop(); + $worker->run(); + $this->assertTrue($resettableReceiver->hasBeenReset()); + } + public function testWorkerDoesNotSendNullMessagesToTheBus() { $receiver = new DummyReceiver([ @@ -283,3 +297,18 @@ public function getRejectCount(): int return $this->rejectCount; } } + +class ResettableDummyReceiver extends DummyReceiver implements ResetInterface +{ + private $hasBeenReset = false; + + public function reset() + { + $this->hasBeenReset = true; + } + + public function hasBeenReset(): bool + { + return $this->hasBeenReset; + } +} diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 1a41009fefc6a..98156c887bda9 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -25,6 +25,7 @@ use Symfony\Component\Messenger\Stamp\ReceivedStamp; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Samuel Roze @@ -102,6 +103,7 @@ public function run(array $options = []): void } $this->dispatchEvent(new WorkerStoppedEvent($this)); + $this->resetReceiverConnections(); } private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, string $transportName): void @@ -155,6 +157,15 @@ public function stop(): void $this->shouldStop = true; } + private function resetReceiverConnections(): void + { + foreach ($this->receivers as $transportName => $receiver) { + if ($receiver instanceof ResetInterface) { + $receiver->reset(); + } + } + } + private function dispatchEvent($event) { if (null === $this->eventDispatcher) { From dc077d2965b7cc5c7b2dc7c06767c9076cf4944f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 2 Apr 2022 07:54:23 +0200 Subject: [PATCH 47/49] Update CHANGELOG for 4.4.40 --- CHANGELOG-4.4.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md index 29cc4c6552b90..273282ab8caf0 100644 --- a/CHANGELOG-4.4.md +++ b/CHANGELOG-4.4.md @@ -7,6 +7,31 @@ in 4.4 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/v4.4.0...v4.4.1 +* 4.4.40 (2022-04-02) + + * bug #45910 [Messenger] reset connection on worker shutdown (SanderHagen) + * bug #45909 [Form][TwigBundle] reset Twig form theme resources between requests (xabbuh) + * bug #45906 [HttpClient] on redirections don't send content related request headers (xabbuh) + * bug #45714 [Messenger] Fix cannot select FOR UPDATE from view on Oracle (rjd22) + * bug #45888 [Messenger] Add mysql indexes back and work around deadlocks using soft-delete (nicolas-grekas) + * bug #45891 [HttpClient] Fix exporting objects with readonly properties (nicolas-grekas) + * bug #45875 [ExpressionLanguage] Fix matches when the regexp is not valid (fabpot) + * bug #45870 [Validator] Fix File constraint invalid max size exception message (fancyweb) + * bug #45851 [Console] Fix exit status on uncaught exception with negative code (acoulton) + * bug #45838 [Serializer] Fix denormalizing union types (T-bond) + * bug #45816 [Mailer] Preserve case of headers (nicolas-grekas) + * bug #45814 [HttpClient] Let curl handle Content-Length headers (nicolas-grekas) + * bug #45813 [HttpClient] Move Content-Type after Content-Length (nicolas-grekas) + * bug #45737 [Lock] SemaphoreStore catching exception from sem_get (Triplkrypl) + * bug #45690 [Mailer] Use recipients in sendmail transport (HypeMC) + * bug #45720 [PropertyInfo] strip only leading `\` when unknown docType (EmilMassey) + * bug #44915 [Console] Fix compact table style to avoid outputting a leading space (Seldaek) + * bug #45676 [Process] Don't return executable directories in PhpExecutableFinder (fancyweb) + * bug #45702 [Form] Fix the usage of the Valid constraints in array-based forms (stof) + * bug #45677 [DependencyInjection] fix `ServiceSubscriberTrait` bug where parent has `__call()` (kbond) + * bug #45678 [HttpClient] Fix reading proxy settings from dotenv when curl is used (nicolas-grekas) + * bug #45671 [FrameworkBundle] Ensure container is reset between tests (nicolas-grekas) + * 4.4.39 (2022-03-05) * bug #45631 [HttpFoundation] Fix PHP 8.1 deprecation in `Response::isNotModified` (HypeMC) From fa1eb6db954a2f5f3672cdd1cd5f0c37fbb38e60 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 2 Apr 2022 07:55:50 +0200 Subject: [PATCH 48/49] Update CONTRIBUTORS for 4.4.40 --- CONTRIBUTORS.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e2781d32f4a30..bd30bf752af21 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -102,6 +102,7 @@ The Symfony Connect username in parenthesis allows to get more information - Henrik Westphal (snc) - Dariusz Górecki (canni) - Fran Moreno (franmomu) + - HypeMC (hypemc) - Jérôme Vasseur (jvasseur) - Mathieu Santostefano (welcomattic) - Dariusz Ruminski @@ -111,19 +112,18 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Holmes (dholmes) - Sebastiaan Stok (sstok) - Alexandre Daubois (alexandre-daubois) - - HypeMC (hypemc) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - John Wards (johnwards) - Tomas Norkūnas (norkunas) - Julien Falque (julienfalque) + - Vincent Langlet (deviling) - Baptiste Clavié (talus) - Massimiliano Arione (garak) - Mathias Arlaud (mtarld) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - - Vincent Langlet (deviling) - Arnaud Le Blanc (arnaud-lb) - Przemysław Bogusz (przemyslaw-bogusz) - Maxime STEINHAUSSER @@ -153,6 +153,7 @@ The Symfony Connect username in parenthesis allows to get more information - Teoh Han Hui (teohhanhui) - Colin Frei - Javier Spagnoletti (phansys) + - Gary PEGEOT (gary-p) - Ruud Kamphuis (ruudk) - Joshua Thijssen - Daniel Wehner (dawehner) @@ -161,7 +162,6 @@ The Symfony Connect username in parenthesis allows to get more information - Gordon Franke (gimler) - Saif Eddin Gmati (azjezz) - Richard van Laak (rvanlaak) - - Gary PEGEOT (gary-p) - Jesse Rushlow (geeshoe) - Fabien Pennequin (fabienpennequin) - Olivier Dolbeau (odolbeau) @@ -355,6 +355,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sébastien Lavoie (lavoiesl) - Dariusz - Farhad Safarov (safarov) + - Hugo Alliaume (kocal) - BoShurik - Thomas Lallement (raziel057) - Michael Voříšek @@ -418,7 +419,6 @@ The Symfony Connect username in parenthesis allows to get more information - Christopher Davis (chrisguitarguy) - Dmitriy Mamontov (mamontovdmitriy) - Ben Ramsey (ramsey) - - Hugo Alliaume (kocal) - Laurent Masforné (heisenberg) - Sergey (upyx) - Giorgio Premi @@ -441,6 +441,7 @@ The Symfony Connect username in parenthesis allows to get more information - Iker Ibarguren (ikerib) - Bob van de Vijver (bobvandevijver) - Peter Kruithof (pkruithof) + - Antoine Lamirault - Michael Holm (hollo) - Arjen van der Meijden - Markus Fasselt (digilist) @@ -478,6 +479,7 @@ The Symfony Connect username in parenthesis allows to get more information - Andrey Esaulov (andremaha) - Grégoire Passault (gregwar) - Jerzy Zawadzki (jzawadzki) + - Phil Taylor (prazgod) - Ismael Ambrosi (iambrosi) - Craig Duncan (duncan3dc) - Emmanuel BORGES (eborges78) @@ -585,8 +587,6 @@ The Symfony Connect username in parenthesis allows to get more information - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) - Vyacheslav Salakhutdinov (megazoll) - - Antoine Lamirault - - Phil Taylor (prazgod) - Hassan Amouhzi - Daniel Gorgan - Tamas Szijarto @@ -618,6 +618,7 @@ The Symfony Connect username in parenthesis allows to get more information - Xavier HAUSHERR - Albert Jessurum (ajessu) - Laszlo Korte + - Jonathan Scheiber (jmsche) - Miha Vrhovnik - Alessandro Desantis - hubert lecorche (hlecorche) @@ -688,6 +689,7 @@ The Symfony Connect username in parenthesis allows to get more information - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - Indra Gunawan (indragunawan) + - Jérôme Tanghe (deuchnord) - Mathias STRASSER (roukmoute) - simon chrzanowski (simonch) - Kamil Kokot (pamil) @@ -831,7 +833,6 @@ The Symfony Connect username in parenthesis allows to get more information - Stefan Kruppa - jhonnyL - sasezaki - - Jonathan Scheiber (jmsche) - Kristof Van Cauwenbergh (kristofvc) - Dawid Pakuła (zulusx) - Marco Lipparini (liarco) @@ -1156,6 +1157,7 @@ The Symfony Connect username in parenthesis allows to get more information - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - Florian Caron (shalalalala) + - Eric COURTIAL - Jeroen van den Enden (stoefke) - Aurélien Fontaine - Pascal Helfenstein @@ -1220,7 +1222,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jérémy REYNAUD (babeuloula) - Mathieu Rochette (mathroc) - Victor Garcia - - Jérôme Tanghe (deuchnord) - Marek Víger (freezy) - Andrew Hilobok (hilobok) - Noah Heck (myesain) @@ -1342,6 +1343,7 @@ The Symfony Connect username in parenthesis allows to get more information - Matt Robinson (inanimatt) - Aleksey Podskrebyshev - Calin Mihai Pristavu + - Gabrielle Langer - David Marín Carreño (davefx) - Fabien LUCAS (flucas2) - Ondrej Machulda (ondram) @@ -1763,6 +1765,7 @@ The Symfony Connect username in parenthesis allows to get more information - Vladimir Luchaninov (luchaninov) - spdionis - rchoquet + - rvoisin - gitlost - Taras Girnyk - Dmitry Derepko @@ -2342,6 +2345,7 @@ The Symfony Connect username in parenthesis allows to get more information - Romain Dorgueil - Christopher Parotat - Dennis Haarbrink + - Urban Suppiger - me_shaon - 蝦米 - Grayson Koonce (breerly) @@ -2822,6 +2826,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ikko Ashimine - Erwin Dirks - Brad Jones + - Markus Ramšak - Billie Thompson - lol768 - jamogon @@ -2898,7 +2903,6 @@ The Symfony Connect username in parenthesis allows to get more information - bokonet - Arrilot - ampaze - - Gabrielle Langer - Chris McGehee - Markus Staab - Pierre-Louis LAUNAY @@ -2920,6 +2924,7 @@ The Symfony Connect username in parenthesis allows to get more information - Lebnik - nsbx - Shude + - RTUnreal - Richard Hodgson - Sven Fabricius - Ondřej Führer From b45c0032d3067367d0bc61cc09f1f4b419a50b6b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 2 Apr 2022 07:55:50 +0200 Subject: [PATCH 49/49] Update VERSION for 4.4.40 --- 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 233c80f10b55d..91093eaef7369 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.40-DEV'; + public const VERSION = '4.4.40'; public const VERSION_ID = 40440; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 40; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2022'; public const END_OF_LIFE = '11/2023';