diff options
Diffstat (limited to 'vendor/league/uri-interfaces/IPv6/Converter.php')
-rw-r--r-- | vendor/league/uri-interfaces/IPv6/Converter.php | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/vendor/league/uri-interfaces/IPv6/Converter.php b/vendor/league/uri-interfaces/IPv6/Converter.php new file mode 100644 index 000000000..f645c1da2 --- /dev/null +++ b/vendor/league/uri-interfaces/IPv6/Converter.php @@ -0,0 +1,137 @@ +<?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 League\Uri\IPv6; + +use Stringable; +use ValueError; + +use function filter_var; +use function implode; +use function inet_pton; +use function str_split; +use function strtolower; +use function unpack; + +use const FILTER_FLAG_IPV6; +use const FILTER_VALIDATE_IP; + +final class Converter +{ + /** + * Significant 10 bits of IP to detect Zone ID regular expression pattern. + * + * @var string + */ + private const HOST_ADDRESS_BLOCK = "\xfe\x80"; + + public static function compressIp(string $ipAddress): string + { + return match (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + false => throw new ValueError('The submitted IP is not a valid IPv6 address.'), + default => strtolower((string) inet_ntop((string) inet_pton($ipAddress))), + }; + } + + public static function expandIp(string $ipAddress): string + { + if (false === filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + throw new ValueError('The submitted IP is not a valid IPv6 address.'); + } + + $hex = (array) unpack('H*hex', (string) inet_pton($ipAddress)); + + return implode(':', str_split(strtolower($hex['hex'] ?? ''), 4)); + } + + public static function compress(Stringable|string|null $host): ?string + { + $components = self::parse($host); + if (null === $components['ipAddress']) { + return match ($host) { + null => $host, + default => (string) $host, + }; + } + + $components['ipAddress'] = self::compressIp($components['ipAddress']); + + return self::build($components); + } + + public static function expand(Stringable|string|null $host): ?string + { + $components = self::parse($host); + if (null === $components['ipAddress']) { + return match ($host) { + null => $host, + default => (string) $host, + }; + } + + $components['ipAddress'] = self::expandIp($components['ipAddress']); + + return self::build($components); + } + + private static function build(array $components): string + { + $components['ipAddress'] ??= null; + $components['zoneIdentifier'] ??= null; + + if (null === $components['ipAddress']) { + return ''; + } + + return '['.$components['ipAddress'].match ($components['zoneIdentifier']) { + null => '', + default => '%'.$components['zoneIdentifier'], + }.']'; + } + + /**] + * @param Stringable|string|null $host + * + * @return array{ipAddress:string|null, zoneIdentifier:string|null} + */ + private static function parse(Stringable|string|null $host): array + { + if (null === $host) { + return ['ipAddress' => null, 'zoneIdentifier' => null]; + } + + $host = (string) $host; + if ('' === $host) { + return ['ipAddress' => null, 'zoneIdentifier' => null]; + } + + if (!str_starts_with($host, '[')) { + return ['ipAddress' => null, 'zoneIdentifier' => null]; + } + + if (!str_ends_with($host, ']')) { + return ['ipAddress' => null, 'zoneIdentifier' => null]; + } + + [$ipv6, $zoneIdentifier] = explode('%', substr($host, 1, -1), 2) + [1 => null]; + if (false === filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return ['ipAddress' => null, 'zoneIdentifier' => null]; + } + + return match (true) { + null === $zoneIdentifier, + is_string($ipv6) && str_starts_with((string)inet_pton($ipv6), self::HOST_ADDRESS_BLOCK) => ['ipAddress' => $ipv6, 'zoneIdentifier' => $zoneIdentifier], + default => ['ipAddress' => null, 'zoneIdentifier' => null], + }; + } +} |