From 3e258504f6e66b5ede1c105f59ef93ca9dcaeb26 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 13 Nov 2019 09:44:43 +0100 Subject: [PATCH 01/76] updated CHANGELOG for 3.4.35 --- CHANGELOG-3.4.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index a72767317ec96..ba1027925d004 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,13 @@ in 3.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/v3.4.0...v3.4.1 +* 3.4.35 (2019-11-13) + + * bug #34344 [Console] Constant STDOUT might be undefined (nicolas-grekas) + * security #cve-2019-18889 [Cache] forbid serializing AbstractAdapter and TagAwareAdapter instances (nicolas-grekas) + * security #cve-2019-18888 [HttpFoundation] fix guessing mime-types of files with leading dash (nicolas-grekas) + * security #cve-2019-18887 [HttpKernel] Use constant time comparison in UriSigner (stof) + * 3.4.34 (2019-11-11) * bug #34297 [DI] fix locators with numeric keys (nicolas-grekas) From 02257c80981f1a30a8a5b13652161f97ad7b7c08 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 13 Nov 2019 09:44:50 +0100 Subject: [PATCH 02/76] updated VERSION for 3.4.35 --- 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 2543f59245851..05b11f3665c5c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.35-DEV'; + const VERSION = '3.4.35'; const VERSION_ID = 30435; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 35; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 8a68d2d358ae7db406f72711c24d5c8f687cf7b6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 13 Nov 2019 09:48:47 +0100 Subject: [PATCH 03/76] bumped Symfony version to 3.4.36 --- 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 05b11f3665c5c..25236948695dc 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.35'; - const VERSION_ID = 30435; + const VERSION = '3.4.36-DEV'; + const VERSION_ID = 30436; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 35; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 36; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From d863fc2b4b33e96ed281376f4e6ffaac01ec0f2f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 13 Nov 2019 10:11:31 +0100 Subject: [PATCH 04/76] bumped Symfony version to 4.3.9 --- 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 eca2ab61f1358..ac9775f5f7c64 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.3.8'; - const VERSION_ID = 40308; + const VERSION = '4.3.9-DEV'; + const VERSION_ID = 40309; const MAJOR_VERSION = 4; const MINOR_VERSION = 3; - const RELEASE_VERSION = 8; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 9; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '01/2020'; const END_OF_LIFE = '07/2020'; From 78dad79fec4a55893cd04e8255c9a66cf34f5be1 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Thu, 14 Nov 2019 16:04:06 +0100 Subject: [PATCH 05/76] [WebProfilerBundle] Require symfony/twig-bundle --- src/Symfony/Bundle/WebProfilerBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 241a5e350b414..c82ef0fe0093d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -20,7 +20,7 @@ "symfony/http-kernel": "~3.4.25|^4.2.6", "symfony/polyfill-php70": "~1.0", "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/twig-bridge": "~2.8|~3.0|~4.0", + "symfony/twig-bundle": "~2.8|~3.0|~4.0", "symfony/var-dumper": "~3.3|~4.0", "twig/twig": "~1.34|~2.4" }, From 600ae332620015e750aed76fc825a4b48f03be52 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 14 Nov 2019 19:27:47 +0100 Subject: [PATCH 06/76] [DI] Use reproducible entropy to generate env placeholders --- .../ParameterBag/EnvPlaceholderParameterBag.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php index fe27324e54f11..872408690b926 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php @@ -24,6 +24,8 @@ class EnvPlaceholderParameterBag extends ParameterBag private $unusedEnvPlaceholders = []; private $providedTypes = []; + private static $counter = 0; + /** * {@inheritdoc} */ @@ -57,7 +59,7 @@ public function get($name) } } - $uniqueName = md5($name.uniqid(mt_rand(), true)); + $uniqueName = md5($name.'_'.self::$counter++); $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), str_replace(':', '_', $env), $uniqueName); $this->envPlaceholders[$env][$placeholder] = $placeholder; @@ -72,7 +74,13 @@ public function get($name) */ public function getEnvPlaceholderUniquePrefix(): string { - return $this->envPlaceholderUniquePrefix ?? $this->envPlaceholderUniquePrefix = 'env_'.bin2hex(random_bytes(8)); + if (null === $this->envPlaceholderUniquePrefix) { + $reproducibleEntropy = unserialize(serialize($this->parameters)); + array_walk_recursive($reproducibleEntropy, function (&$v) { $v = null; }); + $this->envPlaceholderUniquePrefix = 'env_'.substr(md5(serialize($reproducibleEntropy)), -16); + } + + return $this->envPlaceholderUniquePrefix; } /** From e6c9d77b63ba7449b3b386b54f60ed18e0db3872 Mon Sep 17 00:00:00 2001 From: Damien Alexandre Date: Fri, 15 Nov 2019 15:28:29 +0100 Subject: [PATCH 07/76] [Finder] Allow ssh2 stream wrapper for sftp Same fix as #28604 but for the ss2.sftp wrapper. --- src/Symfony/Component/Finder/Finder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index 6624e840791fb..c8f642d872ce3 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -746,7 +746,7 @@ private function normalizeDir($dir) { $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR); - if (preg_match('#^s?ftp://#', $dir)) { + if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) { $dir .= '/'; } From 6adbfa2ae7a3153e9d2368ce9b2ce5bbb362fad7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 15 Nov 2019 16:26:16 +0100 Subject: [PATCH 08/76] [Config] fix id-generation for GlobResource --- src/Symfony/Component/Config/Resource/GlobResource.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index bf332fde32722..6c2539a2de15b 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -41,6 +41,7 @@ class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface */ public function __construct(?string $prefix, string $pattern, bool $recursive, bool $forExclusion = false, array $excludedPrefixes = []) { + ksort($excludedPrefixes); $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); $this->pattern = $pattern; $this->recursive = $recursive; @@ -62,7 +63,7 @@ public function getPrefix() */ public function __toString() { - return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive; + return 'glob.'.$this->prefix.(int) $this->recursive.$this->pattern.(int) $this->forExclusion.implode("\0", $this->excludedPrefixes); } /** From 7e38d83ad00399a93ea267a48d9727e377c3e117 Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Fri, 15 Nov 2019 19:14:27 +0100 Subject: [PATCH 09/76] Link the right file depending on the new version --- UPGRADE-4.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 8d41f32658c58..f970041949e5b 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -26,8 +26,8 @@ file and directory structure of your application: Then, upgrade the contents of your console script and your front controller: -* `bin/console`: https://github.com/symfony/recipes/blob/master/symfony/console/3.3/bin/console -* `public/index.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/public/index.php +* `bin/console`: https://github.com/symfony/recipes/blob/master/symfony/console/4.4/bin/console +* `public/index.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/4.4/public/index.php Lastly, read the following article to add Symfony Flex to your application and upgrade the configuration files: https://symfony.com/doc/current/setup/flex.html From 51c5f69274b0a0375b049fc34a4336ec80b49b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Broutier?= Date: Sat, 16 Nov 2019 12:20:14 +0100 Subject: [PATCH 10/76] Fix MySQL column type definition. Fix wrong MySQL column type definition causing Numeric value out of range exception. Ref #34409 --- .../Session/Storage/Handler/PdoSessionHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index f9e5d1e8f04d8..c9d47b6ed7c27 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -218,7 +218,7 @@ public function createTable() // - trailing space removal // - case-insensitivity // - language processing like é == e - $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; + $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; break; case 'sqlite': $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; From 4d9b6f1bbd86092b76ab279464da3f71e3fa335a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 16 Nov 2019 16:17:02 +0100 Subject: [PATCH 11/76] [HttpFoundation] fix docblock --- .../Session/Storage/Handler/RedisSessionHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php index 46ca6796d8ebb..8ebc5bd8ee75d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -34,8 +34,7 @@ class RedisSessionHandler extends AbstractSessionHandler * List of available options: * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server. * - * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy $redis - * @param array $options An associative array of options + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis * * @throws \InvalidArgumentException When unsupported client or options are passed */ From eaca085aa4ec40b51f0591de411f840481319972 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 16 Nov 2019 16:50:12 +0100 Subject: [PATCH 12/76] [SecurityBundle] fix failing test --- .../SecurityBundle/Tests/Functional/SwitchUserTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php index 31f99da2a08f2..722bef0f78628 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php @@ -25,6 +25,10 @@ public function testSwitchUser($originalUser, $targetUser, $expectedUser, $expec $client->request('GET', '/profile?_switch_user='.$targetUser); + if ('user_does_not_exist' === $targetUser && 403 === $expectedStatus && 500 === $client->getResponse()->getStatusCode()) { + $expectedStatus = 500; // 403 is generated by Symfony >= 4.3.8 + } + $this->assertEquals($expectedStatus, $client->getResponse()->getStatusCode()); $this->assertEquals($expectedUser, $client->getProfile()->getCollector('security')->getUser()); } @@ -68,7 +72,7 @@ public function getTestParameters() return [ 'unauthorized_user_cannot_switch' => ['user_cannot_switch_1', 'user_cannot_switch_1', 'user_cannot_switch_1', 403], 'authorized_user_can_switch' => ['user_can_switch', 'user_cannot_switch_1', 'user_cannot_switch_1', 200], - 'authorized_user_cannot_switch_to_non_existent' => ['user_can_switch', 'user_does_not_exist', 'user_can_switch', 500], + 'authorized_user_cannot_switch_to_non_existent' => ['user_can_switch', 'user_does_not_exist', 'user_can_switch', 403], 'authorized_user_can_switch_to_himself' => ['user_can_switch', 'user_can_switch', 'user_can_switch', 200], ]; } From 5c1f5594f5104a97eba28cc9c49e00e78ccdc401 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 16 Nov 2019 10:57:45 +0100 Subject: [PATCH 13/76] catch exceptions when using PDO directly --- src/Symfony/Component/Cache/Traits/PdoTrait.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Symfony/Component/Cache/Traits/PdoTrait.php b/src/Symfony/Component/Cache/Traits/PdoTrait.php index ec34e72fb530a..b636e7565b2cd 100644 --- a/src/Symfony/Component/Cache/Traits/PdoTrait.php +++ b/src/Symfony/Component/Cache/Traits/PdoTrait.php @@ -159,6 +159,8 @@ public function prune() $delete = $this->getConnection()->prepare($deleteSql); } catch (TableNotFoundException $e) { return true; + } catch (\PDOException $e) { + return true; } $delete->bindValue(':time', time(), \PDO::PARAM_INT); @@ -169,6 +171,8 @@ public function prune() return $delete->execute(); } catch (TableNotFoundException $e) { return true; + } catch (\PDOException $e) { + return true; } } @@ -244,6 +248,7 @@ protected function doClear($namespace) try { $conn->exec($sql); } catch (TableNotFoundException $e) { + } catch (\PDOException $e) { } return true; @@ -260,6 +265,7 @@ protected function doDelete(array $ids) $stmt = $this->getConnection()->prepare($sql); $stmt->execute(array_values($ids)); } catch (TableNotFoundException $e) { + } catch (\PDOException $e) { } return true; @@ -316,6 +322,11 @@ protected function doSave(array $values, $lifetime) $this->createTable(); } $stmt = $conn->prepare($sql); + } catch (\PDOException $e) { + if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $stmt = $conn->prepare($sql); } if ('sqlsrv' === $driver || 'oci' === $driver) { @@ -350,6 +361,11 @@ protected function doSave(array $values, $lifetime) $this->createTable(); } $stmt->execute(); + } catch (\PDOException $e) { + if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $stmt->execute(); } if (null === $driver && !$stmt->rowCount()) { try { From bd0637ebe4e8779b6b7b5b447ca4fd3d5ff44149 Mon Sep 17 00:00:00 2001 From: Mark Beech Date: Wed, 13 Nov 2019 20:07:22 +0000 Subject: [PATCH 14/76] [HttpFoundation] Allow redirecting to URLs that contain a semicolon --- src/Symfony/Component/HttpFoundation/RedirectResponse.php | 2 +- .../Component/HttpFoundation/Tests/RedirectResponseTest.php | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php index 4e3cb4f77b28b..a19efba3e3d5e 100644 --- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php +++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -93,7 +93,7 @@ public function setTargetUrl($url) - + Redirecting to %1$s diff --git a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php index e1ff3bf2bdb98..2bbf5aa1aea5a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php @@ -20,10 +20,7 @@ public function testGenerateMetaRedirect() { $response = new RedirectResponse('foo.bar'); - $this->assertEquals(1, preg_match( - '##', - preg_replace(['/\s+/', '/\'/'], [' ', '"'], $response->getContent()) - )); + $this->assertRegExp('##', preg_replace('/\s+/', ' ', $response->getContent())); } public function testRedirectResponseConstructorNullUrl() From e75e01dda4ddf18aeecf2bee1c780b11bab160fd Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 15 Nov 2019 16:11:40 +0100 Subject: [PATCH 15/76] [FrameworkBundle] Remove project dir from Translator cache vary scanned directories --- .../DependencyInjection/FrameworkExtension.php | 9 ++++++++- .../DependencyInjection/FrameworkExtensionTest.php | 2 ++ .../Tests/Translation/TranslatorTest.php | 14 +++++++++----- .../FrameworkBundle/Translation/Translator.php | 10 +++++----- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index a913f4f93db40..2ad1566a25aa8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1184,11 +1184,18 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $files[$locale][] = (string) $file; } + $projectDir = $container->getParameter('kernel.project_dir'); + $options = array_merge( $translator->getArgument(4), [ 'resource_files' => $files, - 'scanned_directories' => array_merge($dirs, $nonExistingDirs), + 'scanned_directories' => $scannedDirectories = array_merge($dirs, $nonExistingDirs), + 'cache_vary' => [ + 'scanned_directories' => array_map(static function (string $dir) use ($projectDir): string { + return 0 === strpos($dir, $projectDir.'/') ? substr($dir, 1 + \strlen($projectDir)) : $dir; + }, $scannedDirectories), + ], ] ); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 5f2e007ddbcd4..45a3c02e76c3c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -824,6 +824,8 @@ function ($directory) { ); $this->assertNotEmpty($nonExistingDirectories, 'FrameworkBundle should pass non existing directories to Translator'); + + $this->assertSame('Fixtures/translations', $options['cache_vary']['scanned_directories'][3]); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index cd3f3d9fba858..5eaf901d16027 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -255,8 +255,10 @@ public function testCachedCatalogueIsReDumpedWhenScannedDirectoriesChange() __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', ], ], - 'scanned_directories' => [ - __DIR__.'/../Fixtures/Resources/translations/', + 'cache_vary' => [ + 'scanned_directories' => [ + '/Fixtures/Resources/translations/', + ], ], ], 'yml'); @@ -271,9 +273,11 @@ public function testCachedCatalogueIsReDumpedWhenScannedDirectoriesChange() __DIR__.'/../Fixtures/Resources/translations2/ccc.fr.yml', ], ], - 'scanned_directories' => [ - __DIR__.'/../Fixtures/Resources/translations/', - __DIR__.'/../Fixtures/Resources/translations2/', + 'cache_vary' => [ + 'scanned_directories' => [ + '/Fixtures/Resources/translations/', + '/Fixtures/Resources/translations2/', + ], ], ], 'yml'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index f718c8d0157bc..155372ee136f4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -34,6 +34,7 @@ class Translator extends BaseTranslator implements WarmableInterface 'debug' => false, 'resource_files' => [], 'scanned_directories' => [], + 'cache_vary' => [], ]; /** @@ -61,9 +62,10 @@ class Translator extends BaseTranslator implements WarmableInterface * * Available options: * - * * cache_dir: The cache directory (or null to disable caching) - * * debug: Whether to enable debugging or not (false by default) + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) * * resource_files: List of translation resources available grouped by locale. + * * cache_vary: An array of data that is serialized to generate the cached catalogue name. * * @throws InvalidArgumentException */ @@ -82,9 +84,7 @@ public function __construct(ContainerInterface $container, MessageFormatterInter $this->resourceFiles = $this->options['resource_files']; $this->scannedDirectories = $this->options['scanned_directories']; - parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug'], [ - 'scanned_directories' => $this->scannedDirectories, - ]); + parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug'], $this->options['cache_vary']); } /** From a2c924d77220011c5f5939579677d2a82b69a43d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 17 Nov 2019 11:56:39 +0100 Subject: [PATCH 16/76] [Cache] Disable igbinary on PHP >= 7.4 --- .../Cache/Marshaller/DefaultMarshaller.php | 8 ++++---- .../Tests/Marshaller/DefaultMarshallerTest.php | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php b/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php index 196fd625f1fb9..8056090110c41 100644 --- a/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php +++ b/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php @@ -25,9 +25,9 @@ class DefaultMarshaller implements MarshallerInterface public function __construct(bool $useIgbinarySerialize = null) { if (null === $useIgbinarySerialize) { - $useIgbinarySerialize = \extension_loaded('igbinary') && \PHP_VERSION_ID !== 70400; - } elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || \PHP_VERSION_ID === 70400)) { - throw new CacheException('The "igbinary" PHP extension is not '.(\PHP_VERSION_ID === 70400 ? 'compatible with PHP 7.4.0.' : 'loaded.')); + $useIgbinarySerialize = \extension_loaded('igbinary') && \PHP_VERSION_ID < 70400; + } elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || \PHP_VERSION_ID >= 70400)) { + throw new CacheException('The "igbinary" PHP extension is not '.(\PHP_VERSION_ID >= 70400 ? 'compatible with PHP 7.4.' : 'loaded.')); } $this->useIgbinarySerialize = $useIgbinarySerialize; } @@ -66,7 +66,7 @@ public function unmarshall(string $value) return null; } static $igbinaryNull; - if ($value === ($igbinaryNull ?? $igbinaryNull = \extension_loaded('igbinary') && \PHP_VERSION_ID !== 70400 ? igbinary_serialize(null) : false)) { + if ($value === ($igbinaryNull ?? $igbinaryNull = \extension_loaded('igbinary') && \PHP_VERSION_ID < 70400 ? igbinary_serialize(null) : false)) { return null; } $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/DefaultMarshallerTest.php b/src/Symfony/Component/Cache/Tests/Marshaller/DefaultMarshallerTest.php index 141291d6e448b..cc94ad15237a0 100644 --- a/src/Symfony/Component/Cache/Tests/Marshaller/DefaultMarshallerTest.php +++ b/src/Symfony/Component/Cache/Tests/Marshaller/DefaultMarshallerTest.php @@ -24,7 +24,7 @@ public function testSerialize() 'b' => function () {}, ]; - $expected = ['a' => \extension_loaded('igbinary') && \PHP_VERSION_ID !== 70400 ? igbinary_serialize(123) : serialize(123)]; + $expected = ['a' => \extension_loaded('igbinary') && \PHP_VERSION_ID < 70400 ? igbinary_serialize(123) : serialize(123)]; $this->assertSame($expected, $marshaller->marshall($values, $failed)); $this->assertSame(['b'], $failed); } @@ -43,8 +43,8 @@ public function testNativeUnserialize() */ public function testIgbinaryUnserialize() { - if (\PHP_VERSION_ID === 70400) { - $this->markTestSkipped('igbinary is not compatible with PHP 7.4.0.'); + if (\PHP_VERSION_ID >= 70400) { + $this->markTestSkipped('igbinary is not compatible with PHP 7.4.'); } $marshaller = new DefaultMarshaller(); @@ -67,8 +67,8 @@ public function testNativeUnserializeNotFoundClass() */ public function testIgbinaryUnserializeNotFoundClass() { - if (\PHP_VERSION_ID === 70400) { - $this->markTestSkipped('igbinary is not compatible with PHP 7.4.0.'); + if (\PHP_VERSION_ID >= 70400) { + $this->markTestSkipped('igbinary is not compatible with PHP 7.4.'); } $this->expectException('DomainException'); @@ -95,8 +95,8 @@ public function testNativeUnserializeInvalid() */ public function testIgbinaryUnserializeInvalid() { - if (\PHP_VERSION_ID === 70400) { - $this->markTestSkipped('igbinary is not compatible with PHP 7.4.0'); + if (\PHP_VERSION_ID >= 70400) { + $this->markTestSkipped('igbinary is not compatible with PHP 7.4.'); } $this->expectException('DomainException'); From d53b91a45af4f83c83e1ed896b5954bc2aa83e75 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 10 Jul 2019 01:47:30 +0200 Subject: [PATCH 17/76] Add conflict rule for Monolog 2. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 42397d75b851d..9ad5e39ad2ca5 100644 --- a/composer.json +++ b/composer.json @@ -103,6 +103,7 @@ "phpdocumentor/reflection-docblock": "^3.0|^4.0" }, "conflict": { + "monolog/monolog": ">=2", "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.3.0", "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" From 7b7f9667119a7de82cc9d2c8a828d4d479929697 Mon Sep 17 00:00:00 2001 From: Nardberjean Date: Sun, 17 Nov 2019 11:11:27 -0500 Subject: [PATCH 18/76] Update HttpKernel.php --- src/Symfony/Component/HttpKernel/HttpKernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index bca2cd1688e8e..9769d5e802b2d 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -62,7 +62,7 @@ public function __construct(EventDispatcherInterface $dispatcher, ControllerReso */ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) { - $request->headers->set('X-Php-Ob-Level', ob_get_level()); + $request->headers->set('X-Php-Ob-Level', (string) ob_get_level()); try { return $this->handleRaw($request, $type); From c194fffaef3d5df57186117c96f8e99aed82fbbe Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 17 Nov 2019 22:18:02 +0100 Subject: [PATCH 19/76] Adjust pull request template for 5.0 branchout --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b863dea0cbf9a..4d7b45b66b560 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | 4.4 for features / 3.4 or 4.3 for bug fixes +| Branch? | master for features / 3.4, 4.3, 4.4 or 5.0 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no From 934e05c00b89cba6171eeeb19f93e68f43f8352a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 17 Nov 2019 22:52:25 +0100 Subject: [PATCH 20/76] Fix PR template --- .github/PULL_REQUEST_TEMPLATE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4d7b45b66b560..0d234d8fe9b7f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,6 +16,5 @@ Additionally (see https://symfony.com/roadmap): - Never break backward compatibility (see https://symfony.com/bc). - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too.) - - Features and deprecations must be submitted against branch 4.4. - - Legacy code removals go to the master branch. + - Features and deprecations must be submitted against branch master. --> From e7d0787a4dfe68c33c81f09aa8fb59fe67ea90dc Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 17 Nov 2019 16:24:49 +0100 Subject: [PATCH 21/76] [Finder] Fixed docs --- src/Symfony/Component/Finder/Finder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index c8f642d872ce3..d2ea17d87a00f 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -736,7 +736,7 @@ private function searchInDirectory($dir) /** * Normalizes given directory names by removing trailing slashes. * - * Excluding: (s)ftp:// wrapper + * Excluding: (s)ftp:// or ssh2.(s)ftp:// wrapper * * @param string $dir * From d811b0e9b50bb628e3e30429b6a4c7a931f0d30b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 19 Nov 2019 08:54:33 +0100 Subject: [PATCH 22/76] allow button names to start with uppercase letter --- UPGRADE-4.3.md | 2 +- UPGRADE-5.0.md | 2 +- src/Symfony/Component/Form/ButtonBuilder.php | 2 +- src/Symfony/Component/Form/CHANGELOG.md | 2 +- src/Symfony/Component/Form/Tests/ButtonBuilderTest.php | 8 -------- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/UPGRADE-4.3.md b/UPGRADE-4.3.md index 86155031fa29e..e3a4ac469d056 100644 --- a/UPGRADE-4.3.md +++ b/UPGRADE-4.3.md @@ -96,7 +96,7 @@ Form ---- * Using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled is deprecated. - * Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated and will lead to an + * Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated and will lead to an exception in 5.0. * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons is deprecated and will lead to an exception in 5.0. diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 455cd0d62ad09..302cea6dec969 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -110,7 +110,7 @@ Form ---- * Removed support for using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled. - * Using names for buttons that do not start with a lowercase letter, a digit, or an underscore leads to an exception. + * Using names for buttons that do not start with a letter, a digit, or an underscore leads to an exception. * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons leads to an exception. * Using the `date_format`, `date_widget`, and `time_widget` options of the `DateTimeType` when the `widget` option is diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index bf8e147838de1..ca6bf78dc9cb1 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -61,7 +61,7 @@ public function __construct(?string $name, array $options = []) $this->name = $name; $this->options = $options; - if (preg_match('/^([^a-z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) { + if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) { if (isset($matches[1])) { @trigger_error(sprintf('Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index cf7b8cd8cc2d8..e41c46e907a89 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -6,7 +6,7 @@ CHANGELOG * added a `symbol` option to the `PercentType` that allows to disable or customize the output of the percent character * Using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled is deprecated. - * Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated and will lead to an + * Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated and will lead to an exception in 5.0. * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons is deprecated and will lead to an exception in 5.0. diff --git a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php index 966b2927e9adb..7bd78896d1947 100644 --- a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php +++ b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php @@ -47,14 +47,6 @@ public function testNameContainingIllegalCharacters() $this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder('button[]')); } - /** - * @group legacy - */ - public function testNameStartingWithIllegalCharacters() - { - $this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder('Button')); - } - public function getInvalidNames() { return [ From 60b0dae17434ed294c95b330567c3aba2bda5830 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Tue, 19 Nov 2019 11:37:15 +0100 Subject: [PATCH 23/76] [DependencyInjection] Fix dumping multiple deprecated aliases --- .../DependencyInjection/Dumper/PhpDumper.php | 2 +- .../Tests/Dumper/PhpDumperTest.php | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 48267b0d033df..5e2f7d2dd4378 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1226,7 +1226,7 @@ private function addDeprecatedAliases(): string $methodNameAlias = $this->generateMethodName($alias); $idExported = $this->export($id); $messageExported = $this->export($definition->getDeprecationMessage($alias)); - $code = <<docStar} * Gets the $public '$alias' alias. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index b78fcb32e4363..d0fb5ab8c6aa1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1283,6 +1283,30 @@ public function testWither() $wither = $container->get('wither'); $this->assertInstanceOf(Foo::class, $wither->foo); } + + /** + * @group legacy + * @expectedDeprecation The "deprecated1" service alias is deprecated. You should stop using it, as it will be removed in the future. + * @expectedDeprecation The "deprecated2" service alias is deprecated. You should stop using it, as it will be removed in the future. + */ + public function testMultipleDeprecatedAliasesWorking() + { + $container = new ContainerBuilder(); + $container->setDefinition('bar', new Definition('stdClass'))->setPublic(true); + $container->setAlias('deprecated1', 'bar')->setPublic(true)->setDeprecated('%alias_id% is deprecated'); + $container->setAlias('deprecated2', 'bar')->setPublic(true)->setDeprecated('%alias_id% is deprecated'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dump = $dumper->dump(['class' => $class = __FUNCTION__]); + + eval('?>'.$dump); + $container = new $class(); + + $this->assertInstanceOf(\stdClass::class, $container->get('bar')); + $this->assertInstanceOf(\stdClass::class, $container->get('deprecated1')); + $this->assertInstanceOf(\stdClass::class, $container->get('deprecated2')); + } } class Rot13EnvVarProcessor implements EnvVarProcessorInterface From 8d1f32613ba1296f0766f34166e399a442d76912 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 19 Nov 2019 17:14:53 +0100 Subject: [PATCH 24/76] [Validator] ConstraintValidatorTestCase: add missing return value to mocked validate method calls --- .../Validator/Test/ConstraintValidatorTestCase.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php index 18705b7987efe..ed57d396ae00e 100644 --- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php @@ -177,7 +177,8 @@ protected function expectValidateAt($i, $propertyPath, $value, $group) ->willReturn($validator); $validator->expects($this->at(2 * $i + 1)) ->method('validate') - ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group); + ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group) + ->willReturn($validator); } protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null) @@ -189,7 +190,8 @@ protected function expectValidateValueAt($i, $propertyPath, $value, $constraints ->willReturn($contextualValidator); $contextualValidator->expects($this->at(2 * $i + 1)) ->method('validate') - ->with($value, $constraints, $group); + ->with($value, $constraints, $group) + ->willReturn($contextualValidator); } protected function assertNoViolation() From d4f749a4651b2e3f590136ed789bc437a72cf993 Mon Sep 17 00:00:00 2001 From: Anto Date: Thu, 21 Nov 2019 13:46:45 +0100 Subject: [PATCH 25/76] [Serializer] Fix MetadataAwareNameConverter usage with string group fixes 34455 --- .../Serializer/NameConverter/MetadataAwareNameConverter.php | 2 +- .../Tests/NameConverter/MetadataAwareNameConverterTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php index ea69a573632b4..7057e45134ee3 100644 --- a/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php +++ b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php @@ -123,7 +123,7 @@ private function getCacheValueForAttributesMetadata(string $class, array $contex if (!$groups && ($context[AbstractNormalizer::GROUPS] ?? [])) { continue; } - if ($groups && !array_intersect($groups, $context[AbstractNormalizer::GROUPS] ?? [])) { + if ($groups && !array_intersect($groups, (array) ($context[AbstractNormalizer::GROUPS] ?? []))) { continue; } diff --git a/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php index d95c3ca91d2ee..540e59ed58aff 100644 --- a/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php +++ b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php @@ -146,6 +146,8 @@ public function attributeAndContextProvider() return [ ['buz', 'buz', ['groups' => ['a']]], ['buzForExport', 'buz', ['groups' => ['b']]], + ['buz', 'buz', ['groups' => 'a']], + ['buzForExport', 'buz', ['groups' => 'b']], ['buz', 'buz', ['groups' => ['c']]], ['buz', 'buz', []], ]; From dfdcbb401ea7839bfde60fe43cef6ef4f4927b2b Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 13 Sep 2019 16:42:04 +0200 Subject: [PATCH 26/76] [TwigBridge] Add row_attr to all form themes --- .../views/Form/bootstrap_3_horizontal_layout.html.twig | 8 ++++---- .../Resources/views/Form/bootstrap_3_layout.html.twig | 8 ++++---- .../views/Form/bootstrap_4_horizontal_layout.html.twig | 10 +++++----- .../Resources/views/Form/bootstrap_4_layout.html.twig | 2 +- .../views/Form/bootstrap_base_layout.html.twig | 2 +- .../Resources/views/Form/form_div_layout.html.twig | 4 ++-- .../Resources/views/Form/form_table_layout.html.twig | 7 ++++--- .../Resources/views/Form/foundation_5_layout.html.twig | 6 +++--- src/Symfony/Bridge/Twig/composer.json | 4 ++-- src/Symfony/Bundle/FrameworkBundle/composer.json | 4 ++-- 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig index b082d9236b927..4dc3e9c896fb7 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig @@ -27,7 +27,7 @@ col-sm-2 {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+ {{- form_label(form) -}}
{{- form_widget(form, widget_attr) -}} @@ -38,7 +38,7 @@ col-sm-2 {%- endblock form_row %} {% block submit_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} @@ -47,7 +47,7 @@ col-sm-2 {%- endblock submit_row %} {% block reset_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} @@ -60,7 +60,7 @@ col-sm-10 {%- endblock form_group_class %} {% block checkbox_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 7d70044e6c8b8..f0cf0ad81854d 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -111,7 +111,7 @@ {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+ {{- form_label(form) }} {# -#} {{ form_widget(form, widget_attr) }} {# -#} {{- form_help(form) -}} @@ -120,7 +120,7 @@ {%- endblock form_row %} {% block button_row -%} -
+ {{- form_widget(form) -}}
{%- endblock button_row %} @@ -146,14 +146,14 @@ {%- endblock datetime_row %} {% block checkbox_row -%} -
+ {{- form_widget(form) -}} {{- form_errors(form) -}}
{%- endblock checkbox_row %} {% block radio_row -%} -
+ {{- form_widget(form) -}} {{- form_errors(form) -}}
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig index e37de07d6b071..a75e364187743 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig @@ -28,7 +28,7 @@ col-sm-2 {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+ {{- form_label(form) -}}
{{- form_widget(form, widget_attr) -}} @@ -43,7 +43,7 @@ col-sm-2 {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+
{{- form_label(form) -}}
@@ -55,7 +55,7 @@ col-sm-2 {%- endblock fieldset_form_row %} {% block submit_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} @@ -64,7 +64,7 @@ col-sm-2 {%- endblock submit_row %} {% block reset_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} @@ -77,7 +77,7 @@ col-sm-10 {%- endblock form_group_class %} {% block checkbox_row -%} -
{#--#} + {#--#}
{#--#}
{{- form_widget(form) -}} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 97710c79b59c6..2e6dc23d4fbcd 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -282,7 +282,7 @@ {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} - <{{ element|default('div') }} class="form-group"> + <{{ element|default('div') }}{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~ ' form-group')|trim})} %}{{ block('attributes') }}{% endwith %}> {{- form_label(form) -}} {{- form_widget(form, widget_attr) -}} {{- form_help(form) -}} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig index a6ee019a094b6..46069980bd8df 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig @@ -187,7 +187,7 @@ {# Rows #} {% block button_row -%} -
+ {{- form_widget(form) -}}
{%- endblock button_row %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 0a5cd42cfda41..e1a418e84b722 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -325,7 +325,7 @@ {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+ {{- form_label(form) -}} {{- form_errors(form) -}} {{- form_widget(form, widget_attr) -}} @@ -334,7 +334,7 @@ {%- endblock form_row -%} {%- block button_row -%} -
+ {{- form_widget(form) -}}
{%- endblock button_row -%} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig index 10eaf566d097d..255d6b42bf7bd 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -5,7 +5,7 @@ {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} - + {{- form_label(form) -}} @@ -18,7 +18,7 @@ {%- endblock form_row -%} {%- block button_row -%} - + {{- form_widget(form) -}} @@ -27,7 +27,8 @@ {%- endblock button_row -%} {%- block hidden_row -%} - + {%- set style = row_attr.style is defined ? (row_attr.style ~ (row_attr.style|trim|last != ';' ? '; ')) : '' -%} + {{- form_widget(form) -}} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig index d8bb8308a36aa..83c5e30d803bc 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig @@ -277,7 +277,7 @@ {%- if help is not empty -%} {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%} -
+
{{- form_label(form) -}} {{- form_widget(form, widget_attr) -}} @@ -308,7 +308,7 @@ {%- endblock datetime_row %} {% block checkbox_row -%} -
+
{{ form_widget(form) }} {{ form_errors(form) }} @@ -317,7 +317,7 @@ {%- endblock checkbox_row %} {% block radio_row -%} -
+
{{ form_widget(form) }} {{ form_errors(form) }} diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index cb2d81e08b3d2..7a635f9330e1e 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -26,7 +26,7 @@ "symfony/asset": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", - "symfony/form": "^4.3.4", + "symfony/form": "^4.3.5", "symfony/http-foundation": "~4.3", "symfony/http-kernel": "~3.4|~4.0", "symfony/mime": "~4.3", @@ -48,7 +48,7 @@ }, "conflict": { "symfony/console": "<3.4", - "symfony/form": "<4.3.4", + "symfony/form": "<4.3.5", "symfony/http-foundation": "<4.3", "symfony/translation": "<4.2", "symfony/workflow": "<4.3" diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 23fd9efe9990f..b554b2be63eab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -38,7 +38,7 @@ "symfony/css-selector": "~3.4|~4.0", "symfony/dom-crawler": "^4.3", "symfony/polyfill-intl-icu": "~1.0", - "symfony/form": "^4.3.4", + "symfony/form": "^4.3.5", "symfony/expression-language": "~3.4|~4.0", "symfony/http-client": "^4.3", "symfony/mailer": "^4.3", @@ -72,7 +72,7 @@ "symfony/console": "<4.3", "symfony/dotenv": "<4.2", "symfony/dom-crawler": "<4.3", - "symfony/form": "<4.3", + "symfony/form": "<4.3.5", "symfony/messenger": "<4.3.6", "symfony/property-info": "<3.4", "symfony/serializer": "<4.2", From 6ff20f01228c14392023a7a81c1e469121c30e2f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 22 Nov 2019 14:37:01 +0100 Subject: [PATCH 27/76] compatibility with DoctrineBundle 2 --- .../Transport/Doctrine/DoctrineTransportFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php b/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php index 959a4ec7a2c3b..caa13082d9257 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Transport\Doctrine; +use Doctrine\Common\Persistence\ConnectionRegistry; use Symfony\Bridge\Doctrine\RegistryInterface; use Symfony\Component\Messenger\Exception\TransportException; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; @@ -26,8 +27,12 @@ class DoctrineTransportFactory implements TransportFactoryInterface { private $registry; - public function __construct(RegistryInterface $registry) + public function __construct($registry) { + if (!$registry instanceof RegistryInterface && !$registry instanceof ConnectionRegistry) { + throw new \TypeError(sprintf('Expected an instance of %s or %s, but got %s.', RegistryInterface::class, ConnectionRegistry::class, \is_object($registry) ? \get_class($registry) : \gettype($registry))); + } + $this->registry = $registry; } From 7425d2c69d7dcf35ab0a5d574951d074f7ec547f Mon Sep 17 00:00:00 2001 From: elementaire Date: Fri, 22 Nov 2019 15:36:52 +0100 Subject: [PATCH 28/76] Fix error message according to the new regex Complete https://github.com/symfony/symfony/pull/34448 --- src/Symfony/Component/Form/ButtonBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index ca6bf78dc9cb1..0665c589c9a40 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -63,7 +63,7 @@ public function __construct(?string $name, array $options = []) if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) { if (isset($matches[1])) { - @trigger_error(sprintf('Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); } if (isset($matches[2])) { @trigger_error(sprintf('Using names for buttons that do not contain only letters, digits, underscores ("_"), hyphens ("-") and colons (":") ("%s" given) is deprecated since Symfony 4.3 and will throw an exception in 5.0.', $name), E_USER_DEPRECATED); From 2bf6cd2eea53fe8acff38b76483d7049655dafb1 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Sat, 23 Nov 2019 15:44:33 +0100 Subject: [PATCH 29/76] [Security] Fix SwitchUser is broken when the User Provider always returns a valid user --- .../Component/Security/Http/Firewall/SwitchUserListener.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index e383b48768d82..4b82fa1f8c5b7 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -153,7 +153,6 @@ private function attemptSwitchUser(Request $request, $username) try { $this->provider->loadUserByUsername($nonExistentUsername); - throw new \LogicException('AuthenticationException expected'); } catch (AuthenticationException $e) { } } catch (AuthenticationException $e) { From 960faef66f48d71d04d605c9067d05ba95dc7585 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 14 Nov 2019 23:18:30 +0100 Subject: [PATCH 30/76] Avoid empty \"If-Modified-Since\" header in validation request --- src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php | 4 +++- .../Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index addeca8bae143..dbe028065948d 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -352,7 +352,9 @@ protected function validate(Request $request, Response $entry, $catch = false) } // add our cached last-modified validator - $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + if ($entry->headers->has('Last-Modified')) { + $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + } // Add our cached etag validator to the environment. // We keep the etags from the client to handle the case when the client diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index a50e09fb14f93..93d92eb11e7e3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -859,6 +859,7 @@ public function testValidatesCachedResponsesUseSameHttpMethod() public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() { $this->setNextResponse(200, [], 'Hello World', function ($request, $response) { + $this->assertFalse($request->headers->has('If-Modified-Since')); $response->headers->set('Cache-Control', 'public'); $response->headers->set('ETag', '"12345"'); if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) { From 026730e913ef09522a2e57a07147e3d740f13e7a Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 23 Nov 2019 21:28:49 +0100 Subject: [PATCH 31/76] Remove some unused methods parameters --- .../Console/Descriptor/JsonDescriptor.php | 4 ++-- .../DependencyInjection/SecurityExtension.php | 4 ++-- .../Bundle/WebServerBundle/Command/ServerLogCommand.php | 4 ++-- .../DependencyInjection/Loader/XmlFileLoader.php | 9 ++++----- .../Component/Translation/Dumper/XliffFileDumper.php | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index ea5d0c7d0bffa..197db657f0015 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -166,7 +166,7 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev */ protected function describeCallable($callable, array $options = []) { - $this->writeData($this->getCallableData($callable, $options), $options); + $this->writeData($this->getCallableData($callable), $options); } /** @@ -321,7 +321,7 @@ private function getEventDispatcherListenersData(EventDispatcherInterface $event * * @return array */ - private function getCallableData($callable, array $options = []) + private function getCallableData($callable) { $data = []; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 731ce42d39328..795fe053e66e4 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -585,7 +585,7 @@ private function createEncoders($encoders, ContainerBuilder $container) { $encoderMap = []; foreach ($encoders as $class => $encoder) { - $encoderMap[$class] = $this->createEncoder($encoder, $container); + $encoderMap[$class] = $this->createEncoder($encoder); } $container @@ -594,7 +594,7 @@ private function createEncoders($encoders, ContainerBuilder $container) ; } - private function createEncoder($config, ContainerBuilder $container) + private function createEncoder($config) { // a custom encoder service if (isset($config['id'])) { diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php index 73c51cc8e09e4..5c9ce910ddf54 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php @@ -114,7 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output) continue; } - $this->displayLog($input, $output, $clientId, $record); + $this->displayLog($output, $clientId, $record); } } @@ -141,7 +141,7 @@ private function getLogs($socket) } } - private function displayLog(InputInterface $input, OutputInterface $output, $clientId, array $record) + private function displayLog(OutputInterface $output, $clientId, array $record) { if ($this->handler->isHandling($record)) { if (isset($record['log_id'])) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index c4b9b69a03ca2..bd2902f85681a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -278,7 +278,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults) $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); } - $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, false, $definition instanceof ChildDefinition)); + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, $definition instanceof ChildDefinition)); $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); if ($factories = $this->getChildren($service, 'factory')) { @@ -452,11 +452,10 @@ private function processAnonymousServices(\DOMDocument $xml, $file, $defaults) * * @param string $name * @param string $file - * @param bool $lowercase * * @return mixed */ - private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = true, $isChildDefinition = false) + private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $isChildDefinition = false) { $arguments = []; foreach ($this->getChildren($node, $name) as $arg) { @@ -506,10 +505,10 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = $arguments[$key] = new Expression($arg->nodeValue); break; case 'collection': - $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file, false); + $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file); break; case 'iterator': - $arg = $this->getArgumentsAsPhp($arg, $name, $file, false); + $arg = $this->getArgumentsAsPhp($arg, $name, $file); try { $arguments[$key] = new IteratorArgument($arg); } catch (InvalidArgumentException $e) { diff --git a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php index cd867b0967908..f933be8b258df 100644 --- a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php @@ -41,7 +41,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); } if ('2.0' === $xliffVersion) { - return $this->dumpXliff2($defaultLocale, $messages, $domain, $options); + return $this->dumpXliff2($defaultLocale, $messages, $domain); } throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); @@ -129,7 +129,7 @@ private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, return $dom->saveXML(); } - private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = []) + private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain) { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; From 2ff3496d620425bbf7b7c31e7a6eceb512cc6dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sun, 24 Nov 2019 18:18:02 +0100 Subject: [PATCH 32/76] [Workflow] Apply the same logic of precedence between the apply() and the buildTransitionBlockerList() method --- .../Workflow/Tests/StateMachineTest.php | 38 ++++++++++++--- src/Symfony/Component/Workflow/Workflow.php | 46 ++++++++++++------- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php index 9224f7cb129d9..a6c7362f79568 100644 --- a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php +++ b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Workflow\Event\GuardEvent; +use Symfony\Component\Workflow\Exception\NotEnabledTransitionException; use Symfony\Component\Workflow\StateMachine; use Symfony\Component\Workflow\TransitionBlocker; @@ -84,27 +85,52 @@ public function testBuildTransitionBlockerListReturnsExpectedReasonOnBranchMerge $subject = new Subject(); // There may be multiple transitions with the same name. Make sure that transitions - // that are not enabled by the marking are evaluated. + // that are enabled by the marking are evaluated. // see https://github.com/symfony/symfony/issues/28432 - // Test if when you are in place "a"trying transition "t1" then returned + // Test if when you are in place "a" and trying to apply "t1" then it returns // blocker list contains guard blocker instead blockedByMarking $subject->setMarking('a'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $this->assertCount(1, $transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList); - $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage()); $this->assertSame('blocker', $blockers[0]->getCode()); - // Test if when you are in place "d" trying transition "t1" then - // returned blocker list contains guard blocker instead blockedByMarking + // Test if when you are in place "d" and trying to apply "t1" then + // it returns blocker list contains guard blocker instead blockedByMarking $subject->setMarking('d'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $this->assertCount(1, $transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList); - $this->assertSame('Transition blocker of place d', $blockers[0]->getMessage()); $this->assertSame('blocker', $blockers[0]->getCode()); } + + public function testApplyReturnsExpectedReasonOnBranchMerge() + { + $definition = $this->createComplexStateMachineDefinition(); + + $dispatcher = new EventDispatcher(); + $net = new StateMachine($definition, null, $dispatcher); + + $dispatcher->addListener('workflow.guard', function (GuardEvent $event) { + $event->addTransitionBlocker(new TransitionBlocker(sprintf('Transition blocker of place %s', $event->getTransition()->getFroms()[0]), 'blocker')); + }); + + $subject = new Subject(); + + // There may be multiple transitions with the same name. Make sure that all transitions + // that are enabled by the marking are evaluated. + // see https://github.com/symfony/symfony/issues/34489 + + try { + $net->apply($subject, 't1'); + $this->fail(); + } catch (NotEnabledTransitionException $e) { + $blockers = iterator_to_array($e->getTransitionBlockerList()); + $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage()); + $this->assertSame('blocker', $blockers[0]->getCode()); + } + } } diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index 2d56bd9ee5087..53fef69274acc 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -159,25 +159,47 @@ public function apply($subject, $transitionName/*, array $context = []*/) $marking = $this->getMarking($subject); - $transitionBlockerList = null; - $applied = false; - $approvedTransitionQueue = []; + $transitionExist = false; + $approvedTransitions = []; + $bestTransitionBlockerList = null; foreach ($this->definition->getTransitions() as $transition) { if ($transition->getName() !== $transitionName) { continue; } - $transitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition); - if (!$transitionBlockerList->isEmpty()) { + $transitionExist = true; + + $tmpTransitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition); + + if ($tmpTransitionBlockerList->isEmpty()) { + $approvedTransitions[] = $transition; + continue; + } + + if (!$bestTransitionBlockerList) { + $bestTransitionBlockerList = $tmpTransitionBlockerList; continue; } - $approvedTransitionQueue[] = $transition; + + // We prefer to return transitions blocker by something else than + // marking. Because it means the marking was OK. Transitions are + // deterministic: it's not possible to have many transitions enabled + // at the same time that match the same marking with the same name + if (!$tmpTransitionBlockerList->has(TransitionBlocker::BLOCKED_BY_MARKING)) { + $bestTransitionBlockerList = $tmpTransitionBlockerList; + } + } + + if (!$transitionExist) { + throw new UndefinedTransitionException($subject, $transitionName, $this); } - foreach ($approvedTransitionQueue as $transition) { - $applied = true; + if (!$approvedTransitions) { + throw new NotEnabledTransitionException($subject, $transitionName, $this, $bestTransitionBlockerList); + } + foreach ($approvedTransitions as $transition) { $this->leave($subject, $transition, $marking); $context = $this->transition($subject, $transition, $marking, $context); @@ -193,14 +215,6 @@ public function apply($subject, $transitionName/*, array $context = []*/) $this->announce($subject, $transition, $marking); } - if (!$transitionBlockerList) { - throw new UndefinedTransitionException($subject, $transitionName, $this); - } - - if (!$applied) { - throw new NotEnabledTransitionException($subject, $transitionName, $this, $transitionBlockerList); - } - return $marking; } From 6cea35a10870de486c2001e3e2a0b0b3a96309ea Mon Sep 17 00:00:00 2001 From: "Issei.M" Date: Sun, 24 Nov 2019 16:00:50 +0900 Subject: [PATCH 33/76] [Validator] Add Japanese translation --- .../Validator/Resources/translations/validators.ja.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf index 5a391a2e6626e..21e2392c7d96c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf @@ -362,6 +362,10 @@ This password has been leaked in a data breach, it must not be used. Please use another password. このパスワードは漏洩している為使用できません。 + + This value should be between {{ min }} and {{ max }}. + {{ min }}以上{{ max }}以下でなければなりません。 + From dd92d2bb10157504f5da6f26460eca357ad0125b Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Sat, 23 Nov 2019 13:39:00 +0100 Subject: [PATCH 34/76] [Messenger] Error when specified default bus is not among the configured --- .../DependencyInjection/Configuration.php | 5 +++++ .../DependencyInjection/ConfigurationTest.php | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 2b76352048c1c..0f575b90d6362 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -19,6 +19,7 @@ use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\Form\Form; use Symfony\Component\HttpClient\HttpClient; @@ -1136,6 +1137,10 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode) ->ifTrue(function ($v) { return isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']; }) ->thenInvalid('You must specify the "default_bus" if you define more than one bus.') ->end() + ->validate() + ->ifTrue(static function ($v): bool { return isset($v['buses']) && null !== $v['default_bus'] && !isset($v['buses'][$v['default_bus']]); }) + ->then(static function (array $v): void { throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses'])))); }) + ->end() ->children() ->arrayNode('routing') ->normalizeKeys(false) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index f673a65f42a40..e82c8e39bd7ee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -271,6 +271,27 @@ public function testItShowANiceMessageIfTwoMessengerBusesAreConfiguredButNoDefau ]); } + public function testItErrorsWhenDefaultBusDoesNotExist() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The specified default bus "foo" is not configured. Available buses are "bar", "baz".'); + + $processor->processConfiguration($configuration, [ + [ + 'messenger' => [ + 'default_bus' => 'foo', + 'buses' => [ + 'bar' => null, + 'baz' => null, + ], + ], + ], + ]); + } + protected static function getBundleDefaultConfig() { return [ From 1a81dd7de80701d4cfe15e188030f81be67f116e Mon Sep 17 00:00:00 2001 From: jewome62 Date: Sat, 23 Nov 2019 13:13:59 +0100 Subject: [PATCH 35/76] Add DateTimeZoneNormalizer into Dependency Injection --- .../Bundle/FrameworkBundle/Resources/config/serializer.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 23da8b07bcb04..14a6ba9711c76 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -38,6 +38,11 @@ + + + + + From 465f092aeba45eb0c35882f566dc6e3c73d03b7a Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Tue, 19 Nov 2019 22:39:45 +0100 Subject: [PATCH 36/76] Improve Symfony description According to https://symfony.com/stats/downloads :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5796b1acd7ceb..da9e6156c00d7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-[Symfony][1] is a **PHP framework** for web applications and a set of reusable +[Symfony][1] is a **PHP framework** for web and console applications and a set of reusable **PHP components**. Symfony is used by thousands of web applications (including BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including Drupal and Magento). From aa045d10b91f8290f553438900d9a732bec5d6ec Mon Sep 17 00:00:00 2001 From: Sander Toonen Date: Fri, 22 Nov 2019 22:50:07 +0100 Subject: [PATCH 37/76] [Monolog Bridge] Fixed accessing static property as non static. --- src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index c27b0803e200e..c8e21b2f42622 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -39,7 +39,7 @@ public function onKernelResponse(FilterResponseEvent $event) } if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { - $this->sendHeaders = false; + self::$sendHeaders = false; $this->headers = []; return; @@ -57,7 +57,7 @@ public function onKernelResponse(FilterResponseEvent $event) */ protected function sendHeader($header, $content) { - if (!$this->sendHeaders) { + if (!self::$sendHeaders) { return; } From f15e0e6a43f0048549a255c42229900290d64690 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Sun, 24 Nov 2019 19:49:09 +0100 Subject: [PATCH 38/76] [Messenger] Adding exception to amqp transport in case amqp ext is not installed --- .../Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php | 3 +++ .../Component/Messenger/Transport/AmqpExt/Connection.php | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php index 60d5e806e357c..b3cb7a6dc8261 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php @@ -28,6 +28,9 @@ public function testSupportsOnlyAmqpTransports() $this->assertFalse($factory->supports('invalid-dsn', [])); } + /** + * @requires extension amqp + */ public function testItCreatesTheTransport() { $factory = new AmqpTransportFactory(); diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php index 267f1c67cd11c..de147413978f1 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Transport\AmqpExt; use Symfony\Component\Messenger\Exception\InvalidArgumentException; +use Symfony\Component\Messenger\Exception\LogicException; /** * An AMQP connection. @@ -60,6 +61,10 @@ class Connection public function __construct(array $connectionOptions, array $exchangeOptions, array $queuesOptions, AmqpFactory $amqpFactory = null) { + if (!\extension_loaded('amqp')) { + throw new LogicException(sprintf('You cannot use the "%s" as the "amqp" extension is not installed.', __CLASS__)); + } + $this->connectionOptions = array_replace_recursive([ 'delay' => [ 'exchange_name' => 'delays', From 7524ac2866fee55fe563b3252450dede03d9f468 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Mon, 25 Nov 2019 17:36:22 +0100 Subject: [PATCH 39/76] [Filesystem] [Serializer] fixes English grammar typo --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 2 +- .../Component/Serializer/Mapping/Factory/ClassResolverTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index a5b7f6dceb51f..e9e7784a3af40 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -801,7 +801,7 @@ public function testSymlink() $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; - // $file does not exists right now: creating "broken" links is a wanted feature + // $file does not exist right now: creating "broken" links is a wanted feature $this->filesystem->symlink($file, $link); $this->assertTrue(is_link($link)); diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php index 73c02a647c57e..5c58476383427 100644 --- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php +++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php @@ -29,7 +29,7 @@ trait ClassResolverTrait * * @return string * - * @throws InvalidArgumentException If the class does not exists + * @throws InvalidArgumentException If the class does not exist */ private function getClass($value) { From 492e1b5afd823da24e8f05d139cc2cd2d5c7bc9c Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Mon, 25 Nov 2019 17:25:29 +0100 Subject: [PATCH 40/76] [Intl] [Workflow] fixes English grammar typos --- src/Symfony/Component/Intl/Countries.php | 2 +- src/Symfony/Component/Intl/Currencies.php | 6 +++--- src/Symfony/Component/Intl/Languages.php | 2 +- src/Symfony/Component/Intl/Locales.php | 2 +- src/Symfony/Component/Intl/Scripts.php | 2 +- .../Component/Workflow/MarkingStore/MethodMarkingStore.php | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Intl/Countries.php b/src/Symfony/Component/Intl/Countries.php index 07812f5131797..66489177a4eef 100644 --- a/src/Symfony/Component/Intl/Countries.php +++ b/src/Symfony/Component/Intl/Countries.php @@ -55,7 +55,7 @@ public static function exists(string $country): bool /** * Gets the country name from alpha2 code. * - * @throws MissingResourceException if the country code does not exists + * @throws MissingResourceException if the country code does not exist */ public static function getName(string $country, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Currencies.php b/src/Symfony/Component/Intl/Currencies.php index 7459d633cba9e..c155c8f09f425 100644 --- a/src/Symfony/Component/Intl/Currencies.php +++ b/src/Symfony/Component/Intl/Currencies.php @@ -46,7 +46,7 @@ public static function exists(string $currency): bool } /** - * @throws MissingResourceException if the currency code does not exists + * @throws MissingResourceException if the currency code does not exist */ public static function getName(string $currency, string $displayLocale = null): string { @@ -78,7 +78,7 @@ public static function getNames(string $displayLocale = null): array } /** - * @throws MissingResourceException if the currency code does not exists + * @throws MissingResourceException if the currency code does not exist */ public static function getSymbol(string $currency, string $displayLocale = null): string { @@ -115,7 +115,7 @@ public static function getNumericCode(string $currency): int } /** - * @throws MissingResourceException if the numeric code does not exists + * @throws MissingResourceException if the numeric code does not exist */ public static function forNumericCode(int $numericCode): array { diff --git a/src/Symfony/Component/Intl/Languages.php b/src/Symfony/Component/Intl/Languages.php index a70a7e8b9c101..9776a8501ecb5 100644 --- a/src/Symfony/Component/Intl/Languages.php +++ b/src/Symfony/Component/Intl/Languages.php @@ -52,7 +52,7 @@ public static function exists(string $language): bool /** * Gets the language name from alpha2 code. * - * @throws MissingResourceException if the language code does not exists + * @throws MissingResourceException if the language code does not exist */ public static function getName(string $language, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Locales.php b/src/Symfony/Component/Intl/Locales.php index aee16beb16678..1b2d0d2adf258 100644 --- a/src/Symfony/Component/Intl/Locales.php +++ b/src/Symfony/Component/Intl/Locales.php @@ -49,7 +49,7 @@ public static function exists(string $locale): bool } /** - * @throws MissingResourceException if the locale does not exists + * @throws MissingResourceException if the locale does not exist */ public static function getName(string $locale, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Scripts.php b/src/Symfony/Component/Intl/Scripts.php index 943ef8b46919f..bb29f0095bee3 100644 --- a/src/Symfony/Component/Intl/Scripts.php +++ b/src/Symfony/Component/Intl/Scripts.php @@ -41,7 +41,7 @@ public static function exists(string $script): bool } /** - * @throws MissingResourceException if the script code does not exists + * @throws MissingResourceException if the script code does not exist */ public static function getName(string $script, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php index 3835765265087..e5ba4deae13f5 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php @@ -51,7 +51,7 @@ public function getMarking($subject) $method = 'get'.ucfirst($this->property); if (!method_exists($subject, $method)) { - throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method)); } $marking = $subject->{$method}(); @@ -81,7 +81,7 @@ public function setMarking($subject, Marking $marking, array $context = []) $method = 'set'.ucfirst($this->property); if (!method_exists($subject, $method)) { - throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method)); } $subject->{$method}($marking, $context); From 22a7f40cb88c600d1b5e35549ce82523648ac4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 25 Nov 2019 23:12:50 +0100 Subject: [PATCH 41/76] [FWBundle] Remove unused parameter --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 2ad1566a25aa8..f29c72e95a6e2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -283,7 +283,7 @@ public function load(array $configs, ContainerBuilder $container) } if ($this->messengerConfigEnabled = $this->isConfigEnabled($container, $config['messenger'])) { - $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['serializer'], $config['validation']); + $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['validation']); } else { $container->removeDefinition('console.command.messenger_consume_messages'); $container->removeDefinition('console.command.messenger_debug'); @@ -1639,7 +1639,7 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont } } - private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $serializerConfig, array $validationConfig) + private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $validationConfig) { if (!interface_exists(MessageBusInterface::class)) { throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); From 685c36c3d2695a7e4fe589675f0a79d4d7fe65bd Mon Sep 17 00:00:00 2001 From: Emanuele Panzeri Date: Wed, 27 Nov 2019 17:54:05 +0100 Subject: [PATCH 42/76] [Cache] Make sure we get the correct number of values from redis::mget() --- src/Symfony/Component/Cache/Traits/RedisTrait.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 395a9b8051b4f..c4b12b64bb37b 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -179,7 +179,13 @@ protected function doFetch(array $ids) } }); } else { - $values = array_combine($ids, $this->redis->mget($ids)); + $values = $this->redis->mget($ids); + + if (!\is_array($values) || \count($values) !== \count($ids)) { + return []; + } + + $values = array_combine($ids, $values); } foreach ($values as $id => $v) { From 2b62dc3fc54febbe0b98ef899089f1de5f297622 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 25 Nov 2019 19:09:41 +0100 Subject: [PATCH 43/76] [Process] add tests for php executable finder if file does not exist --- .../Process/Tests/PhpExecutableFinderTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index 795be8fa24bfc..c6804765f7694 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -69,4 +69,16 @@ public function testFindArguments() $this->assertEquals($f->findArguments(), [], '::findArguments() returns no arguments'); } } + + 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'); + + putenv('PHP_BINARY='.$phpBinaryEnv); + } } From 4c671a448722023fea984da6f1bc4e9ba1589887 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 27 Nov 2019 23:48:43 +0100 Subject: [PATCH 44/76] [HttpClient] turn exception into log when the request has no content-type --- .../Component/HttpClient/Response/NativeResponse.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/Response/NativeResponse.php b/src/Symfony/Component/HttpClient/Response/NativeResponse.php index ff0eb560b1396..b8af399a94b10 100644 --- a/src/Symfony/Component/HttpClient/Response/NativeResponse.php +++ b/src/Symfony/Component/HttpClient/Response/NativeResponse.php @@ -109,11 +109,18 @@ public function __destruct() private function open(): void { - set_error_handler(function ($type, $msg) { throw new TransportException($msg); }); + $url = $this->url; + + set_error_handler(function ($type, $msg) use (&$url) { + if (E_NOTICE !== $type || 'fopen(): Content-type not specified assuming application/x-www-form-urlencoded' !== $msg) { + throw new TransportException($msg); + } + + $this->logger && $this->logger->info(sprintf('%s for "%s".', $msg, $url ?? $this->url)); + }); try { $this->info['start_time'] = microtime(true); - $url = $this->url; while (true) { $context = stream_context_get_options($this->context); From d4c34e00ac1ec1c9c78745761ac5976fcfc794a5 Mon Sep 17 00:00:00 2001 From: Sylvain METAYER Date: Tue, 26 Nov 2019 16:13:48 +0100 Subject: [PATCH 45/76] [VarDumper] notice on potential undefined index --- .../Dumper/ContextProvider/SourceContextProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php index e43e19f4356f6..6f4caba63b989 100644 --- a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php +++ b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php @@ -52,8 +52,8 @@ public function getContext(): ?array && 'dump' === $trace[$i]['function'] && VarDumper::class === $trace[$i]['class'] ) { - $file = $trace[$i]['file']; - $line = $trace[$i]['line']; + $file = $trace[$i]['file'] ?? $file; + $line = $trace[$i]['line'] ?? $line; while (++$i < $this->limit) { if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) { From 9f19ae185d9d28f93047b42596258335fff95c17 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 27 Nov 2019 15:31:43 +0100 Subject: [PATCH 46/76] [Config] Remove extra sprintf arg --- src/Symfony/Component/Config/Definition/VariableNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Definition/VariableNode.php b/src/Symfony/Component/Config/Definition/VariableNode.php index 0d722c6bd28a8..ad70d6e0e08cb 100644 --- a/src/Symfony/Component/Config/Definition/VariableNode.php +++ b/src/Symfony/Component/Config/Definition/VariableNode.php @@ -85,7 +85,7 @@ protected function finalizeValue($value) // this avoids ever passing an empty value to final validation closures if (!$this->allowEmptyValue && $this->isHandlingPlaceholder() && $this->finalValidationClosures) { @trigger_error(sprintf('Setting path "%s" to an environment variable is deprecated since Symfony 4.3. Remove "cannotBeEmpty()", "validate()" or include a prefix/suffix value instead.', $this->getPath()), E_USER_DEPRECATED); -// $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath(), json_encode($value))); +// $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath())); // if ($hint = $this->getInfo()) { // $e->addHint($hint); // } From c6ed0f020880595dee3282c9088ba2f0f065bd74 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Wed, 27 Nov 2019 10:48:32 +0100 Subject: [PATCH 47/76] more robust initialization from request Request::getPort is declared as int|string but can actually return null. --- src/Symfony/Component/Routing/RequestContext.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Routing/RequestContext.php b/src/Symfony/Component/Routing/RequestContext.php index 8ebad8e2538db..ed50cd70d835a 100644 --- a/src/Symfony/Component/Routing/RequestContext.php +++ b/src/Symfony/Component/Routing/RequestContext.php @@ -67,8 +67,8 @@ public function fromRequest(Request $request) $this->setMethod($request->getMethod()); $this->setHost($request->getHost()); $this->setScheme($request->getScheme()); - $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort()); - $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort); + $this->setHttpPort($request->isSecure() || null === $request->getPort() ? $this->httpPort : $request->getPort()); + $this->setHttpsPort($request->isSecure() && null !== $request->getPort() ? $request->getPort() : $this->httpsPort); $this->setQueryString($request->server->get('QUERY_STRING', '')); return $this; From 069d214210e579682849657e37377405d0c2ebfc Mon Sep 17 00:00:00 2001 From: Ali Tavafi Date: Tue, 26 Nov 2019 17:54:05 +0330 Subject: [PATCH 48/76] [Console] Fix trying to access array offset on value of type int --- src/Symfony/Component/Console/Input/ArrayInput.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index a04b6b68ea0c8..b2ebc7c2258e2 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -39,8 +39,8 @@ public function __construct(array $parameters, InputDefinition $definition = nul */ public function getFirstArgument() { - foreach ($this->parameters as $key => $value) { - if ($key && '-' === $key[0]) { + foreach ($this->parameters as $param => $value) { + if ($param && \is_string($param) && '-' === $param[0]) { continue; } @@ -107,7 +107,7 @@ public function __toString() { $params = []; foreach ($this->parameters as $param => $val) { - if ($param && '-' === $param[0]) { + if ($param && \is_string($param) && '-' === $param[0]) { if (\is_array($val)) { foreach ($val as $v) { $params[] = $param.('' != $v ? '='.$this->escapeToken($v) : ''); From 388528da502368054c362487ab36ec7b27d1c6fd Mon Sep 17 00:00:00 2001 From: Roy-Orbison Date: Wed, 20 Nov 2019 15:04:29 +1030 Subject: [PATCH 49/76] Simpler example for Apache basic auth workaround Uses a simpler regex and existing back-reference instead of reading header twice. --- src/Symfony/Component/HttpFoundation/ServerBag.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/ServerBag.php b/src/Symfony/Component/HttpFoundation/ServerBag.php index 4c82b1774873f..f3b64023489ef 100644 --- a/src/Symfony/Component/HttpFoundation/ServerBag.php +++ b/src/Symfony/Component/HttpFoundation/ServerBag.php @@ -46,13 +46,13 @@ public function getHeaders() /* * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default * For this workaround to work, add these lines to your .htaccess file: - * RewriteCond %{HTTP:Authorization} ^(.+)$ - * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{HTTP:Authorization} .+ + * RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] * * A sample .htaccess file: * RewriteEngine On - * RewriteCond %{HTTP:Authorization} ^(.+)$ - * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{HTTP:Authorization} .+ + * RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] * RewriteCond %{REQUEST_FILENAME} !-f * RewriteRule ^(.*)$ app.php [QSA,L] */ From 53241df2dfbf524544a42ee6810caba91f8bd29f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 28 Nov 2019 11:05:51 +0100 Subject: [PATCH 50/76] Revert "minor #34608 [Process] add tests for php executable finder if file does not exist (ahmedash95)" This reverts commit 5cacc5dd691d92926855ae7b62bb664fbc811539, reversing changes made to f0a6de27366741cc2ae16589ad1bd2965d797f63. --- .../Process/Tests/PhpExecutableFinderTest.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index c6804765f7694..795be8fa24bfc 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -69,16 +69,4 @@ public function testFindArguments() $this->assertEquals($f->findArguments(), [], '::findArguments() returns no arguments'); } } - - 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'); - - putenv('PHP_BINARY='.$phpBinaryEnv); - } } From b9d5237f675d57c53ae559374416104669fdc990 Mon Sep 17 00:00:00 2001 From: Maxime Helias Date: Thu, 28 Nov 2019 11:09:39 +0100 Subject: [PATCH 51/76] [DI] Missing test on YamlFileLoader --- .../Tests/Loader/YamlFileLoaderTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 1d187848b3b39..cce3921853e5d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -349,6 +349,9 @@ public function testParsesIteratorArgument() $lazyDefinition = $container->getDefinition('lazy_context'); $this->assertEquals([new IteratorArgument(['k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')]), new IteratorArgument([])], $lazyDefinition->getArguments(), '->load() parses lazy arguments'); + + $message = 'The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.'; + $this->assertSame($message, $container->getDefinition('deprecated_service')->getDeprecationMessage('deprecated_service')); } public function testAutowire() @@ -717,4 +720,17 @@ public function testOverriddenDefaultsBindings() $this->assertSame('overridden', $container->get('bar')->quz); } + + /** + * @group legacy + * @expectedDeprecation The configuration key "factory" is unsupported for the service "foo" which is defined as an alias in %s. + * @expectedDeprecation The configuration key "parent" is unsupported for the service "foo" which is defined as an alias in %s. + */ + public function testAliasDefinitionContainsUnsupportedElements() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('legacy_invalid_alias_definition.yml'); + $this->assertTrue($container->has('foo')); + } } From 22931a0d4af501f1396e7bdd8734cae108579b5f Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Sat, 9 Nov 2019 09:46:41 +0100 Subject: [PATCH 52/76] [PhpUnitBridge] Read configuration CLI directive --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index 6ea2e950eda17..8cc14d0bbe341 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -14,15 +14,21 @@ error_reporting(-1); -$getEnvVar = function ($name, $default = false) { +global $argv, $argc; +$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array(); +$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0; +$getEnvVar = function ($name, $default = false) use ($argv) { if (false !== $value = getenv($name)) { return $value; } static $phpunitConfig = null; if (null === $phpunitConfig) { + $opt = min(array_search('-c', $opts = array_reverse($argv), true) ?: INF, array_search('--configuration', $opts, true) ?: INF); $phpunitConfigFilename = null; - if (file_exists('phpunit.xml')) { + if (INF !== $opt && isset($opts[$opt - 1])) { + $phpunitConfigFilename = $opts[$opt - 1]; + } elseif (file_exists('phpunit.xml')) { $phpunitConfigFilename = 'phpunit.xml'; } elseif (file_exists('phpunit.xml.dist')) { $phpunitConfigFilename = 'phpunit.xml.dist'; @@ -165,10 +171,6 @@ class SymfonyBlacklistPhpunit {} } -global $argv, $argc; -$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array(); -$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0; - if ($PHPUNIT_VERSION < 8.0) { $argv = array_filter($argv, function ($v) use (&$argc) { if ('--do-not-cache-result' !== $v) return true; --$argc; return false; }); } elseif (filter_var(getenv('SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE'), FILTER_VALIDATE_BOOLEAN)) { From f40373eb7c6e762776d269e8604bc402952c6362 Mon Sep 17 00:00:00 2001 From: Maria Grazia Patteri Date: Wed, 23 Oct 2019 23:18:21 +0200 Subject: [PATCH 53/76] [Debug] work around failing chdir() on Darwin --- src/Symfony/Component/Debug/DebugClassLoader.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 42e803b9db4ed..ad6adbd6b2412 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -455,7 +455,11 @@ private function darwinRealpath($real) $real = self::$darwinCache[$kDir][0]; } else { $dir = getcwd(); - chdir($real); + + if (!@chdir($real)) { + return $real.$file; + } + $real = getcwd().'/'; chdir($dir); From 75404e5287e80131999db3a98cd066ce8b36520a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vilius=20Grigali=C5=ABnas?= Date: Wed, 23 Oct 2019 15:11:19 +0300 Subject: [PATCH 54/76] [Form] Keep preferred_choices order for choice groups --- .../Factory/DefaultChoiceListFactory.php | 13 +++++++- .../Factory/DefaultChoiceListFactoryTest.php | 31 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index 68da1b2516b8d..338877e05e434 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -101,6 +101,14 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, unset($otherViews[$key]); } } + + foreach ($preferredViewsOrder as $key => $groupViewsOrder) { + if ($groupViewsOrder) { + $preferredViewsOrder[$key] = min($groupViewsOrder); + } else { + unset($preferredViewsOrder[$key]); + } + } } else { // Otherwise use the original structure of the choices self::addChoiceViewsFromStructuredValues( @@ -245,6 +253,9 @@ private static function addChoiceViewsGroupedByCallable($groupBy, $choice, $valu $preferredViews[$groupLabel] = new ChoiceGroupView($groupLabel); $otherViews[$groupLabel] = new ChoiceGroupView($groupLabel); } + if (!isset($preferredViewsOrder[$groupLabel])) { + $preferredViewsOrder[$groupLabel] = []; + } self::addChoiceView( $choice, @@ -255,7 +266,7 @@ private static function addChoiceViewsGroupedByCallable($groupBy, $choice, $valu $attr, $isPreferred, $preferredViews[$groupLabel]->choices, - $preferredViewsOrder, + $preferredViewsOrder[$groupLabel], $otherViews[$groupLabel]->choices ); } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index 6282007195b40..3074f0f1602c8 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -256,6 +256,37 @@ public function testCreateViewFlatPreferredChoicesSameOrder() ); } + public function testCreateViewFlatPreferredChoiceGroupsSameOrder() + { + $view = $this->factory->createView( + $this->list, + [$this->obj4, $this->obj2, $this->obj1, $this->obj3], + null, // label + null, // index + [$this, 'getGroup'] + ); + + $preferredLabels = array_map(static function (ChoiceGroupView $groupView): array { + return array_map(static function (ChoiceView $view): string { + return $view->label; + }, $groupView->choices); + }, $view->preferredChoices); + + $this->assertEquals( + [ + 'Group 2' => [ + 2 => 'C', + 3 => 'D', + ], + 'Group 1' => [ + 0 => 'A', + 1 => 'B', + ], + ], + $preferredLabels + ); + } + public function testCreateViewFlatPreferredChoicesEmptyArray() { $view = $this->factory->createView( From a0430f6917c17058e17369e34e6d6c8142bd5bbf Mon Sep 17 00:00:00 2001 From: Dario Savella Date: Thu, 17 Oct 2019 23:13:13 +0200 Subject: [PATCH 55/76] [Serializer] CsvEncoder::NO_HEADERS_KEY ignored when used in constructor --- .../Serializer/Encoder/CsvEncoder.php | 7 +-- .../Tests/Encoder/CsvEncoderTest.php | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php index a2cd1fe39a10c..a5bcd1251b8a2 100644 --- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php @@ -40,6 +40,7 @@ class CsvEncoder implements EncoderInterface, DecoderInterface self::HEADERS_KEY => [], self::KEY_SEPARATOR_KEY => '.', self::NO_HEADERS_KEY => false, + self::AS_COLLECTION_KEY => false, ]; /** @@ -101,7 +102,7 @@ public function encode($data, $format, array $context = []) $headers = array_merge(array_values($headers), array_diff($this->extractHeaders($data), $headers)); - if (!($context[self::NO_HEADERS_KEY] ?? false)) { + if (!($context[self::NO_HEADERS_KEY] ?? $this->defaultContext[self::NO_HEADERS_KEY])) { fputcsv($handle, $headers, $delimiter, $enclosure, $escapeChar); } @@ -147,7 +148,7 @@ public function decode($data, $format, array $context = []) if (null === $headers) { $nbHeaders = $nbCols; - if ($context[self::NO_HEADERS_KEY] ?? false) { + if ($context[self::NO_HEADERS_KEY] ?? $this->defaultContext[self::NO_HEADERS_KEY]) { for ($i = 0; $i < $nbCols; ++$i) { $headers[] = [$i]; } @@ -187,7 +188,7 @@ public function decode($data, $format, array $context = []) } fclose($handle); - if ($context[self::AS_COLLECTION_KEY] ?? false) { + if ($context[self::AS_COLLECTION_KEY] ?? $this->defaultContext[self::AS_COLLECTION_KEY]) { return $result; } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php index 11ee3302d4981..cc7522f9653f8 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php @@ -202,6 +202,24 @@ public function testEncodeCustomSettingsPassedInContext() ])); } + public function testEncodeCustomSettingsPassedInConstructor() + { + $encoder = new CsvEncoder([ + CsvEncoder::DELIMITER_KEY => ';', + CsvEncoder::ENCLOSURE_KEY => "'", + CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::KEY_SEPARATOR_KEY => '-', + ]); + $value = ['a' => 'he\'llo', 'c' => ['d' => 'foo']]; + + $this->assertSame(<<<'CSV' +a;c-d +'he''llo';foo + +CSV + , $encoder->encode($value, 'csv')); + } + public function testEncodeEmptyArray() { $this->assertEquals("\n\n", $this->encoder->encode([], 'csv')); @@ -373,6 +391,15 @@ public function testEncodeWithoutHeader() , $this->encoder->encode([['a', 'b'], ['c', 'd']], 'csv', [ CsvEncoder::NO_HEADERS_KEY => true, ])); + $encoder = new CsvEncoder([CsvEncoder::NO_HEADERS_KEY => true]); + $this->assertSame(<<<'CSV' +a,b +c,d + +CSV + , $encoder->encode([['a', 'b'], ['c', 'd']], 'csv', [ + CsvEncoder::NO_HEADERS_KEY => true, + ])); } public function testSupportsDecoding() @@ -524,6 +551,23 @@ public function testDecodeCustomSettingsPassedInContext() ])); } + public function testDecodeCustomSettingsPassedInConstructor() + { + $encoder = new CsvEncoder([ + CsvEncoder::DELIMITER_KEY => ';', + CsvEncoder::ENCLOSURE_KEY => "'", + CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::KEY_SEPARATOR_KEY => '-', + CsvEncoder::AS_COLLECTION_KEY => true, // Can be removed in 5.0 + ]); + $expected = [['a' => 'hell\'o', 'bar' => ['baz' => 'b']]]; + $this->assertEquals($expected, $encoder->decode(<<<'CSV' +a;bar-baz +'hell''o';b;c +CSV + , 'csv')); + } + public function testDecodeMalformedCollection() { $expected = [ @@ -553,6 +597,15 @@ public function testDecodeWithoutHeader() a,b c,d +CSV + , 'csv', [ + CsvEncoder::NO_HEADERS_KEY => true, + ])); + $encoder = new CsvEncoder([CsvEncoder::NO_HEADERS_KEY => true]); + $this->assertEquals([['a', 'b'], ['c', 'd']], $encoder->decode(<<<'CSV' +a,b +c,d + CSV , 'csv', [ CsvEncoder::NO_HEADERS_KEY => true, From 54aac56ab86ec4471de0b6624c101cd42a12967d Mon Sep 17 00:00:00 2001 From: Rimas Kudelis Date: Thu, 28 Nov 2019 12:42:53 +0200 Subject: [PATCH 56/76] [EventDispatcher] Better error reporting when arguments to dispatch() are swapped --- src/Symfony/Component/EventDispatcher/EventDispatcher.php | 2 +- .../Component/EventDispatcher/LegacyEventDispatcherProxy.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index ebfd1ec6d5e7d..f7c66309827a4 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -54,7 +54,7 @@ public function dispatch($event/*, string $eventName = null*/) if (\is_object($event)) { $eventName = $eventName ?? \get_class($event); - } elseif (\is_string($event) && (null === $eventName || $eventName instanceof Event)) { + } elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) { @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', EventDispatcherInterface::class), E_USER_DEPRECATED); $swap = $event; $event = $eventName ?? new Event(); diff --git a/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php b/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php index be0d381a29be6..003de2efddafd 100644 --- a/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php +++ b/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php @@ -59,7 +59,7 @@ public function dispatch($event/*, string $eventName = null*/) if (\is_object($event)) { $eventName = $eventName ?? \get_class($event); - } elseif (\is_string($event) && (null === $eventName || $eventName instanceof Event)) { + } elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) { @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', ContractsEventDispatcherInterface::class), E_USER_DEPRECATED); $swap = $event; $event = $eventName ?? new Event(); From 3378890e704ed397f2b30ad9677c689cf7a6352c Mon Sep 17 00:00:00 2001 From: Thomas Bisignani Date: Thu, 28 Nov 2019 13:52:59 +0100 Subject: [PATCH 57/76] [HttpFoundation] Fixed typo --- src/Symfony/Component/HttpFoundation/BinaryFileResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index f43114111b076..ea7ac846974bd 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -348,7 +348,7 @@ public static function trustXSendfileTypeHeader() } /** - * If this is set to true, the file will be unlinked after the request is send + * If this is set to true, the file will be unlinked after the request is sent * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used. * * @param bool $shouldDelete From 4d4786812581115ae63d1f07f72ecc923b35a430 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 26 Oct 2019 13:22:27 +0200 Subject: [PATCH 58/76] [Console] Fix commands description with numeric namespaces --- .../Descriptor/ApplicationDescription.php | 24 +++++---- .../Descriptor/ApplicationDescriptionTest.php | 53 +++++++++++++++++++ 2 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php diff --git a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php index 442a569711c07..7e214712da6f3 100644 --- a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php +++ b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php @@ -129,23 +129,29 @@ private function sortCommands(array $commands) { $namespacedCommands = []; $globalCommands = []; + $sortedCommands = []; foreach ($commands as $name => $command) { $key = $this->application->extractNamespace($name, 1); - if (!$key) { - $globalCommands['_global'][$name] = $command; + if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) { + $globalCommands[$name] = $command; } else { $namespacedCommands[$key][$name] = $command; } } - ksort($namespacedCommands); - $namespacedCommands = array_merge($globalCommands, $namespacedCommands); - foreach ($namespacedCommands as &$commandsSet) { - ksort($commandsSet); + if ($globalCommands) { + ksort($globalCommands); + $sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands; } - // unset reference to keep scope clear - unset($commandsSet); - return $namespacedCommands; + if ($namespacedCommands) { + ksort($namespacedCommands); + foreach ($namespacedCommands as $key => $commandsSet) { + ksort($commandsSet); + $sortedCommands[$key] = $commandsSet; + } + } + + return $sortedCommands; } } diff --git a/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php b/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php new file mode 100644 index 0000000000000..33d5c3840f3e3 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Descriptor\ApplicationDescription; + +final class ApplicationDescriptionTest extends TestCase +{ + /** + * @dataProvider getNamespacesProvider + */ + public function testGetNamespaces(array $expected, array $names) + { + $application = new TestApplication(); + foreach ($names as $name) { + $application->add(new Command($name)); + } + + $this->assertSame($expected, array_keys((new ApplicationDescription($application))->getNamespaces())); + } + + public function getNamespacesProvider() + { + return [ + [['_global'], ['foobar']], + [['a', 'b'], ['b:foo', 'a:foo', 'b:bar']], + [['_global', 'b', 'z', 22, 33], ['z:foo', '1', '33:foo', 'b:foo', '22:foo:bar']], + ]; + } +} + +final class TestApplication extends Application +{ + /** + * {@inheritdoc} + */ + protected function getDefaultCommands() + { + return []; + } +} From 51045927da82923a0481b27030e8247236150df5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 28 Nov 2019 14:25:45 +0100 Subject: [PATCH 59/76] Fix tests --- .../Tests/Loader/YamlFileLoaderTest.php | 14 +------------- .../EventDispatcher/Tests/EventDispatcherTest.php | 6 ++++-- .../Tests/LegacyEventDispatcherProxyTest.php | 8 ++++++-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index ea398cec83343..e956898b0246f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -372,7 +372,7 @@ public function testParsesIteratorArgument() $this->assertEquals([new IteratorArgument(['k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')]), new IteratorArgument([])], $lazyDefinition->getArguments(), '->load() parses lazy arguments'); - $message = 'The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.'; + $message = 'The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.'; $this->assertSame($message, $container->getDefinition('deprecated_service')->getDeprecationMessage('deprecated_service')); } @@ -824,18 +824,6 @@ public function testOverriddenDefaultsBindings() $this->assertSame('overridden', $container->get('bar')->quz); } - /** - * @group legacy - * @expectedDeprecation The configuration key "factory" is unsupported for the service "foo" which is defined as an alias in %s. - * @expectedDeprecation The configuration key "parent" is unsupported for the service "foo" which is defined as an alias in %s. - */ - public function testAliasDefinitionContainsUnsupportedElements() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('legacy_invalid_alias_definition.yml'); - $this->assertTrue($container->has('foo')); - /** * When creating a tagged iterator using the array syntax, all optional parameters should be properly handled. */ diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php index 83d10a89be744..d80e50f76aa50 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -431,10 +431,12 @@ public function testLegacySignatureWithEvent() $this->dispatcher->dispatch('foo', new Event()); } + /** + * @group legacy + * @expectedDeprecation Calling the "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead. + */ public function testLegacySignatureWithNewEventObject() { - $this->expectException('TypeError'); - $this->expectExceptionMessage('Argument 1 passed to "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()" must be an object, string given.'); $this->dispatcher->dispatch('foo', new ContractsEvent()); } } diff --git a/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php b/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php index 09e5e927e138f..c8ad0f6da8567 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php @@ -41,10 +41,14 @@ public function testLegacySignatureWithEvent() $this->createEventDispatcher()->dispatch('foo', new Event()); } + /** + * @group legacy + * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3. + * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3. + * @expectedDeprecation Calling the "Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead. + */ public function testLegacySignatureWithNewEventObject() { - $this->expectException('TypeError'); - $this->expectExceptionMessage('Argument 1 passed to "Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch()" must be an object, string given.'); $this->createEventDispatcher()->dispatch('foo', new ContractsEvent()); } From 53127c54a17de2fbfc7c551a94c3105841bc7f3c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 26 Nov 2019 10:41:30 +0100 Subject: [PATCH 60/76] bug #34554 [HttpClient] Fix early cleanup of pushed HTTP/2 responses (lyrixx) This PR was merged into the 4.4 branch. Discussion ---------- [HttpClient] Fix early cleanup of pushed HTTP/2 responses | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | | License | MIT | Doc PR | Commits ------- 0f51da6ec7 [HttpClient] Fix early cleanup of pushed HTTP/2 responses --- .travis.yml | 14 +- .../Component/HttpClient/CurlHttpClient.php | 30 +++- .../HttpClient/Response/CurlResponse.php | 8 - .../HttpClient/ScopingHttpClient.php | 10 +- .../HttpClient/Tests/CurlHttpClientTest.php | 140 +++++++++++++++--- .../HttpClient/Tests/Fixtures/tls/server.crt | 20 +++ .../HttpClient/Tests/Fixtures/tls/server.key | 27 ++++ .../Component/HttpClient/composer.json | 3 +- .../HttpClient/Test/Fixtures/web/index.php | 21 +++ .../HttpClient/Test/HttpClientTestCase.php | 2 - .../HttpClient/Test/TestHttpServer.php | 19 +-- 11 files changed, 237 insertions(+), 57 deletions(-) create mode 100644 src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt create mode 100644 src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key diff --git a/.travis.yml b/.travis.yml index 5b09c1fb3463f..b21a71d97299d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -141,6 +141,12 @@ before_install: (cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2) fi + - | + # Install vulcain + wget https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz -O - | tar xz + sudo mv vulcain /usr/local/bin + docker pull php:7.3-alpine + - | # php.ini configuration for PHP in $TRAVIS_PHP_VERSION $php_extra; do @@ -277,8 +283,14 @@ install: echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && ([ -e composer.lock ] && ${COMPOSER_UP/update/install} || $COMPOSER_UP --prefer-lowest --prefer-stable) && $PHPUNIT_X'" echo "$COMPONENTS" | xargs -n1 -I{} tar --append -f ~/php-ext/composer-lowest.lock.tar {}/composer.lock else + if [[ $PHP = ${MIN_PHP%.*} ]]; then + tfold src/Symfony/Component/HttpClient.h2push docker run -it --rm -v $(pwd):/app -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push + fi + echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" - tfold src/Symfony/Component/Console.tty $PHPUNIT src/Symfony/Component/Console --group tty + + tfold src/Symfony/Component/Console.tty $PHPUNIT --group tty + if [[ $PHP = ${MIN_PHP%.*} ]]; then export PHP=$MIN_PHP tfold src/Symfony/Component/Process.sigchild SYMFONY_DEPRECATIONS_HELPER=weak php-$MIN_PHP/sapi/cli/php ./phpunit --colors=always src/Symfony/Component/Process/ diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 632822b1ed15d..683b08da461ff 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -23,6 +23,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * A performant implementation of the HttpClientInterface contracts based on the curl extension. @@ -34,7 +35,7 @@ * * @experimental in 4.3 */ -final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface +final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface { use HttpClientTrait; use LoggerAwareTrait; @@ -298,9 +299,17 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa return new ResponseStream(CurlResponse::stream($responses, $timeout)); } - public function __destruct() + public function reset() { + if ($this->logger) { + foreach ($this->multi->pushedResponses as $url => $response) { + $this->logger->debug(sprintf('Unused pushed response: "%s"', $url)); + } + } + $this->multi->pushedResponses = []; + $this->multi->dnsCache->evictions = $this->multi->dnsCache->evictions ?: $this->multi->dnsCache->removals; + $this->multi->dnsCache->removals = $this->multi->dnsCache->hostnames = []; if (\is_resource($this->multi->handle)) { if (\defined('CURLMOPT_PUSHFUNCTION')) { @@ -318,6 +327,11 @@ public function __destruct() } } + public function __destruct() + { + $this->reset(); + } + private static function handlePush($parent, $pushed, array $requestHeaders, CurlClientState $multi, int $maxPendingPushes, ?LoggerInterface $logger): int { $headers = []; @@ -337,12 +351,6 @@ private static function handlePush($parent, $pushed, array $requestHeaders, Curl $url = $headers[':scheme'][0].'://'.$headers[':authority'][0]; - if ($maxPendingPushes <= \count($multi->pushedResponses)) { - $logger && $logger->debug(sprintf('Rejecting pushed response from "%s" for "%s": the queue is full', $origin, $url)); - - return CURL_PUSH_DENY; - } - // curl before 7.65 doesn't validate the pushed ":authority" header, // but this is a MUST in the HTTP/2 RFC; let's restrict pushes to the original host, // ignoring domains mentioned as alt-name in the certificate for now (same as curl). @@ -352,6 +360,12 @@ private static function handlePush($parent, $pushed, array $requestHeaders, Curl return CURL_PUSH_DENY; } + if ($maxPendingPushes <= \count($multi->pushedResponses)) { + $fifoUrl = key($multi->pushedResponses); + unset($multi->pushedResponses[$fifoUrl]); + $logger && $logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl)); + } + $url .= $headers[':path'][0]; $logger && $logger->debug(sprintf('Queueing pushed response: "%s"', $url)); diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 908b4572697f8..13320acfbba91 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -208,15 +208,7 @@ public function __destruct() } finally { $this->close(); - // Clear local caches when the only remaining handles are about pushed responses if (!$this->multi->openHandles) { - if ($this->logger) { - foreach ($this->multi->pushedResponses as $url => $response) { - $this->logger->debug(sprintf('Unused pushed response: "%s"', $url)); - } - } - - $this->multi->pushedResponses = []; // Schedule DNS cache eviction for the next request $this->multi->dnsCache->evictions = $this->multi->dnsCache->evictions ?: $this->multi->dnsCache->removals; $this->multi->dnsCache->removals = $this->multi->dnsCache->hostnames = []; diff --git a/src/Symfony/Component/HttpClient/ScopingHttpClient.php b/src/Symfony/Component/HttpClient/ScopingHttpClient.php index cc5872b3e5bde..97cfe375e417e 100644 --- a/src/Symfony/Component/HttpClient/ScopingHttpClient.php +++ b/src/Symfony/Component/HttpClient/ScopingHttpClient.php @@ -15,6 +15,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Auto-configure the default options based on the requested URL. @@ -23,7 +24,7 @@ * * @experimental in 4.3 */ -class ScopingHttpClient implements HttpClientInterface +class ScopingHttpClient implements HttpClientInterface, ResetInterface { use HttpClientTrait; @@ -92,4 +93,11 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa { return $this->client->stream($responses, $timeout); } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php index 604a37f37a336..f83edf91b6f39 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -13,13 +13,23 @@ use Psr\Log\AbstractLogger; use Symfony\Component\HttpClient\CurlHttpClient; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; use Symfony\Contracts\HttpClient\HttpClientInterface; +/* +Tests for HTTP2 Push need a recent version of both PHP and curl. This docker command should run them: +docker run -it --rm -v $(pwd):/app -v /path/to/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push +The vulcain binary can be found at https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz - see https://github.com/dunglas/vulcain for source +*/ + /** * @requires extension curl */ class CurlHttpClientTest extends HttpClientTestCase { + private static $vulcainStarted = false; + protected function getHttpClient(string $testCase): HttpClientInterface { return new CurlHttpClient(); @@ -28,7 +38,81 @@ protected function getHttpClient(string $testCase): HttpClientInterface /** * @requires PHP 7.2.17 */ - public function testHttp2Push() + public function testHttp2PushVulcain() + { + $client = $this->getVulcainClient(); + $logger = new TestLogger(); + $client->setLogger($logger); + + $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [ + 'headers' => [ + 'Preload' => '/documents/*/id', + ], + ])->toArray(); + + foreach ($responseAsArray['documents'] as $document) { + $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray(); + } + + $client->reset(); + + $expected = [ + 'Request: "GET https://127.0.0.1:3000/json"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/1"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/2"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"', + 'Response: "200 https://127.0.0.1:3000/json/1"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"', + 'Response: "200 https://127.0.0.1:3000/json/2"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json/3"', + ]; + $this->assertSame($expected, $logger->logs); + } + + /** + * @requires PHP 7.2.17 + */ + public function testHttp2PushVulcainWithUnusedResponse() + { + $client = $this->getVulcainClient(); + $logger = new TestLogger(); + $client->setLogger($logger); + + $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [ + 'headers' => [ + 'Preload' => '/documents/*/id', + ], + ])->toArray(); + + $i = 0; + foreach ($responseAsArray['documents'] as $document) { + $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray(); + if (++$i >= 2) { + break; + } + } + + $client->reset(); + + $expected = [ + 'Request: "GET https://127.0.0.1:3000/json"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/1"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/2"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"', + 'Response: "200 https://127.0.0.1:3000/json/1"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"', + 'Response: "200 https://127.0.0.1:3000/json/2"', + 'Unused pushed response: "https://127.0.0.1:3000/json/3"', + ]; + $this->assertSame($expected, $logger->logs); + } + + private function getVulcainClient(): CurlHttpClient { if (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) { $this->markTestSkipped('PHP 7.3.0 to 7.3.3 don\'t support HTTP/2 PUSH'); @@ -38,32 +122,44 @@ public function testHttp2Push() $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH'); } - $logger = new class() extends AbstractLogger { - public $logs = []; + $client = new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]); - public function log($level, $message, array $context = []) - { - $this->logs[] = $message; - } - }; + if (static::$vulcainStarted) { + return $client; + } - $client = new CurlHttpClient([], 6, 2); - $client->setLogger($logger); + if (['application/json'] !== $client->request('GET', 'http://127.0.0.1:8057/json')->getHeaders()['content-type']) { + $this->markTestSkipped('symfony/http-client-contracts >= 2.0.1 required'); + } - $index = $client->request('GET', 'https://http2.akamai.com/'); - $index->getContent(); + $process = new Process(['vulcain'], null, [ + 'DEBUG' => 1, + 'UPSTREAM' => 'http://127.0.0.1:8057', + 'ADDR' => ':3000', + 'KEY_FILE' => __DIR__.'/Fixtures/tls/server.key', + 'CERT_FILE' => __DIR__.'/Fixtures/tls/server.crt', + ]); + $process->start(); - $css = $client->request('GET', 'https://http2.akamai.com/resources/push.css'); + register_shutdown_function([$process, 'stop']); + sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1); - $css->getHeaders(); + if (!$process->isRunning()) { + throw new ProcessFailedException($process); + } - $expected = [ - 'Request: "GET https://http2.akamai.com/"', - 'Queueing pushed response: "https://http2.akamai.com/resources/push.css"', - 'Response: "200 https://http2.akamai.com/"', - 'Accepting pushed response: "GET https://http2.akamai.com/resources/push.css"', - 'Response: "200 https://http2.akamai.com/resources/push.css"', - ]; - $this->assertSame($expected, $logger->logs); + static::$vulcainStarted = true; + + return $client; + } +} + +class TestLogger extends AbstractLogger +{ + public $logs = []; + + public function log($level, $message, array $context = []): void + { + $this->logs[] = $message; } } diff --git a/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt new file mode 100644 index 0000000000000..3903667223308 --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPjCCAiYCCQDpVvfmCZt2GzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV +UzEUMBIGA1UEBwwLR290aGFtIENpdHkxEjAQBgNVBAMMCWxvY2FsaG9zdDEoMCYG +CSqGSIb3DQEJARYZZHVuZ2xhcyttZXJjdXJlQGdtYWlsLmNvbTAeFw0xOTAxMjMx +NTUzMzlaFw0yOTAxMjAxNTUzMzlaMGExCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtH +b3RoYW0gQ2l0eTESMBAGA1UEAwwJbG9jYWxob3N0MSgwJgYJKoZIhvcNAQkBFhlk +dW5nbGFzK21lcmN1cmVAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5KLE6F +1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng04PLw +L6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491d9od +MtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4afDiI7 +lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPvF16Z +tu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +AQB42AW7E57yOky8GpsKLoa9u7okwvvg8CQJ117X8a2MElBGnmMd9tjLa/pXAx2I +bN7jSTSadXiPNYCx4ueiJa4Dwy+C8YkwUbhRf3+mc7Chnz0SXouTjh7OUeeA06jS +W2VAR2pKB0pdJtAkXxIy21Juu8KF5uZqVq1oimgKw2lRUIMdKaqsrVwESk6u5Ojj +3DS40q9DzFnwKGCuZpspvMdWYLscotzLrCbnHp+guWDigEHS3CKzKbNo327nVg6X +7UjqqtPZ2mCsnUx3QTDJsr3gcSqhzmB+Q6I/0Q2Nx/aMmbsNegu+LC3GjFtL59Bv +B8pB/MxID0j47SwPKQghZvb3 +-----END CERTIFICATE----- diff --git a/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key new file mode 100644 index 0000000000000..8c278f843df24 --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5K +LE6F1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng0 +4PLwL6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491 +d9odMtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4af +DiI7lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPv +F16Ztu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABAoIBAQCczVNGe7oRADMh +EP/wM4ghhUTvHAndWrzFkFs4fJX1UKi34ZQoFTEdOZ6f1fHwj3f/qa8cDNJar5X9 +puJ+siotL3Suks2iT83dbhN63SCpiM2sqvuzu3Xp7vWwNOo5fqR2x46CmQ5uVn5S +EbZ09/mbEza5FvmwnB49rLepxY6F8P+vK5ZnCZYS2SHpOxv3U9wG8gmcHRI9ejbC +X9rwuu3oT23bfbJ0tn6Qh8O3R1kXZUUXqnxsn554cZZrXg5+ygbt4HfDVWMLpqy/ +5wG0FCpU8QvjF4L8qErP7TZRrWGFtti1RtACbu9LrWvO/74v54td5V28U6kqlDJR +ff4Mi4whAoGBAOBzReQIxGwoYApPyhF+ohvF39JEEXYfkzk94t6hbgyBFBFvqdFY +shT59im2P9LyDvTd5DnCIo52Sj7vM9H80tRjAA0A8okGOczk31ABbH8aZ2orU/0G +EJe4PV4r3bpLO6DKTYsicgRpXI3aHHLvYFXOVNrQKfrKCQ+GFMVuhDdRAoGBANKe +3Dn3XOq7EW42GZey1xUxrfQRJp491KXHvjYt7z7zSiUzqN+mqIqz6ngCjJWbyQsl +Ud9N9U+4rNfYYLHQ0resjxGQRtmooOHlLhT6pEplXDgQb2SmCg2u22SKkkXA7zOV +OFbNryXgkYThsA6ih8LiKM8aFn7zttRSEeTpfye7AoGBALhIzRyiuiuXpuswgdeF +YrJs8A1jB/c1i5qXHlvurT2lCYYbaZHSQj0I0r2CvrqDNhaEzStDIz5XDzTHD4Qd +EjmBo3wJyBkLPI/nZxb4ZE2jrz8znf0EasE3a2OTnrSjqqylDa/sMzM+EtkBORSB +SFaLV45lFeKs2W2eiBVmXTZRAoGAJoA7qaz6Iz6G9SqWixB6GLm4HsFz2cFbueJF +dwn2jf9TMnG7EQcaECDLX5y3rjGIEq2DxdouWaBcmChJpLeTjVfR31gMW4Vjw2dt +gRBAMAlPTkBS3Ictl0q7eCmMi4u1Liy828FFnxrp/uxyjnpPbuSAqTsPma1bYnyO +INY+FDkCgYAe9e39/vXe7Un3ysjqDUW+0OMM+kg4ulhiopzKY+QbHiSWmUUDtvcN +asqrYiX1d59e2ZNiqrlBn86I8549St81bWSrRMNf7R+WVb79RApsABeUaEoyo3lq +0UgOBM8Nt558kaja/YfJf/jwNC1DPuu5x5t38ZcqAkqrZ/HEPkFdGQ== +-----END RSA PRIVATE KEY----- diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index b3be8925e57bb..b33198956ccc1 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -22,7 +22,8 @@ "php": "^7.1.3", "psr/log": "^1.0", "symfony/http-client-contracts": "^1.1.7", - "symfony/polyfill-php73": "^1.11" + "symfony/polyfill-php73": "^1.11", + "symfony/service-contracts": "^1.0|^2" }, "require-dev": { "nyholm/psr7": "^1.0", diff --git a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php index 08dd1b6dcac7f..7553389523486 100644 --- a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php +++ b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php @@ -145,6 +145,27 @@ header('Content-Encoding: gzip'); echo str_repeat('-', 1000); exit; + + case '/json': + header("Content-Type: application/json"); + echo json_encode([ + 'documents' => [ + ['id' => '/json/1'], + ['id' => '/json/2'], + ['id' => '/json/3'], + ], + ]); + exit; + + case '/json/1': + case '/json/2': + case '/json/3': + header("Content-Type: application/json"); + echo json_encode([ + 'title' => $vars['REQUEST_URI'], + ]); + + exit; } header('Content-Type: application/json', true); diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index e8d8b19eee9c6..3bfca2340fa30 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -24,8 +24,6 @@ */ abstract class HttpClientTestCase extends TestCase { - private static $server; - public static function setUpBeforeClass(): void { TestHttpServer::start(); diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php index 8e7a469c42603..0adb1a52a3036 100644 --- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php +++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php @@ -19,31 +19,22 @@ */ class TestHttpServer { - private static $server; + private static $started; public static function start() { - if (null !== self::$server) { + if (self::$started) { return; } $finder = new PhpExecutableFinder(); $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:8057'])); $process->setWorkingDirectory(__DIR__.'/Fixtures/web'); - $process->setTimeout(300); $process->start(); - self::$server = new class() { - public $process; - - public function __destruct() - { - $this->process->stop(); - } - }; - - self::$server->process = $process; - + register_shutdown_function([$process, 'stop']); sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1); + + self::$started = true; } } From fffeccd7445092bb7ec140adfb827fb677096aad Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 28 Nov 2019 16:39:39 +0100 Subject: [PATCH 61/76] [Config] don't break on virtual stack frames in ClassExistenceResource --- .../Config/Resource/ClassExistenceResource.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php index 1ba8e76248322..340e28f245a85 100644 --- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -191,15 +191,17 @@ public static function throwOnRequiredClass($class, \Exception $previous = null) } $props = [ - 'file' => $trace[$i]['file'], - 'line' => $trace[$i]['line'], + 'file' => isset($trace[$i]['file']) ? $trace[$i]['file'] : null, + 'line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : null, 'trace' => \array_slice($trace, 1 + $i), ]; foreach ($props as $p => $v) { - $r = new \ReflectionProperty('Exception', $p); - $r->setAccessible(true); - $r->setValue($e, $v); + if (null !== $v) { + $r = new \ReflectionProperty('Exception', $p); + $r->setAccessible(true); + $r->setValue($e, $v); + } } } From a1129f938c63c2b91bf3da20cd41a662c241433c Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 23 Nov 2019 18:59:57 +0100 Subject: [PATCH 62/76] [Console] Fix autocomplete multibyte input support --- .../Console/Helper/QuestionHelper.php | 6 +++--- .../Tests/Helper/QuestionHelperTest.php | 20 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index af4d0b9cca459..cccaeb0248884 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -264,7 +264,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; - $fullChoice = substr($fullChoice, 0, -1); + $fullChoice = self::substr($fullChoice, 0, -1); // Move cursor backwards $output->write("\033[1D"); } @@ -278,7 +278,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu } // Pop the last character off the end of our string - $ret = substr($ret, 0, $i); + $ret = self::substr($ret, 0, $i); } elseif ("\033" === $c) { // Did we read an escape sequence? $c .= fread($inputStream, 2); @@ -304,7 +304,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); $output->write($remainingCharacters); $fullChoice .= $remainingCharacters; - $i = \strlen($fullChoice); + $i = self::strlen($fullChoice); } if ("\n" === $c) { diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 02cc6ce7e0249..2e91d49feeca9 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -175,19 +175,20 @@ public function testAskWithAutocomplete() // Acm // AcsTest // - // - // Test + // + // Test // // S // F00oo - $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + // F⭐⭐ + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\nF⭐\t\177\177⭐\t\n"); $dialog = new QuestionHelper(); $helperSet = new HelperSet([new FormatterHelper()]); $dialog->setHelperSet($helperSet); $question = new Question('Please select a bundle', 'FrameworkBundle'); - $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle']); + $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle', 'F⭐Y']); $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); @@ -197,6 +198,7 @@ public function testAskWithAutocomplete() $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals('F⭐Y', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } public function testAskWithAutocompleteWithNonSequentialKeys() @@ -680,12 +682,13 @@ public function testLegacyAskWithAutocomplete() // Acm // AcsTest // - // - // Test + // + // Test // // S // F00oo - $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + // F⭐⭐ + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\nF⭐\t⭐\t\n"); $dialog = new QuestionHelper(); $dialog->setInputStream($inputStream); @@ -693,7 +696,7 @@ public function testLegacyAskWithAutocomplete() $dialog->setHelperSet($helperSet); $question = new Question('Please select a bundle', 'FrameworkBundle'); - $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle']); + $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle', 'F⭐Y']); $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); @@ -703,6 +706,7 @@ public function testLegacyAskWithAutocomplete() $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('F⭐Y', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } /** From b9b3fd89a32b1ef52a25ac0e58bce1f8172996bf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 29 Nov 2019 14:59:03 +0100 Subject: [PATCH 63/76] Fix CI --- .travis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b21a71d97299d..b1f4265d25f82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -266,6 +266,11 @@ install: run_tests () { set -e export PHP=$1 + + if [[ !$deps && $PHP = 7.2 ]]; then + tfold src/Symfony/Component/HttpClient.h2push "$COMPOSER_UP symfony/contracts && docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push" + fi + if [[ $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then echo -e "\\n\\e[33;1mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m" return @@ -283,10 +288,6 @@ install: echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && ([ -e composer.lock ] && ${COMPOSER_UP/update/install} || $COMPOSER_UP --prefer-lowest --prefer-stable) && $PHPUNIT_X'" echo "$COMPONENTS" | xargs -n1 -I{} tar --append -f ~/php-ext/composer-lowest.lock.tar {}/composer.lock else - if [[ $PHP = ${MIN_PHP%.*} ]]; then - tfold src/Symfony/Component/HttpClient.h2push docker run -it --rm -v $(pwd):/app -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push - fi - echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" tfold src/Symfony/Component/Console.tty $PHPUNIT --group tty From a1ce0ed08602917d76810dbc2471dc25e28bda48 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 29 Nov 2019 15:40:35 +0100 Subject: [PATCH 64/76] fix dumping number-like string parameters --- .../DependencyInjection/Dumper/XmlDumper.php | 5 +++++ .../Tests/Fixtures/containers/container8.php | 11 +++++++++++ .../Tests/Fixtures/php/services8.php | 11 +++++++++++ .../Tests/Fixtures/xml/services8.xml | 11 +++++++++++ .../Tests/Fixtures/yaml/services8.yml | 11 +++++++++++ 5 files changed, 49 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index cfc932843937f..eff421ec4e71f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -304,6 +304,11 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent if (\in_array($value, ['null', 'true', 'false'], true)) { $element->setAttribute('type', 'string'); } + + if (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) { + $element->setAttribute('type', 'string'); + } + $text = $this->document->createTextNode(self::phpToXml($value)); $element->appendChild($text); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php index 5b3c01c23cd52..edcd045eaabbb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php @@ -9,6 +9,17 @@ 'bar' => 'foo is %%foo bar', 'escape' => '@escapeme', 'values' => [true, false, null, 0, 1000.3, 'true', 'false', 'null'], + 'null string' => 'null', + 'string of digits' => '123', + 'string of digits prefixed with minus character' => '-123', + 'true string' => 'true', + 'false string' => 'false', + 'binary number string' => '0b0110', + 'numeric string' => '-1.2E2', + 'hexadecimal number string' => '0xFF', + 'float string' => '10100.1', + 'positive float string' => '+10100.1', + 'negative float string' => '-10100.1', ])); return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index ce4815ef81975..e7a0214a10f53 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -151,6 +151,17 @@ protected function getDefaultParameters() 6 => 'false', 7 => 'null', ], + 'null string' => 'null', + 'string of digits' => '123', + 'string of digits prefixed with minus character' => '-123', + 'true string' => 'true', + 'false string' => 'false', + 'binary number string' => '0b0110', + 'numeric string' => '-1.2E2', + 'hexadecimal number string' => '0xFF', + 'float string' => '10100.1', + 'positive float string' => '+10100.1', + 'negative float string' => '-10100.1', ]; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml index d0f9015c5a547..4b07bbb7da50c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml @@ -18,6 +18,17 @@ false null + null + 123 + -123 + true + false + 0b0110 + -1.2E2 + 0xFF + 10100.1 + +10100.1 + -10100.1 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml index 4e37bc9315c9b..002b1d4bcd3a8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml @@ -4,6 +4,17 @@ parameters: bar: 'foo is %%foo bar' escape: '@@escapeme' values: [true, false, null, 0, 1000.3, 'true', 'false', 'null'] + null string: 'null' + string of digits: '123' + string of digits prefixed with minus character: '-123' + true string: 'true' + false string: 'false' + binary number string: '0b0110' + numeric string: '-1.2E2' + hexadecimal number string: '0xFF' + float string: '10100.1' + positive float string: '+10100.1' + negative float string: '-10100.1' services: service_container: From 8de2a226a8de220bacbe1981f972c560c37d4e29 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 23 Nov 2019 20:59:48 +0100 Subject: [PATCH 65/76] [Config][ReflectionClassResource] Handle parameters with undefined constant as their default values --- .../Resource/ReflectionClassResource.php | 50 +++++++++++++++++-- .../Resource/ReflectionClassResourceTest.php | 18 +++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index d5e6b829cfeca..4c8f89cd3f204 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -151,12 +151,56 @@ private function generateSignature(\ReflectionClass $class) } } else { foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { - yield preg_replace('/^ @@.*/m', '', $m); - $defaults = []; + $parametersWithUndefinedConstants = []; foreach ($m->getParameters() as $p) { - $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null; + if (!$p->isDefaultValueAvailable()) { + $defaults[$p->name] = null; + + continue; + } + + if (!$p->isDefaultValueConstant() || \defined($p->getDefaultValueConstantName())) { + $defaults[$p->name] = $p->getDefaultValue(); + + continue; + } + + $defaults[$p->name] = $p->getDefaultValueConstantName(); + $parametersWithUndefinedConstants[$p->name] = true; + } + + if (!$parametersWithUndefinedConstants) { + yield preg_replace('/^ @@.*/m', '', $m); + } else { + $stack = [ + $m->getDocComment(), + $m->getName(), + $m->isAbstract(), + $m->isFinal(), + $m->isStatic(), + $m->isPublic(), + $m->isPrivate(), + $m->isProtected(), + $m->returnsReference(), + \PHP_VERSION_ID >= 70000 && $m->hasReturnType() ? (\PHP_VERSION_ID >= 70100 ? $m->getReturnType()->getName() : (string) $m->getReturnType()) : '', + ]; + + foreach ($m->getParameters() as $p) { + if (!isset($parametersWithUndefinedConstants[$p->name])) { + $stack[] = (string) $p; + } else { + $stack[] = $p->isOptional(); + $stack[] = \PHP_VERSION_ID >= 70000 && $p->hasType() ? (\PHP_VERSION_ID >= 70100 ? $p->getType()->getName() : (string) $p->getType()) : ''; + $stack[] = $p->isPassedByReference(); + $stack[] = \PHP_VERSION_ID >= 50600 ? $p->isVariadic() : ''; + $stack[] = $p->getName(); + } + } + + yield implode(',', $stack); } + yield print_r($defaults, true); } } diff --git a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php index 76cad1433bb20..74ed6b3edc2a9 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php @@ -63,8 +63,12 @@ public function testIsFreshForDeletedResources() /** * @dataProvider provideHashedSignature */ - public function testHashedSignature($changeExpected, $changedLine, $changedCode) + public function testHashedSignature($changeExpected, $changedLine, $changedCode, $setContext = null) { + if ($setContext) { + $setContext(); + } + $code = <<<'EOPHP' /* 0*/ /* 1*/ class %s extends ErrorException @@ -82,7 +86,9 @@ public function testHashedSignature($changeExpected, $changedLine, $changedCode) /*13*/ protected function prot($a = []) {} /*14*/ /*15*/ private function priv() {} -/*16*/ } +/*16*/ +/*17*/ public function ccc($bar = A_CONSTANT_THAT_FOR_SURE_WILL_NEVER_BE_DEFINED_CCCCCC) {} +/*18*/ } EOPHP; static $expectedSignature, $generateSignature; @@ -97,7 +103,9 @@ public function testHashedSignature($changeExpected, $changedLine, $changedCode) } $code = explode("\n", $code); - $code[$changedLine] = $changedCode; + if (null !== $changedCode) { + $code[$changedLine] = $changedCode; + } eval(sprintf(implode("\n", $code), $class = 'Foo'.str_replace('.', '_', uniqid('', true)))); $signature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class)))); @@ -145,6 +153,10 @@ public function provideHashedSignature() yield [0, 7, 'protected int $prot;']; yield [0, 9, 'private string $priv;']; } + + yield [1, 17, 'public function ccc($bar = 187) {}']; + yield [1, 17, 'public function ccc($bar = ANOTHER_ONE_THAT_WILL_NEVER_BE_DEFINED_CCCCCCCCC) {}']; + yield [1, 17, null, static function () { \define('A_CONSTANT_THAT_FOR_SURE_WILL_NEVER_BE_DEFINED_CCCCCC', 'foo'); }]; } public function testEventSubscriber() From b2ae60a73bb59c24da09ce8678290d939791ed0b Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 29 Nov 2019 20:07:18 +0100 Subject: [PATCH 66/76] [Validator] Update Slovenian translations --- .../Resources/translations/validators.sl.xlf | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf index 6f5fd98ca192e..cb12a8a9daa4d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf @@ -318,6 +318,54 @@ Error Napaka + + This is not a valid UUID. + To ni veljaven UUID. + + + This value should be a multiple of {{ compared_value }}. + Ta vrednost bi morala biti večkratnik od {{ compared_value }}. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + Ta poslovna identifikacijska koda (BIC) ni povezana z IBAN {{ iban }}. + + + This value should be valid JSON. + Ta vrednost bi morala biti veljaven JSON. + + + This collection should contain only unique elements. + Ta zbirka bi morala vsebovati samo edinstvene elemente. + + + This value should be positive. + Ta vrednost bi morala biti pozitivna. + + + This value should be either positive or zero. + Ta vrednost bi morala biti pozitivna ali enaka nič. + + + This value should be negative. + Ta vrednost bi morala biti negativna. + + + This value should be either negative or zero. + Ta vrednost bi morala biti negativna ali enaka nič. + + + This value is not a valid timezone. + Ta vrednost ni veljaven časovni pas. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + To geslo je ušlo pri kršitvi varnosti podatkov in ga ne smete uporabljati. Prosimo, uporabite drugo geslo. + + + This value should be between {{ min }} and {{ max }}. + Ta vrednost bi morala biti med {{ min }} in {{ max }}. + From d625a7370530423fed7ea262aaa14405b8f2e643 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Thu, 28 Nov 2019 04:41:34 +0100 Subject: [PATCH 67/76] [Security] Fix clearing remember-me cookie after deauthentication --- .../Security/Factory/RememberMeFactory.php | 6 +- .../DependencyInjection/SecurityExtension.php | 10 ++- .../Tests/Functional/ClearRememberMeTest.php | 77 +++++++++++++++++++ .../app/ClearRememberMe/bundles.php | 18 +++++ .../Functional/app/ClearRememberMe/config.yml | 32 ++++++++ .../app/ClearRememberMe/routing.yml | 7 ++ .../Bundle/SecurityBundle/composer.json | 2 +- .../Http/Firewall/ContextListener.php | 11 +++ .../Tests/Firewall/ContextListenerTest.php | 20 ++++- 9 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/routing.yml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 34de3d6701470..8d419c0edd8f0 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -81,7 +81,11 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, throw new \RuntimeException('Each "security.remember_me_aware" tag must have a provider attribute.'); } - $userProviders[] = new Reference($attribute['provider']); + // context listeners don't need a provider + if ('none' !== $attribute['provider']) { + $userProviders[] = new Reference($attribute['provider']); + } + $container ->getDefinition($serviceId) ->addMethodCall('setRememberMeServices', [new Reference($rememberMeServicesId)]) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 795fe053e66e4..19e9beb1e37e6 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -374,6 +374,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $listeners[] = new Reference('security.channel_listener'); $contextKey = null; + $contextListenerId = null; // Context serializer listener if (false === $firewall['stateless']) { $contextKey = $id; @@ -390,7 +391,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a } $this->logoutOnUserChangeByContextKey[$contextKey] = [$id, $logoutOnUserChange]; - $listeners[] = new Reference($this->createContextListener($container, $contextKey, $logoutOnUserChange)); + $listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey, $logoutOnUserChange)); $sessionStrategyId = 'security.authentication.session_strategy'; } else { $this->statelessFirewallKeys[] = $id; @@ -463,7 +464,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null; // Authentication listeners - list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint); + list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId); $config->replaceArgument(7, $configuredEntryPoint ?: $defaultEntryPoint); @@ -519,7 +520,7 @@ private function createContextListener($container, $contextKey, $logoutUserOnCha return $this->contextListeners[$contextKey] = $listenerId; } - private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider = null, array $providerIds, $defaultEntryPoint) + private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider = null, array $providerIds, $defaultEntryPoint, $contextListenerId = null) { $listeners = []; $hasListeners = false; @@ -537,6 +538,9 @@ private function createAuthenticationListeners($container, $id, $firewall, &$aut } elseif ('remember_me' === $key) { // RememberMeFactory will use the firewall secret when created $userProvider = null; + if ($contextListenerId) { + $container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']); + } } else { $userProvider = $defaultProvider ?: $this->getFirstProvider($id, $key, $providerIds); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php new file mode 100644 index 0000000000000..3a1046b0c4a17 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; +use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; + +class ClearRememberMeTest extends AbstractWebTestCase +{ + public function testUserChangeClearsCookie() + { + $client = $this->createClient(['test_case' => 'ClearRememberMe', 'root_config' => 'config.yml']); + + $client->request('POST', '/login', [ + '_username' => 'johannes', + '_password' => 'test', + ]); + + $this->assertSame(302, $client->getResponse()->getStatusCode()); + $cookieJar = $client->getCookieJar(); + $this->assertNotNull($cookieJar->get('REMEMBERME')); + + $client->request('GET', '/foo'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertNull($cookieJar->get('REMEMBERME')); + } +} + +class RememberMeFooController +{ + public function __invoke(UserInterface $user) + { + return new Response($user->getUsername()); + } +} + +class RememberMeUserProvider implements UserProviderInterface +{ + private $inner; + + public function __construct(InMemoryUserProvider $inner) + { + $this->inner = $inner; + } + + public function loadUserByUsername($username) + { + return $this->inner->loadUserByUsername($username); + } + + public function refreshUser(UserInterface $user) + { + $user = $this->inner->refreshUser($user); + + $alterUser = \Closure::bind(function (User $user) { $user->password = 'foo'; }, null, User::class); + $alterUser($user); + + return $user; + } + + public function supportsClass($class) + { + return $this->inner->supportsClass($class); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/bundles.php new file mode 100644 index 0000000000000..9a26fb163a77d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; + +return [ + new FrameworkBundle(), + new SecurityBundle(), +]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml new file mode 100644 index 0000000000000..e5cefd37df76d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml @@ -0,0 +1,32 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + check_path: login + remember_me: true + remember_me: + always_remember_me: true + secret: key + anonymous: ~ + logout_on_user_change: true + + access_control: + - { path: ^/foo, roles: ROLE_USER } + +services: + Symfony\Bundle\SecurityBundle\Tests\Functional\RememberMeUserProvider: + public: true + decorates: security.user.provider.concrete.in_memory + arguments: ['@Symfony\Bundle\SecurityBundle\Tests\Functional\RememberMeUserProvider.inner'] diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/routing.yml new file mode 100644 index 0000000000000..08975bdcb3832 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/routing.yml @@ -0,0 +1,7 @@ +login: + path: /login + +foo: + path: /foo + defaults: + _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\RememberMeFooController diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 1f0e56e6eedde..1a8057b6fbd08 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -19,7 +19,7 @@ "php": "^5.5.9|>=7.0.8", "ext-xml": "*", "symfony/config": "~3.4|~4.0", - "symfony/security": "~3.4.15|~4.0.15|^4.1.4", + "symfony/security": "~3.4.36|~4.3.9|^4.4.1", "symfony/dependency-injection": "^3.4.3|^4.0.3", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-php70": "~1.0" diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index c6b89793e66e7..ea9f51f9224ad 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -27,6 +27,7 @@ use Symfony\Component\Security\Core\Role\SwitchUserRole; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; /** * ContextListener manages the SecurityContext persistence through a session. @@ -44,6 +45,7 @@ class ContextListener implements ListenerInterface private $registered; private $trustResolver; private $logoutOnUserChange = false; + private $rememberMeServices; /** * @param iterable|UserProviderInterface[] $userProviders @@ -103,6 +105,10 @@ public function handle(GetResponseEvent $event) if ($token instanceof TokenInterface) { $token = $this->refreshUser($token); + + if (!$token && $this->logoutOnUserChange && $this->rememberMeServices) { + $this->rememberMeServices->loginFail($request); + } } elseif (null !== $token) { if (null !== $this->logger) { $this->logger->warning('Expected a security token from the session, got something else.', ['key' => $this->sessionKey, 'received' => $token]); @@ -268,4 +274,9 @@ public static function handleUnserializeCallback($class) { throw new \UnexpectedValueException('Class not found: '.$class, 0x37313bc); } + + public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices) + { + $this->rememberMeServices = $rememberMeServices; + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index 25915f212a4c0..acab7087cb92f 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -31,6 +31,7 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Firewall\ContextListener; +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; class ContextListenerTest extends TestCase { @@ -278,6 +279,19 @@ public function testIfTokenIsNotDeauthenticated() $this->assertSame($goodRefreshedUser, $tokenStorage->getToken()->getUser()); } + public function testRememberMeGetsCanceledIfTokenIsDeauthenticated() + { + $tokenStorage = new TokenStorage(); + $refreshedUser = new User('foobar', 'baz'); + + $rememberMeServices = $this->createMock(RememberMeServicesInterface::class); + $rememberMeServices->expects($this->once())->method('loginFail'); + + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], null, true, $rememberMeServices); + + $this->assertNull($tokenStorage->getToken()); + } + public function testTryAllUserProvidersUntilASupportingUserProviderIsFound() { $tokenStorage = new TokenStorage(); @@ -347,7 +361,7 @@ protected function runSessionOnKernelResponse($newToken, $original = null) return $session; } - private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders, UserInterface $user = null, $logoutOnUserChange = false) + private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders, UserInterface $user = null, $logoutOnUserChange = false, RememberMeServicesInterface $rememberMeServices = null) { $user = $user ?: new User('foo', 'bar'); $session = new Session(new MockArraySessionStorage()); @@ -359,6 +373,10 @@ private function handleEventWithPreviousSession(TokenStorageInterface $tokenStor $listener = new ContextListener($tokenStorage, $userProviders, 'context_key'); $listener->setLogoutOnUserChange($logoutOnUserChange); + + if ($rememberMeServices) { + $listener->setRememberMeServices($rememberMeServices); + } $listener->handle(new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST)); } } From 7f803bc6743a80a2799c19de15dc27d3387f69c3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 29 Nov 2019 13:21:45 +0100 Subject: [PATCH 68/76] Fix the translation commands when a template contains a syntax error --- .../Tests/Translation/TwigExtractorTest.php | 23 ++++++------------- .../Bridge/Twig/Translation/TwigExtractor.php | 9 +------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php index 82d72983657ba..28932d9449d6a 100644 --- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -16,7 +16,6 @@ use Symfony\Bridge\Twig\Translation\TwigExtractor; use Symfony\Component\Translation\MessageCatalogue; use Twig\Environment; -use Twig\Error\Error; use Twig\Loader\ArrayLoader; class TwigExtractorTest extends TestCase @@ -78,23 +77,15 @@ public function getExtractData() /** * @dataProvider resourcesWithSyntaxErrorsProvider */ - public function testExtractSyntaxError($resources) + public function testExtractSyntaxError($resources, array $messages) { - $this->expectException('Twig\Error\Error'); $twig = new Environment($this->getMockBuilder('Twig\Loader\LoaderInterface')->getMock()); $twig->addExtension(new TranslationExtension($this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock())); $extractor = new TwigExtractor($twig); - - try { - $extractor->extract($resources, new MessageCatalogue('en')); - } catch (Error $e) { - $this->assertSame(\dirname(__DIR__).strtr('/Fixtures/extractor/syntax_error.twig', '/', \DIRECTORY_SEPARATOR), $e->getFile()); - $this->assertSame(1, $e->getLine()); - $this->assertSame('Unclosed "block".', $e->getMessage()); - - throw $e; - } + $catalogue = new MessageCatalogue('en'); + $extractor->extract($resources, $catalogue); + $this->assertSame($messages, $catalogue->all()); } /** @@ -103,9 +94,9 @@ public function testExtractSyntaxError($resources) public function resourcesWithSyntaxErrorsProvider() { return [ - [__DIR__.'/../Fixtures'], - [__DIR__.'/../Fixtures/extractor/syntax_error.twig'], - [new \SplFileInfo(__DIR__.'/../Fixtures/extractor/syntax_error.twig')], + [__DIR__.'/../Fixtures', ['messages' => ['Hi!' => 'Hi!']]], + [__DIR__.'/../Fixtures/extractor/syntax_error.twig', []], + [new \SplFileInfo(__DIR__.'/../Fixtures/extractor/syntax_error.twig'), []], ]; } diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index b7c787226656f..107d8cc4bff17 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -12,7 +12,6 @@ namespace Symfony\Bridge\Twig\Translation; use Symfony\Component\Finder\Finder; -use Symfony\Component\Finder\SplFileInfo; use Symfony\Component\Translation\Extractor\AbstractFileExtractor; use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\MessageCatalogue; @@ -58,13 +57,7 @@ public function extract($resource, MessageCatalogue $catalogue) try { $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); } catch (Error $e) { - if ($file instanceof \SplFileInfo) { - $path = $file->getRealPath() ?: $file->getPathname(); - $name = $file instanceof SplFileInfo ? $file->getRelativePathname() : $path; - $e->setSourceContext(new Source('', $name, $path)); - } - - throw $e; + // ignore errors, these should be fixed by using the linter } } } From 4b9b93f5d661915b0d5f239e3447f53061e42bf6 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Tue, 26 Nov 2019 22:03:03 +0100 Subject: [PATCH 69/76] [Messenger] add tests to FailedMessagesShowCommand --- .../Command/FailedMessagesShowCommandTest.php | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php index f632d9890b343..ae9b39a3125b3 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; +use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; /** * @group time-sensitive @@ -94,4 +95,101 @@ public function testMultipleRedeliveryFails() $redeliveryStamp2->getRedeliveredAt()->format('Y-m-d H:i:s')), $tester->getDisplay(true)); } + + public function testReceiverShouldBeListable() + { + $receiver = $this->createMock(ReceiverInterface::class); + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $this->expectExceptionMessage('The "failure_receiver" receiver does not support listing or showing specific messages.'); + + $tester = new CommandTester($command); + $tester->execute(['id' => 15]); + } + + public function testListMessages() + { + $sentToFailureStamp = new SentToFailureTransportStamp('async'); + $redeliveryStamp = new RedeliveryStamp(0, 'failure_receiver', 'Things are bad!'); + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp(15), + $sentToFailureStamp, + $redeliveryStamp, + ]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]); + + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $tester = new CommandTester($command); + $tester->execute([]); + $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), + $tester->getDisplay(true)); + } + + public function testListMessagesReturnsNoMessagesFound() + { + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('all')->with()->willReturn([]); + + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $tester = new CommandTester($command); + $tester->execute([]); + $this->assertStringContainsString('[OK] No failed messages were found.', $tester->getDisplay(true)); + } + + public function testListMessagesReturnsPaginatedMessages() + { + $sentToFailureStamp = new SentToFailureTransportStamp('async'); + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp(15), + $sentToFailureStamp, + new RedeliveryStamp(0, 'failure_receiver', 'Things are bad!'), + ]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]); + + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $tester = new CommandTester($command); + $tester->execute(['--max' => 1]); + $this->assertStringContainsString('Showing first 1 messages.', $tester->getDisplay(true)); + } + + public function testInvalidMessagesThrowsException() + { + $sentToFailureStamp = new SentToFailureTransportStamp('async'); + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp(15), + $sentToFailureStamp, + ]); + $receiver = $this->createMock(ListableReceiverInterface::class); + + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $this->expectExceptionMessage('The message "15" was not found.'); + + $tester = new CommandTester($command); + $tester->execute(['id' => 15]); + } } From 861783cc156092ba1e98fa3986b7fbe1fa6e35d2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 1 Dec 2019 09:33:36 +0100 Subject: [PATCH 70/76] Fix CS --- .../CompilerPass/RegisterMappingsPass.php | 5 +---- .../Tests/Compiler/ResolveBindingsPassTest.php | 1 - src/Symfony/Component/Ldap/Ldap.php | 6 +----- src/Symfony/Component/Routing/Matcher/UrlMatcher.php | 4 +--- src/Symfony/Component/Security/Http/Tests/FirewallTest.php | 1 - 5 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php index 7ac1856114d7e..4e025dc194c93 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -222,10 +222,7 @@ private function getManagerName(ContainerBuilder $container) } } - throw new InvalidArgumentException(sprintf( - 'Could not find the manager name parameter in the container. Tried the following parameter names: "%s"', - implode('", "', $this->managerParameters) - )); + throw new InvalidArgumentException(sprintf('Could not find the manager name parameter in the container. Tried the following parameter names: "%s"', implode('", "', $this->managerParameters))); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index fd526caa94264..bfd3d67238277 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; diff --git a/src/Symfony/Component/Ldap/Ldap.php b/src/Symfony/Component/Ldap/Ldap.php index dda47b5376bb1..fd4955ef5d791 100644 --- a/src/Symfony/Component/Ldap/Ldap.php +++ b/src/Symfony/Component/Ldap/Ldap.php @@ -73,11 +73,7 @@ public function escape($subject, $ignore = '', $flags = 0) public static function create($adapter, array $config = []) { if (!isset(self::$adapterMap[$adapter])) { - throw new DriverNotFoundException(sprintf( - 'Adapter "%s" not found. You should use one of: %s', - $adapter, - implode(', ', self::$adapterMap) - )); + throw new DriverNotFoundException(sprintf('Adapter "%s" not found. You should use one of: %s', $adapter, implode(', ', self::$adapterMap))); } $class = self::$adapterMap[$adapter]; diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index 663ebcbefaaec..b8599b23ad759 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -80,9 +80,7 @@ public function match($pathinfo) throw new NoConfigurationException(); } - throw 0 < \count($this->allow) - ? new MethodNotAllowedException(array_unique($this->allow)) - : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); + throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); } /** diff --git a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php index 774db6128f379..3114f2da17812 100644 --- a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php +++ b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Http\Tests; use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Http\Firewall; From 2c04fad016259e1235fdd4382d40dd5ea6d25a1c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 1 Dec 2019 09:39:44 +0100 Subject: [PATCH 71/76] Fix CS --- src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php | 1 - .../HttpKernel/EventListener/ExceptionListener.php | 1 - .../HttpKernel/EventListener/LocaleAwareListener.php | 1 - src/Symfony/Component/HttpKernel/Tests/KernelTest.php | 2 -- src/Symfony/Component/VarExporter/Internal/Exporter.php | 6 +++--- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php index 7a238478bdcf5..210b6657aa142 100644 --- a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormConfigInterface; use Symfony\Component\Form\FormTypeExtensionInterface; diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php index ab5f65cb63195..1282f155fef45 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -16,7 +16,6 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleAwareListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleAwareListener.php index fb8e67e7ed492..49e3f2eec1ece 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleAwareListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleAwareListener.php @@ -12,7 +12,6 @@ namespace Symfony\Component\HttpKernel\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 26bf7d8aff265..c3d6872d34155 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -651,8 +651,6 @@ protected function getBundle($dir = null, $parent = null, $className = null, $bu * * @param array $methods Additional methods to mock (besides the abstract ones) * @param array $bundles Bundles to register - * - * @return Kernel */ protected function getKernel(array $methods = [], array $bundles = [], bool $debug = false): Kernel { diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 06d747e1385e2..dc490c3757503 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -222,14 +222,14 @@ public static function export($value, $indent = '') )); if ("'" === $m[2]) { - return substr($m[1], 0, -2); + return substr($m[1], 0, -2); } if ('n".\'' === substr($m[1], -4)) { - return substr_replace($m[1], "\n".$subIndent.".'".$m[2], -2); + return substr_replace($m[1], "\n".$subIndent.".'".$m[2], -2); } - return $m[1].$m[2]; + return $m[1].$m[2]; }, $code, -1, $count); if ($count && 0 === strpos($code, "''.")) { From cd2a8d2628f8f8c89389ebb1b1746d036cec3997 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 1 Dec 2019 10:59:32 +0100 Subject: [PATCH 72/76] [Console] fix tests --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 2 ++ .../Component/Console/Tests/Helper/QuestionHelperTest.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index fdde44ed74587..03d251caa952e 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -282,6 +282,8 @@ function ($match) use ($ret) { $output->write($c); break; } + + $numMatches = 0; } continue; diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index f89faa1eb8f35..fd1e03c388d65 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -711,7 +711,7 @@ public function testTraversableMultiselectAutocomplete() // F00o,A,SecurityBundle // Acme,As<29x BACKSPACE>S // Ac,As<3x BACKSPACE>d - $inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nSecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n"); + $inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n"); $dialog = new QuestionHelper(); $helperSet = new HelperSet([new FormatterHelper()]); From abc2880be4015e9db3900ac420adf26854a8facd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 1 Dec 2019 11:04:45 +0100 Subject: [PATCH 73/76] [Console] fix typos --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 2 +- .../Component/Console/Tests/Helper/QuestionHelperTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index cccaeb0248884..1f93c06451630 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -264,7 +264,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; - $fullChoice = self::substr($fullChoice, 0, -1); + $fullChoice = self::substr($fullChoice, 0, $i); // Move cursor backwards $output->write("\033[1D"); } diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 2e91d49feeca9..d2afee42ffa26 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -180,7 +180,7 @@ public function testAskWithAutocomplete() // // S // F00oo - // F⭐⭐ + // F⭐ $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\nF⭐\t\177\177⭐\t\n"); $dialog = new QuestionHelper(); From a94e76a866c15db5521720aa111cf194cccb4606 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 1 Dec 2019 11:45:41 +0100 Subject: [PATCH 74/76] Fix failures on PHP 7.4 --- src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php | 2 +- .../Component/Config/Resource/ClassExistenceResource.php | 4 ++-- .../Component/PropertyAccess/PropertyAccessor.php | 9 +++++---- .../PropertyAccess/Tests/PropertyAccessorTest.php | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index 76673e0a03c7b..e03d37b94de41 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -271,7 +271,7 @@ private function generateItems(array $keys) public static function throwOnRequiredClass($class) { $e = new \ReflectionException("Class $class does not exist"); - $trace = $e->getTrace(); + $trace = debug_backtrace(); $autoloadFrame = [ 'function' => 'spl_autoload_call', 'args' => [$class], diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php index 340e28f245a85..4e0d0d834e6ab 100644 --- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -72,7 +72,7 @@ public function isFresh($timestamp) spl_autoload_register(__CLASS__.'::throwOnRequiredClass'); } $autoloadedClass = self::$autoloadedClass; - self::$autoloadedClass = $this->resource; + self::$autoloadedClass = ltrim($this->resource, '\\'); try { $exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); @@ -161,7 +161,7 @@ public static function throwOnRequiredClass($class, \Exception $previous = null) throw $e; } - $trace = $e->getTrace(); + $trace = debug_backtrace(); $autoloadFrame = [ 'function' => 'spl_autoload_call', 'args' => [$class], diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 9aab91638cbd9..4297d3947ff53 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -260,13 +260,14 @@ private static function throwInvalidArgumentException($message, $trace, $i) return; } - if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && \array_key_exists(0, $trace[$i]['args'])) { + if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file']) { $pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface ')); $pos += \strlen($delim); - $type = $trace[$i]['args'][0]; - $type = \is_object($type) ? \get_class($type) : \gettype($type); + $j = strpos($message, ',', $pos); + $type = substr($message, 2 + $j, strpos($message, ' given', $j) - $j - 2); + $message = substr($message, $pos, $j - $pos); - throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type)); + throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', $message, 'NULL' === $type ? 'null' : $type)); } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index d7ee358e07a79..0eb4baacf222d 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -532,7 +532,7 @@ public function testThrowTypeError() public function testThrowTypeErrorWithNullArgument() { $this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException'); - $this->expectExceptionMessage('Expected argument of type "DateTime", "NULL" given'); + $this->expectExceptionMessage('Expected argument of type "DateTime", "null" given'); $object = new TypeHinted(); $this->propertyAccessor->setValue($object, 'date', null); From 6937f831ba6914ddb00cbcbd0d61e2e8640c0742 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 1 Dec 2019 15:00:11 +0100 Subject: [PATCH 75/76] updated CHANGELOG for 4.3.9 --- CHANGELOG-4.3.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG-4.3.md b/CHANGELOG-4.3.md index a5b9b78847b5c..3695556e82fad 100644 --- a/CHANGELOG-4.3.md +++ b/CHANGELOG-4.3.md @@ -7,6 +7,46 @@ in 4.3 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.3.0...v4.3.1 +* 4.3.9 (2019-12-01) + + * bug #34649 more robust initialization from request (dbu) + * bug #34671 [Security] Fix clearing remember-me cookie after deauthentication (chalasr) + * bug #34711 Fix the translation commands when a template contains a syntax error (fabpot) + * bug #34560 [Config][ReflectionClassResource] Handle parameters with undefined constant as their default values (fancyweb) + * bug #34695 [Config] don't break on virtual stack frames in ClassExistenceResource (nicolas-grekas) + * bug #34716 [DependencyInjection] fix dumping number-like string parameters (xabbuh) + * bug #34558 [Console] Fix autocomplete multibyte input support (fancyweb) + * bug #34130 [Console] Fix commands description with numeric namespaces (fancyweb) + * bug #34677 [EventDispatcher] Better error reporting when arguments to dispatch() are swapped (rimas-kudelis) + * bug #33573 [TwigBridge] Add row_attr to all form themes (fancyweb) + * bug #34019 [Serializer] CsvEncoder::NO_HEADERS_KEY ignored when used in constructor (Dario Savella) + * bug #34083 [Form] Keep preferred_choices order for choice groups (vilius-g) + * bug #34091 [Debug] work around failing chdir() on Darwin (mary2501) + * bug #34305 [PhpUnitBridge] Read configuration CLI directive (ro0NL) + * bug #34490 [Serializer] Fix MetadataAwareNameConverter usage with string group (antograssiot) + * bug #34632 [Console] Fix trying to access array offset on value of type int (Tavafi) + * bug #34669 [HttpClient] turn exception into log when the request has no content-type (nicolas-grekas) + * bug #34636 [VarDumper] notice on potential undefined index (sylvainmetayer) + * bug #34668 [Cache] Make sure we get the correct number of values from redis::mget() (thePanz) + * bug #34569 [Workflow] Apply the same logic of precedence between the apply() and the buildTransitionBlockerList() method (lyrixx) + * bug #34533 [Monolog Bridge] Fixed accessing static property as non static. (Sander-Toonen) + * bug #34546 [Serializer] Add DateTimeZoneNormalizer into Dependency Injection (jewome62) + * bug #34547 [Messenger] Error when specified default bus is not among the configured (vudaltsov) + * bug #34551 [Security] SwitchUser is broken when the User Provider always returns a valid user (tucksaun) + * bug #34385 Avoid empty "If-Modified-Since" header in validation request (mpdude) + * bug #34458 [Validator] ConstraintValidatorTestCase: add missing return value to mocked validate method calls (ogizanagi) + * bug #34451 [DependencyInjection] Fix dumping multiple deprecated aliases (shyim) + * bug #34448 [Form] allow button names to start with uppercase letter (xabbuh) + * bug #34419 [Cache] Disable igbinary on PHP >= 7.4 (nicolas-grekas) + * bug #34366 [HttpFoundation] Allow redirecting to URLs that contain a semicolon (JayBizzle) + * bug #34397 [FrameworkBundle] Remove project dir from Translator cache vary scanned directories (fancyweb) + * bug #34408 [Cache] catch exceptions when using PDO directly (xabbuh) + * bug #34410 [HttpFoundation] Fix MySQL column type definition. (jbroutier) + * bug #34398 [Config] fix id-generation for GlobResource (nicolas-grekas) + * bug #34396 [Finder] Allow ssh2 stream wrapper for sftp (damienalexandre) + * bug #34383 [DI] Use reproducible entropy to generate env placeholders (nicolas-grekas) + * bug #34381 [WebProfilerBundle] Require symfony/twig-bundle (fancyweb) + * 4.3.8 (2019-11-13) * bug #34344 [Console] Constant STDOUT might be undefined (nicolas-grekas) From 7ac4f9062de3f4758f48884001561eae5fe1b935 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 1 Dec 2019 15:00:23 +0100 Subject: [PATCH 76/76] updated VERSION for 4.3.9 --- 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 ac9775f5f7c64..2274f3fb61c1f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.3.9-DEV'; + const VERSION = '4.3.9'; const VERSION_ID = 40309; const MAJOR_VERSION = 4; const MINOR_VERSION = 3; const RELEASE_VERSION = 9; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2020'; const END_OF_LIFE = '07/2020';