本章目录
为什么要有会话控制
--http协议的特性
http协议是无状态协议,它不会记录来自同一个客户端的2个请求之间的关系(比如共享几个变量),所以引入会话控制,解决此问题。
--什么是会话控制
HTTP协议是无状态的协议,它没有一个内在机制维护两个“请求”之间的状态,例如,用户登录一个网站后,再去请求该网站打开其他页面时,HTTP无法识别该用户已登录.会话控制的思想能够在同一个网站内跟踪某个用户,记录用户的行为,即它允许WEB服务器跟踪同一个浏览器用户的连接。
--解决页面间传递变量的方法
- GET,POST方法解决(使用a链接,在链接中使用GET/POST传递变量),这种方式不适合大量链接,变量(比如在1个页面中有1000个链接,这1000个链接都使用5个共享变量,需要在1000个链接都写上5个变量的GET/POST,很麻烦)。
- 将变量存在文件或数据库中,第2个页面再去读写文件/数据库(这种方式中,所有人共用变量,也不是跟踪用户的首选)。
- cookie技术(字面翻译:服务器给客户端的礼物,小甜点): 客户端使用一个文件(cookie文件)保存用户信息,再访问同一个网站的其他页面,都会带着这些信息过去,服务器在每个页面通过这些信息区分用户。
cookie是服务器发给客户端的,就像商场的会员卡一样,会员卡是商场给用户的,每次用户都拿这个会员卡来商场,会员卡中有用户的信息。
session在服务器端保存用户信息,用户都会被分配一个session id,服务器通过session id来区分用户。
cookie概述
--cookie工作流程
- 客户端访问服务器。
- 服务器给在用户电脑上设置cookie信息。
- 用户使用cookie访问其他页面。
--cookie保存在哪
windows下,cookie保存在c:\我的文档\用户名\cookies下。例如:
- C:\Documents and Settings\Administrator\Cookies
--php中设置cookie(cookie是作为http数据表包的头信息发送到客户端的)
使用网络函数setcookie()。
- bool setcookie ( string name [, string value [, int expire
- [, string path [, string domain [, bool secure [, bool httponly]]]]]] )
- name是变量名;
- value变量值;
- expire变量生存期;(到此时间点之前都有效)
- path规定了客户端访问哪些文件夹下的web页面时带上这个cookie(如果不设定,默认为根下所有页面);
- domain,规定了客户端访问哪个域名使用此cookie(一般不用设置,系统默认为给客户端设置cookie的域名);
- secure,是否使用https传输cookie值。
一般只设定前3个参数。
注意:setcookie()是头函数(更改http的包头),它必须写在所有输出语句之前。
--php中使用cookie
客户端中设定了cookie后,当它访问domain/path下的其他web页面就会使用cookie。这些页面接收到cookie后,就把cookie的变量存放到超全局数组$_COOKIE[]中。
注意:cookie中使用数组时,关联key不用加引号(和平时使用php有些不同),因为setcookie是原封不动的复制字符串,所以加的引号也会被复制。
--删除cookie
- 使用setcookie()将value设为'',就可以删除某个值。
- 或者将expire设为当前时间,也可以删除某个值。
当所有值都删除后,系统会删除文件。
session概述
--什么是session
session用于会话控制,在服务器端保存用户信息(cookie在客户端保存用户信息),用户都会被分配一个session id,服务器通过session id来区分用户。
用户保存session id的方法:
- cookie中保存;
- url的变量中带上session id(访问该网站的各页面的url中都带上该session id,就会被服务器识别是哪个用户。
session的保存位置,在php.ini中设置。
--session原理(使用cookie)
- 用户第一次访问带有session的页面时,服务器会创建一个session id以cookie的形式发送给客户端。(cookie的变量名可以用session_name()函数获取,这个值设置在php.ini中,默认是"PHPSESSID")客户端以后的访问中将在cookie中带此session id。
- string session_name ( [string name] )
session_name函数返回当前session的名称.
服务器端可以使用session_id()函数,如果无参数,则返回当前session的 id;如果有参数则按参数设置session id。
- string session_id ( [string id] )
session工作流程 (基于cookie)
--开启session
使用php函数session_start()开启session处理。开启session后,php会将和session相关的内建环境变量载入到内存中。
- bool session_start ( void )
session_start()创建一个session或恢复一个已有的session(基于客户端Request中所带的session id,session id 可以通过url或cookie中带)
创建一个session后,会给客户端发送session名 = session id的cookie,之后客户端访问服务器都会带上这个cookie,带上cookie的访问就能找到对应的session值。
注意:如果是基于cookie的session,开启session之前不能有其他输出语句,因为开启session后会在客户端中保存cookie。
嫌写session_start()麻烦,可以在php.ini中设置session.auto_start =1 ,就可以自动在每个页面开启session。(但这样的话就不能把对象放到session中,因为类的定义必须在启动session之前加载)
--session保存变量的方法
通过超全局数组$_SESSION[]保存session相关的各变量。
$_SESSION[]的值会和该session关联起来。
开启session后,系统会自动读取文件中session的值到$_SESSION[],php中可以直接使用该全局数组。
--注销session的步骤
- 1.开启session。
- 2.清空session值:$_SESSION = array();
- 3.删除客户端cookie的session id:
- if(isset($_COOKIE[session_name()]))
- {
- setcookie(session_name,'',time-3600,'/');
- }
- 4.彻底销毁sessioon:使用session_destroy()删除该session的其他信息;
- bool session_destroy ( void )
session工作流程 (基于url)
--设置session id
- 如果是新建session,则不用设置;
- 如果是已有session,则需要设置session id,因为url方式中,session_start()不会自动获取用户的session id。
- if(isset($_GET['sid']))
- {
- session_id($_GET['sid']);
- }
如果url里session变量的名称是php.ini中session的name,那么可以不用设置session id,系统会默认用session_id($_GET['php.ini中session的name'])来设置session id。
--开启session
使用php函数session_start()开启session处理。
--由于不在客户端的cookie中保存session id,所以要在每个跳转链接中添加session id变量名(本例用的是'sid')=session id(使用session_id()获得)
链接后面加上:
- URL?sid =' <?php echo session_id() ?> '
-- SID常量
使用SID常量,可以自动根据用户情况切换url和cookie方式的session。
- 用户支持cookie,SID 的值是空字符串 '';
- 用户不支持cookie,SID 的值是一个字符串: session_name() = session_id();
所以在实际使用中,链接后面加上:
- URL?SID
--最简方式
不管用SID还是session_name,都需要在每个链接上加内容,比较麻烦,实际可以通过修改php.ini的配置来自动为每个链接加上SID。
- session.use_trans_sid = 1
该值设为1,就会自动为链接加SID(推荐在Linux下的apache服务器下使用,windows下该机制工作可能存在问题)
注意:
- 对于链接是html语言形式存在的,该功能可以使用 ;
- 对于链接是php打印输出echo "<a href="URL"></a>"形式存在的,必须用SID,因为系统不会知道这是个链接。
--使用和注销session(同cookie方式)
--解决保存用户会话信息时,空间不够用问题
session默认保存到临时文件中,如果用来保存用户信息,由于文件太大,空间不够用,所以可以设为其他保存方式:
- 在linux下使用nfs保存session数据;
- samba方式(linux和window共享文件的一种机制);
- 使用数据库;
- 使用memcache保存用户会话数据;
--关于session的配置设置(php.ini设置)
- session.save_path = "文件存放路径" //保存session数据文件的存放路径
文件名都是以sess_为前缀,加session id的格式。
- session.name = 名称 //session的名称,默认是PHPSESSID
- session.use_trans_sid = 1 //启用透明的SID的支持。默认为0,表示不支持。
- //所谓透明的SID,就是自动在所有链接后添加SID,
- //SID可在不支持cookie时转换为字符串"session name = session id"。
- session.gc_maxlifetime = 时间值 //垃圾回收时间
垃圾文件的产生是由于用户未注销而直接关闭浏览器,导致原来保存的session成为没有用户需要的垃圾文件。
- session.gc_probability = 1
- session.gc_divisor = 100
垃圾管理进程(gc)启动的概率,该概率 = session.gc_probability/session.gc_divisor 本例为1/100。
每次执行session_start()会进行一次判断,按照session.gc_probability/session.gc_divisor的概率计算,如果判断结果为成功就启动gc,gc经过session.gc_maxlifetime秒后就会清除垃圾。
如果一直使用该session的话,session数据不会被当做垃圾删除,因为每次使用session时gc都会重新计时。
gc不是在session运行maxlifetime秒后清除垃圾,而是在session开始的时候计算已有所有session的保存时间,如果超过了maxlifetime秒,则删除。
- session.use_cookies = 1 //是否使用cookie,默认为1,使用
- session.cookie_path = '/' //cookie使用的目录(规定了客户端访问哪个目录
- //使用cookie)
- session.cookie_domain = '' //规定了客户端访问哪个域名使用此cookie(一般不用设置,
- //系统默认为给客户端设置cookie的域名)
- session.cookie_lifetime = 0 //cookie的有效时间(从产生cookie开始计算)
- session.save_handler = files //控制session文件的写入方式,
- //默认为files表示写入文件,还可以设置memcache或设为写入数据库
当值设为user时,使用函数session_set_save_handler()定义写入方式。
--session_set_save_handler()的用法
session_set_save_handler()是用户自己控制文件写入的方式(配置文件设为user时,由该函数定义写入方式) ,使用本函数可以自己定义写入文件的名称(假如以以文件方式写),位置,可以自己掌控。
- bool session_set_save_handler ( callback open, callback close, callback read,
- callback write, callback destroy, callback gc )
使用本函数,首先要在php.ini中设置session的读写的处理方式为手动(session.save_handler = user),默认是php自动。
本函数注册的回调函数将在特定时候被系统调用,开启session后,就开启了这个自动调用进程。下面是各回调函数的介绍:
- open($save_path, $session_name):打开文件时所执行的操作。会在session开始的时候运行,即运行session_start()的时候运行open()。如果不手工传参数,默认是配置文件中的path和session name。
- close():关闭文件时所执行的操作。即session_destroy(),session_write_close()的时候运行。
- read($id):定义了读取session数据到$_SESSION[]中所用的方法。会在session_start()的时候运行。如果不手工设置,默认是当前session id。
- write($id,$sess_data):强制提交$_SESSION[]数据写入文件所用方法。会在session结束的时候运行,即session_destroy(),session_write_close()的时候运行。
- destroy($id):会在session结束的时候运行,即session_destroy()的时候运行。
- gc($maxlifetime):清除垃圾所执行的操作。(gc()不会清除当前session的数据)在运行open()、read()、session_start()的时候,会根据 session.gc_probability/session.gc_divisor的值还有maxlifetime时间是否超过(上次修改时间+maxlifetime是否大于当前时间),决定是否运行gc()。
session start的时候运行:open(),read(),gc();
session destroy的时候运行:close(),write(),destroy().
--gc()和destroy()的异同:
- 同:他们都有垃圾清除的功能,都能删除不用的session文件。
- 异:
- destroy()是程序控制,手动运行;gc()是按照策略,自动运行(满足概率就运行)。
- destroy()一般是在session结束的时候运行;gc()是在session开始的时候运行。
session写到数据库
--使用面向对象的方法写数据库
- 定义表结构(定义需要存储的值)
- 定义类(用于存储操作)
- 类的静态方法(session_set_save_handler()的各回调函数都要定义为Session类的静态方法,静态方法使用的属性也要是静态的)
- 类的静态属性(对应数据库中需要存储的各值,还有PDO类的对象)
- class Session {
- private static $handler=null;
- private static $ip=null;
- private static $lifetime=null;
- private static $time=null; //当前时间
- private static function init($handler){
- self::$handler=$handler; //将PDO对象赋值给Session的静态成员
- self::$ip = !emptyempty($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"]
- : 'unknown';
- self::$lifetime=ini_get('session.gc_maxlifetime');
- self::$time=time();
- }
- static function start(PDO $pdo){
- self::init($pdo);
- session_set_save_handler(
- array(__CLASS__,"open"),
- array(__CLASS__,"close"),
- array(__CLASS__,"read"),
- array(__CLASS__,"write"),
- array(__CLASS__,"destroy"),
- array(__CLASS__,"gc")
- );
- session_start();
- } //开启session的准备工作:初始化类(Session)的静态常量,注册session写入函数,开启session
- public static function open($path, $name){
- return true;
- } //由于是数据库操作,不需要打开文件,所以直接返回true
- public static function close(){
- return true;
- }//由于是数据库操作,不需要关闭文件,所以直接返回true
- public static function read($PHPSESSID){
- $sql="select PHPSESSID, update_time, client_ip, data
- from session where PHPSESSID= ?";
- $stmt=self::$handler->prepare($sql);
- $stmt->execute(array($PHPSESSID));
- if(!$result=$stmt->fetch(PDO::FETCH_ASSOC)){
- return '';
- }
- if( self::$ip != $result["client_ip"]){
- self::destroy($PHPSESSID);
- return '';
- }
- if(($result["update_time"] + self::$lifetime) < self::$time ){
- self::destroy($PHPSESSID);
- return '';
- }
- return $result['data'];
- }
- public static function write($PHPSESSID, $data){
- $sql="select PHPSESSID, update_time, client_ip, data from session
- where PHPSESSID= ?";
- $stmt=self::$handler->prepare($sql);
- $stmt->execute(array($PHPSESSID));
- if($result=$stmt->fetch(PDO::FETCH_ASSOC)){
- if($result['data'] != $data || self::$time > ($result['update_time']+30))
- {
- $sql="update session set update_time = ?, data =?
- where PHPSESSID = ?";
- $stm=self::$handler->prepare($sql);
- $stm->execute(array(self::$time, $data, $PHPSESSID));
- }
- }else{
- if(!emptyempty($data)){
- $sql="insert into session(PHPSESSID, update_time, client_ip, data)
- values(?,?,?,?)";
- $sth=self::$handler->prepare($sql);
- $sth->execute(array($PHPSESSID, self::$time, self::$ip, $data));
- }
- }
- return true;
- }
- public static function destroy($PHPSESSID){
- $sql="delete from session where PHPSESSID = ?";
- $stmt=self::$handler->prepare($sql);
- $stmt->execute(array($PHPSESSID));
- return true;
- }
- private static function gc($lifetime){
- $sql = "delete from session where update_time < ?";
- $stmt=self::$handler->prepare($sql);
- $stmt->execute(array(self::$time-$lifetime));
- return true;
- }
- }
- try{
- $pdo=new PDO("mysql:host=localhost;dbname=xsphpdb", "root", "123456");
- }catch(PDOException $e){
- echo $e->getMessage();
- }
- Session::start($pdo);
写到Memcache中
--使用面向对象的方法写Memcache
- 定义表结构(定义需要存储的值)
- 定义类(用于存储操作)
- 类的静态方法(session_set_save_handler()的各回调函数都要定义为Session类的静态方法,静态方法使用的属性也要是静态的)
- 类的静态属性(对应Memecache中需要存储的各值,还有Memcache类的对象)
- class MemSession {
- private static $handler=null; //Memcache对象
- private static $lifetime=null; //有效时间
- private static $time = null; //当前时间
- const NS='session_'; //Memcache索引的前缀
- private static function init($handler){
- self::$handler=$handler;
- self::$lifetime=ini_get('session.gc_maxlifetime');
- self::$time=time();
- } //初始化静态成员
- public static function start(Memcache $memcache){
- self::init($memcache);
- session_set_save_handler(
- array(__CLASS__, 'open'),
- array(__CLASS__, 'close'),
- array(__CLASS__, 'read'),
- array(__CLASS__, 'write'),
- array(__CLASS__, 'destroy'),
- array(__CLASS__, 'gc')
- );
- session_start();
- } //开启session的准备工作:初始化类(Session)的静态常量,注册session写入函数,开启session
- public static function open($path, $name){
- return true;
- } //由于是Memcache操作,所以不需要打开文件,直接返回true
- public static function close(){
- return true;
- }//由于是Memcache操作,所以不需要关闭文件,直接返回true
- public static function read($PHPSESSID){
- $out=self::$handler->get(self::session_key($PHPSESSID));
- if($out===false || $out == null)
- return '';
- return $out;
- }
- public static function write($PHPSESSID, $data){
- $method=$data ? 'set' : 'replace';
- return self::$handler->$method(self::session_key($PHPSESSID),
- $data, MEMCACHE_COMPRESSED, self::$lifetime);
- } //self::$handler->$method,里面$method是值为变量名的变量
- public static function destroy($PHPSESSID){
- return self::$handler->delete(self::session_key($PHPSESSID));
- }
- public static function gc($lifetime){
- return true;
- } //因为memcache有垃圾清理机制,所以不用gc,返回true
- private static function session_key($PHPSESSID){
- $session_key=self::NS.$PHPSESSID;
- return $session_key;
- } //组合memcache键值的方法
- }
- $memcache=new Memcache;
- $memcache->connect("localhost", 11211) or die("could not connect!");
- MemSession::start($memcache);