Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 85383c1

Browse files
[HttpFoundation] Add parse_str()-based methods for query strings normalization
1 parent b2fafc6 commit 85383c1

File tree

4 files changed

+101
-4
lines changed

4 files changed

+101
-4
lines changed

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,9 +524,11 @@ public function __toString()
524524
*/
525525
public function overrideGlobals()
526526
{
527-
$this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
527+
$query = $this->query->all();
528+
ksort($query);
529+
$this->server->set('QUERY_STRING', http_build_query($query, '', '&', PHP_QUERY_RFC3986));
528530

529-
$_GET = $this->query->all();
531+
$_GET = $query;
530532
$_POST = $this->request->all();
531533
$_SERVER = $this->server->all();
532534
$_COOKIE = $this->cookies->all();
@@ -656,6 +658,24 @@ public static function normalizeQueryString($qs)
656658
return implode('&', $parts);
657659
}
658660

661+
/**
662+
* Normalizes a query string using `parse_str()`.
663+
*
664+
* It builds a normalized query string, where root-keys/value pairs are alphabetized,
665+
* have consistent escaping and unneeded delimiters are removed.
666+
*/
667+
public static function normalizeQueryStringForPhp(string $qs): string
668+
{
669+
if ('' == $qs) {
670+
return '';
671+
}
672+
673+
parse_str($qs, $qs);
674+
ksort($qs);
675+
676+
return http_build_query($qs, '', '&', PHP_QUERY_RFC3986);
677+
}
678+
659679
/**
660680
* Enables support for the _method request parameter to determine the intended HTTP method.
661681
*
@@ -1037,6 +1057,20 @@ public function getUri()
10371057
return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
10381058
}
10391059

1060+
/**
1061+
* Generates a `parse_str()`-normalized URI (URL) for the Request.
1062+
*
1063+
* @see getQueryStringForPhp()
1064+
*/
1065+
public function getUriForPhp(): string
1066+
{
1067+
if (null !== $qs = $this->getQueryStringForPhp()) {
1068+
$qs = '?'.$qs;
1069+
}
1070+
1071+
return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
1072+
}
1073+
10401074
/**
10411075
* Generates a normalized URI for the given path.
10421076
*
@@ -1119,6 +1153,19 @@ public function getQueryString()
11191153
return '' === $qs ? null : $qs;
11201154
}
11211155

1156+
/**
1157+
* Generates the query string for the Request, using `parse_str()` it value.
1158+
*
1159+
* It builds a normalized query string, where root-keys/value pairs are alphabetized
1160+
* and have consistent escaping.
1161+
*/
1162+
public function getQueryStringForPhp(): ?string
1163+
{
1164+
$qs = static::normalizeQueryStringForPhp($this->server->get('QUERY_STRING'));
1165+
1166+
return '' === $qs ? null : $qs;
1167+
}
1168+
11221169
/**
11231170
* Checks whether the request is secure or not.
11241171
*

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,56 @@ public function getQueryStringNormalizationData()
696696
// Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
697697
// PHP also does not include them when building _GET.
698698
array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'),
699+
700+
// Also reorder nested query string keys
701+
array('foo[]=Z&foo[]=A', 'foo%5B%5D=A&foo%5B%5D=Z', 'reorders values'),
702+
array('foo[Z]=B&foo[A]=B', 'foo%5BA%5D=B&foo%5BZ%5D=B', 'reorders keys'),
703+
704+
array('utf8=✓', 'utf8=%E2%9C%93', 'encodes UTF-8'),
705+
);
706+
}
707+
708+
/**
709+
* @dataProvider getQueryStringNormalizationDataForPhp
710+
*/
711+
public function testGetQueryStringForPhp($query, $expectedQuery, $msg)
712+
{
713+
$request = new Request();
714+
715+
$request->server->set('QUERY_STRING', $query);
716+
$this->assertSame($expectedQuery, $request->getQueryStringForPhp(), $msg);
717+
}
718+
719+
public function getQueryStringNormalizationDataForPhp()
720+
{
721+
return array(
722+
array('foo', 'foo=', 'works with valueless parameters'),
723+
array('foo=', 'foo=', 'includes a dangling equal sign'),
724+
array('bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'),
725+
array('foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'),
726+
727+
// GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
728+
// PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str.
729+
array('him=John%20Doe&her=Jane+Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes spaces in both encodings "%20" and "+"'),
730+
731+
array('foo[]=1&foo[]=2', 'foo%5B0%5D=1&foo%5B1%5D=2', 'allows array notation'),
732+
array('foo=1&foo=2', 'foo=2', 'merges repeated parameters'),
733+
array('pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'),
734+
array('0', '0=', 'allows "0"'),
735+
array('Jane Doe&John%20Doe', 'Jane_Doe=&John_Doe=', 'normalizes encoding in keys'),
736+
array('her=Jane Doe&him=John%20Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes encoding in values'),
737+
array('foo=bar&&&test&&', 'foo=bar&test=', 'removes unneeded delimiters'),
738+
array('formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'),
739+
740+
// Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
741+
// PHP also does not include them when building _GET.
742+
array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'),
743+
744+
// Don't reorder nested query string keys
745+
array('foo[]=Z&foo[]=A', 'foo%5B0%5D=Z&foo%5B1%5D=A', 'keeps order of values'),
746+
array('foo[Z]=B&foo[A]=B', 'foo%5BZ%5D=B&foo%5BA%5D=B', 'keeps order of keys'),
747+
748+
array('utf8=✓', 'utf8=%E2%9C%93', 'encodes UTF-8'),
699749
);
700750
}
701751

src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ private function record(Request $request, string $event)
661661
private function getTraceKey(Request $request): string
662662
{
663663
$path = $request->getPathInfo();
664-
if ($qs = $request->getQueryString()) {
664+
if ($qs = $request->getQueryStringForPhp()) {
665665
$path .= '?'.$qs;
666666
}
667667

src/Symfony/Component/HttpKernel/HttpCache/Store.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ public function getPath($key)
421421
*/
422422
protected function generateCacheKey(Request $request)
423423
{
424-
return 'md'.hash('sha256', $request->getUri());
424+
return 'md'.hash('sha256', $request->getUriForPhp());
425425
}
426426

427427
/**

0 commit comments

Comments
 (0)