本章目录

1.为什么要有会话控制

2.cookie概述

3.session概述

4.session工作流程(基于cookie)

5.session工作流程(基于url)

6.session高级用法

7.session写到数据库

8.session写到memcache

 

 

为什么要有会话控制                           

--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下。例如:

  1. C:\Documents and Settings\Administrator\Cookies

 

--php中设置cookie(cookie是作为http数据表包的头信息发送到客户端的)

    使用网络函数setcookie()。

  1. bool setcookie ( string name [, string value [, int expire 
  2. [, 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。
  1. string session_name ( [string name] ) 

     session_name函数返回当前session的名称.

    服务器端可以使用session_id()函数,如果无参数,则返回当前session的 id;如果有参数则按参数设置session id。

  1. string session_id ( [string id] ) 

 

session工作流程 (基于cookie)           

--开启session

    使用php函数session_start()开启session处理。开启session后,php会将和session相关的内建环境变量载入到内存中。

  1. 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:
  1. if(isset($_COOKIE[session_name()])) 
  2.     setcookie(session_name,'',time-3600,'/'); 
  •  4.彻底销毁sessioon:使用session_destroy()删除该session的其他信息;
  1. bool session_destroy ( void ) 

 

 

session工作流程 (基于url)          

--设置session id

  • 如果是新建session,则不用设置;
  • 如果是已有session,则需要设置session id,因为url方式中,session_start()不会自动获取用户的session id
  1. if(isset($_GET['sid'])) 
  2.     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()获得)

     链接后面加上: 

  1. URL?sid =' <?php echo session_id() ?> '

 

 

-- SID常量

    使用SID常量,可以自动根据用户情况切换url和cookie方式的session。

  • 用户支持cookie,SID 的值是空字符串 '';
  • 用户不支持cookie,SID 的值是一个字符串: session_name() = session_id();

    所以在实际使用中,链接后面加上:

  1. URL?SID 

 

--最简方式

    不管用SID还是session_name,都需要在每个链接上加内容,比较麻烦,实际可以通过修改php.ini的配置来自动为每个链接加上SID。

  1. session.use_trans_sid = 1 

    该值设为1,就会自动为链接加SID(推荐在Linux下的apache服务器下使用,windows下该机制工作可能存在问题)

注意:

  • 对于链接是html语言形式存在的,该功能可以使用 ;
  • 对于链接是php打印输出echo "<a href="URL"></a>"形式存在的,必须用SID,因为系统不会知道这是个链接。

 

--使用和注销session(同cookie方式)

 

session高级用法                           

--解决保存用户会话信息时,空间不够用问题

    session默认保存到临时文件中,如果用来保存用户信息,由于文件太大,空间不够用,所以可以设为其他保存方式:

  • 在linux下使用nfs保存session数据;
  • samba方式(linux和window共享文件的一种机制);
  • 使用数据库;
  • 使用memcache保存用户会话数据;

 

--关于session的配置设置(php.ini设置)

  1. session.save_path = "文件存放路径" //保存session数据文件的存放路径  

    文件名都是以sess_为前缀,加session id的格式。

 

  1. session.name = 名称 //session的名称,默认是PHPSESSID 

 

  1. session.use_trans_sid = 1  //启用透明的SID的支持。默认为0,表示不支持。 
  2. //所谓透明的SID,就是自动在所有链接后添加SID,
  3. //SID可在不支持cookie时转换为字符串"session name = session id" 

 

  1. session.gc_maxlifetime = 时间值    //垃圾回收时间 

    垃圾文件的产生是由于用户未注销而直接关闭浏览器,导致原来保存的session成为没有用户需要的垃圾文件。 

 

  1. session.gc_probability = 1 
  2. 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秒,则删除。

 

  1. session.use_cookies = 1  //是否使用cookie,默认为1,使用

 

  1. session.cookie_path = '/'  //cookie使用的目录(规定了客户端访问哪个目录
  2. //使用cookie)

 

  1. session.cookie_domain = ''  //规定了客户端访问哪个域名使用此cookie(一般不用设置,
  2. //系统默认为给客户端设置cookie的域名) 

 

  1. session.cookie_lifetime = 0  //cookie的有效时间(从产生cookie开始计算)

 

  1. session.save_handler = files   //控制session文件的写入方式,
  2. //默认为files表示写入文件,还可以设置memcache或设为写入数据库 

     当值设为user时,使用函数session_set_save_handler()定义写入方式。

 

--session_set_save_handler()的用法

    session_set_save_handler()是用户自己控制文件写入的方式(配置文件设为user时,由该函数定义写入方式) ,使用本函数可以自己定义写入文件的名称(假如以以文件方式写),位置,可以自己掌控。

  1. bool session_set_save_handler ( callback open, callback close, callback read, 
  2. 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类的对象)

 

  1. class Session { 
  2.         private static $handler=null; 
  3.         private static $ip=null; 
  4.         private static $lifetime=null; 
  5.         private static $time=null;  //当前时间
  6.  
  7.         private static function init($handler){ 
  8.             self::$handler=$handler; //将PDO对象赋值给Session的静态成员
  9.             self::$ip = !emptyempty($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"
  10. 'unknown'
  11.             self::$lifetime=ini_get('session.gc_maxlifetime'); 
  12.             self::$time=time(); 
  13.         } 
  14.  
  15.         static function start(PDO $pdo){ 
  16.             self::init($pdo); 
  17.             session_set_save_handler( 
  18.                     array(__CLASS__,"open"), 
  19.                     array(__CLASS__,"close"), 
  20.                     array(__CLASS__,"read"), 
  21.                     array(__CLASS__,"write"), 
  22.                     array(__CLASS__,"destroy"), 
  23.                     array(__CLASS__,"gc"
  24.                 ); 
  25.  
  26.             session_start(); 
  27.         } //开启session的准备工作:初始化类(Session)的静态常量,注册session写入函数,开启session
  28.  
  29.         public static function open($path$name){ 
  30.             return true; 
  31.         } //由于是数据库操作,不需要打开文件,所以直接返回true
  32.  
  33.         public static function close(){ 
  34.             return true; 
  35.         }//由于是数据库操作,不需要关闭文件,所以直接返回true
  36.          
  37.         public static function read($PHPSESSID){ 
  38.             $sql="select PHPSESSID, update_time, client_ip, data 
  39. from session where PHPSESSID= ?"
  40.  
  41.             $stmt=self::$handler->prepare($sql); 
  42.  
  43.             $stmt->execute(array($PHPSESSID)); 
  44.              
  45.             if(!$result=$stmt->fetch(PDO::FETCH_ASSOC)){ 
  46.                 return ''
  47.             } 
  48.  
  49.             if( self::$ip  != $result["client_ip"]){ 
  50.                 self::destroy($PHPSESSID); 
  51.                 return ''
  52.             } 
  53.  
  54.             if(($result["update_time"] + self::$lifetime) < self::$time ){ 
  55.                 self::destroy($PHPSESSID); 
  56.                 return ''
  57.             } 
  58.  
  59.             return $result['data']; 
  60.  
  61.         } 
  62.  
  63.         public static function write($PHPSESSID$data){ 
  64.             $sql="select PHPSESSID, update_time, client_ip, data from session 
  65. where PHPSESSID= ?"
  66.  
  67.             $stmt=self::$handler->prepare($sql); 
  68.  
  69.             $stmt->execute(array($PHPSESSID)); 
  70.  
  71.             if($result=$stmt->fetch(PDO::FETCH_ASSOC)){ 
  72.                 if($result['data'] != $data || self::$time > ($result['update_time']+30))
  73.                     $sql="update session set update_time = ?, data =? 
  74. where PHPSESSID = ?"
  75.                      
  76.                     $stm=self::$handler->prepare($sql); 
  77.                     $stm->execute(array(self::$time$data$PHPSESSID)); 
  78.                  
  79.                 } 
  80.             }else
  81.                 if(!emptyempty($data)){ 
  82.                     $sql="insert into session(PHPSESSID, update_time, client_ip, data) 
  83. values(?,?,?,?)"
  84.  
  85.                     $sth=self::$handler->prepare($sql); 
  86.  
  87.                     $sth->execute(array($PHPSESSID, self::$time, self::$ip$data)); 
  88.                 } 
  89.             } 
  90.  
  91.             return true; 
  92.         } 
  93.  
  94.         public static function destroy($PHPSESSID){ 
  95.             $sql="delete from session where PHPSESSID = ?"
  96.  
  97.             $stmt=self::$handler->prepare($sql); 
  98.  
  99.             $stmt->execute(array($PHPSESSID)); 
  100.  
  101.             return true; 
  102.         } 
  103.  
  104.         private static function gc($lifetime){ 
  105.             $sql = "delete from session where update_time < ?"
  106.  
  107.             $stmt=self::$handler->prepare($sql); 
  108.  
  109.             $stmt->execute(array(self::$time-$lifetime)); 
  110.             return true; 
  111.         }    
  112.     } 
  113.  
  114.     try{ 
  115.         $pdo=new PDO("mysql:host=localhost;dbname=xsphpdb""root""123456"); 
  116.     }catch(PDOException $e){ 
  117.         echo $e->getMessage(); 
  118.     } 
  119.  
  120.     Session::start($pdo); 

 

 写到Memcache中

 --使用面向对象的方法写Memcache

  • 定义表结构(定义需要存储的值)
  •  定义类(用于存储操作)
    • 类的静态方法(session_set_save_handler()的各回调函数都要定义为Session类的静态方法,静态方法使用的属性也要是静态的)
    • 类的静态属性(对应Memecache中需要存储的各值,还有Memcache类的对象)
  1. class MemSession { 
  2.         private static $handler=null; //Memcache对象 
  3.         private static $lifetime=null; //有效时间 
  4.         private static $time = null; //当前时间 
  5.         const NS='session_'//Memcache索引的前缀 
  6.          
  7.         private static function init($handler){ 
  8.             self::$handler=$handler
  9.             self::$lifetime=ini_get('session.gc_maxlifetime'); 
  10.  
  11.             self::$time=time(); 
  12.         } //初始化静态成员 
  13.  
  14.         public static function start(Memcache $memcache){ 
  15.             self::init($memcache); 
  16.  
  17.             session_set_save_handler( 
  18.                     array(__CLASS__'open'), 
  19.                     array(__CLASS__'close'), 
  20.                     array(__CLASS__'read'), 
  21.                     array(__CLASS__'write'), 
  22.                     array(__CLASS__'destroy'), 
  23.                     array(__CLASS__'gc'
  24.                 ); 
  25.             session_start(); 
  26.         } //开启session的准备工作:初始化类(Session)的静态常量,注册session写入函数,开启session 
  27.  
  28.      
  29.         public static function open($path$name){ 
  30.             return true; 
  31.         } //由于是Memcache操作,所以不需要打开文件,直接返回true 
  32.  
  33.         public static function close(){ 
  34.             return true; 
  35.         }//由于是Memcache操作,所以不需要关闭文件,直接返回true 
  36.  
  37.         public static function read($PHPSESSID){ 
  38.             $out=self::$handler->get(self::session_key($PHPSESSID)); 
  39.  
  40.             if($out===false || $out == null) 
  41.                 return ''
  42.  
  43.             return $out
  44.         } 
  45.  
  46.         public static function write($PHPSESSID$data){ 
  47.              
  48.             $method=$data ? 'set' : 'replace'
  49.  
  50.             return self::$handler->$method(self::session_key($PHPSESSID), 
  51. $data, MEMCACHE_COMPRESSED, self::$lifetime); 
  52.         } //self::$handler->$method,里面$method是值为变量名的变量
  53.  
  54.         public static function destroy($PHPSESSID){ 
  55.             return self::$handler->delete(self::session_key($PHPSESSID)); 
  56.         } 
  57.  
  58.         public static function gc($lifetime){ 
  59.             return true; 
  60.         }  //因为memcache有垃圾清理机制,所以不用gc,返回true
  61.  
  62.         private static function session_key($PHPSESSID){ 
  63.             $session_key=self::NS.$PHPSESSID
  64.  
  65.             return $session_key
  66.         } //组合memcache键值的方法  
  67.     } 
  68.  
  69.     $memcache=new Memcache; 
  70.  
  71.     $memcache->connect("localhost", 11211) or die("could not connect!"); 
  72.  
  73.     MemSession::start($memcache);