| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- <?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],
- };
- }
- }
|