Skip to content

Commit 8e1e441

Browse files
committed
Adding QueryParsingMode Enum
1 parent 9e6a046 commit 8e1e441

File tree

2 files changed

+106
-25
lines changed

2 files changed

+106
-25
lines changed

QueryParsingMode.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/**
4+
* League.Uri (https://uri.thephpleague.com)
5+
*
6+
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace League\Uri;
15+
16+
enum QueryParsingMode
17+
{
18+
/**
19+
* Parses the query string using parse_str algorithm.
20+
*/
21+
case Native;
22+
23+
/**
24+
* Parses the query string like parse_str without mangling result keys.
25+
*
26+
* The result is similar to PHP parse_str when used with its second argument,
27+
* with the difference that variable names are not mangled.
28+
*
29+
* Behavior details:
30+
* - Empty names are ignored
31+
* - If a name is duplicated, the last value overwrites the previous one
32+
* - If no "[" is detected, the value is added using the name as the array key
33+
* - If "[" is detected but no matching "]" exists, the value is added using the name as the array key
34+
* - If bracket usage is malformed, the remaining part is dropped
35+
* - "." and " " are NOT converted to "_"
36+
* - If no "]" exists, the first "[" is not converted to "_"
37+
* - No whitespace trimming is performed on keys
38+
*
39+
* @see https://www.php.net/parse_str
40+
* @see https://wiki.php.net/rfc/on_demand_name_mangling
41+
*/
42+
case Unmangled;
43+
44+
/**
45+
* Same as QueryParsingMode::Unmangled and additionally
46+
* preserves null values instead of converting them
47+
* to empty strings.
48+
*/
49+
case PreserveNull;
50+
}

QueryString.php

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
use function array_keys;
2929
use function get_object_vars;
3030
use function http_build_query;
31+
use function implode;
3132
use function is_array;
3233
use function is_object;
3334
use function is_resource;
3435
use function is_scalar;
3536
use function rawurldecode;
37+
use function str_replace;
3638
use function strpos;
3739
use function substr;
3840

@@ -258,39 +260,62 @@ private static function isRecursive(array &$arr): bool
258260
}
259261

260262
/**
261-
* Parses the query string like parse_str without mangling the results.
263+
* Parses the query string.
264+
*
265+
* The result depends on the query parsing mode
262266
*
263267
* @see QueryString::extractFromValue()
264-
* @see http://php.net/parse_str
265-
* @see https://wiki.php.net/rfc/on_demand_name_mangling
266268
*
267269
* @param non-empty-string $separator
268270
*
269271
* @throws SyntaxError
270272
*/
271-
public static function extract(Stringable|string|bool|null $query, string $separator = '&', int $encType = PHP_QUERY_RFC3986): array
272-
{
273-
return self::extractFromValue($query, Converter::fromEncodingType($encType)->withSeparator($separator));
273+
public static function extract(
274+
Stringable|string|bool|null $query,
275+
string $separator = '&',
276+
int $encType = PHP_QUERY_RFC3986,
277+
QueryParsingMode $queryParsingMode = QueryParsingMode::Unmangled,
278+
): array {
279+
return self::extractFromValue(
280+
$query,
281+
Converter::fromEncodingType($encType)->withSeparator($separator),
282+
$queryParsingMode,
283+
);
274284
}
275285

276286
/**
277-
* Parses the query string like parse_str without mangling the results.
287+
* Parses the query string.
278288
*
279-
* The result is similar as PHP parse_str when used with its
280-
* second argument with the difference that variable names are
281-
* not mangled.
282-
*
283-
* @see http://php.net/parse_str
284-
* @see https://wiki.php.net/rfc/on_demand_name_mangling
289+
* The result depends on the query parsing mode
285290
*
286291
* @throws SyntaxError
287292
*/
288-
public static function extractFromValue(Stringable|string|bool|null $query, ?Converter $converter = null): array
289-
{
290-
return self::convert(self::decodePairs(
291-
($converter ?? Converter::fromRFC3986())->toPairs($query),
292-
self::PAIR_VALUE_PRESERVED
293-
));
293+
public static function extractFromValue(
294+
Stringable|string|bool|null $query,
295+
?Converter $converter = null,
296+
QueryParsingMode $queryParsingMode = QueryParsingMode::Unmangled,
297+
): array {
298+
$pairs = ($converter ?? Converter::fromRFC3986())->toPairs($query);
299+
if (QueryParsingMode::Native === $queryParsingMode) {
300+
if ([] === $pairs) {
301+
return [];
302+
}
303+
304+
$data = [];
305+
foreach ($pairs as [$key, $value]) {
306+
$key = str_replace('&', '%26', (string) $key);
307+
$data[] = null === $value ? $key : $key.'='.str_replace('&', '%26', $value);
308+
}
309+
310+
parse_str(implode('&', $data), $result);
311+
312+
return $result;
313+
}
314+
315+
return self::convert(
316+
self::decodePairs($pairs, self::PAIR_VALUE_PRESERVED),
317+
$queryParsingMode
318+
);
294319
}
295320

296321
/**
@@ -349,11 +374,11 @@ private static function decodePairs(array $pairs, int $pairValueState): array
349374
* Converts a collection of key/value pairs and returns
350375
* the store PHP variables as elements of an array.
351376
*/
352-
public static function convert(iterable $pairs): array
377+
public static function convert(iterable $pairs, QueryParsingMode $queryParsingMode = QueryParsingMode::Unmangled): array
353378
{
354379
$returnedValue = [];
355380
foreach ($pairs as $pair) {
356-
$returnedValue = self::extractPhpVariable($returnedValue, $pair);
381+
$returnedValue = self::extractPhpVariable($returnedValue, $pair, queryParsingMode: $queryParsingMode);
357382
}
358383

359384
return $returnedValue;
@@ -384,11 +409,17 @@ public static function convert(iterable $pairs): array
384409
* @param array|string $name the pair key
385410
* @param string $value the pair value
386411
*/
387-
private static function extractPhpVariable(array $data, array|string $name, string $value = ''): array
388-
{
412+
private static function extractPhpVariable(
413+
array $data,
414+
array|string $name,
415+
?string $value = '',
416+
QueryParsingMode $queryParsingMode = QueryParsingMode::Unmangled
417+
): array {
389418
if (is_array($name)) {
390419
[$name, $value] = $name;
391-
$value = rawurldecode((string) $value);
420+
if (null !== $value || QueryParsingMode::PreserveNull !== $queryParsingMode) {
421+
$value = rawurldecode((string) $value);
422+
}
392423
}
393424

394425
if ('' === $name) {
@@ -430,7 +461,7 @@ private static function extractPhpVariable(array $data, array|string $name, stri
430461
return $data;
431462
}
432463

433-
$data[$key] = self::extractPhpVariable($data[$key], $name, $value);
464+
$data[$key] = self::extractPhpVariable($data[$key], $name, $value, $queryParsingMode);
434465

435466
return $data;
436467
}

0 commit comments

Comments
 (0)