From 3d0d59de20a5ee2cc2bb4d6038b26577072463cd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 13 Jun 2020 17:57:51 +0200 Subject: [PATCH] [FrameworkBundle] preserve dots in query-string when redirecting --- .../Controller/RedirectController.php | 57 ++++++++++++++++++- .../Controller/RedirectControllerTest.php | 22 ++++--- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index 67be757db3..58f06d5df9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -62,7 +62,17 @@ class RedirectController $attributes = []; if (false === $ignoreAttributes || \is_array($ignoreAttributes)) { $attributes = $request->attributes->get('_route_params'); - $attributes = $keepQueryParams ? array_merge($request->query->all(), $attributes) : $attributes; + + if ($keepQueryParams) { + if ($query = $request->server->get('QUERY_STRING')) { + $query = self::parseQuery($query); + } else { + $query = $request->query->all(); + } + + $attributes = array_merge($query, $attributes); + } + unset($attributes['route'], $attributes['permanent'], $attributes['ignoreAttributes'], $attributes['keepRequestMethod'], $attributes['keepQueryParams']); if ($ignoreAttributes) { $attributes = array_diff_key($attributes, array_flip($ignoreAttributes)); @@ -175,4 +185,49 @@ class RedirectController throw new \RuntimeException(sprintf('The parameter "path" or "route" is required to configure the redirect action in "%s" routing configuration.', $request->attributes->get('_route'))); } + + private static function parseQuery(string $query) + { + $q = []; + + foreach (explode('&', $query) as $v) { + if (false !== $i = strpos($v, "\0")) { + $v = substr($v, 0, $i); + } + + if (false === $i = strpos($v, '=')) { + $k = urldecode($v); + $v = ''; + } else { + $k = urldecode(substr($v, 0, $i)); + $v = substr($v, $i); + } + + if (false !== $i = strpos($k, "\0")) { + $k = substr($k, 0, $i); + } + + $k = ltrim($k, ' '); + + if (false === $i = strpos($k, '[')) { + $q[] = bin2hex($k).$v; + } else { + $q[] = substr_replace($k, bin2hex(substr($k, 0, $i)), 0, $i).$v; + } + } + + parse_str(implode('&', $q), $q); + + $query = []; + + foreach ($q as $k => $v) { + if (false !== $i = strpos($k, '_')) { + $query[substr_replace($k, hex2bin(substr($k, 0, $i)).'[', 0, 1 + $i)] = $v; + } else { + $query[hex2bin($k)] = $v; + } + } + + return $query; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 5920cfc37a..732ac3e2d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -302,17 +302,16 @@ class RedirectControllerTest extends TestCase $baseUrl = '/base'; $port = 80; - $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'base=zaza'); - $request->query = new ParameterBag(['base' => 'zaza']); + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'b.se=zaza'); $request->attributes = new ParameterBag(['_route_params' => ['base2' => 'zaza']]); $urlGenerator = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); - $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?base=zaza&base2=zaza')->with('/test', ['base' => 'zaza', 'base2' => 'zaza'], UrlGeneratorInterface::ABSOLUTE_URL); + $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?b.se=zaza&base2=zaza')->with('/test', ['b.se' => 'zaza', 'base2' => 'zaza'], UrlGeneratorInterface::ABSOLUTE_URL); $controller = new RedirectController($urlGenerator); - $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?base=zaza&base2=zaza'); + $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?b.se=zaza&base2=zaza'); $request->attributes->set('_route_params', ['base2' => 'zaza', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); - $this->assertRedirectUrl($controller($request), '/test?base=zaza&base2=zaza'); + $this->assertRedirectUrl($controller($request), '/test?b.se=zaza&base2=zaza'); } public function testRedirectWithQueryWithRouteParamsOveriding() @@ -322,17 +321,16 @@ class RedirectControllerTest extends TestCase $baseUrl = '/base'; $port = 80; - $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'base=zaza'); - $request->query = new ParameterBag(['base' => 'zaza']); - $request->attributes = new ParameterBag(['_route_params' => ['base' => 'zouzou']]); + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'b.se=zaza'); + $request->attributes = new ParameterBag(['_route_params' => ['b.se' => 'zouzou']]); $urlGenerator = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); - $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?base=zouzou')->with('/test', ['base' => 'zouzou'], UrlGeneratorInterface::ABSOLUTE_URL); + $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?b.se=zouzou')->with('/test', ['b.se' => 'zouzou'], UrlGeneratorInterface::ABSOLUTE_URL); $controller = new RedirectController($urlGenerator); - $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?base=zouzou'); + $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?b.se=zouzou'); - $request->attributes->set('_route_params', ['base' => 'zouzou', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); - $this->assertRedirectUrl($controller($request), '/test?base=zouzou'); + $request->attributes->set('_route_params', ['b.se' => 'zouzou', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); + $this->assertRedirectUrl($controller($request), '/test?b.se=zouzou'); } public function testMissingPathOrRouteParameter()