質問
少し調べてみると、 IPv6 を処理するためのPHPの機能がわずかに不足していることに気付きました。 。個人的な満足のために、移行を支援する関数をいくつか作成しました。
IPv6ToLong()
関数は、ここで取り上げたものに対する一時的な解決策です。 IPv6互換アドレスをリレーショナルデータベースに保存する方法。 IPを2つの整数に分割し、配列で返します。
/**
* Convert an IPv4 address to IPv6
*
* @param string IP Address in dot notation (192.168.1.100)
* @return string IPv6 formatted address or false if invalid input
*/
function IPv4To6($Ip) {
static $Mask = '::ffff:'; // This tells IPv6 it has an IPv4 address
$IPv6 = (strpos($Ip, '::') === 0);
$IPv4 = (strpos($Ip, '.') > 0);
if (!$IPv4 && !$IPv6) return false;
if ($IPv6 && $IPv4) $Ip = substr($Ip, strrpos($Ip, ':')+1); // Strip IPv4 Compatibility notation
elseif (!$IPv4) return $Ip; // Seems to be IPv6 already?
$Ip = array_pad(explode('.', $Ip), 4, 0);
if (count($Ip) > 4) return false;
for ($i = 0; $i < 4; $i++) if ($Ip[$i] > 255) return false;
$Part7 = base_convert(($Ip[0] * 256) + $Ip[1], 10, 16);
$Part8 = base_convert(($Ip[2] * 256) + $Ip[3], 10, 16);
return $Mask.$Part7.':'.$Part8;
}
/**
* Replace '::' with appropriate number of ':0'
*/
function ExpandIPv6Notation($Ip) {
if (strpos($Ip, '::') !== false)
$Ip = str_replace('::', str_repeat(':0', 8 - substr_count($Ip, ':')).':', $Ip);
if (strpos($Ip, ':') === 0) $Ip = '0'.$Ip;
return $Ip;
}
/**
* Convert IPv6 address to an integer
*
* Optionally split in to two parts.
*
* @see https://stackoverflow.com/questions/420680/
*/
function IPv6ToLong($Ip, $DatabaseParts= 2) {
$Ip = ExpandIPv6Notation($Ip);
$Parts = explode(':', $Ip);
$Ip = array('', '');
for ($i = 0; $i < 4; $i++) $Ip[0] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
for ($i = 4; $i < 8; $i++) $Ip[1] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
if ($DatabaseParts == 2)
return array(base_convert($Ip[0], 2, 10), base_convert($Ip[1], 2, 10));
else return base_convert($Ip[0], 2, 10) + base_convert($Ip[1], 2, 10);
}
これらの関数については、通常、最初にこの関数を呼び出して実装します。
/**
* Attempt to find the client's IP Address
*
* @param bool Should the IP be converted using ip2long?
* @return string|long The IP Address
*/
function GetRealRemoteIp($ForDatabase= false, $DatabaseParts= 2) {
$Ip = '0.0.0.0';
if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != '')
$Ip = $_SERVER['HTTP_CLIENT_IP'];
elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != '')
$Ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != '')
$Ip = $_SERVER['REMOTE_ADDR'];
if (($CommaPos = strpos($Ip, ',')) > 0)
$Ip = substr($Ip, 0, ($CommaPos - 1));
$Ip = IPv4To6($Ip);
return ($ForDatabase ? IPv6ToLong($Ip, $DatabaseParts) : $Ip);
}
ここで車輪を再発明するのか、何か間違ったことをしたのかを教えてください。
この実装は、IPv4をIPv6に変換します。触れないIPv6アドレス。
解決
inet_ntop()
はどうですか。次に、整数に切り刻む代わりに、varbinary(16)
を使用してそれを保存します。
他のヒント
PHP.netのフィルター拡張機能には IPv4アドレスとIPv6アドレスを一致させるためのいくつかの定数は、アドレスの確認に役立つ場合があります。ただし、変換ユーティリティは見ていません。
mysqlのbinary(16)にアドレスを保存することもできます。そのため、IPv6ToLong()からバイナリで出力するオプションが必要です。
これは本当に多くのIPv6対応WebサーバーがクライアントIPとして:: FFFF:1.2.3.4を報告し、ip2longと互換性がないため、PHPでネイティブに追加する必要があるものであり、多くのものを破損します。 / p>
これは、filter_varを使用した代替関数です(PHP <!> gt; = 5.2)
function IPv4To6($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === true) {
if (strpos($ip, '.') > 0) {
$ip = substr($ip, strrpos($ip, ':')+1);
} else { //native ipv6
return $ip;
}
}
$is_v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
if (!$is_v4) { return false; }
$iparr = array_pad(explode('.', $ip), 4, 0);
$Part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
$Part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
return '::ffff:'.$Part7.':'.$Part8;
}
さかのぼって、IPv4とIPv6の両方で機能する2つの関数dtr_pton
とdtr_ntop
を作成しました。印刷可能とバイナリの間でそれらを前後に変換します。
最初の関数VARBINARY(39)
は、指定された引数が有効なIPv4または有効なIPv6であるかどうかを確認します。結果に応じて、例外がスローされるか、IPのバイナリ表現が返されます。この機能を使用することにより、結果に対してANDまたはORを実行できます(サブネット化/ whathaveyouの場合)。これらをデータベースにVARCHAR(39)
または<=>として保存することをお勧めします。
/**
* dtr_pton
*
* Converts a printable IP into an unpacked binary string
*
* @author Mike Mackintosh - mike@bakeryphp.com
* @param string $ip
* @return string $bin
*/
function dtr_pton( $ip ){
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
return current( unpack( "A4", inet_pton( $ip ) ) );
}
elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
return current( unpack( "A16", inet_pton( $ip ) ) );
}
throw new \Exception("Please supply a valid IPv4 or IPv6 address");
return false;
}
2番目の関数<=>は、IPのバイナリ表現を変換して印刷可能なIPアドレスに戻します。
/**
* dtr_ntop
*
* Converts an unpacked binary string into a printable IP
*
* @author Mike Mackintosh - mike@bakeryphp.com
* @param string $str
* @return string $ip
*/
function dtr_ntop( $str ){
if( strlen( $str ) == 16 OR strlen( $str ) == 4 ){
return inet_ntop( pack( "A".strlen( $str ) , $str ) );
}
throw new \Exception( "Please provide a 4 or 16 byte string" );
return false;
}
また、ここにIPv6アドレスをすばやく展開する方法があります StackOverflowにあります
function expand($ip){
$hex = unpack("H*hex", inet_pton($ip));
$ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);
return $ip;
}
また、私のテーマのブログ HighOnPHP:PHPでIPv6を使用するための5つのヒント。この記事では、 GitHub:mikemackintosh \にあるオブジェクト指向クラスで上記のメソッドの一部を使用します。 dTR-IP