Skip to content

Commit 8492799

Browse files
committed
Documenting Uri codec methods
1 parent 5dad3e1 commit 8492799

File tree

1 file changed

+80
-74
lines changed

1 file changed

+80
-74
lines changed

Encoder.php

Lines changed: 80 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
use SensitiveParameter;
2020
use Stringable;
2121

22+
use function gettype;
2223
use function in_array;
24+
use function is_scalar;
2325
use function preg_match;
2426
use function preg_replace_callback;
2527
use function rawurldecode;
2628
use function rawurlencode;
29+
use function sprintf;
2730
use function strtoupper;
2831

2932
final class Encoder
@@ -100,94 +103,46 @@ public static function encodeQueryOrFragment(Stringable|string|null $component):
100103
public static function encodeQueryKeyValue(mixed $component): ?string
101104
{
102105
static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.']+|'.self::REGEXP_PART_ENCODED.'/';
103-
104-
$encodeMatches = static fn (array $matches): string => match (1) {
105-
preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
106-
default => $matches[0],
107-
};
108-
109-
$component = self::filterComponent($component);
106+
$encoder = static fn (array $found): string => 1 === preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($found[0])) ? rawurlencode($found[0]) : $found[0];
107+
$filteredComponent = self::filterComponent($component);
110108

111109
return match (true) {
112-
!is_scalar($component) => throw new SyntaxError(sprintf('A pair key/value must be a scalar value `%s` given.', gettype($component))),
113-
1 === preg_match(self::REGEXP_CHARS_INVALID, $component) => rawurlencode($component),
114-
1 === preg_match($pattern, $component) => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
115-
default => $component,
110+
null === $filteredComponent => throw new SyntaxError(sprintf('A pair key/value must be a scalar value `%s` given.', gettype($component))),
111+
1 === preg_match(self::REGEXP_CHARS_INVALID, $filteredComponent) => rawurlencode($filteredComponent),
112+
default => (string) preg_replace_callback($pattern, $encoder, $filteredComponent),
116113
};
117114
}
118115

119-
/**
120-
* Decodes the URI component without decoding the unreserved characters which are already encoded.
121-
*/
122-
public static function decodePartial(Stringable|string|int|null $component): ?string
123-
{
124-
$decodeMatches = static fn (array $matches): string => match (1) {
125-
preg_match(self::REGEXP_CHARS_PREVENTS_DECODING, $matches[0]) => strtoupper($matches[0]),
126-
default => rawurldecode($matches[0]),
127-
};
128-
129-
return self::decode($component, $decodeMatches);
130-
}
131-
132116
/**
133117
* Decodes all the URI component characters.
134118
*/
135-
public static function decodeAll(Stringable|string|int|null $component): ?string
119+
public static function decodeAll(Stringable|string|null $component): ?string
136120
{
137121
return self::decode($component, static fn (array $matches): string => rawurldecode($matches[0]));
138122
}
139123

140-
private static function filterComponent(mixed $component): ?string
124+
/**
125+
* Decodes the URI component without decoding the unreserved characters which are already encoded.
126+
*/
127+
public static function decodePartial(Stringable|string|int|null $component): ?string
141128
{
142-
return match (true) {
143-
true === $component => '1',
144-
false === $component => '0',
145-
$component instanceof UriComponentInterface => $component->value(),
146-
$component instanceof Stringable,
147-
is_scalar($component) => (string) $component,
148-
null === $component => null,
149-
default => throw new SyntaxError(sprintf('The component must be a scalar value `%s` given.', gettype($component))),
150-
};
151-
}
129+
$decoder = static function (array $matches): string {
130+
if (1 === preg_match(self::REGEXP_CHARS_PREVENTS_DECODING, $matches[0])) {
131+
return strtoupper($matches[0]);
132+
}
152133

153-
private static function encode(Stringable|string|int|bool|null $component, string $pattern): ?string
154-
{
155-
$component = self::filterComponent($component);
156-
$encodeMatches = static fn (array $matches): string => match (1) {
157-
preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
158-
default => $matches[0],
134+
return rawurldecode($matches[0]);
159135
};
160136

161-
return match (true) {
162-
null === $component,
163-
'' === $component => $component,
164-
default => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
165-
};
137+
return self::decode($component, $decoder);
166138
}
167139

168140
/**
169-
* Decodes all the URI component characters.
141+
* Decodes the component unreserved characters.
170142
*/
171-
private static function decode(Stringable|string|int|null $component, Closure $decodeMatches): ?string
172-
{
173-
$component = self::filterComponent($component);
174-
if (null === $component || '' === $component) {
175-
return $component;
176-
}
177-
178-
if (1 === preg_match(self::REGEXP_CHARS_INVALID, $component)) {
179-
throw new SyntaxError('Invalid component string: '.$component.'.');
180-
}
181-
182-
if (1 === preg_match(self::REGEXP_CHARS_ENCODED, $component)) {
183-
return (string) preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decodeMatches(...), $component);
184-
}
185-
186-
return $component;
187-
}
188-
189-
public static function decodeUnreservedCharacters(?string $str): ?string
143+
public static function decodeUnreservedCharacters(Stringable|string|null $str): ?string
190144
{
145+
$str = self::filterComponent($str);
191146
if (null === $str || '' === $str) {
192147
return $str;
193148
}
@@ -196,38 +151,89 @@ public static function decodeUnreservedCharacters(?string $str): ?string
196151
}
197152

198153
/**
199-
* Decodes the URI query path while preserving delim character that should not be decoded for a path inside a URI.
154+
* Decodes the path component while preserving characters that should not be decoded in the context of a full valid URI.
200155
*/
201156
public static function decodePath(Stringable|string|null $path): ?string
202157
{
203-
$decodeMatches = static function (array $matches): string {
158+
$decoder = static function (array $matches): string {
204159
$encodedChar = strtoupper($matches[0]);
205160

206161
return in_array($encodedChar, ['%2F', '%20', '%3F', '%23'], true) ? $encodedChar : rawurldecode($encodedChar);
207162
};
208163

209-
return self::decode($path, $decodeMatches);
164+
return self::decode($path, $decoder);
210165
}
211166

212167
/**
213-
* Decodes the URI query string while preserving delim character that should not be decoded for a query inside a URI.
168+
* Decodes the query component while preserving characters that should not be decoded in the context of a full valid URI.
214169
*/
215170
public static function decodeQuery(Stringable|string|null $path): ?string
216171
{
217-
$decodeMatches = static function (array $matches): string {
172+
$decoder = static function (array $matches): string {
218173
$encodedChar = strtoupper($matches[0]);
219174

220175
return in_array($encodedChar, ['%26', '%3D', '%20', '%23'], true) ? $encodedChar : rawurldecode($encodedChar);
221176
};
222177

223-
return self::decode($path, $decodeMatches);
178+
return self::decode($path, $decoder);
224179
}
225180

226181
/**
227-
* Decodes the URI query string while preserving delim character that should not be decoded for a query inside a URI.
182+
* Decodes the fragment component while preserving characters that should not be decoded in the context of a full valid URI.
228183
*/
229184
public static function decodeFragment(Stringable|string|null $path): ?string
230185
{
231186
return self::decode($path, static fn (array $matches): string => '%20' === $matches[0] ? $matches[0] : rawurldecode($matches[0]));
232187
}
188+
189+
private static function filterComponent(mixed $component): ?string
190+
{
191+
return match (true) {
192+
true === $component => '1',
193+
false === $component => '0',
194+
$component instanceof UriComponentInterface => $component->value(),
195+
$component instanceof Stringable,
196+
is_scalar($component) => (string) $component,
197+
null === $component => null,
198+
default => throw new SyntaxError(sprintf('The component must be a scalar value `%s` given.', gettype($component))),
199+
};
200+
}
201+
202+
/**
203+
* Encodes the URI component characters using a regular expression to find which characters need encoding.
204+
*/
205+
private static function encode(Stringable|string|int|bool|null $component, string $pattern): ?string
206+
{
207+
$component = self::filterComponent($component);
208+
if (null === $component || '' === $component) {
209+
return $component;
210+
}
211+
212+
return (string) preg_replace_callback(
213+
$pattern,
214+
static fn (array $found): string => 1 === preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($found[0])) ? rawurlencode($found[0]) : $found[0],
215+
$component
216+
);
217+
}
218+
219+
/**
220+
* Decodes the URI component characters using a closure.
221+
*/
222+
private static function decode(Stringable|string|int|null $component, Closure $decoder): ?string
223+
{
224+
$component = self::filterComponent($component);
225+
if (null === $component || '' === $component) {
226+
return $component;
227+
}
228+
229+
if (1 === preg_match(self::REGEXP_CHARS_INVALID, $component)) {
230+
throw new SyntaxError('Invalid component string: '.$component.'.');
231+
}
232+
233+
if (1 === preg_match(self::REGEXP_CHARS_ENCODED, $component)) {
234+
return (string) preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decoder, $component);
235+
}
236+
237+
return $component;
238+
}
233239
}

0 commit comments

Comments
 (0)