From 618cd80b0d9106472e6ee746fe1580c2f1161686 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Fri, 14 Feb 2020 09:25:43 +0100 Subject: [PATCH 01/25] Correctly remove trace level options for HttpCache --- src/Symfony/Component/HttpClient/CachingHttpClient.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/HttpClient/CachingHttpClient.php b/src/Symfony/Component/HttpClient/CachingHttpClient.php index 367140485f8a..a6af1b5f2d5f 100644 --- a/src/Symfony/Component/HttpClient/CachingHttpClient.php +++ b/src/Symfony/Component/HttpClient/CachingHttpClient.php @@ -55,6 +55,8 @@ public function __construct(HttpClientInterface $client, StoreInterface $store, unset($defaultOptions['allow_revalidate']); unset($defaultOptions['stale_while_revalidate']); unset($defaultOptions['stale_if_error']); + unset($defaultOptions['trace_level']); + unset($defaultOptions['trace_header']); if ($defaultOptions) { [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions); From 8f2fbf6e2c52f794c8c7178bd6cf90fd9f075f51 Mon Sep 17 00:00:00 2001 From: Denis Brumann Date: Fri, 14 Feb 2020 09:11:12 +0100 Subject: [PATCH 02/25] Fixes typo in error message. --- src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php b/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php index 93fe76706271..7224c5f34a21 100644 --- a/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php +++ b/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php @@ -37,7 +37,7 @@ class TwigErrorRenderer implements ErrorRendererInterface public function __construct(Environment $twig, HtmlErrorRenderer $fallbackErrorRenderer = null, $debug = false) { if (!\is_bool($debug) && !\is_callable($debug)) { - throw new \TypeError(sprintf('Argument 2 passed to %s() must be a boolean or a callable, %s given.', __METHOD__, \is_object($debug) ? \get_class($debug) : \gettype($debug))); + throw new \TypeError(sprintf('Argument 3 passed to %s() must be a boolean or a callable, %s given.', __METHOD__, \is_object($debug) ? \get_class($debug) : \gettype($debug))); } $this->twig = $twig; From 3f995ac602d42ef036491e43157239ccb0ec351d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 14 Feb 2020 11:01:23 +0100 Subject: [PATCH 03/25] [HttpKernel] fix registering DebugHandlersListener regardless of the PHP_SAPI --- .../HttpKernel/EventListener/DebugHandlersListener.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index df9df09c0bc3..957a3cdb0f4c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -65,6 +65,9 @@ public function __construct(callable $exceptionHandler = null, LoggerInterface $ */ public function configure(Event $event = null) { + if ($event instanceof ConsoleEvent && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { + return; + } if (!$event instanceof KernelEvent ? !$this->firstCall : !$event->isMasterRequest()) { return; } @@ -148,7 +151,7 @@ public static function getSubscribedEvents() { $events = [KernelEvents::REQUEST => ['configure', 2048]]; - if ('cli' === \PHP_SAPI && \defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) { + if (\defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) { $events[ConsoleEvents::COMMAND] = ['configure', 2048]; } From f9659719198f278943dd5401e8a260dd840f86c2 Mon Sep 17 00:00:00 2001 From: Makdessi Alex Date: Thu, 13 Feb 2020 15:34:42 +0100 Subject: [PATCH 04/25] [VarDumper] fixed DateCaster not displaying additional fields --- .../Component/VarDumper/Caster/DateCaster.php | 6 ++- .../VarDumper/Tests/Caster/DateCasterTest.php | 37 ++++++++++++++++++- .../Tests/Fixtures/DateTimeChild.php | 8 ++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Tests/Fixtures/DateTimeChild.php diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php index f3258b19a6f2..70f229a0d8b7 100644 --- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php @@ -31,7 +31,11 @@ public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') ; - $a = []; + unset( + $a[Caster::PREFIX_DYNAMIC.'date'], + $a[Caster::PREFIX_DYNAMIC.'timezone'], + $a[Caster::PREFIX_DYNAMIC.'timezone_type'] + ); $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); $stub->class .= $d->format(' @U'); diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php index da8b4d0fa5c3..87485448dcc9 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php @@ -16,6 +16,7 @@ use Symfony\Component\VarDumper\Caster\DateCaster; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\DateTimeChild; /** * @author Dany Maillard @@ -55,7 +56,7 @@ public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos) $stub = new Stub(); $date = new \DateTime($time, new \DateTimeZone($timezone)); - $cast = DateCaster::castDateTime($date, ['foo' => 'bar'], $stub, false, 0); + $cast = DateCaster::castDateTime($date, Caster::castObject($date, \DateTime::class), $stub, false, 0); $xDump = << "foo" + "\\x00~\\x00date" => $xDate +] +EODUMP; + + $this->assertDumpEquals($xDump, $dateCast); + + $xDump = <<assertDumpMatchesFormat($xDump, $dateCast["\0~\0date"]); + } + /** * @dataProvider provideIntervals */ diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/DateTimeChild.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/DateTimeChild.php new file mode 100644 index 000000000000..2ea2df6514ae --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/DateTimeChild.php @@ -0,0 +1,8 @@ + Date: Fri, 14 Feb 2020 19:15:20 +0100 Subject: [PATCH 05/25] Add missing autoload calls Until either php 7.4 or doctrine/persistence 2 is required, these will be needed to make sure php recognises signatures using the old names as compatible with signatures using the new names. This is necessary for types defined outside Symfony that extend types from Symfony and still use the old names in signatures of methods they override. More details at https://dev.to/greg0ire/how-to-deprecate-a-type-in-php-48cf Fixes https://github.com/doctrine/DoctrineMongoDBBundle/issues/616 --- src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php | 2 ++ src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php | 2 ++ .../Bridge/Doctrine/Security/User/EntityUserProvider.php | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index c32a60b254cd..97f5facf62e7 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -282,3 +282,5 @@ public function reset() $this->choiceLoaders = []; } } + +interface_exists(ObjectManager::class); diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php index 72fab88d44b9..e462d5692ca7 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -97,3 +97,5 @@ private function parameterToArray(Parameter $parameter): array return [$parameter->getName(), $parameter->getType(), $parameter->getValue()]; } } + +interface_exists(ObjectManager::class); diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php index cdcdbb8febf9..69bbe66965cd 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -153,3 +153,6 @@ private function getClassMetadata(): ClassMetadata return $this->getObjectManager()->getClassMetadata($this->classOrAlias); } } + +interface_exists(ObjectManager::class); +interface_exists(ObjectRepository::class); From 87d51c1e4bb3f121b86355df846c6abb8f004b85 Mon Sep 17 00:00:00 2001 From: Daniel Gorgan Date: Fri, 14 Feb 2020 17:15:50 +0200 Subject: [PATCH 06/25] Set previous exception when rethrown from controller resolver --- .../Component/HttpKernel/Controller/ControllerResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index c981642fee4f..619628b5c7a9 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -88,7 +88,7 @@ public function getController(Request $request) try { $callable = $this->createController($controller); } catch (\InvalidArgumentException $e) { - throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $e->getMessage())); + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $e->getMessage()), 0, $e); } return $callable; From e13470c823ad8032167aa8cbf905eaaed087ebf9 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 15 Feb 2020 14:27:16 +0100 Subject: [PATCH 07/25] [Console] Inline exact-match handling with 4.4 --- src/Symfony/Component/Console/Application.php | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 280a34d5c5b1..b5637277e88d 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -608,6 +608,10 @@ public function find($name) } } + if ($this->has($name)) { + return $this->get($name); + } + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $commands = preg_grep('{^'.$expr.'}', $allCommands); @@ -645,15 +649,7 @@ public function find($name) // filter out aliases for commands which are already on the list if (\count($commands) > 1) { $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; - - if (isset($commandList[$name])) { - return $this->get($name); - } - - foreach ($commands as $k => $nameOrAlias) { - if ($nameOrAlias === $name) { - return $this->get($nameOrAlias); - } + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) { if (!$commandList[$nameOrAlias] instanceof Command) { $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); } @@ -662,14 +658,8 @@ public function find($name) $aliases[$nameOrAlias] = $commandName; - if ($commandName === $nameOrAlias || !\in_array($commandName, $commands)) { - continue; - } - - unset($commands[$k]); - } - - $commands = array_unique($commands); + return $commandName === $nameOrAlias || !\in_array($commandName, $commands); + })); } $exact = \in_array($name, $commands, true) || isset($aliases[$name]); From 50d744589ea2ee6bf01e82a6d3933641e54a66fa Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Sat, 15 Feb 2020 15:50:41 +0100 Subject: [PATCH 08/25] [Routing] Add locale requirement for localized routes --- .../Component/Routing/Loader/AnnotationClassLoader.php | 2 ++ .../Routing/Loader/Configurator/Traits/AddTrait.php | 2 ++ src/Symfony/Component/Routing/Loader/XmlFileLoader.php | 2 ++ .../Component/Routing/Loader/YamlFileLoader.php | 2 ++ .../Routing/Tests/Loader/AnnotationClassLoaderTest.php | 3 +++ .../Routing/Tests/Loader/PhpFileLoaderTest.php | 10 +++++----- .../Routing/Tests/Loader/XmlFileLoaderTest.php | 3 +++ .../Routing/Tests/Loader/YamlFileLoaderTest.php | 3 +++ 8 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 6390a79f5811..ce61032ac341 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -18,6 +18,7 @@ use Symfony\Component\Routing\Annotation\Route as RouteAnnotation; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCompiler; /** * AnnotationClassLoader loads routing information from a PHP class and its methods. @@ -211,6 +212,7 @@ protected function addRoute(RouteCollection $collection, $annot, $globals, \Refl $this->configureRoute($route, $class, $method, $annot); if (0 !== $locale) { $route->setDefault('_locale', $locale); + $route->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER)); $route->setDefault('_canonical_route', $name); $collection->add($name.'.'.$locale, $route); } else { diff --git a/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php b/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php index 085fde4bc9f4..84899aa2e27f 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php @@ -15,6 +15,7 @@ use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCompiler; trait AddTrait { @@ -67,6 +68,7 @@ final public function add(string $name, $path): RouteConfigurator $routes->add($name.'.'.$locale, $route = $this->createRoute($path)); $this->collection->add($this->name.$name.'.'.$locale, $route); $route->setDefault('_locale', $locale); + $route->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER)); $route->setDefault('_canonical_route', $this->name.$name); } diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index dc208f28e16c..da8f34b293c1 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -16,6 +16,7 @@ use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCompiler; /** * XmlFileLoader loads XML routing files. @@ -129,6 +130,7 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $node, $p foreach ($paths as $locale => $p) { $defaults['_locale'] = $locale; $defaults['_canonical_route'] = $id; + $requirements['_locale'] = preg_quote($locale, RouteCompiler::REGEX_DELIMITER); $route = new Route($p, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); $collection->add($id.'.'.$locale, $route); } diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php index 0de36c93b2e5..868da9bd30b4 100644 --- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCompiler; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Yaml; @@ -140,6 +141,7 @@ protected function parseRoute(RouteCollection $collection, $name, array $config, foreach ($config['path'] as $locale => $path) { $localizedRoute = clone $route; $localizedRoute->setDefault('_locale', $locale); + $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER)); $localizedRoute->setDefault('_canonical_route', $name); $localizedRoute->setPath($path); $collection->add($name.'.'.$locale, $localizedRoute); diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php index 441900775114..c8794e10b674 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -125,6 +125,9 @@ public function testLocalizedPathRoutes() $this->assertCount(2, $routes); $this->assertEquals('/path', $routes->get('action.en')->getPath()); $this->assertEquals('/pad', $routes->get('action.nl')->getPath()); + + $this->assertEquals('nl', $routes->get('action.nl')->getRequirement('_locale')); + $this->assertEquals('en', $routes->get('action.en')->getRequirement('_locale')); } public function testLocalizedPathRoutesWithExplicitPathPropety() diff --git a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php index 71f9df15470a..789848c66021 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php @@ -229,11 +229,11 @@ public function testRoutingI18nConfigurator() $expectedCollection = new RouteCollection(); - $expectedCollection->add('foo.en', (new Route('/glish/foo'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'foo'])); - $expectedCollection->add('bar.en', (new Route('/glish/bar'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'bar'])); - $expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'baz'])); - $expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_foo'])); - $expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_bar'])); + $expectedCollection->add('foo.en', (new Route('/glish/foo'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'foo'])->setRequirement('_locale', 'en')); + $expectedCollection->add('bar.en', (new Route('/glish/bar'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'bar'])->setRequirement('_locale', 'en')); + $expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'baz'])->setRequirement('_locale', 'en')); + $expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_foo'])->setRequirement('_locale', 'fr')); + $expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_bar'])->setRequirement('_locale', 'fr')); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_i18n.php'))); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_i18n.php'))); diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index e27149f93125..66d54fc985c4 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -185,6 +185,9 @@ public function testLocalizedImports() $this->assertEquals('/le-prefix/le-suffix', $routeCollection->get('imported.fr')->getPath()); $this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath()); + + $this->assertEquals('fr', $routeCollection->get('imported.fr')->getRequirement('_locale')); + $this->assertEquals('en', $routeCollection->get('imported.en')->getRequirement('_locale')); } public function testLocalizedImportsOfNotLocalizedRoutes() diff --git a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php index caad0aa978ea..52c21c287fcf 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php @@ -321,6 +321,9 @@ public function testImportingRoutesWithLocales() $this->assertCount(2, $routes); $this->assertEquals('/nl/voorbeeld', $routes->get('imported.nl')->getPath()); $this->assertEquals('/en/example', $routes->get('imported.en')->getPath()); + + $this->assertEquals('nl', $routes->get('imported.nl')->getRequirement('_locale')); + $this->assertEquals('en', $routes->get('imported.en')->getRequirement('_locale')); } public function testImportingNonLocalizedRoutesWithLocales() From 771c642a43fcfe72f0b333d006a890634dcbef2b Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 17 Feb 2020 09:30:34 +0100 Subject: [PATCH 09/25] [PhpUnitBridge] Add compatibility to PHPUnit 9 #35662 --- .../Bridge/PhpUnit/Legacy/CommandForV9.php | 63 +++++++++++++++++++ src/Symfony/Bridge/PhpUnit/TextUI/Command.php | 4 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php new file mode 100644 index 000000000000..aa48ca5f2487 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\TextUI\Command as BaseCommand; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\Registry; +use PHPUnit\TextUI\TestRunner as BaseRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; + +/** + * {@inheritdoc} + * + * @internal + */ +class CommandForV9 extends BaseCommand +{ + /** + * {@inheritdoc} + */ + protected function createRunner(): BaseRunner + { + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : []; + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (isset($this->arguments['configuration'])) { + $configuration = $this->arguments['configuration']; + if (!$configuration instanceof Configuration) { + $configuration = Registry::getInstance()->get($this->arguments['configuration']); + } + foreach ($configuration->listeners() as $registeredListener) { + if ('Symfony\Bridge\PhpUnit\SymfonyTestsListener' === ltrim($registeredListener->className(), '\\')) { + $registeredLocally = true; + break; + } + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = new SymfonyTestsListener(); + } + + return parent::createRunner(); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php index be73e4d2beb5..8690812b56b5 100644 --- a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php +++ b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php @@ -13,8 +13,10 @@ if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) { class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV5', 'Symfony\Bridge\PhpUnit\TextUI\Command'); -} else { +} elseif (version_compare(\PHPUnit\Runner\Version::id(), '9.0.0', '<')) { class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV6', 'Symfony\Bridge\PhpUnit\TextUI\Command'); +} else { + class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV9', 'Symfony\Bridge\PhpUnit\TextUI\Command'); } if (false) { From 8c4de564a8697cf63c49c39501060ffb138f4762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Bogusz?= Date: Sun, 16 Feb 2020 16:22:50 +0100 Subject: [PATCH 10/25] [Validator] Add the missing translations for the Polish ("pl") locale --- .../Resources/translations/validators.pl.xlf | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index f1910c99d575..d1dd1471a77c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -330,10 +330,46 @@ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. Ten kod BIC (Business Identifier Code) nie jest powiązany z międzynarodowym numerem rachunku bankowego (IBAN) {{ iban }}. + + This value should be valid JSON. + Ta wartość powinna być prawidłowym formatem JSON. + + + This collection should contain only unique elements. + Ten zbiór powinien zawierać tylko unikalne elementy. + + + This value should be positive. + Ta wartość powinna być dodatnia. + + + This value should be either positive or zero. + Ta wartość powinna być dodatnia lub równa zero. + + + This value should be negative. + Ta wartość powinna być ujemna. + + + This value should be either negative or zero. + Ta wartość powinna być ujemna lub równa zero. + + + This value is not a valid timezone. + Ta wartość nie jest prawidłową strefą czasową. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + To hasło wyciekło w wyniku naruszenia danych i nie może być użyte. Proszę użyć innego hasła. + This value should be between {{ min }} and {{ max }}. Ta wartość powinna być pomiędzy {{ min }} a {{ max }}. + + This value is not a valid hostname. + Ta wartość nie jest prawidłową nazwą hosta. + From dc11c8d1b8764639ed7d382ff3dfdf6c4011f63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4fer?= Date: Sun, 16 Feb 2020 17:40:22 +0100 Subject: [PATCH 11/25] Docs: Typo, grammar --- .../Component/Security/Core/User/UserInterface.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index 58f6cfb3bb14..fdad6b055b32 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -21,10 +21,10 @@ * password (for checking against a submitted password), assigning roles * and so on. * - * Regardless of how your user are loaded or where they come from (a database, - * configuration, web service, etc), you will have a class that implements - * this interface. Objects that implement this interface are created and - * loaded by different objects that implement UserProviderInterface + * Regardless of how your users are loaded or where they come from (a database, + * configuration, web service, etc.), you will have a class that implements + * this interface. Objects that implement this interface are created and + * loaded by different objects that implement UserProviderInterface. * * @see UserProviderInterface * @see AdvancedUserInterface From 2f65a7a2556b09cbee7e7afb64b4bb432eaa5a7e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 18 Feb 2020 12:08:29 +0100 Subject: [PATCH 12/25] [Config] don't throw on missing excluded paths --- .../Component/DependencyInjection/Loader/FileLoader.php | 2 +- .../DependencyInjection/Tests/Loader/FileLoaderTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 77cad3c046d7..0a800f01c3f7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -110,7 +110,7 @@ private function findClasses($namespace, $pattern, $excludePattern) $excludePrefix = null; if ($excludePattern) { $excludePattern = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePattern)); - foreach ($this->glob($excludePattern, true, $resource) as $path => $info) { + foreach ($this->glob($excludePattern, true, $resource, true) as $path => $info) { if (null === $excludePrefix) { $excludePrefix = $resource->getPrefix(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index ffe58a67ef3b..cbcc6995170c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -134,6 +134,13 @@ public function testRegisterClassesWithExclude() ], array_keys($container->getAliases()) ); + + $loader->registerClasses( + new Definition(), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + 'Prototype/*', + 'Prototype/NotExistingDir' + ); } public function testNestedRegisterClasses() From 63f9e013a1f43b024f2dc73235f41f5076103b3d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 18 Feb 2020 14:07:02 +0100 Subject: [PATCH 13/25] [Ldap] force default network timeout --- src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php index 3ff790d05181..2dcb215b921a 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php @@ -107,6 +107,10 @@ protected function configureOptions(OptionsResolver $resolver) $value['referrals'] = $options['referrals']; } + if (!isset($value['network_timeout'])) { + $value['network_timeout'] = ini_get('default_socket_timeout'); + } + return $value; }); From 1c24ccc6352eba5e81d75d2c8dac01d284ef418d Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Tue, 18 Feb 2020 22:48:57 +0100 Subject: [PATCH 14/25] fix typo --- .../Security/Guard/Provider/GuardAuthenticationProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 26890db36738..ddc8c5029778 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -82,7 +82,7 @@ public function authenticate(TokenInterface $token) return $token; } - // this AccountExpiredException causes the user to be logged out + // this causes the user to be logged out throw new AuthenticationExpiredException(); } From 094e4bbbc2ba86bb4f2e12aefaf522e0c48ddaf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 19 Feb 2020 15:48:16 +0100 Subject: [PATCH 15/25] Do not rely on the current locale when dumping a Graphviz object Funny bug ! With `de_DE.UTF-8` as locale: ``` php > echo sprintf('%s', 0.5); 0,5 php > echo sprintf('%d', 0.5); 0 php > echo sprintf('%f', 0.5); 0,500000 php > echo (string) 0.5; 0,5 ``` So now we force `'0.5'` --- .../Component/DependencyInjection/Dumper/GraphvizDumper.php | 5 +++-- src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php index f06e6e80d503..0591e024f575 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -32,10 +32,11 @@ class GraphvizDumper extends Dumper { private $nodes; private $edges; + // All values should be strings private $options = [ 'graph' => ['ratio' => 'compress'], - 'node' => ['fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'], - 'edge' => ['fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5], + 'node' => ['fontsize' => '11', 'fontname' => 'Arial', 'shape' => 'record'], + 'edge' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => '0.5'], 'node.instance' => ['fillcolor' => '#9999ff', 'style' => 'filled'], 'node.definition' => ['fillcolor' => '#eeeeee'], 'node.missing' => ['fillcolor' => '#ff9999', 'style' => 'filled'], diff --git a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php index c3d673fa91e5..0f6cb3308293 100644 --- a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php @@ -26,10 +26,11 @@ */ class GraphvizDumper implements DumperInterface { + // All values should be strings protected static $defaultOptions = [ 'graph' => ['ratio' => 'compress', 'rankdir' => 'LR'], - 'node' => ['fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1], - 'edge' => ['fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'arrowhead' => 'normal', 'arrowsize' => 0.5], + 'node' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => '#333333', 'fillcolor' => 'lightblue', 'fixedsize' => '1', 'width' => '1'], + 'edge' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => '#333333', 'arrowhead' => 'normal', 'arrowsize' => '0.5'], ]; /** From 018ec1ae5cf6609535c61d7b3631a4d20e28702a Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 19 Feb 2020 23:15:06 +0100 Subject: [PATCH 16/25] [DoctrineBridge][DoctrineExtractor] Fix indexBy with custom and some core types --- .../PropertyInfo/DoctrineExtractor.php | 80 ++++++++++++------- .../PropertyInfo/DoctrineExtractorTest.php | 13 +++ .../PropertyInfo/Fixtures/DoctrineDummy.php | 10 +++ .../Fixtures/DoctrineRelation.php | 10 +++ 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index 1902adc0269a..ea96b59f4504 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -117,7 +117,9 @@ public function getTypes($class, $property, array $context = []) $typeOfField = $subMetadata->getTypeOfField($indexProperty); } - $collectionKeyType = $this->getPhpType($typeOfField); + if (!$collectionKeyType = $this->getPhpType($typeOfField)) { + return null; + } } } @@ -137,39 +139,46 @@ public function getTypes($class, $property, array $context = []) if ($metadata->hasField($property)) { $typeOfField = $metadata->getTypeOfField($property); - $nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property); - - switch ($typeOfField) { - case DBALType::DATE: - case DBALType::DATETIME: - case DBALType::DATETIMETZ: - case 'vardatetime': - case DBALType::TIME: - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')]; - case 'date_immutable': - case 'datetime_immutable': - case 'datetimetz_immutable': - case 'time_immutable': - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTimeImmutable')]; - - case 'dateinterval': - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateInterval')]; - - case DBALType::TARRAY: - return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; + if (!$builtinType = $this->getPhpType($typeOfField)) { + return null; + } - case DBALType::SIMPLE_ARRAY: - return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]; + $nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property); - case DBALType::JSON_ARRAY: - return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; + switch ($builtinType) { + case Type::BUILTIN_TYPE_OBJECT: + switch ($typeOfField) { + case DBALType::DATE: + case DBALType::DATETIME: + case DBALType::DATETIMETZ: + case 'vardatetime': + case DBALType::TIME: + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')]; + + case 'date_immutable': + case 'datetime_immutable': + case 'datetimetz_immutable': + case 'time_immutable': + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTimeImmutable')]; + + case 'dateinterval': + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateInterval')]; + } - default: - $builtinType = $this->getPhpType($typeOfField); + break; + case Type::BUILTIN_TYPE_ARRAY: + switch ($typeOfField) { + case DBALType::TARRAY: + case DBALType::JSON_ARRAY: + return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; - return $builtinType ? [new Type($builtinType, $nullable)] : null; + case DBALType::SIMPLE_ARRAY: + return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]; + } } + + return [new Type($builtinType, $nullable)]; } return null; @@ -234,7 +243,22 @@ private function getPhpType($doctrineType) return Type::BUILTIN_TYPE_RESOURCE; case DBALType::OBJECT: + case DBALType::DATE: + case DBALType::DATETIME: + case DBALType::DATETIMETZ: + case 'vardatetime': + case DBALType::TIME: + case 'date_immutable': + case 'datetime_immutable': + case 'datetimetz_immutable': + case 'time_immutable': + case 'dateinterval': return Type::BUILTIN_TYPE_OBJECT; + + case DBALType::TARRAY: + case DBALType::SIMPLE_ARRAY: + case DBALType::JSON_ARRAY: + return Type::BUILTIN_TYPE_ARRAY; } return null; diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index cad2dfeaac89..9c658a324613 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -11,11 +11,13 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\Setup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation; use Symfony\Component\PropertyInfo\Type; /** @@ -62,6 +64,8 @@ public function testGetProperties() 'bar', 'indexedBar', 'indexedFoo', + 'indexedByDt', + 'indexedByCustomType', ], $this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') ); @@ -153,6 +157,15 @@ public function typesProvider() ['simpleArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]], ['customFoo', null], ['notMapped', null], + ['indexedByDt', [new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + Collection::class, + true, + new Type(Type::BUILTIN_TYPE_OBJECT), + new Type(Type::BUILTIN_TYPE_OBJECT, false, DoctrineRelation::class) + )]], + ['indexedByCustomType', null], ]; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index d02deb15bb7b..c8bd04e4ec5f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -112,4 +112,14 @@ class DoctrineDummy private $bigint; public $notMapped; + + /** + * @OneToMany(targetEntity="DoctrineRelation", mappedBy="dt", indexBy="dt") + */ + protected $indexedByDt; + + /** + * @OneToMany(targetEntity="DoctrineRelation", mappedBy="customType", indexBy="customType") + */ + private $indexedByCustomType; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index 85660d3d6b66..5730cf81dd49 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -39,4 +39,14 @@ class DoctrineRelation * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedFoo") */ protected $foo; + + /** + * @Column(type="datetime") + */ + private $dt; + + /** + * @Column(type="foo") + */ + private $customType; } From e8f3e8495906389c7d70ebb92c6a04639e5d297e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20TAMARELLE?= Date: Wed, 19 Feb 2020 22:39:02 +0100 Subject: [PATCH 17/25] Use strict assertion in asset tests --- .../Tests/Context/RequestStackContextTest.php | 2 +- .../Component/Asset/Tests/PackageTest.php | 4 ++-- .../Component/Asset/Tests/PackagesTest.php | 16 ++++++++-------- .../Component/Asset/Tests/PathPackageTest.php | 6 +++--- .../Component/Asset/Tests/UrlPackageTest.php | 6 +++--- .../VersionStrategy/EmptyVersionStrategyTest.php | 2 +- .../JsonManifestVersionStrategyTest.php | 6 +++--- .../StaticVersionStrategyTest.php | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php b/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php index 7f24534eba20..d9a54c066ec3 100644 --- a/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php +++ b/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php @@ -37,7 +37,7 @@ public function testGetBasePathSet() $requestStackContext = new RequestStackContext($requestStack); - $this->assertEquals($testBasePath, $requestStackContext->getBasePath()); + $this->assertSame($testBasePath, $requestStackContext->getBasePath()); } public function testIsSecureFalse() diff --git a/src/Symfony/Component/Asset/Tests/PackageTest.php b/src/Symfony/Component/Asset/Tests/PackageTest.php index 8f6626ae4d2a..8d7f7b8a26a7 100644 --- a/src/Symfony/Component/Asset/Tests/PackageTest.php +++ b/src/Symfony/Component/Asset/Tests/PackageTest.php @@ -24,7 +24,7 @@ class PackageTest extends TestCase public function testGetUrl($version, $format, $path, $expected) { $package = new Package($version ? new StaticVersionStrategy($version, $format) : new EmptyVersionStrategy()); - $this->assertEquals($expected, $package->getUrl($path)); + $this->assertSame($expected, $package->getUrl($path)); } public function getConfigs() @@ -50,6 +50,6 @@ public function getConfigs() public function testGetVersion() { $package = new Package(new StaticVersionStrategy('v1')); - $this->assertEquals('v1', $package->getVersion('/foo')); + $this->assertSame('v1', $package->getVersion('/foo')); } } diff --git a/src/Symfony/Component/Asset/Tests/PackagesTest.php b/src/Symfony/Component/Asset/Tests/PackagesTest.php index b2d0de375051..4b30e30a5e48 100644 --- a/src/Symfony/Component/Asset/Tests/PackagesTest.php +++ b/src/Symfony/Component/Asset/Tests/PackagesTest.php @@ -24,13 +24,13 @@ public function testGetterSetters() $packages->setDefaultPackage($default = $this->getMockBuilder('Symfony\Component\Asset\PackageInterface')->getMock()); $packages->addPackage('a', $a = $this->getMockBuilder('Symfony\Component\Asset\PackageInterface')->getMock()); - $this->assertEquals($default, $packages->getPackage()); - $this->assertEquals($a, $packages->getPackage('a')); + $this->assertSame($default, $packages->getPackage()); + $this->assertSame($a, $packages->getPackage('a')); $packages = new Packages($default, ['a' => $a]); - $this->assertEquals($default, $packages->getPackage()); - $this->assertEquals($a, $packages->getPackage('a')); + $this->assertSame($default, $packages->getPackage()); + $this->assertSame($a, $packages->getPackage('a')); } public function testGetVersion() @@ -40,8 +40,8 @@ public function testGetVersion() ['a' => new Package(new StaticVersionStrategy('a'))] ); - $this->assertEquals('default', $packages->getVersion('/foo')); - $this->assertEquals('a', $packages->getVersion('/foo', 'a')); + $this->assertSame('default', $packages->getVersion('/foo')); + $this->assertSame('a', $packages->getVersion('/foo', 'a')); } public function testGetUrl() @@ -51,8 +51,8 @@ public function testGetUrl() ['a' => new Package(new StaticVersionStrategy('a'))] ); - $this->assertEquals('/foo?default', $packages->getUrl('/foo')); - $this->assertEquals('/foo?a', $packages->getUrl('/foo', 'a')); + $this->assertSame('/foo?default', $packages->getUrl('/foo')); + $this->assertSame('/foo?a', $packages->getUrl('/foo', 'a')); } public function testNoDefaultPackage() diff --git a/src/Symfony/Component/Asset/Tests/PathPackageTest.php b/src/Symfony/Component/Asset/Tests/PathPackageTest.php index d00cc76c2a94..4d57559847eb 100644 --- a/src/Symfony/Component/Asset/Tests/PathPackageTest.php +++ b/src/Symfony/Component/Asset/Tests/PathPackageTest.php @@ -23,7 +23,7 @@ class PathPackageTest extends TestCase public function testGetUrl($basePath, $format, $path, $expected) { $package = new PathPackage($basePath, new StaticVersionStrategy('v1', $format)); - $this->assertEquals($expected, $package->getUrl($path)); + $this->assertSame($expected, $package->getUrl($path)); } public function getConfigs() @@ -55,7 +55,7 @@ public function testGetUrlWithContext($basePathRequest, $basePath, $format, $pat { $package = new PathPackage($basePath, new StaticVersionStrategy('v1', $format), $this->getContext($basePathRequest)); - $this->assertEquals($expected, $package->getUrl($path)); + $this->assertSame($expected, $package->getUrl($path)); } public function getContextConfigs() @@ -83,7 +83,7 @@ public function testVersionStrategyGivesAbsoluteURL() ->willReturn('https://cdn.com/bar/main.css'); $package = new PathPackage('/subdirectory', $versionStrategy, $this->getContext('/bar')); - $this->assertEquals('https://cdn.com/bar/main.css', $package->getUrl('main.css')); + $this->assertSame('https://cdn.com/bar/main.css', $package->getUrl('main.css')); } private function getContext($basePath) diff --git a/src/Symfony/Component/Asset/Tests/UrlPackageTest.php b/src/Symfony/Component/Asset/Tests/UrlPackageTest.php index 8fc617a68220..196c2ead3b26 100644 --- a/src/Symfony/Component/Asset/Tests/UrlPackageTest.php +++ b/src/Symfony/Component/Asset/Tests/UrlPackageTest.php @@ -24,7 +24,7 @@ class UrlPackageTest extends TestCase public function testGetUrl($baseUrls, $format, $path, $expected) { $package = new UrlPackage($baseUrls, new StaticVersionStrategy('v1', $format)); - $this->assertEquals($expected, $package->getUrl($path)); + $this->assertSame($expected, $package->getUrl($path)); } public function getConfigs() @@ -58,7 +58,7 @@ public function testGetUrlWithContext($secure, $baseUrls, $format, $path, $expec { $package = new UrlPackage($baseUrls, new StaticVersionStrategy('v1', $format), $this->getContext($secure)); - $this->assertEquals($expected, $package->getUrl($path)); + $this->assertSame($expected, $package->getUrl($path)); } public function getContextConfigs() @@ -85,7 +85,7 @@ public function testVersionStrategyGivesAbsoluteURL() ->willReturn('https://cdn.com/bar/main.css'); $package = new UrlPackage('https://example.com', $versionStrategy); - $this->assertEquals('https://cdn.com/bar/main.css', $package->getUrl('main.css')); + $this->assertSame('https://cdn.com/bar/main.css', $package->getUrl('main.css')); } public function testNoBaseUrls() diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php index 430146fd5070..1728c2e99b4d 100644 --- a/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php +++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php @@ -29,6 +29,6 @@ public function testApplyVersion() $emptyVersionStrategy = new EmptyVersionStrategy(); $path = 'test-path'; - $this->assertEquals($path, $emptyVersionStrategy->applyVersion($path)); + $this->assertSame($path, $emptyVersionStrategy->applyVersion($path)); } } diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php index d74f3f632168..ffcc62b995d3 100644 --- a/src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php +++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php @@ -20,21 +20,21 @@ public function testGetVersion() { $strategy = $this->createStrategy('manifest-valid.json'); - $this->assertEquals('main.123abc.js', $strategy->getVersion('main.js')); + $this->assertSame('main.123abc.js', $strategy->getVersion('main.js')); } public function testApplyVersion() { $strategy = $this->createStrategy('manifest-valid.json'); - $this->assertEquals('css/styles.555def.css', $strategy->getVersion('css/styles.css')); + $this->assertSame('css/styles.555def.css', $strategy->getVersion('css/styles.css')); } public function testApplyVersionWhenKeyDoesNotExistInManifest() { $strategy = $this->createStrategy('manifest-valid.json'); - $this->assertEquals('css/other.css', $strategy->getVersion('css/other.css')); + $this->assertSame('css/other.css', $strategy->getVersion('css/other.css')); } public function testMissingManifestFileThrowsException() diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php index c56a8726a845..d054e842f2c5 100644 --- a/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php +++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php @@ -21,7 +21,7 @@ public function testGetVersion() $version = 'v1'; $path = 'test-path'; $staticVersionStrategy = new StaticVersionStrategy($version); - $this->assertEquals($version, $staticVersionStrategy->getVersion($path)); + $this->assertSame($version, $staticVersionStrategy->getVersion($path)); } /** @@ -31,7 +31,7 @@ public function testApplyVersion($path, $version, $format) { $staticVersionStrategy = new StaticVersionStrategy($version, $format); $formatted = sprintf($format ?: '%s?%s', $path, $version); - $this->assertEquals($formatted, $staticVersionStrategy->applyVersion($path)); + $this->assertSame($formatted, $staticVersionStrategy->applyVersion($path)); } public function getConfigs() From 3515793cb353cb30a260ac76b720288e23a1fefe Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 19 Feb 2020 20:56:03 +0100 Subject: [PATCH 18/25] fix remember me --- .../Provider/RememberMeAuthenticationProvider.php | 7 +++++++ .../RememberMeAuthenticationProviderTest.php | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index d9a6883cf9a9..e1a22b79a8b3 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -15,7 +15,9 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; class RememberMeAuthenticationProvider implements AuthenticationProviderInterface { @@ -49,6 +51,11 @@ public function authenticate(TokenInterface $token) } $user = $token->getUser(); + + if (!$token->getUser() instanceof UserInterface) { + throw new LogicException(sprintf('Method "%s::getUser()" must return a "%s" instance, "%s" returned.', \get_class($token), UserInterface::class, \is_object($user) ? \get_class($user) : \gettype($user))); + } + $this->userChecker->checkPreAuth($user); $this->userChecker->checkPostAuth($user); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php index 418cd77df4fc..ce0502269e01 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php @@ -13,8 +13,10 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\User\User; class RememberMeAuthenticationProviderTest extends TestCase { @@ -24,6 +26,7 @@ public function testSupports() $this->assertTrue($provider->supports($this->getSupportedToken())); $this->assertFalse($provider->supports($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); + $this->assertFalse($provider->supports($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken')->disableOriginalConstructor()->getMock())); } public function testAuthenticateWhenTokenIsNotSupported() @@ -45,6 +48,17 @@ public function testAuthenticateWhenSecretsDoNotMatch() $provider->authenticate($token); } + public function testAuthenticateThrowsOnNonUserInterfaceInstance() + { + $this->expectException('Symfony\Component\Security\Core\Exception\LogicException'); + $this->expectExceptionMessage('Method "Symfony\Component\Security\Core\Authentication\Token\RememberMeToken::getUser()" must return a "Symfony\Component\Security\Core\User\UserInterface" instance, "string" returned.'); + + $provider = $this->getProvider(); + $token = new RememberMeToken(new User('dummyuser', null), 'foo', 'test'); + $token->setUser('stringish-user'); + $provider->authenticate($token); + } + public function testAuthenticateWhenPreChecksFails() { $this->expectException('Symfony\Component\Security\Core\Exception\DisabledException'); From 971b177d27ec43fca1ed6c027971190508bc5063 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Thu, 20 Feb 2020 16:09:42 +0000 Subject: [PATCH 19/25] Fix versioned namespace clears When using namespace versioning to achieve atomic cache clears, only delete cache keys matching the old/current version. This resolves tag inconsistency issues whereby the process running the clear would delete keys set against the new version by more recently spawned concurreny processes. Most seriously this could result in newly set data keys remaining, but with empty associated tag sets meaning the invalidation via tags was no longer possible. Clearing specific prefixes is not supported when using versioned namespaces as it is desirable to clear all old keys as they will no longer be used and would otherwise eventually fill cache memory. --- src/Symfony/Component/Cache/Traits/AbstractTrait.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php index f9a1d8fdafa2..7639424ea78c 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractTrait.php @@ -111,9 +111,14 @@ public function hasItem($key) */ public function clear(/*string $prefix = ''*/) { - $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : ''; $this->deferred = []; if ($cleared = $this->versioningIsEnabled) { + if ('' === $namespaceVersionToClear = $this->namespaceVersion) { + foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) { + $namespaceVersionToClear = $v; + } + } + $namespaceToClear = $this->namespace.$namespaceVersionToClear; $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5); try { $cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0); @@ -124,10 +129,13 @@ public function clear(/*string $prefix = ''*/) $this->namespaceVersion = $namespaceVersion; $this->ids = []; } + } else { + $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : ''; + $namespaceToClear = $this->namespace.$prefix; } try { - return $this->doClear($this->namespace.$prefix) || $cleared; + return $this->doClear($namespaceToClear) || $cleared; } catch (\Exception $e) { CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]); From 034e1de6e66a964b3bd4d56bfd78b118b147bcce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcello=20M=C3=B6nkemeyer?= Date: Wed, 19 Feb 2020 14:20:42 +0000 Subject: [PATCH 20/25] [PhpUnitBridge] Use trait instead of extending deprecated class --- .../Bridge/PhpUnit/Legacy/CoverageListenerForV6.php | 7 +++++-- src/Symfony/Bridge/PhpUnit/composer.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV6.php index 0917ea4710c2..1b3ceec161f8 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV6.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV6.php @@ -11,8 +11,9 @@ namespace Symfony\Bridge\PhpUnit\Legacy; -use PHPUnit\Framework\BaseTestListener; use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; /** * CoverageListener adds `@covers ` on each test when possible to @@ -22,8 +23,10 @@ * * @internal */ -class CoverageListenerForV6 extends BaseTestListener +class CoverageListenerForV6 implements TestListener { + use TestListenerDefaultImplementation; + private $trait; public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 25dcb0cd1027..f7d9492613ab 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -24,7 +24,7 @@ "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0" }, "autoload": { "files": [ "bootstrap.php" ], From 0086562c771c2a3e12d901583624e1bbd44aa46e Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Fri, 21 Feb 2020 16:29:16 +0100 Subject: [PATCH 21/25] [Validator] Remove specific check for Valid targets --- src/Symfony/Component/Validator/Mapping/ClassMetadata.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 03b0ae6d8f57..aaa30cddd3b4 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -14,7 +14,6 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\Traverse; -use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\GroupDefinitionException; @@ -183,10 +182,6 @@ public function addConstraint(Constraint $constraint) throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint))); } - if ($constraint instanceof Valid) { - throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint))); - } - if ($constraint instanceof Traverse) { if ($constraint->traverse) { // If traverse is true, traversal should be explicitly enabled From 4b83ae7547da7f5cc0b971208ac50dd3889a48dc Mon Sep 17 00:00:00 2001 From: Andrey Sevastianov Date: Thu, 13 Feb 2020 19:48:28 +0200 Subject: [PATCH 22/25] [ExpressionLanguage] Fixed collisions of character operators with object properties --- .../Component/ExpressionLanguage/Lexer.php | 2 +- .../Resources/bin/generate_operator_regex.php | 10 ++++-- .../Tests/ExpressionLanguageTest.php | 11 +++++++ .../ExpressionLanguage/Tests/LexerTest.php | 12 +++++++ .../ExpressionLanguage/Tests/ParserTest.php | 33 +++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/Lexer.php b/src/Symfony/Component/ExpressionLanguage/Lexer.php index 48b15ffe7c23..01ca29301836 100644 --- a/src/Symfony/Component/ExpressionLanguage/Lexer.php +++ b/src/Symfony/Component/ExpressionLanguage/Lexer.php @@ -73,7 +73,7 @@ public function tokenize($expression) // strings $tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1); $cursor += \strlen($match[0]); - } elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) { + } elseif (preg_match('/(?<=^|[\s(])not in(?=[\s(])|\!\=\=|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\>\=|(?<=^|[\s(])or(?=[\s(])|\<\=|\*\*|\.\.|(?<=^|[\s(])in(?=[\s(])|&&|\|\||(?<=^|[\s(])matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) { // operators $tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1); $cursor += \strlen($match[0]); diff --git a/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php b/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php index e1196c7f514a..c86e96252618 100644 --- a/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php +++ b/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php @@ -15,9 +15,13 @@ $regex = []; foreach ($operators as $operator => $length) { - // an operator that ends with a character must be followed by - // a whitespace or a parenthesis - $regex[] = preg_quote($operator, '/').(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : ''); + // Collisions of character operators: + // - an operator that begins with a character must have a space or a parenthesis before or starting at the beginning of a string + // - an operator that ends with a character must be followed by a whitespace or a parenthesis + $regex[] = + (ctype_alpha($operator[0]) ? '(?<=^|[\s(])' : '') + .preg_quote($operator, '/') + .(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : ''); } echo '/'.implode('|', $regex).'/A'; diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index f69ee57982e8..fc2f80e17b3a 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -233,6 +233,17 @@ public function testCachingWithDifferentNamesOrder() $expressionLanguage->compile($expression, ['B' => 'b', 'a']); } + public function testOperatorCollisions() + { + $expressionLanguage = new ExpressionLanguage(); + $expression = 'foo.not in [bar]'; + $compiled = $expressionLanguage->compile($expression, ['foo', 'bar']); + $this->assertSame('in_array($foo->not, [0 => $bar])', $compiled); + + $result = $expressionLanguage->evaluate($expression, ['foo' => (object) ['not' => 'test'], 'bar' => 'test']); + $this->assertTrue($result); + } + /** * @dataProvider getRegisterCallbacks */ diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php index 9ab6b1e843ce..6c3d1a7d2519 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php @@ -112,6 +112,18 @@ public function getTokenizeData() [new Token('string', '#foo', 1)], '"#foo"', ], + [ + [ + new Token('name', 'foo', 1), + new Token('punctuation', '.', 4), + new Token('name', 'not', 5), + new Token('operator', 'in', 9), + new Token('punctuation', '[', 12), + new Token('name', 'bar', 13), + new Token('punctuation', ']', 16), + ], + 'foo.not in [bar]', + ], ]; } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php index 84b30dc151cf..2d5a0a6c8c81 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php @@ -53,6 +53,9 @@ public function getParseData() $arguments->addElement(new Node\ConstantNode(2)); $arguments->addElement(new Node\ConstantNode(true)); + $arrayNode = new Node\ArrayNode(); + $arrayNode->addElement(new Node\NameNode('bar')); + return [ [ new Node\NameNode('a'), @@ -151,6 +154,36 @@ public function getParseData() 'bar', ['foo' => 'bar'], ], + + // Operators collisions + [ + new Node\BinaryNode( + 'in', + new Node\GetAttrNode( + new Node\NameNode('foo'), + new Node\ConstantNode('not', true), + new Node\ArgumentsNode(), + Node\GetAttrNode::PROPERTY_CALL + ), + $arrayNode + ), + 'foo.not in [bar]', + ['foo', 'bar'], + ], + [ + new Node\BinaryNode( + 'or', + new Node\UnaryNode('not', new Node\NameNode('foo')), + new Node\GetAttrNode( + new Node\NameNode('foo'), + new Node\ConstantNode('not', true), + new Node\ArgumentsNode(), + Node\GetAttrNode::PROPERTY_CALL + ) + ), + 'not foo or foo.not', + ['foo'], + ], ]; } From e15f05e03fd9891fcc256dc781bd272f8123ab79 Mon Sep 17 00:00:00 2001 From: Anna Filina Date: Sat, 22 Feb 2020 09:12:42 -0500 Subject: [PATCH 23/25] [BrowserKit] Nested file array prevents uploading file --- .../Component/BrowserKit/HttpBrowser.php | 35 ++++--- .../BrowserKit/Tests/HttpBrowserTest.php | 94 ++++++++++++++++++- 2 files changed, 114 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/BrowserKit/HttpBrowser.php b/src/Symfony/Component/BrowserKit/HttpBrowser.php index b2331ea492a8..a1e6dd9af011 100644 --- a/src/Symfony/Component/BrowserKit/HttpBrowser.php +++ b/src/Symfony/Component/BrowserKit/HttpBrowser.php @@ -73,18 +73,9 @@ private function getBodyAndExtraHeaders(Request $request): array } $fields = $request->getParameters(); - $hasFile = false; - foreach ($request->getFiles() as $name => $file) { - if (!isset($file['tmp_name'])) { - continue; - } - - $hasFile = true; - $fields[$name] = DataPart::fromPath($file['tmp_name'], $file['name']); - } - if ($hasFile) { - $part = new FormDataPart($fields); + if ($uploadedFiles = $this->getUploadedFiles($request->getFiles())) { + $part = new FormDataPart($uploadedFiles); return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()]; } @@ -119,4 +110,26 @@ private function getHeaders(Request $request): array return $headers; } + + /** + * Recursively go through the list. If the file has a tmp_name, convert it to a DataPart. + * Keep the original hierarchy. + */ + private function getUploadedFiles(array $files): array + { + $uploadedFiles = []; + foreach ($files as $name => $file) { + if (!\is_array($file)) { + return $uploadedFiles; + } + if (!isset($file['tmp_name'])) { + $uploadedFiles[$name] = $this->getUploadedFiles($file); + } + if (isset($file['tmp_name'])) { + $uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']); + } + } + + return $uploadedFiles; + } } diff --git a/src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php b/src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php index 44eed997bdee..fa3d531aa986 100644 --- a/src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php @@ -27,17 +27,17 @@ public function getBrowser(array $server = [], History $history = null, CookieJa /** * @dataProvider validContentTypes */ - public function testRequestHeaders(array $request, array $exepectedCall) + public function testRequestHeaders(array $requestArguments, array $expectedArguments) { $client = $this->createMock(HttpClientInterface::class); $client ->expects($this->once()) ->method('request') - ->with(...$exepectedCall) + ->with(...$expectedArguments) ->willReturn($this->createMock(ResponseInterface::class)); $browser = new HttpBrowser($client); - $browser->request(...$request); + $browser->request(...$requestArguments); } public function validContentTypes() @@ -61,7 +61,7 @@ public function validContentTypes() ]; } - public function testMultiPartRequest() + public function testMultiPartRequestWithSingleFile() { $client = $this->createMock(HttpClientInterface::class); $client @@ -81,4 +81,90 @@ public function testMultiPartRequest() file_put_contents($path, 'my_file'); $browser->request('POST', 'http://example.com/', [], ['file' => ['tmp_name' => $path, 'name' => 'foo']]); } + + public function testMultiPartRequestWithNormalFlatArray() + { + $client = $this->createMock(HttpClientInterface::class); + $this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']); + + $browser = new HttpBrowser($client); + $browser->request('POST', 'http://example.com/', [], [ + 'file1' => $this->getUploadedFile('file1'), + 'file2' => $this->getUploadedFile('file2'), + ]); + } + + public function testMultiPartRequestWithNormalNestedArray() + { + $client = $this->createMock(HttpClientInterface::class); + $this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']); + + $browser = new HttpBrowser($client); + $browser->request('POST', 'http://example.com/', [], [ + 'level1' => [ + 'level2' => [ + 'file1' => $this->getUploadedFile('file1'), + 'file2' => $this->getUploadedFile('file2'), + ], + ], + ]); + } + + public function testMultiPartRequestWithBracketedArray() + { + $client = $this->createMock(HttpClientInterface::class); + $this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']); + + $browser = new HttpBrowser($client); + $browser->request('POST', 'http://example.com/', [], [ + 'form[file1]' => $this->getUploadedFile('file1'), + 'form[file2]' => $this->getUploadedFile('file2'), + ]); + } + + public function testMultiPartRequestWithInvalidItem() + { + $client = $this->createMock(HttpClientInterface::class); + $this->expectClientToSendRequestWithFiles($client, ['file1_content']); + + $browser = new HttpBrowser($client); + $browser->request('POST', 'http://example.com/', [], [ + 'file1' => $this->getUploadedFile('file1'), + 'file2' => 'INVALID', + ]); + } + + private function uploadFile(string $data): string + { + $path = tempnam(sys_get_temp_dir(), 'http'); + file_put_contents($path, $data); + + return $path; + } + + private function getUploadedFile(string $name): array + { + return [ + 'tmp_name' => $this->uploadFile($name.'_content'), + 'name' => $name.'_name', + ]; + } + + protected function expectClientToSendRequestWithFiles(HttpClientInterface $client, $fileContents) + { + $client + ->expects($this->once()) + ->method('request') + ->with('POST', 'http://example.com/', $this->callback(function ($options) use ($fileContents) { + $this->assertStringContainsString('Content-Type: multipart/form-data', implode('', $options['headers'])); + $this->assertInstanceOf('\Generator', $options['body']); + $body = implode('', iterator_to_array($options['body'], false)); + foreach ($fileContents as $content) { + $this->assertStringContainsString($content, $body); + } + + return true; + })) + ->willReturn($this->createMock(ResponseInterface::class)); + } } From 83d4aa7683cee0b33dedc09402bfa82a77c85371 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sun, 23 Feb 2020 12:54:43 +0100 Subject: [PATCH 24/25] [Debug][ErrorHandler] improved deprecation notices for methods new args and return type --- src/Symfony/Component/Debug/DebugClassLoader.php | 2 +- .../Debug/Tests/DebugClassLoaderTest.php | 12 ++++++------ .../Component/ErrorHandler/DebugClassLoader.php | 4 ++-- .../ErrorHandler/Tests/DebugClassLoaderTest.php | 16 ++++++++-------- src/Symfony/Component/ErrorHandler/composer.json | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 9e071fc7600a..3ff450b70058 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -391,7 +391,7 @@ public function checkAnnotations(\ReflectionClass $refl, $class) foreach ($matches as list(, $parameterType, $parameterName)) { if (!isset($definedParameters[$parameterName])) { $parameterType = trim($parameterType); - self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, $method->class); + self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($class) ? 'interface' : 'parent class', $method->class); } } } diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index 5ef3cb05c583..c946607959f6 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -292,12 +292,12 @@ class_exists(Fixtures\SubClassWithAnnotatedParameters::class, true); $this->assertSame([ 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', ], $deprecations); } diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php index ce6a86a2ab33..9e410ccc3d63 100644 --- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php +++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php @@ -607,7 +607,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array if ($canAddReturnType && 'docblock' === $this->patchTypes['force'] && false === strpos($method->getFileName(), \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR)) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } elseif ('' !== $declaringClass && $this->patchTypes['deprecations']) { - $deprecations[] = sprintf('Method "%s::%s()" will return "%s" as of its next major version. Doing the same in child class "%s" will be required when upgrading.', $declaringClass, $method->name, $normalizedType, $className); + $deprecations[] = sprintf('Method "%s::%s()" will return "%s" as of its next major version. Doing the same in %s "%s" will be required when upgrading.', $declaringClass, $method->name, $normalizedType, interface_exists($declaringClass) ? 'implementation' : 'child class', $className); } } } @@ -664,7 +664,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array foreach ($matches as list(, $parameterType, $parameterName)) { if (!isset($definedParameters[$parameterName])) { $parameterType = trim($parameterType); - self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, $className); + self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); } } } diff --git a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php index 5394854738af..adf55432ee39 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php @@ -289,12 +289,12 @@ class_exists(Fixtures\SubClassWithAnnotatedParameters::class, true); $this->assertSame([ 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', ], $deprecations); } @@ -371,8 +371,8 @@ class_exists('Test\\'.ReturnType::class, true); $this->assertSame([ 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeGrandParent::returnTypeGrandParent()" will return "string" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentInterface::returnTypeParentInterface()" will return "string" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeInterface::returnTypeInterface()" will return "string" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentInterface::returnTypeParentInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeInterface::returnTypeInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableType()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableTypeWithNull()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNullableReturnableType()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', diff --git a/src/Symfony/Component/ErrorHandler/composer.json b/src/Symfony/Component/ErrorHandler/composer.json index cfe822f4e8b3..614bd4f5ac1f 100644 --- a/src/Symfony/Component/ErrorHandler/composer.json +++ b/src/Symfony/Component/ErrorHandler/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.1.3", "psr/log": "~1.0", - "symfony/debug": "^4.4", + "symfony/debug": "^4.4.5", "symfony/var-dumper": "^4.4|^5.0" }, "require-dev": { From 3e35fa59ea443616b79eaa3e0e99e924c969db61 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 21 Feb 2020 17:23:44 +0100 Subject: [PATCH 25/25] [DoctrineBridge] Use new Types::* constants and support new json type --- .../Doctrine/Form/DoctrineOrmTypeGuesser.php | 39 ++++++----- .../PropertyInfo/DoctrineExtractor.php | 59 +++++++++------- .../RememberMe/DoctrineTokenProvider.php | 13 +++- .../PropertyInfo/DoctrineExtractorTest.php | 69 ++++++++++++------- .../PropertyInfo/Fixtures/DoctrineDummy.php | 2 +- .../Fixtures/DoctrineDummy210.php | 30 ++++++++ 6 files changed, 142 insertions(+), 70 deletions(-) create mode 100644 src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy210.php diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index ff4b54d24bdc..4ab64b428c73 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -15,6 +15,7 @@ use Doctrine\Common\Persistence\Mapping\MappingException as LegacyCommonMappingException; use Doctrine\Common\Util\ClassUtils; use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\MappingException as LegacyMappingException; use Doctrine\Persistence\ManagerRegistry; @@ -30,12 +31,18 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface private $cache = []; + private static $useDeprecatedConstants; + /** * @param ManagerRegistry|LegacyManagerRegistry $registry */ public function __construct($registry) { $this->registry = $registry; + + if (null === self::$useDeprecatedConstants) { + self::$useDeprecatedConstants = !class_exists(Types::class); + } } /** @@ -57,30 +64,30 @@ public function guessType($class, $property) } switch ($metadata->getTypeOfField($property)) { - case Type::TARRAY: + case self::$useDeprecatedConstants ? Type::TARRAY : 'array': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CollectionType', [], Guess::MEDIUM_CONFIDENCE); - case Type::BOOLEAN: + case self::$useDeprecatedConstants ? Type::BOOLEAN : Types::BOOLEAN: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CheckboxType', [], Guess::HIGH_CONFIDENCE); - case Type::DATETIME: - case Type::DATETIMETZ: + case self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE: + case self::$useDeprecatedConstants ? Type::DATETIMETZ : Types::DATETIMETZ_MUTABLE: case 'vardatetime': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', [], Guess::HIGH_CONFIDENCE); case 'dateinterval': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateIntervalType', [], Guess::HIGH_CONFIDENCE); - case Type::DATE: + case self::$useDeprecatedConstants ? Type::DATE : Types::DATE_MUTABLE: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', [], Guess::HIGH_CONFIDENCE); - case Type::TIME: + case self::$useDeprecatedConstants ? Type::TIME : Types::TIME_MUTABLE: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', [], Guess::HIGH_CONFIDENCE); - case Type::DECIMAL: - case Type::FLOAT: + case self::$useDeprecatedConstants ? Type::DECIMAL : Types::DECIMAL: + case self::$useDeprecatedConstants ? Type::FLOAT : Types::FLOAT: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', [], Guess::MEDIUM_CONFIDENCE); - case Type::INTEGER: - case Type::BIGINT: - case Type::SMALLINT: + case self::$useDeprecatedConstants ? Type::INTEGER : Types::INTEGER: + case self::$useDeprecatedConstants ? Type::BIGINT : Types::BIGINT: + case self::$useDeprecatedConstants ? Type::SMALLINT : Types::SMALLINT: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\IntegerType', [], Guess::MEDIUM_CONFIDENCE); - case Type::STRING: + case self::$useDeprecatedConstants ? Type::STRING : Types::STRING: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::MEDIUM_CONFIDENCE); - case Type::TEXT: + case self::$useDeprecatedConstants ? Type::TEXT : Types::TEXT: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextareaType', [], Guess::MEDIUM_CONFIDENCE); default: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::LOW_CONFIDENCE); @@ -103,7 +110,7 @@ public function guessRequired($class, $property) // Check whether the field exists and is nullable or not if (isset($classMetadata->fieldMappings[$property])) { - if (!$classMetadata->isNullable($property) && Type::BOOLEAN !== $classMetadata->getTypeOfField($property)) { + if (!$classMetadata->isNullable($property) && (self::$useDeprecatedConstants ? Type::BOOLEAN : Types::BOOLEAN) !== $classMetadata->getTypeOfField($property)) { return new ValueGuess(true, Guess::HIGH_CONFIDENCE); } @@ -140,7 +147,7 @@ public function guessMaxLength($class, $property) return new ValueGuess($mapping['length'], Guess::HIGH_CONFIDENCE); } - if (\in_array($ret[0]->getTypeOfField($property), [Type::DECIMAL, Type::FLOAT])) { + if (\in_array($ret[0]->getTypeOfField($property), self::$useDeprecatedConstants ? [Type::DECIMAL, Type::FLOAT] : [Types::DECIMAL, Types::FLOAT])) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } @@ -155,7 +162,7 @@ public function guessPattern($class, $property) { $ret = $this->getMetadata($class); if ($ret && isset($ret[0]->fieldMappings[$property]) && !$ret[0]->hasAssociation($property)) { - if (\in_array($ret[0]->getTypeOfField($property), [Type::DECIMAL, Type::FLOAT])) { + if (\in_array($ret[0]->getTypeOfField($property), self::$useDeprecatedConstants ? [Type::DECIMAL, Type::FLOAT] : [Types::DECIMAL, Types::FLOAT])) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index ea96b59f4504..4ceca2c6acae 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -14,6 +14,7 @@ use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as LegacyClassMetadataFactory; use Doctrine\Common\Persistence\Mapping\MappingException as LegacyMappingException; use Doctrine\DBAL\Types\Type as DBALType; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\MappingException as OrmMappingException; use Doctrine\Persistence\Mapping\ClassMetadataFactory; @@ -31,12 +32,18 @@ class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeE { private $classMetadataFactory; + private static $useDeprecatedConstants; + /** * @param ClassMetadataFactory|LegacyClassMetadataFactory $classMetadataFactory */ public function __construct($classMetadataFactory) { $this->classMetadataFactory = $classMetadataFactory; + + if (null === self::$useDeprecatedConstants) { + self::$useDeprecatedConstants = !class_exists(Types::class); + } } /** @@ -149,11 +156,11 @@ public function getTypes($class, $property, array $context = []) switch ($builtinType) { case Type::BUILTIN_TYPE_OBJECT: switch ($typeOfField) { - case DBALType::DATE: - case DBALType::DATETIME: - case DBALType::DATETIMETZ: + case self::$useDeprecatedConstants ? DBALType::DATE : Types::DATE_MUTABLE: + case self::$useDeprecatedConstants ? DBALType::DATETIME : Types::DATETIME_MUTABLE: + case self::$useDeprecatedConstants ? DBALType::DATETIMETZ : Types::DATETIMETZ_MUTABLE: case 'vardatetime': - case DBALType::TIME: + case self::$useDeprecatedConstants ? DBALType::TIME : Types::TIME_MUTABLE: return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')]; case 'date_immutable': @@ -169,11 +176,12 @@ public function getTypes($class, $property, array $context = []) break; case Type::BUILTIN_TYPE_ARRAY: switch ($typeOfField) { - case DBALType::TARRAY: - case DBALType::JSON_ARRAY: + case self::$useDeprecatedConstants ? DBALType::TARRAY : 'array': + case 'json_array': + case self::$useDeprecatedConstants ? false : Types::JSON: return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; - case DBALType::SIMPLE_ARRAY: + case self::$useDeprecatedConstants ? DBALType::SIMPLE_ARRAY : Types::SIMPLE_ARRAY: return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]; } } @@ -221,33 +229,33 @@ private function isAssociationNullable(array $associationMapping) private function getPhpType($doctrineType) { switch ($doctrineType) { - case DBALType::SMALLINT: - case DBALType::INTEGER: + case self::$useDeprecatedConstants ? DBALType::SMALLINT : Types::SMALLINT: + case self::$useDeprecatedConstants ? DBALType::INTEGER : Types::INTEGER: return Type::BUILTIN_TYPE_INT; - case DBALType::FLOAT: + case self::$useDeprecatedConstants ? DBALType::FLOAT : Types::FLOAT: return Type::BUILTIN_TYPE_FLOAT; - case DBALType::BIGINT: - case DBALType::STRING: - case DBALType::TEXT: - case DBALType::GUID: - case DBALType::DECIMAL: + case self::$useDeprecatedConstants ? DBALType::BIGINT : Types::BIGINT: + case self::$useDeprecatedConstants ? DBALType::STRING : Types::STRING: + case self::$useDeprecatedConstants ? DBALType::TEXT : Types::TEXT: + case self::$useDeprecatedConstants ? DBALType::GUID : Types::GUID: + case self::$useDeprecatedConstants ? DBALType::DECIMAL : Types::DECIMAL: return Type::BUILTIN_TYPE_STRING; - case DBALType::BOOLEAN: + case self::$useDeprecatedConstants ? DBALType::BOOLEAN : Types::BOOLEAN: return Type::BUILTIN_TYPE_BOOL; - case DBALType::BLOB: + case self::$useDeprecatedConstants ? DBALType::BLOB : Types::BLOB: case 'binary': return Type::BUILTIN_TYPE_RESOURCE; - case DBALType::OBJECT: - case DBALType::DATE: - case DBALType::DATETIME: - case DBALType::DATETIMETZ: + case self::$useDeprecatedConstants ? DBALType::OBJECT : Types::OBJECT: + case self::$useDeprecatedConstants ? DBALType::DATE : Types::DATE_MUTABLE: + case self::$useDeprecatedConstants ? DBALType::DATETIME : Types::DATETIME_MUTABLE: + case self::$useDeprecatedConstants ? DBALType::DATETIMETZ : Types::DATETIMETZ_MUTABLE: case 'vardatetime': - case DBALType::TIME: + case self::$useDeprecatedConstants ? DBALType::TIME : Types::TIME_MUTABLE: case 'date_immutable': case 'datetime_immutable': case 'datetimetz_immutable': @@ -255,9 +263,10 @@ private function getPhpType($doctrineType) case 'dateinterval': return Type::BUILTIN_TYPE_OBJECT; - case DBALType::TARRAY: - case DBALType::SIMPLE_ARRAY: - case DBALType::JSON_ARRAY: + case self::$useDeprecatedConstants ? DBALType::TARRAY : 'array': + case self::$useDeprecatedConstants ? DBALType::SIMPLE_ARRAY : Types::SIMPLE_ARRAY: + case 'json_array': + case self::$useDeprecatedConstants ? false : Types::JSON: return Type::BUILTIN_TYPE_ARRAY; } diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index 64515fac7184..ef0612df3128 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -12,7 +12,8 @@ namespace Symfony\Bridge\Doctrine\Security\RememberMe; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Types\Type as DoctrineType; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; @@ -40,9 +41,15 @@ class DoctrineTokenProvider implements TokenProviderInterface { private $conn; + private static $useDeprecatedConstants; + public function __construct(Connection $conn) { $this->conn = $conn; + + if (null === self::$useDeprecatedConstants) { + self::$useDeprecatedConstants = !class_exists(Types::class); + } } /** @@ -90,7 +97,7 @@ public function updateToken($series, $tokenValue, \DateTime $lastUsed) ]; $paramTypes = [ 'value' => \PDO::PARAM_STR, - 'lastUsed' => DoctrineType::DATETIME, + 'lastUsed' => self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE, 'series' => \PDO::PARAM_STR, ]; $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes); @@ -119,7 +126,7 @@ public function createNewToken(PersistentTokenInterface $token) 'username' => \PDO::PARAM_STR, 'series' => \PDO::PARAM_STR, 'value' => \PDO::PARAM_STR, - 'lastUsed' => DoctrineType::DATETIME, + 'lastUsed' => self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE, ]; $this->conn->executeUpdate($sql, $paramValues, $paramTypes); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 9c658a324613..c4efb6c93610 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -13,10 +13,12 @@ use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Type as DBALType; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\Setup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy210; use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation; use Symfony\Component\PropertyInfo\Type; @@ -45,29 +47,40 @@ protected function setUp() public function testGetProperties() { + // Fields + $expected = [ + 'id', + 'guid', + 'time', + 'timeImmutable', + 'dateInterval', + 'jsonArray', + 'simpleArray', + 'float', + 'decimal', + 'bool', + 'binary', + 'customFoo', + 'bigint', + ]; + + if (class_exists(Types::class)) { + $expected[] = 'json'; + } + + // Associations + $expected = array_merge($expected, [ + 'foo', + 'bar', + 'indexedBar', + 'indexedFoo', + 'indexedByDt', + 'indexedByCustomType', + ]); + $this->assertEquals( - [ - 'id', - 'guid', - 'time', - 'timeImmutable', - 'dateInterval', - 'json', - 'simpleArray', - 'float', - 'decimal', - 'bool', - 'binary', - 'customFoo', - 'bigint', - 'foo', - 'bar', - 'indexedBar', - 'indexedFoo', - 'indexedByDt', - 'indexedByCustomType', - ], - $this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') + $expected, + $this->extractor->getProperties(!class_exists(Types::class) ? 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy' : DoctrineDummy210::class) ); } @@ -91,7 +104,7 @@ public function testGetPropertiesWithEmbedded() */ public function testExtract($property, array $type = null) { - $this->assertEquals($type, $this->extractor->getTypes('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy', $property, [])); + $this->assertEquals($type, $this->extractor->getTypes(!class_exists(Types::class) ? 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy' : DoctrineDummy210::class, $property, [])); } public function testExtractWithEmbedded() @@ -117,7 +130,7 @@ public function testExtractWithEmbedded() public function typesProvider() { - return [ + $provider = [ ['id', [new Type(Type::BUILTIN_TYPE_INT)]], ['guid', [new Type(Type::BUILTIN_TYPE_STRING)]], ['bigint', [new Type(Type::BUILTIN_TYPE_STRING)]], @@ -128,7 +141,7 @@ public function typesProvider() ['decimal', [new Type(Type::BUILTIN_TYPE_STRING)]], ['bool', [new Type(Type::BUILTIN_TYPE_BOOL)]], ['binary', [new Type(Type::BUILTIN_TYPE_RESOURCE)]], - ['json', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]], + ['jsonArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]], ['foo', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation')]], ['bar', [new Type( Type::BUILTIN_TYPE_OBJECT, @@ -167,6 +180,12 @@ public function typesProvider() )]], ['indexedByCustomType', null], ]; + + if (class_exists(Types::class)) { + $provider[] = ['json', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]]; + } + + return $provider; } public function testGetPropertiesCatchException() diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index c8bd04e4ec5f..81264fad27c5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -74,7 +74,7 @@ class DoctrineDummy /** * @Column(type="json_array") */ - private $json; + private $jsonArray; /** * @Column(type="simple_array") diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy210.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy210.php new file mode 100644 index 000000000000..d3916143deab --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy210.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\ManyToMany; +use Doctrine\ORM\Mapping\ManyToOne; +use Doctrine\ORM\Mapping\OneToMany; + +/** + * @Entity + */ +final class DoctrineDummy210 extends DoctrineDummy +{ + /** + * @Column(type="json", nullable=true) + */ + private $json; +}