bug #37271 [FrameworkBundle] preserve dots in query-string when redirecting (nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[FrameworkBundle] preserve dots in query-string when redirecting

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #29664
| License       | MIT
| Doc PR        | -

Requires #37270 to pass.

Commits
-------

3d0d59de20 [FrameworkBundle] preserve dots in query-string when redirecting
This commit is contained in:
Nicolas Grekas 2020-06-18 20:03:08 +02:00
commit 8ec9770b68
2 changed files with 66 additions and 13 deletions

View File

@ -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;
}
}

View File

@ -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()