2017年12月17日 星期日

PHP、MySQL,IP 轉整數存資料庫

MySQL
INT:0 ~ 4294967295
mysql> SELECT INET_ATON("0.0.0.0"); #0
mysql> SELECT INET_ATON("255.255.255.255"); #4294967295
mysql> SELECT INET_NTOA("0"); #0.0.0.0
mysql> SELECT INET_NTOA("4294967295"); #255.255.255.255
註:
INET_ATON = Address to number
INET_NTOA = Number to Address



PHP(32 bit)
INT:-2147483648 ~ 2147483647
var_dump(ip2long("0.0.0.0")); #int(0)
var_dump(ip2long("127.255.255.255")); #int(2147483647)
var_dump(ip2long("255.255.255.255")); #int(-1)
var_dump(ip2long("128.0.0.0")); #int(-2147483648)
var_dump(long2ip(0)); #string(7) "0.0.0.0"
var_dump(long2ip(2147483647)); #string(15) "127.255.255.255"
var_dump(long2ip(-1)); #string(15) "255.255.255.255"
var_dump(long2ip(-2147483648)); #string(9) "128.0.0.0"
因為在32位元系統,PHP int 型態有分正負號(signed),
範圍為 -2147483648 ~ 2147483647(http://php.net/manual/en/function.intval.php)

可用 sprintf(%u) 轉換成不帶正負號的 (unsigned) 的10進制字串,
可轉成:"0" ~ "4294967295"
var_dump(sprintf("%u", ip2long("0.0.0.0"))); #string(1) "0"
var_dump(sprintf("%u", ip2long("127.255.255.255"))); #string(10) "2147483647"
var_dump(sprintf("%u", ip2long("255.255.255.255"))); #string(10) "4294967295"
var_dump(sprintf("%u", ip2long("128.0.0.0"))); #string(10) "2147483648"
var_dump(long2ip(0)); #string(7) "0.0.0.0"
var_dump(long2ip(2147483647)); #string(15) "127.255.255.255"
var_dump(long2ip(4294967295)); #string(15) "255.255.255.255"
var_dump(long2ip(2147483648)); #string(9) "128.0.0.0"



PHP(64 bit)
INT:0 ~ 4294967295
var_dump(ip2long("0.0.0.0")); #int(0)
var_dump(ip2long("127.255.255.255")); #int(2147483647)
var_dump(ip2long("255.255.255.255")); #int(4294967295)
var_dump(ip2long("128.0.0.0")); #int(2147483648)
var_dump(long2ip(0)); #string(7) "0.0.0.0"
var_dump(long2ip(2147483647)); #string(15) "127.255.255.255"
var_dump(long2ip(4294967295)); #string(15) "255.255.255.255"
var_dump(long2ip(2147483648)); #string(9) "128.0.0.0"

在 64 位元的 PHP,int 最大值可到 9223372036854775807,
有無用 sprintf(%u) 不影響轉出的數字範圍
var_dump(sprintf("%u", ip2long("0.0.0.0"))); #string(1) "0"
var_dump(sprintf("%u", ip2long("127.255.255.255"))); #string(10) "2147483647"
var_dump(sprintf("%u", ip2long("255.255.255.255"))); #string(10) "4294967295"
var_dump(sprintf("%u", ip2long("128.0.0.0"))); #string(10) "2147483648"
var_dump(long2ip(0)); #string(7) "0.0.0.0"
var_dump(long2ip(2147483647)); #string(15) "127.255.255.255"
var_dump(long2ip(4294967295)); #string(15) "255.255.255.255"
var_dump(long2ip(2147483648)); #string(9) "128.0.0.0"

[其他測試]
64 位元的 PHP 將 32 位元的負數結果轉回 IP,似乎也可正常執行(環境:PHP 7.0.12)
var_dump(long2ip(-1)); #string(15) "255.255.255.255"
var_dump(long2ip(-2)); #string(15) "255.255.255.254"
var_dump(long2ip(-3)); #string(15) "255.255.255.253"
var_dump(long2ip(-2147483647)); #string(9) "128.0.0.1"
var_dump(long2ip(-2147483648)); #string(9) "128.0.0.0"


結論:
  1. IP 轉數字存進 MySQL,MySQL 欄位建議使用 INT UNSIGNED,只須 4 bytes,儲存範圍剛好 0~4294967295
  2. 可用 MySQL INET_ATON() 將IP處理成數字存入。
  3. 或先用 PHP long2ip() 處理成數字(要相容 32bit PHP,則long2ip的值須再用 sprintf(%u) 處理)

參考:

沒有留言:

張貼留言