Files
wp-multisite-waas/dependencies/league/uri-interfaces/Encoder.php
2024-11-30 18:24:12 -07:00

149 lines
6.2 KiB
PHP

<?php
/**
* League.Uri (https://uri.thephpleague.com)
*
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare (strict_types=1);
namespace WP_Ultimo\Dependencies\League\Uri;
use Closure;
use WP_Ultimo\Dependencies\League\Uri\Contracts\UriComponentInterface;
use WP_Ultimo\Dependencies\League\Uri\Exceptions\SyntaxError;
use Stringable;
use function preg_match;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function strtoupper;
final class Encoder
{
private const REGEXP_CHARS_INVALID = '/[\\x00-\\x1f\\x7f]/';
private const REGEXP_CHARS_ENCODED = ',%[A-Fa-f0-9]{2},';
private const REGEXP_CHARS_PREVENTS_DECODING = ',%
2[A-F|1-2|4-9]|
3[0-9|B|D]|
4[1-9|A-F]|
5[0-9|A|F]|
6[1-9|A-F]|
7[0-9|E]
,ix';
private const REGEXP_PART_SUBDELIM = "\\!\$&'\\(\\)\\*\\+,;\\=%";
private const REGEXP_PART_UNRESERVED = 'A-Za-z\\d_\\-.~';
private const REGEXP_PART_ENCODED = '%(?![A-Fa-f\\d]{2})';
/**
* Encode User.
*
* All generic delimiters MUST be encoded
*/
public static function encodeUser(Stringable|string|null $component) : ?string
{
static $pattern = '/[^' . self::REGEXP_PART_UNRESERVED . self::REGEXP_PART_SUBDELIM . ']+|' . self::REGEXP_PART_ENCODED . '/';
return self::encode($component, $pattern);
}
/**
* Encode Password.
*
* Generic delimiters ":" MUST NOT be encoded
*/
public static function encodePassword(Stringable|string|null $component) : ?string
{
static $pattern = '/[^' . self::REGEXP_PART_UNRESERVED . self::REGEXP_PART_SUBDELIM . ':]+|' . self::REGEXP_PART_ENCODED . '/';
return self::encode($component, $pattern);
}
/**
* Encode Path.
*
* Generic delimiters ":", "@", and "/" MUST NOT be encoded
*/
public static function encodePath(Stringable|string|null $component) : string
{
static $pattern = '/[^' . self::REGEXP_PART_UNRESERVED . self::REGEXP_PART_SUBDELIM . ':@\\/]+|' . self::REGEXP_PART_ENCODED . '/';
return (string) self::encode($component, $pattern);
}
/**
* Encode Query or Fragment.
*
* Generic delimiters ":", "@", "?", and "/" MUST NOT be encoded
*/
public static function encodeQueryOrFragment(Stringable|string|null $component) : ?string
{
static $pattern = '/[^' . self::REGEXP_PART_UNRESERVED . self::REGEXP_PART_SUBDELIM . ':@\\/?]+|' . self::REGEXP_PART_ENCODED . '/';
return self::encode($component, $pattern);
}
public static function encodeQueryKeyValue(mixed $component) : ?string
{
static $pattern = '/[^' . self::REGEXP_PART_UNRESERVED . ']+|' . self::REGEXP_PART_ENCODED . '/';
$encodeMatches = static fn(array $matches): string => match (1) {
preg_match('/[^' . self::REGEXP_PART_UNRESERVED . ']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
default => $matches[0],
};
$component = self::filterComponent($component);
return match (\true) {
!\is_scalar($component) => throw new SyntaxError(\sprintf('A pair key/value must be a scalar value `%s` given.', \gettype($component))),
1 === preg_match(self::REGEXP_CHARS_INVALID, $component) => rawurlencode($component),
1 === preg_match($pattern, $component) => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
default => $component,
};
}
/**
* Decodes the URI component without decoding the unreserved characters which are already encoded.
*/
public static function decodePartial(Stringable|string|int|null $component) : ?string
{
$decodeMatches = static fn(array $matches): string => match (1) {
preg_match(self::REGEXP_CHARS_PREVENTS_DECODING, $matches[0]) => strtoupper($matches[0]),
default => rawurldecode($matches[0]),
};
return self::decode($component, $decodeMatches);
}
/**
* Decodes all the URI component characters.
*/
public static function decodeAll(Stringable|string|int|null $component) : ?string
{
$decodeMatches = static fn(array $matches): string => rawurldecode($matches[0]);
return self::decode($component, $decodeMatches);
}
private static function filterComponent(mixed $component) : ?string
{
return match (\true) {
\true === $component => '1',
\false === $component => '0',
$component instanceof UriComponentInterface => $component->value(),
$component instanceof Stringable, \is_scalar($component) => (string) $component,
null === $component => null,
default => throw new SyntaxError(\sprintf('The component must be a scalar value `%s` given.', \gettype($component))),
};
}
private static function encode(Stringable|string|int|bool|null $component, string $pattern) : ?string
{
$component = self::filterComponent($component);
$encodeMatches = static fn(array $matches): string => match (1) {
preg_match('/[^' . self::REGEXP_PART_UNRESERVED . ']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
default => $matches[0],
};
return match (\true) {
null === $component, '' === $component => $component,
default => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
};
}
/**
* Decodes all the URI component characters.
*/
private static function decode(Stringable|string|int|null $component, Closure $decodeMatches) : ?string
{
$component = self::filterComponent($component);
return match (\true) {
null === $component => null,
1 === preg_match(self::REGEXP_CHARS_INVALID, $component) => throw new SyntaxError('Invalid component string: ' . $component . '.'),
1 === preg_match(self::REGEXP_CHARS_ENCODED, $component) => preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decodeMatches(...), $component),
default => $component,
};
}
}