乌大湿胡扯——连续签到在PHP如何实现


我们今天来扯一个编程思路。

我们群里有签到功能,会有个签到排行榜,我们来分析一下是怎样实现的。

可能有的同学会说,那还不简单,判断前一天有有没有签到,有的话就是连续,没有的话,就是不连续。

没错,是这么个道理。

但是,每个用户都记录每天的签到时间,这个数据不少啊,是可以将旧数据删除,但怎样才是旧呢?

只保留昨天的?那昨天没签到的话,“上一次签到时间”就没有了。

还有,每次判断都要把前一次签到时间拿出来判断是不是“昨天”,或者从“昨天”签到表里查一下有没有你。

是不是觉得有点麻烦?


我不是说那个办法不行,是行的,只是,我们有一个方案的时候,就停止了思考,我觉得不利于我们学习编程。


今天跟大家一起来分析一下腾讯QQ群签到功能。

我们签到后,可以看到连续签到排行榜,这里有显示每个群友连续签到的次数。

我们先来考虑一个问题,昨天我签了,今天我还没签,那这个榜上,会不会有我呢?(假设这个榜能显示所有有连续签到的人)

用别的号签到后,就可以看到排行榜,事实上,我昨天有签,今天没签,那么别人今天还是能在榜上看到我的。

然后我们再来第二个假设,假设今天我就是不签,到了明天,我签到之前,群友们从榜上还能看到我吗?

明天我签到,就肯定有了,连续数是1。

事实,明天的榜上将看不到我了,因为我今天没签。

那问题是,我的名是什么时候被从榜上摘下来的呢?换句话说,就是我的连续签到是什么时候被清零的呢?

我们知道,一般用于web的php,都是被动的,没人访问,就不会执行。


同样的结果,可以有无数种实现方式,只要思路对了,代码、数据库,都是浮云。


今天跟大家分享一下我的思路,胡扯一下。

首先,我们要记录两个数值,就是两个字段,一个是最后签到时间,一个是连续签到天数。

如果,我昨天没有签到,那么今天签到的时候,要做两件事,1是把最后签到时间设为当前时间;2就是把连续签到天数设为1。

如果,我昨天是有签到的,那么今天签到的时候,要做两件事,1是把最后签到时间设为当前时间;2就是把连续签到天数+1。

我们先分析这两种情况,有什么相同。就是都要把最后签到时间设为当前时间。也就是只要签到,就要做。

不同的是,昨天没签今天签,就连续1天,昨天有签今天签,就连续天+1。

我们想想有没有办法将两种情况都设为相同?就是把不同的先抽出来放一边,把相同的写成同样的,单独考虑不同的情况后,剩下做的事就无视各种情况了。

要将+1这个变化为设为某个值,貌似不太科学,这个是可变的,不同的时候不同的人签到,者是不同的值。

我们从另外一边考虑,连续天设为1,要转化为连续天+1,0+1 = 1,也就是说,只要事先将连续天设为了0,那么剩下的事就完全一样了。

先把设为0放一边,那么,无论昨天有没有签到,只要今天签到,都是做两件事,1是把最后签到时间设为当前时间;2就是把连续签到天数+1。

那么,把“设为0”放在哪会好呢?还是放在今天签到的事件里,那我们刚才的折腾就好像有点多余了。


我们不妨先看另外一个需求,排行榜。

我们先看需要,我前天有签到,连续天数是n,因为我昨天没签到,那么,今天你去看排行榜的时候,应该看不到我才对,因为我昨天没签,断开了。

也就是,到了今天,无论我今天有没有签到,我的签到纪录已经被清0,连续签到天数已经被设为0。看回刚才的疑问“设为0”放在哪会好些?可以得出,肯定不是放在签到事件里了。

还是刚说的情况,在昨天,还没到23:59:59,都不能判定我昨天没签,很可能我会在23:59:59签到了,也就是说,在昨天,我的连签天是不会被清的。

到了今天,由于我昨天真的没签到,所以,必须是断了,现在要考虑的是,我的连签天数到底是在什么时候什么事件里被清了。

继续分析,无论今天去看榜的人,在什么时间点去看,我的连签都已经是0了,极限化思维,在0点0分0秒去看榜,我的连签都已经是0了,而刚才说的昨天的23:59:59都不能将我的连签清0,

那么可以得出,清0应该是在今天0:00:00这一瞬间去做的。


时间可以确定了,事件呢?我们先从最简单PHP模式去分析,即web,即http协议下,我们的网页的PHP程序,是要在被用户访问,发起了http请求,才会被动地运行,也就是说,没人去访问网站,就没有PHP程序会被运行,那么,在0:00:00怎样才能把我的连签天数清0?貌似,行不通,无法实现。

我们的思维不能被一开始我们的结论限制死了,往往一些我们认为是对的决策,反而会限制了我们的思路。当我们走到某个胡同的时候,可以尝试退回去再思考。
我们退回刚才的决定来想想,是不是真的必须0:00:00就要清0呢?
换一个角度去想,这个清0是给谁看的?
在还没人来看的时候,这个清0不做,也没人看得见啊,是不是我可以等到有人来看,再清0也可以呢?

初步看,是这样的,我们好像没必要在0:00:00秒去清0,在第一个人来看榜的时候清,就可以了。


问题看似已经解决了。如果真的以为这样可以解决了,那就是被大湿引到坑里去了,这是个坑,必须是坑,有bug。

看这么个情况,还是刚才那个假设,我前天有签到,连签天是n,昨天没签到,今天,这么巧,去看榜的人一个都没有,我的连签没被清,我今天签到了,看上面的,无论什么情况,签到就把连签加1,好吧,我的连签是n+1了。

又那么巧,在今天我签到后,就有人来看榜了,因为我昨天没签到,把我的连签清0了,结果就是,我今天明明有签到了,为什么我的连签会是0?

又那么假设,我今天签到后,一直也没人去看榜,明天,我又签到了,然后有人看榜,由于明天的昨天即今天,我有签到,所以,我没被清0,那么,我的连签没断~!我明明是断了一天,在这bug下,我居然没断。

这个两种情况,综合起来,问题都出在:在看榜前,我签到了。

有的同学很聪明,想到bug的解决办法了:在签到事件里,先判断将连签清0了,再+1;在看榜事件里,判断到昨天没签到的,再分今天有没有签到,今天也没签,就清0。

看起来,这个的确可以将刚两种bug的情况解决了。但是。。。。。。我们刚才将签到事件两种情况抽取成相同的功夫,是白费了,又回到原来的样子。

先总结一下:

签到事件:将昨天没签到的人的连签清0,连签+1;

看榜事件:将昨天没签到今天也还没签到的人的连签清0,获取排行榜。


看来,是乌大湿带着大家转了一圈,还是回到原点:签到时,必须判断是否连签。

真的必须吗?真的没有化简的方案吗?

再跟重新看一遍,重新再走一圈,看看有没有突破口。

看到刚才那里,说没办法实现0:00:00去执行清0操作。

假设这个我们有办法实现(暂时不管实现方式),那么,我们签到事件不用管清0的事了,直接连签+1;看榜事件也不用双重判断了,直接取排行榜就行了。多省事啊~!!

我们只要能突破这个0:00:00去执行清0操作,其他的事就简单多了,省事省心省CPU省内存!

因为我们在0:00:00去清0,肯定会比签到都早,所以不用判断今天有没有签到。

实现定时间点运行,我们刚才想的php做不到,是不是可以通过其他方式可以做得到呢?

例如,http://www.5dphp.com/art_267.html mysql可以做得到。

还有,linux下的crontab定时执行脚本,也可以做得到。

如果有研究过php的cli模式的同学,我们也php也可以做得到,参考手册time_sleep_until函数。


我这里就说说用php的cli模式。

我们在手册或者其他资料里,可以知道,php脚本在cli模式下,是不会超时的。也就是说,while(1)这样的写法,可以让脚本无限循环一直执行某些操作,直到接收到中断信号。

time_sleep_until函数可以让脚本休眠到指定时间点,那么我们就可以在while(1)的循环体里,让脚本休眠到次日的0:00:00,然后再执行清0操作。

脚本不难,我不上码。然后接下来的事,就是让脚本运行,或者在后台运行,这个可以根据操作系统,使用不用的方式或者命令。


扯了这么多,总结一下,不是总结这个连续签到的功能实现,是总结我们的编程思路。

很明显,大湿最后扯到了php以外的东西,或者是php web应用以外的东西,我们利用这些不是php的技术,轻松方便简洁地解决了php麻烦的事情。

这里也体现一个情况,程序写的复杂、强悍,不一定就能体现得很高手。反而利用好自己掌握的知识,轻松解决需求,尽管写的代码很简单,也不失大师风范。

我是大湿,不是大师。


好,今天的胡扯到这里,谢谢大家。

欢迎大家继续关注乌大湿的胡扯教程,胡扯不犯罪,欢迎加入Q裙跟大伙一直胡扯瞎扯。93701629。