PHP默认的存储机制并不适用大项目,流量很大的时候会产生很多的session文件,用文件存储session数据效率上不高。如果一个项目一台服务器无法满足需求,用多台服务器做负载均衡时,就更无法用默认的session存储机制处理了。最好的解决办法是重写PHP的session存储机制。
怎么重写?先看一个函数:
在session_start()开启之前,运行这个函数session_set_save_handler()等于不再使用PHP默认的session存储机制,而是使用代码重新定义的六个方法或函数来完成session的打开、关闭、读、写、销毁、回收六种操作。
session_set_save_handler有两种传参方式,如果用面向过程的方式重写session存储机制就按第一种方式传参,如果用面向对象的方式重写则按第二种方式传参。
其实这两种方式没什么区别,无非就是传参数时有所不同。
下面我们用面向对象的方式重写session的存储机制:
还记得mysql的memory存储引擎吗?memory表的数据就是存在内存里的,我们用它存储session数据。
1)先定义一个memory存储引擎的session表
2)开始写session类:
构造函数里12行告诉PHP使用用户自定义的方法操作session,13行代码相当于注册了6个方法分别操作session。
以下这6个方法必须要写的。
注意:往session写入数据,是在脚本运行的最后才写入。如果有用session记录当前时间戳的需求,一定要考虑到差值的问题。
打比方:
写一个限制同一用户频繁访问同一页面的功能(限制两次访问间隔为20秒)。
用户A第一次访问页面B,脚本在开头就执行了往session中写入当前时间戳(脚本不到1秒就执行完了)。
过了10秒钟,用户A第二次访问了页面B(在新标签中打开)。
第二次脚本从session中就获取到第一次访问的时间戳。( 第-次时间戳 - 第二次时间戳 < 20 )。
所以程序就可以拒绝用户的第二次访问请求。
用户A第一次访问页面B,脚本在开头就执行了往session中写入当前时间戳(可脚本花费了60秒才执行完)。
过了10秒钟,用户A第二次访问了页面B(在新标签中打开)
由于第一次访问,脚本还没有执行完。第二次脚本从session中就获取不到第一次访问的时间戳。
所以程序就不会拒绝用户的第二次访问请求。
3)在入口文件index.php执行session类:
14行代码实例化session类,这样我们就重写好了session的存储机制。
运行下:
出错了,这个错误楠神研究了半天,通过打印:
通过69行打印对象属性知道原来查询的数据表不是“session”,而是“admin_list”。
注意:
写到这忽然觉着楠神前面的代码出现了点小毛病,下面解释下,可能有些乱,我也是写到这才明白(写程序就这样不可能一开始什么都明明白白)。
出错的地方:使用Db::table('表名')静态方法获取一个Db对象,不应该把它赋值给一个属性变量后再使用。打比方:
程序按顺序执行:
1)$_sess = Db::table('session');$_sess->select();Db对象里table参数此刻是“session”。
2)$_admin = Db::table('admin_list');$_admin->select();由于我们的Db类实现了单例模式,$_sess和$_admin的值是同一个Db对象,Db对象里table参数此刻变成“admin_list”。
3)由于不再执行$_sess = Db::table('session');这一步了,$_sess->select();Db对象里table参数此刻还是“admin_list”。
这就是我们上面出错的主要原因。怎么解决呢?
最简单的解决方法:不要把Db::table('表名')赋值给一个属性变量,在类的其他方法里还是直接使用Db::table('表名')。(楠神已把代码全部修改过来了)
复杂点的方法思路:为每个表单独建一个模型类,类里面有记录表名的属性,继承Db类,用哪个表就实例化那个表的模型类。根据前面面向对象的知识,可试着去写一写,由于改动比较大,我就不去写了。
解决了上面的小毛病,我们的session重写就可以正常实现了。