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

Skip to content

Commit 4440d02

Browse files
[HttpFoundation] add HeaderUtils::parseQuery(): it does the same as parse_str() but preserves dots in variable names
1 parent f0fa3b0 commit 4440d02

File tree

7 files changed

+106
-50
lines changed

7 files changed

+106
-50
lines changed

src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Controller;
1313

14+
use Symfony\Component\HttpFoundation\HeaderUtils;
1415
use Symfony\Component\HttpFoundation\RedirectResponse;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpFoundation\Response;
@@ -65,7 +66,7 @@ public function redirectAction(Request $request, string $route, bool $permanent
6566

6667
if ($keepQueryParams) {
6768
if ($query = $request->server->get('QUERY_STRING')) {
68-
$query = self::parseQuery($query);
69+
$query = HeaderUtils::parseQuery($query);
6970
} else {
7071
$query = $request->query->all();
7172
}
@@ -185,49 +186,4 @@ public function __invoke(Request $request): Response
185186

186187
throw new \RuntimeException(sprintf('The parameter "path" or "route" is required to configure the redirect action in "%s" routing configuration.', $request->attributes->get('_route')));
187188
}
188-
189-
private static function parseQuery(string $query)
190-
{
191-
$q = [];
192-
193-
foreach (explode('&', $query) as $v) {
194-
if (false !== $i = strpos($v, "\0")) {
195-
$v = substr($v, 0, $i);
196-
}
197-
198-
if (false === $i = strpos($v, '=')) {
199-
$k = urldecode($v);
200-
$v = '';
201-
} else {
202-
$k = urldecode(substr($v, 0, $i));
203-
$v = substr($v, $i);
204-
}
205-
206-
if (false !== $i = strpos($k, "\0")) {
207-
$k = substr($k, 0, $i);
208-
}
209-
210-
$k = ltrim($k, ' ');
211-
212-
if (false === $i = strpos($k, '[')) {
213-
$q[] = bin2hex($k).$v;
214-
} else {
215-
$q[] = substr_replace($k, bin2hex(substr($k, 0, $i)), 0, $i).$v;
216-
}
217-
}
218-
219-
parse_str(implode('&', $q), $q);
220-
221-
$query = [];
222-
223-
foreach ($q as $k => $v) {
224-
if (false !== $i = strpos($k, '_')) {
225-
$query[substr_replace($k, hex2bin(substr($k, 0, $i)).'[', 0, 1 + $i)] = $v;
226-
} else {
227-
$query[hex2bin($k)] = $v;
228-
}
229-
}
230-
231-
return $query;
232-
}
233189
}

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"symfony/dependency-injection": "^5.2",
2424
"symfony/event-dispatcher": "^5.1",
2525
"symfony/error-handler": "^4.4.1|^5.0.1",
26-
"symfony/http-foundation": "^4.4|^5.0",
26+
"symfony/http-foundation": "^5.2",
2727
"symfony/http-kernel": "^5.0",
2828
"symfony/polyfill-mbstring": "~1.0",
2929
"symfony/polyfill-php80": "^1.15",

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.2.0
5+
-----
6+
7+
* added `HeaderUtils::parseQuery()`: it does the same as `parse_str()` but preserves dots in variable names
8+
49
5.1.0
510
-----
611

src/Symfony/Component/HttpFoundation/HeaderUtils.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,64 @@ public static function makeDisposition(string $disposition, string $filename, st
193193
return $disposition.'; '.self::toString($params, ';');
194194
}
195195

196+
/**
197+
* Like parse_str(), but preserves dots in variable names.
198+
*/
199+
public static function parseQuery(string $query, bool $ignoreBrackets = false, string $separator = '&'): array
200+
{
201+
$q = [];
202+
203+
foreach (explode($separator, $query) as $v) {
204+
if (false !== $i = strpos($v, "\0")) {
205+
$v = substr($v, 0, $i);
206+
}
207+
208+
if (false === $i = strpos($v, '=')) {
209+
$k = urldecode($v);
210+
$v = '';
211+
} else {
212+
$k = urldecode(substr($v, 0, $i));
213+
$v = substr($v, $i);
214+
}
215+
216+
if (false !== $i = strpos($k, "\0")) {
217+
$k = substr($k, 0, $i);
218+
}
219+
220+
$k = ltrim($k, ' ');
221+
222+
if ($ignoreBrackets) {
223+
$q[$k][] = urldecode(substr($v, 1));
224+
225+
continue;
226+
}
227+
228+
if (false === $i = strpos($k, '[')) {
229+
$q[] = bin2hex($k).$v;
230+
} else {
231+
$q[] = substr_replace($k, bin2hex(substr($k, 0, $i)), 0, $i).$v;
232+
}
233+
}
234+
235+
if ($ignoreBrackets) {
236+
return $q;
237+
}
238+
239+
parse_str(implode('&', $q), $q);
240+
241+
$query = [];
242+
243+
foreach ($q as $k => $v) {
244+
if (false !== $i = strpos($k, '_')) {
245+
$query[substr_replace($k, hex2bin(substr($k, 0, $i)).'[', 0, 1 + $i)] = $v;
246+
} else {
247+
$query[hex2bin($k)] = $v;
248+
}
249+
}
250+
251+
return $query;
252+
}
253+
196254
private static function groupParts(array $matches, string $separators): array
197255
{
198256
$separator = $separators[0];

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public static function create(string $uri, string $method = 'GET', array $parame
399399

400400
$queryString = '';
401401
if (isset($components['query'])) {
402-
parse_str(html_entity_decode($components['query']), $qs);
402+
$qs = HeaderUtils::parseQuery(html_entity_decode($components['query']));
403403

404404
if ($query) {
405405
$query = array_replace($qs, $query);
@@ -660,7 +660,7 @@ public static function normalizeQueryString(?string $qs)
660660
return '';
661661
}
662662

663-
parse_str($qs, $qs);
663+
$qs = HeaderUtils::parseQuery($qs);
664664
ksort($qs);
665665

666666
return http_build_query($qs, '', '&', PHP_QUERY_RFC3986);

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,41 @@ public function provideMakeDispositionFail()
129129
['attachment', 'föö.html'],
130130
];
131131
}
132+
133+
/**
134+
* @dataProvider provideParseQuery
135+
*/
136+
public function testParseQuery(string $query, string $expected = null)
137+
{
138+
$this->assertSame($expected ?? $query, http_build_query(HeaderUtils::parseQuery($query), '', '&'));
139+
}
140+
141+
public function provideParseQuery()
142+
{
143+
return [
144+
['a=b&c=d'],
145+
['a.b=c'],
146+
['a+b=c'],
147+
["a\0b=c", 'a='],
148+
['a%00b=c', 'a=c'],
149+
['a[b=c', 'a%5Bb=c'],
150+
['a]b=c', 'a%5Db=c'],
151+
['a[b]=c', 'a%5Bb%5D=c'],
152+
['a[b][c.d]=c', 'a%5Bb%5D%5Bc.d%5D=c'],
153+
['a%5Bb%5D=c'],
154+
];
155+
}
156+
157+
public function testParseCookie()
158+
{
159+
$query = 'a.b=c; def%5Bg%5D=h';
160+
$this->assertSame($query, http_build_query(HeaderUtils::parseQuery($query, false, ';'), '', '; '));
161+
}
162+
163+
public function testParseueryIgnoreBrackets()
164+
{
165+
$this->assertSame(['a.b' => ['A', 'B']], HeaderUtils::parseQuery('a.b=A&a.b=B', true));
166+
$this->assertSame(['a.b[]' => ['A']], HeaderUtils::parseQuery('a.b[]=A', true));
167+
$this->assertSame(['a.b[]' => ['A']], HeaderUtils::parseQuery('a.b%5B%5D=A', true));
168+
}
132169
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ public function getQueryStringNormalizationData()
807807
['foo=1&foo=2', 'foo=2', 'merges repeated parameters'],
808808
['pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'],
809809
['0', '0=', 'allows "0"'],
810-
['Foo Bar&Foo%20Baz', 'Foo_Bar=&Foo_Baz=', 'normalizes encoding in keys'],
810+
['Foo Bar&Foo%20Baz', 'Foo%20Bar=&Foo%20Baz=', 'normalizes encoding in keys'],
811811
['bar=Foo Bar&baz=Foo%20Baz', 'bar=Foo%20Bar&baz=Foo%20Baz', 'normalizes encoding in values'],
812812
['foo=bar&&&test&&', 'foo=bar&test=', 'removes unneeded delimiters'],
813813
['formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'],

0 commit comments

Comments
 (0)