一个小坑 | PHP7 的 ip2long 和 long2ip 细节变动

起因

项目中IP地址一直使用的 int(11) signed 数据类型,最近将PHP版本从5.6.25升级到了PHP7.1.23,算是最新版了吧,因为截至目前Imagick拓展只支持到PHP7.1.23,因此就选择了兼容的最新版本PHP7.1.23,目前PHP官方的版本下载情况如下:

1540625444519

整体迁移还是非常平滑的,基本没有出现任何错误,但是在用户登录部分记录最后登录IP的时候,出现了一个数据库错误:Out of range value for column 'last_login_ip' at row 1

过程

因为使用的是ip2long转换并保存的所以一直是围绕在这个字段周围进行排查的。

过程还是非常顺利,很快就找到了问题并解决了,没有花太多时间。

但是对于ip2long的具体改动还是值得研究的。

于是大致写了一段测试代码如下图:

1540626147251

运行结果如下图所示:

1540626469416

关键部分:

1540626838163

文件就不贴上了了,有兴趣的运行一遍以上代码就有结果了。

从表中很明显的看出来,在PHP7.1.23下,将负数的IP通过long2ip转换成IP是可以正常的,但是再次通过ip2long转换成整数时,结果却变成了一个非负整数。

再次尝试将这个非负整数通过long2ip转换成IP地址,IP可以正常还原。

首先,原来ip2long的取值范围是4字节 -2 147 483 648 至 2 147 483 647 ,在Mysql中用int(11) signed 刚好能存储全部范围。而PHP7的取值范围变成了0 至 4 294 967 295,为了图方便直接改成bigint是最省事的方法,例如这篇文章: https://blog.csdn.net/hotlinhao/article/details/76230149

但是作为一个程序员真不应该如此草草了事,bitint直接使用了8字节的大小,存储大小直接翻了一番,而对数据库的查询效率方面的影响不确定,但或多或少肯定会有一些影响的,并且在数据量越来越多的情况下很越来越明显。

在PHP官方网站中大致找了一下这个版本以及附近的几个版本的函数改动(比如: http://php.net/migration71 ),并没有找到关于ip2long以及long2ip两个方法的改动记录,也不确定是官方漏掉了还是在之前的版本有提到。

其实解决方法非常简单,对Mysql的基本字段数据类型了解的话,很明显就能想得到,通过 unsigned 就能够完美的解决问题,int(11) unsigned int整数型的无符号范围正好是0 至 4 294 967 295

结果

希望个人在使用或者部署环境的时候,注意一下这个问题,最好在实际部署环境测试相关函数的执行结果便于确定数据库字段的选择。

前车之鉴,后车之师,个人踩过的一个小坑,记录下来希望能帮助到各位。

文章有问题的地方望各位前辈不吝赐教。