32

少し徹底的に調べてみると、 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';
    // [snip: deleted some dangerous code not relevant to question. @webb]
    if (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アドレス。

4

5 に答える 5

19

どうinet_ntop()ですか?次に、物事を整数に切り刻む代わりに、 a を使用しvarbinary(16)てそれを格納します。

于 2009-01-18T18:46:55.883 に答える
1

filter_varを使用した代替関数を次に示します(PHP> = 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;
}
于 2011-01-25T09:09:21.653 に答える
1

アドレスを mysql の binary(16) に格納することもできるため、IPv6ToLong() からバイナリで出力するオプションが必要です。

これは、特に多くの IPv6 対応 Web サーバーがクライアント IP として ::FFFF:1.2.3.4 を報告し、ip2long と互換性がなく、多くのものを壊す場合に、PHP にネイティブに追加する必要があるものです。

于 2010-01-23T14:23:01.870 に答える
1

PHP.net のFilter 拡張機能には、IPv4 と IPv6 のアドレスを照合するための定数が含まれており、アドレスのチェックに役立つ場合があります。ただし、変換ユーティリティは見たことがありません。

于 2009-01-18T18:38:28.613 に答える
1

さかのぼって、IPv4 と IPv6 の両方で動作する2 つの関数dtr_ptonを書きました。dtr_ntop印刷可能とバイナリの間でそれらを前後に変換します。

最初の関数はdtr_pton、指定された引数が有効な IPv4 または有効な IPv6 であるかどうかを確認します。結果に応じて、例外がスローされるか、IP のバイナリ表現が返される可能性があります。この関数を使用すると、結果に対して AND または OR を実行できます (サブネット化/whathaveyou の場合)。VARBINARY(39)これらをデータベースにまたはとして保存することをお勧めします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 番目の関数dtr_ntopは、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;
}

また、 StackOverflow で見つかったIPv6 アドレスを展開する簡単な方法を次に示します。

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

于 2013-07-08T18:22:39.267 に答える