Как конвертировать IPv6 из двоичного файла для хранения в MySQL

Я пытаюсь сохранить IPv6-адреса в MySQL 5.0 эффективным образом. Я прочитал другие вопросы, связанные с этим, такие как этот . Автор этого вопроса в конечном итоге выбрал два поля BIGINT. Мои поиски также показали еще один часто используемый механизм: использование DECIMAL (39,0) для хранения адреса IPv6. У меня есть два вопроса.

  1. Каковы преимущества и недостатки использования DECIMAL (39,0) по сравнению с другими методами, такими как 2 * BIGINT?
  2. Как преобразовать (в PHP) из двоичного формата, возвращаемого inet_pton (), в формат десятичной строки, используемый MySQL, и как мне преобразовать обратно, чтобы я мог печатать с помощью inet_ntop ()?

VARBINARY(16) мы пошли вместо столбца VARBINARY(16) и использовали inet_pton() и inet_ntop() для преобразования:

https://bitbucket.org/skion/mysql-udf-ipv6

Функции могут быть загружены в работающий сервер MySQL и предоставят вам INET6_NTOP и INET6_PTON в SQL, так же, как знакомые функции INET_NTOA и INET_ATON для IPv4.

Изменить: теперь в MySQL есть совместимые функции, только с разными именами . Используйте только выше, если вы находитесь в pre-5.6 MySQL и ищете удобный путь к будущему обновлению.

Вот функции, которые я теперь использую для преобразования IP-адресов из формата DECIMAL (39,0). Они называются inet_ptod и inet_dtop для «представления от десятичного до десятичного» и «десятичного к представлению». Для PHP требуется поддержка IPv6 и bcmath.

 /** * Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL * * @param string $ip_address An IP address in IPv4, IPv6 or decimal notation * @return string The IP address in decimal notation */ function inet_ptod($ip_address) { // IPv4 address if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) { $ip_address = '::' . $ip_address; } // IPv6 address if (strpos($ip_address, ':') !== false) { $network = inet_pton($ip_address); $parts = unpack('N*', $network); foreach ($parts as &$part) { if ($part < 0) { $part = bcadd((string) $part, '4294967296'); } if (!is_string($part)) { $part = (string) $part; } } $decimal = $parts[4]; $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); return $decimal; } // Decimal address return $ip_address; } /** * Convert an IP address from decimal format to presentation format * * @param string $decimal An IP address in IPv4, IPv6 or decimal notation * @return string The IP address in presentation format */ function inet_dtop($decimal) { // IPv4 or IPv6 format if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) { return $decimal; } // Decimal format $parts = array(); $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0); $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336')); $parts[2] = bcdiv($decimal, '18446744073709551616', 0); $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616')); $parts[3] = bcdiv($decimal, '4294967296', 0); $decimal = bcsub($decimal, bcmul($parts[3], '4294967296')); $parts[4] = $decimal; foreach ($parts as &$part) { if (bccomp($part, '2147483647') == 1) { $part = bcsub($part, '4294967296'); } $part = (int) $part; } $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]); $ip_address = inet_ntop($network); // Turn IPv6 to IPv4 if it's IPv4 if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) { return substr($ip_address, 2); } return $ip_address; }