当服务器端所保存的session数据,变的数量数量巨大是,如何更好地管理?
如果以文件的形式出现,目录内的文件过多,应该将目录分子目录管理:
通过简单的服务器的配置,就可以将session分子目录保存
session.save_path:保存路径
session.save_path = "N:e:/php1116/temp";(php.ini)——表示目录以N级子目录的形式管理:表示,在temp下建立N级的子目录
确定子目录的方法是,依据session的首字母,确定第一集子目录名。
类推,几级子目录,就是使用第几个sessionID的字母,来确定名字。
注意:该子目录需要手动创建子目录。
Tips:如何确定sessionID的每个字母的可能性字符?
确定的方式,由配置文件(php.ini)session.hash_bits_per_character = 5 决定,表示的是,sessionID中,每个字符,表示几个二进制位。从而决定了sessionID的每个字符的可能性:
????sessionID的总长度是固定的(128bit位),为了方便表示成字符的形式。
????当前的这个配置,指的是,使用每个字符,表示几个二进制位的意思。
????默认5个,一个字符,表示5个位。5个位的组合,有32中可能性(00000—11111(2^5-1)由0-9,a-v)。
如果session数据需要共享如何处理?
????当站点负载太大,一台web服务器支持不了!此时将session数据,放置在一台数据服务器上。(mysql,memcache内存数据服务器)
????以session数据存储在mysql服务器上为例,说明,session的存储机制重写(session入库)
????
????至少:1,在session开启时,完成读数据。
????????2,在脚本结束时,将数据写入。
????改写,默认的session在存储读取数据的处理代码,完成其他位置的数据的存储读写:
????告知PHP,在需要读session数据和谐session数据时,使用用户设置的行为(定义可以完成读和写的方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
//定义几个需要储存session数据的方法
function?sess_read()?{
}
function?sess_write()?{
}?
function?sess_destroy()?{
}
function?sess_open()?{
}
function?sess_close()?{
}
function?sess_dc()?{
}
?
内置函数session_set_save_handler(开始,关闭,读,写,销毁,垃圾回收);
1
2
3
4
5
6
7
8
9
<?php
session_set_save_handler(
????'sess_open',
????'sess_close',
????'sess_read',
????'sess_write',
????'sess_destroy',
????'sess_gc'
);
?
session表中每条记录????对应????session文件
sess_id字段????????对应????session文件名字
sess_data字段????????对应????session文件内容
在该方法运行时,sessionID一定是确定好了的(PHP核心)。
接收的参数是:$sess_id当前的sessionID,返回值是session数据的内容即可,不需要反序列化(PHP核心)。如果没读到,返回数据为空字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function?sess_read($sess_id)?{
????//连接数据
????mysql_connect('127.0.0.1','root','1234abcd');
????mysql_query('set?names?utf8');
????mysql_query('use?itcast');
????//读数据
????$sql?=?"select?sess_data?from?`session`?where?sess_id?=?'$sess_id'";
????$result?=?mysql_query($sql);
????if?($row?=?mysql_fetch_assoc($result))?{
????????return?$row['sess_data'];
????}?else?{
????????return?'';
????}
}
?
负责将session数据保存到session表内,不需要我们序列化(PHP核心完成)
需要的参数:
当前的sessionID和买药存储的数据
1
2
3
4
5
6
7
8
9
10
<?php
function?sess_read($sess_id)?{
????//连接数据
????mysql_connect('127.0.0.1','root','1234abcd');
????mysql_query('set?names?utf8');
????mysql_query('use?itcast');
????//存在则更新,不存在则插入
????$sql?=?"insert?into?`session`?values?('$sess_id','$sess_data')?on?duplicate?key?update?sess_data='$sess_data'";
????$result?=?mysql_query($sql);
}
?
删除,当前session表中,当前session记录
得到当前的sessionID,执行delete,以sessionID作为条件
当用户脚本调用了session_destroy()函数后,该自定义函数被调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
/**
?*?销毁
?*
?*?@param?$sess_id?string
?*
?*?@return?bool
?*/
?function?sess_destroy($sess_id)?{
????//连接数据
????mysql_connect('127.0.0.1:3306','root','root');
????mysql_query('set?names?utf8');
????mysql_query('use?itcast');
????$sql?=?"delete?from?`session`?where?sess_id='$sess_id'";
????return?mysql_query($sql);
?}
?
初始化,session存储所需要的资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
<?php
/**
?*?开始
?*?初始化资源
?*
?*/
?function?sess_open()?{
????//连接数据
????mysql_connect('127.0.0.1:3306','root','root');
????mysql_query('set?names?utf8');
????mysql_query('use?itcast');
?}
Tips:接收到$sess_name,$sess_path参数:
$sess_name,cookie中,保存sessionID的cookie变量的名:
PHPSESSID
session.name = PHPSESSID(php.ini),可以使用php函数session_name()得到当前的配置值(ini_get('session.name'))
$sess_path指的是session的存储路径看,session.save_path配置
session存储时占用的资源,典型的return true
1
2
3
4
<?php
function?sess_close()?{
????return?true;
}
session的有效期是关闭浏览器失效。原因是:浏览器所保存的sessionID是临时性的,关闭浏览器丢失。
问题1:如何识别那些记录(文件)是垃圾?
????判断哪些记录(文件),在多久之后已经没有被使用过了就认为是垃圾。默认是1440是,24m
????session.gc_maxlifrtime = 1440(php.ini)
问题2:如何删除?
????记录下来当前数据(文件)的最后的处理时间:
????增加一个字段保存,最后的修改时间
????
????在写的时候,记录当前的时间戳才可以:
1
2
3
4
5
6
<?php
function?sess_write($sess_id,$sess_data)?{
????//存在则更新,不存在测插入
????$sql?=?"insert?into?`session`?values?('$sess_id',$sess_data',unix_timestamp())?on?duplicate?key?update?sess_data='$sess_data',expire=unix_timpstamp()";
????return?mysql_query($sql);
}
????怎么执行?在session_start()开启session机制时,有机率的调用垃圾回收方法来找到过期的session数据并把它删除。
????机率由以下几个配置决定:
????可能性:session.gc_probability = 1
????除数 :session.gc_divisor = 1000
????1/1000的可能性。
????sess_gc(),可以得到当前的最大保存周期为参数
1
2
3
4
5
6
7
8
9
10
<?php
/**
?*?垃圾回收
?*
?*?@param?$maxlifetime
?*/
?function?sess_gc($maxlifetime)?{
????$sql?=?"delete?from?`session`?where?expire?<?unix_timstamp()?-?$maxlifetime";
????return?mysql_query($sql);
?}
应该先重写session的存储机制,再开启session
seesion_set_save_handler();
session_start();
如果session设置为自动开启怎么办?
????此时,ini_set();也不顶用。因为自动开启,意味着用户脚本执行前,开启完毕了。
????可以利用Apache的配置文件,.htaccess分布式配置文件完成。
????php_flag session.auto_start 0(0,表示关闭php的自动开启,也就是说Apache的配置文件可以控制php的配置文件)
增加一个session入库的工具类(SessionDBTool.class.php)。在框架目录中,为工具类,独立使用子目录进行管理。
在open的时候,完成数据库的连接。利用mysqlDB类完成,增加一个属性保存MySQLDB类的对象,在open执行是,得到该对象。
1
2
3
4
5
6
7
8
9
10
<?php
private?$db;
/**
?*?开始
?*?初始化资源
?*/?
?function?sess_open()?{
????//连接数据
????$this->db?=?new?MySQLDB($GLOBALS['config']['database']);
?}
余下的所有数据库的操作,有$this->db完成:
1
2
3
4
5
6
7
<?php
function?sess_read($sess_id)?{
????//读数据
????$sql?=?"select?sess_data?from?`session`?where?sess_id?=?'$sess_id'";
????return?(string)?$this->db->fetchColumn();//得到的数据一定是个字符串?
}
?
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
?*?垃圾回收
?*
?*?@param?$maxlifetime
?*/
function?sess_gc($maxlifetime)?{
????//读数据
????$sql?=?"delete?from?`session`?where?expire?<?unix_timestamp()-$maxlifetime";
????return?$this->db->query($sql);
}
处理表:保证数据库内存在it_session表
创建该表
将语句内都改成正确的格式:
1
2
3
4
5
6
<?php
function?see_write($sess_id,$sess_data)?{
????//存在则更新,不存在则插入
????$sql?=?"insert?into?`it_session`?values?('$sess_id','$sess_data',unix_timestamp())?on?duplicate?key?update?sess_data='$sess_data'";
????return?$this->db->query($sql);
}
将权限改成public
增加该类对象的构造方法。设置处理器,此时是由六个对象方法,充当的session数据的处理器。使用一个数组,第一元素是对象,第二元素是方法名,来实现:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
/**
?*?构造
?*/
?public?function?__construct()?{
????array($this,'sess_open'),
????array($this,'sess_close'),
????array($this,'sess_read'),
????array($this,'sess_write'),
????array($this,'sess_destroy'),
????array($this,'see_dc')
?};
开启session
凡事session_start(),则直接实例化该类对象即可。
back/AdminController????signinAction()
1
2
3
4
5
6
7
if?($result)?{
????//合法管理员
????//记录登录标识
????//使用SessionDB工具
????new?SessionDBTool;
????$_SESSION['is_login']?=?'yes';
}
back/IndexController????IndexAction()
1
2
3
4
public?function?indexAction()?{
????//判断当前是否登陆
????new?SessionDBTool;
}
以规则的方式:
framework/Framework.class.php????static userAutoload()
1
2
3
4
//是否是工具类
elseif?(substr($class_name,-4)?==?'Tool')?{
????require?FRAME_TOOL_PATH.$class_name.'.class.php';
}
将这个目录常量设置好:framework/Framework.class.php????static initPath()
1
2
3
private?static?function?initPath()?{
????define('FRAME_TOOL_PATH',FRAME_PATH.'tool'.DS);//工具类目录
}
测试:shop.235.com/index.php?p=back&c=Index&a=index
Tips:应该再修改一个配置session.save_handler=files,最好将这个值,改成user,表示用户自定义。ini_set('session.save_handler','user');
1
2
3
4
5
<?php
public?function?__construct()?{
????//以用户的方式,存储session数据
????ini_set('session.save_handler','user');
}
?
session,在浏览器关闭就失效了。原因是cookie中的sessionID丢失了。延长seesionID的cookie变量的生命周期即可。
setcookie(名,值,有效期)名:session_name;值:session_id();
setcookie(session_name(),session_id(),time()+3600);
$_SESSION['where'] = 'itcast-php';
php同时提供一个专门的函数:session_set_cookie_params(有效期,有效路径,有效域名);//设置一下session相关的cookie变量(sessionID变量)的参数信息
有效期:时间区间的概念,不是时间戳。
session_set_cookie_params(3600);
session_start();
firbug或者自带查看器(网络),查看Set-Cookie
Tips:将session数据的垃圾判定时间加长,session.gc_maxlifetime = 3600
session的sessionID保存在cookie内。典型的,如果浏览器用户强制禁用了cookie技术,则浏览器用户放弃会话技术,不提供对session的支持。(例如Firefox)
技术上,可以实现,找到可以在多次请求间传递数据的方案,就可以传递sessionID,典型的使用URL上传递PHPSESSID=随机生成的名字,即可
session.user_only_cookies设置为0(php.ini),表示可以用其他的方案传递sessionID
session.user_trans_sid设置成1(php.ini),表示自动传输到sessionID在url上(或者表单内部)
此时,如果浏览器金庸了cookie,则php会自动地在所有的超链接的href属性的url值上,增加一个参数PHPSESSID值为当前的sessionID(观察浏览器请求地址栏)
在form表单内可以自动生成,典型为,value="随机生成的名字" name="PHPSESSID"
Tips:使用php输出的url字符串,不能自动增加该参数:需要手动添加。 ?
1
2
3
$_SESSION['where']?=?'itcast-php';
header('Location:103.php?'.session_name().'='.session_id());
?>
控制器
back/IndexController????IndexAction()
模型????暂时没有
视图????
view/back/index.html
采用框架集的布局模式:frameset框架集????frame框架????采用(上,左中右)
因此后台首页的整体需要: 整体 、上、左、中、右5个功能才能完成。每个功能应该为一个动作。
我们把exshop的后台首页拿来用,修改frame下的src为我们定义的参数
例如:top:????src="index.php?p=back&c=Index&a=top"
back/IndexController创建四个动作,topAction(),menuAction(),dragAction(),mainAction
浏览器:shop.235.com/index.php?p=back&c=Index&a=index(Firefox查看发出5个请求)
控制器
back/IndexController????topAction()????加载top部分的视图
1
2
3
public?function?topAction()?{
????require?CURRENT_VIEW_PATH?.?'top.html';
}
模型
视图????back/top.html????Tips:注意修改href中CSS的路径
除了index动作之外,后台所有的动作都需要登陆后才能执行。如何增加一段,可以在某个平台内,都执行的代码?(增加平台基础控制器)
?
在每个平台的控制目录下:
1
2
3
<?php
class?PlatformController?extends?Controller?{
}
当前平台的其他控制器继承:
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
?*?后台首页控制器类
?*/
?class?IndexController?extends?PlatformController?{
}
/**
?*?后台管理员控制器类
?*/
?class?AdminController?extends?PlatformController?{
?}
?
back/PlatformController????checkLogin()
1
2
3
4
5
6
7
8
9
<?php
class?PlatformController?extends?Controller?{
????/**
?????*?验证管理员是否登陆
?????*/
?????protected?function?checkLogin()?{
?????
?????}
}
在控制器方法执行前,先判断当前用户是否登陆。
实例化控制器对象。在公共后台平台控制器的构造方法中,完成对验证是否登陆的调用。
back/PlatformController????__construct()
1
2
3
4
5
6
7
8
9
<?php
class?PlatformController?extends?Controller?{
????/**
?????*?验证管理员是否登陆
?????*/
?????protected?function?checkLogin()?{
????????$this->checkLogin();
?????}
}
back/PlatformController????checkLogin()
后台,所有的,但是除了登陆相关的动作,才需要验证。因此,在验证是否登陆是,先排除当前不需要验证的动作:
1
2
3
if?(CONTROLLER_NAME?!=?'Admin'?||?!in_array(ACTION_NAME,array('login','signin')))?{
????//验证
}
验证码是干什么用的?防止计算机,模拟浏览器提交表单,速度快,次数多,得到可以被登陆后才可以看到的数据。
典型的:暴力破解,论坛灌水,恶意评论。
验证原理?
将可能的值,形成一个合集,丛集合中随机获取。
gd就是php中最常用的图片处理技术。
首先开启gd扩展:extendsion=php_gd2.dll(php.ini)
gd的基本使用流程:
PHP操作图片的工具即可。就类似用户操作图片处理软件一样
场景:利用PHP制作一张 纯色的绿色的500 * 400 的图片
利用函数:创建新画布(新建)
imagecreatetruecolor(),创建一个真彩色图片画布
imagecreate(),创建一个调色板图片画布,需要提供尺寸,宽,高
基于原有图片创建(打开)
imagecreatefromXXX()其中XXX表示图片格式
imagecreatefromjpeg(),imagecreatefrompng(),imagecreatefromgif()
利用gd提供的各种工具函数(写字,填充,画矩形,画圆),去完成对画布的操作。
使用绿色填充当前画布
选择颜色(分配颜色)????将颜色分配到画布上,此画布,可以使用该颜色进行操作
????函数:imagecolorallocate();为画布分配颜色
????颜色标识 = imagecolorallocate(画布,R,G,B);
????采用RGB颜色管理方式,设置颜色,每个颜色值在0—255之间的整数
????
填充画布????imagefill()函数可以完成填充
????填充结果 = imagefill(画布,填充位置X,填充位置Y,颜色)
????将填充点的周围相邻的颜色相同的点进行填充。
????填充位置表示:使用填充点的坐标表示(X,Y)。原点在哪里,左上角,在画布的左上角,范围从(0,0)开始表示原点到右下角 X-1,Y-1
????
一个画布资源,可以输出任意多次,并且可以保存成任意格式。
使用函数:????imageXXX()XXX表示类型
????????imagejpeg()输出jpeg
????????imagepng()输出png
????????imagegif()输出gif
imageXXX(画布)支持,直接输出到浏览器,或者输出到某个文件内,
如果增加第二个参数,则表示输出到文件。
如果没有第二个参数,则输出到浏览器。
但是,浏览器会将服务器做出的响应数据当作文本html字符串看,因此,需要告知浏览器,服务器个你的响应数据,是图片png格式的图片:
使用header()函数,header('Content-Type:image/png');
imagedestroy(画布)
背景在优先的图片内随机切换、白色边框、码值由大写字母和数字组成、颜色(黑白随机)
去ecshop下把图片拿到captcha文件夹
随机得到图片名,基于该图片,创建画布:画布 = imagecreatefromjpeg(文件地址);
增加白色边框(画一个矩形即可)
利用一个函数:结果 = imagerectangle(画布,左上角X,左上角Y,右下角X,右下角Y,颜色);
分配白色:
写码值????生成码值
保存到session:
将字符串写到图片上:结果 = imagestring(画布,字体大小,位置X,位置Y,字符串,颜色);
字体大小:1-5,由小到大
颜色,黑白随机
测试: