php: ip2long returning negative val

后端 未结 6 804
灰色年华
灰色年华 2020-12-30 04:18
function ip_address_to_number($IPaddress) { 
     if(!$IPaddress) {
      return false;
     } else {
      $ips = split(\'\\.\',$IPaddress);
      return($ips[3] +          


        
相关标签:
6条回答
  • 2020-12-30 04:39

    You can use -

    // IP Address to Number
    function inet_aton($ip)
    {
        $ip = trim($ip);
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
        return sprintf("%u", ip2long($ip));  
    }
    
    // Number to IP Address
    function inet_ntoa($num)
    {
        $num = trim($num);
        if ($num == "0") return "0.0.0.0";
        return long2ip(-(4294967295 - ($num - 1))); 
    }
    
    0 讨论(0)
  • 2020-12-30 04:52
      $ips[3]                             = 17
    + $ips[2] * 256 = 248 * 256           = 63488
    + $ips[1] * 65536 = 117 * 65536       = 7667712
    + $ips[0] * 16777216 = 200 * 16777216 = 3355443200
                                          = 3363174417
    

    PHP max integer value (32-bit) is 2147483647, which is < 3363174417

    Quoting from the ip2long() PHP manual page

    Note: Because PHP's integer type is signed, and many IP addresses will result in negative integers, you need to use the "%u" formatter of sprintf() or printf() to get the string representation of the unsigned IP address.

    0 讨论(0)
  • 2020-12-30 04:55
    <?php
    function _ip2long($input)
    {
        $r = null;
        if (is_string($input))
        {
            $_input = trim($input);
            if (filter_var($_input, FILTER_VALIDATE_IP))
            {
                $PRE_r = explode('.', $_input);
                $r = ($PRE_r[0] * pow(256, 3)) + ($PRE_r[1] * pow(256, 2)) + ($PRE_r[2] * 256) + ($PRE_r[0]);
            } else
            { 
                $r = false;
            }
        } else
        {
            $r = false;
        }
        return ($r);
    }
    $out = _ip2long('127.0.0.1');
    if (false === $out)
    {
      print('Invalid IP');
    } else
    {
      print($out);
    }
    ?>
    
    0 讨论(0)
  • 2020-12-30 05:01
    glopes@nebm:~$ php -r "printf('%u', -931792879);"
    3363174417
    

    There you go. My guess is that you are on a system with 32-bit ints and your ip_address_to_number is actually returning a float.

    You see, with 32-bit ints, your maximum positive integer is (2^31) - 1 = 2 147 483 647, so the integer wraps around.

    If you want to mimic the behaviour of the PHP function, do:

    function ip_address_to_number($IPaddress) { 
     if(!$IPaddress) {
      return false;
     } else {
      $ips = split('\.',$IPaddress);
      return($ips[3] | $ips[2] << 8 | $ips[1] << 16 | $ips[0] << 24);
     }
    }
    

    (by the way, split has been deprecated)

    0 讨论(0)
  • 2020-12-30 05:04

    Try this instead:

    $ip = sprintf('%u', ip2long($_SERVER['REMOTE_ADDR']));
    

    sprintf will then write it as an unsigned integer.

    0 讨论(0)
  • 2020-12-30 05:04

    Did some performance tests to compare running ip2long through sprintf vs array explode and then times or bitwise shift:

    <?php
        header ('Content-Type: text/plain');
    
        function ip_range($start, $count) {
            $start = ip2long($start);
            return array_map('long2ip', range($start, $start + $count) );
        }
    
        $iterations = 500000;
        $results = array();
        $ips = ip_range('192.168.1.1', $iterations);
        $time = microtime(true);
        foreach ($ips as $ip) {
            $result = sprintf('%u', ip2long($ip));
        }
        $time = microtime(true) - $time;
        $results['ip2long'] = array ('total' => $time, 'cycles' => $iterations, 'average' => ($time / $iterations) . 's', 'speed' => ($iterations/$time) . ' hashes per second.' );
    
        $time = microtime(true);
        foreach ($ips as $ip) {
            $aIp = explode('.', $ip);
            if (count($aIp) == 4) {
                $result = /*sprintf('%u',*/ $aIp[0]*16777216 + $aIp[1]*65536 + $aIp[2]*256 + $aIp[3] /*)*/;
            }
            else
            {
                $result = false;
            }
        }
        $time = microtime(true) - $time;
        $results['explode multiple'] = array ('total' => $time, 'cycles' => $iterations, 'average' => ($time / $iterations) . 's', 'speed' => ($iterations/$time) . ' hashes per second.' );
    
        $time = microtime(true);
        foreach ($ips as $ip) {
            $aIp = explode('.', $ip);
            if (count($aIp) == 4) {
                $result = /*sprintf('%u',*/ $aIp[3] | $aIp[2] << 8 | $aIp[1] << 16 | $aIp[0] << 24 /*)*/;
            }
            else
            {
                $result = false;
            }
        }
        $time = microtime(true) - $time;
        $results['explode bitwise'] = array ('total' => $time, 'cycles' => $iterations, 'average' => ($time / $iterations) . 's', 'speed' => ($iterations/$time) . ' hashes per second.' );
    
    
        die(var_dump($results));
    

    These are the results:

    array(3) {
    
    
    ["ip2long"]=>
      array(4) {
        ["total"]=>
        float(0.92530012130737)
        ["cycles"]=>
        int(500000)
        ["average"]=>
        string(19) "1.8506002426147E-6s"
        ["speed"]=>
        string(34) "540365.21609177 hashes per second."
      }
      ["explode multiple"]=>
      array(4) {
        ["total"]=>
        float(0.91870212554932)
        ["cycles"]=>
        int(500000)
        ["average"]=>
        string(19) "1.8374042510986E-6s"
        ["speed"]=>
        string(34) "544246.04678153 hashes per second."
      }
      ["explode bitwise"]=>
      array(4) {
        ["total"]=>
        float(0.9184091091156)
        ["cycles"]=>
        int(500000)
        ["average"]=>
        string(19) "1.8368182182312E-6s"
        ["speed"]=>
        string(34) "544419.68730197 hashes per second."
      }
    }
    

    When wrapping the bitwise and multiply in sprintf they are slower than ip2long but since it's not needed, they are faster.

    0 讨论(0)
提交回复
热议问题