16
function ip_address_to_number($IPaddress) { 
     if(!$IPaddress) {
      return false;
     } else {
      $ips = split('\.',$IPaddress);
      return($ips[3] + $ips[2]*256 + $ips[1]*65536 + $ips[0]*16777216);
     }
}

that function executes the same code as the php bundled function ip2long. however, when i print these 2 values, i get 2 different returns. why? (im using php 5.2.10 on a wamp environment).

ip2long('200.117.248.17'); //returns **-931792879**

ip_address_to_number('200.117.248.17'); // returns **3363174417**

Applied and continued here: Showing my country based on my IP, mysql optimized

Community
  • 1
  • 1
Andres SK
  • 10,779
  • 25
  • 90
  • 152

6 Answers6

35

Try this instead:

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

sprintf will then write it as an unsigned integer.

webbiedave
  • 48,414
  • 8
  • 88
  • 101
  • The bitwise operation provided by Artefacto is significantly faster than the PHP implementation due to not needing `sprintf`. – Kristian Williams Jun 23 '15 at 07:50
  • 2
    Not true, Kristian. It isn't merely bitwise operations, it's array creation/access plus bitwise operations. Regardless, it's a micro-optimization and anyone would be hard-pressed to show any non-negligible speed differences between the two. Use whichever approach is more readable/comfortable for you. – webbiedave May 16 '16 at 21:22
10
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)

Artefacto
  • 96,375
  • 17
  • 202
  • 225
6
  $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.

Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • You are of course correct. The question is, why would he want the unsigned string representation of the IP in base 10. As far as string representations go, '200.117.248.17' is much more readable. With the integer representation, he has a more compact representation (4 bytes) and he can do bitwise operations on the IP. The "negative value" is irrelevant for that. – Artefacto Jun 17 '10 at 15:19
  • +1: You'd be surprised how many problems are solved by reading the manual. – Powerlord Jun 17 '10 at 15:20
  • 4
    @Artefacto: No, the question is "Why don't the PHP developers fix this issue and not have `ip2long` return negative values?" – Powerlord Jun 17 '10 at 15:23
  • @R. Bemrose Because that would not make sense. The only way to do that would be to have ip2long return a float since an integer is not large enough to hold all the values, while a float has 52 bits for the fraction. That would kill all the bitwise operations on IPs, which, by the way, together with storing IPs efficiently, is the purpose of the function. – Artefacto Jun 17 '10 at 15:30
1

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))); 
}
user2253362
  • 81
  • 1
  • 3
0
<?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);
}
?>
Mohammad Lotfi
  • 369
  • 5
  • 16
0

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.

Kristian Williams
  • 2,275
  • 22
  • 25