views:

1850

answers:

3

I got a problem with ip2long in PHP5. It appears, that in 32bit OS ip2long returns signed int, and in 64bit OS unsigned int is returned. My application is working on 10 servers, and some are 32bit and some are 64bit, so I need all them to work same way. In PHP documentation there is a trick to make that result always unsigned, but since I got my database already full of data, I want to have it signed.

So how to change an unsigned int into a signed one in PHP?

I know it's a simple thing, but I couldn't google it.

+6  A: 

PHP does not support unsigned integers as a type, but what you can do is simply turn the result of ip2long into an unsigned int string by having sprintf interpret the value as unsigned with %u:

 $ip="128.1.2.3";
 $signed=ip2long($ip);             // -2147417597 in this example
 $unsigned=sprintf("%u", $signed); //  2147549699 in this example

Edit, since you really wanted it to be signed even on 64 bit systems - here's how you'd convert the 64 bit +ve value to a 32 bit signed equivalent:

$ip = ip2long($ip);
if (PHP_INT_SIZE == 8)
{
    if ($ip>0x7FFFFFFF)
    {
        $ip-=0x100000000;
    }
}
Paul Dixon
That could be found in PHP documentation. And that's opposite of that, what I asked.
Thinker
ah yes, will modify
Paul Dixon
I think that's the solution I needed. I didn't know about PHP_INT_SIZE. Thank's for your edit.
Thinker
The "if (PHP_INT_SIZE == 8)" guard isn't really required - you'll get the result you want on 32 and 64 bit systems without it, but it makes the example more illustrative.
Paul Dixon
+1  A: 

-Misunderstood problem, see Paul Dixon's answer above.


64bit unsigned integers are not technically supported in PHP5. It will use the native type. To convert to a 32bit signed int from a 64bit signed int without losing the high bit, you could mask and then type cast the output:

$ip_int = ip2long($ip);
if (PHP_INT_SIZE == 8) // 64bit native
{
  $temp_int = (int)(0x7FFFFFFF & $ip_int);
  $temp_int |= (int)(0x80000000 & ($ip_int >> 32));
  $ip_int = $temp_int;
}

On a 64 bit system, printing this value ($ip_int) will display an 'unsigned' integer since we've removed the high bit. However this should allow you to take the output and store it how you wish.

cyberbill
Paul Dixon
caught myself right after posting it. modified example. ty.
cyberbill
On a 64 bit system, the return value of ip2long is always positive. That's the nature of the OP's problem: on 64bit systems he always get a +ve value, but on 32bit systems he gets some -ve values.
Paul Dixon
I had misread the 'unsigned' nature of his question. I had believed the output was coming back signed since there are no unsigned ints. I'd vote up your now modified answer, perfect, but do not have enough points
cyberbill
This is not working, but the idea with PHP_INT_SIZE was good. Your code is always returning positive values, but a signed value was my need.
Thinker
excellent, :) now I can.
cyberbill
+1  A: 

Fwiw, if you're using MySQL it's usually a lot easier and cleaner if you just pass in the IPs as strings to the database, and let MySQL do the conversion using INET_ATON() (when INSERTing/UPDAT(E)'ing) and INET_NTOA() (when SELECTing). MySQL does not have any of the problems described here.

Examples:

SELECT INET_NTOA(ip_column) FROM t;

INSERT INTO t (ip_column) VALUES (INET_ATON('10.0.0.1'));

The queries are also much more readable.

Note that you can not mix INET_NTOA()/INET_ATON() in MySQL with ip2long()/long2ip() in PHP, since MySQL uses an INT UNSIGNED datatype, while PHP uses a signed integer. Mixing signed and unsigned integers will seriously mess up your data!

Wouter Bolsterlee