Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit dd81e32

Browse filesBrowse files
[HttpFoundation] add HeaderUtils::parseQuery(): it does the same as parse_str() but preserves dots in variable names
1 parent ce8f8a5 commit dd81e32
Copy full SHA for dd81e32

File tree

7 files changed

+106
-50
lines changed
Filter options

7 files changed

+106
-50
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php
+2-46Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+1-1Lines 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.2",
2828
"symfony/polyfill-mbstring": "~1.0",
2929
"symfony/polyfill-php80": "^1.15",

‎src/Symfony/Component/HttpFoundation/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/CHANGELOG.md
+5Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/HeaderUtils.php
+58Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Request.php
+2-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Tests/HeaderUtilsTest.php
+37Lines 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 testParseQueryIgnoreBrackets()
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+1-1Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.