标题:【PHP小课堂】深入学习PHP中的SESSION(一)

文章目录
    分类:PHP 标签:PHP

    深入学习PHP中的SESSION(一)

    其实 SESSION 这个话题本来也并不想多说的,毕竟它也是我们学习 PHP 的一个必然要接触的内容。或者说,不管是什么语言,只要是做 WEB 开发,都会和 SESSION 打交道,但是在面试的时候,SESSION 和 Cookie 也是经常会被问到的问题。既然如此,那么我们还是来深入的学习一下 SESSION 中的一些函数的具体作用吧。

    SESSION 会话传输

    在 PHP 的 SESSION 使用中,我们有两种方式来传输 SESSION 信息,分别是 Cookie 和 URL 两种方式。其实它们的本质是差不多的,都是为了在页面间传输 session id 这个东西。在系统中,就是通过这个 session id 来获得我们保存的 SESSION 信息。而 SESSION 信息的原始内容,则是以类似于序列化的方式通过一个文件保存在系统的某个目录中的,比如说 Linux 中默认就是保存在 /tmp 这个目录下。


    我们首先来看看最简单的 SESSION 是如何使用的。

    session_start();
    $_SESSION['user'] = ['id' => 1, 'name' => 'test_user'];

    在第一个页面中,我们使用 session_start() 开启 SESSION 功能。这个函数是必须的,它来告诉我们接下来的脚本 SESSION 已经启用,可以通过 SESSION 来存取数据。而 SESSION 赋值和取值的方式则都是通过 $_SESSION 这个全局数组变量来进行操作的。


    在另一个页面中,直接取出 SESSION 中的数据。

    session_start();
    print_r($_SESSION['user']);

    上面这种情况,我们都是在系统默认的情况下,没有修改任何内容,因此 session id 是通过 Cookie 传输的。如果要修改成使用 URL 的方式的话,需要修改 php.ini 中的一些配置选项。

    // 修改 php.ini
    // session.use_trans_sid  = 1
    // session.use_cookie = 0
    // session.use_only_cookie = 0
    // echo SID;

    修改这些配置信息后,常量 SID 中就会有内容,它的内容就是 PHPSESSID=xxxxx 这样的信息。就像我们通过查看 Cookie 一样,在 Cookie 中,保存的 session id 其实也是在这个 PHPSESSID 里面。接下来,我们就可以通过这个 SID 放到 URL 中进行 session id 的传输。

    <a href="41.php?<?php htmlspecialchars(SID); ?>">URL传输</a>

    除了 SID 之外,还可以通过 session_name() 和 session_id() 来获取类似的 PHPSESSID 名称和它的数据信息。

    echo session_name(), "=", session_id(), "<br/>"; // PHPSESSID=i3tdf5bmmdq8eduh56ja7s87ka

    这两个函数都有一个可选的参数。如果给这个参数赋值的话,那么 session_name() 函数将修改 SESSION 的键值名称,而 session_id() 则会装载给定的 session id ,就像在一些接口开发中如果要使用 SESSION 来做登录的话,就可以通过接口来传输这个 session id 。

    session_name('session_id'); // 需要在 session_start() 之前设置
    session_start();
    
    echo session_name(), "=", session_id(), "<br/>"; // session_id=plt0dnc18t6l6uu30dp4s78hhg

    在另一个文件中,使用 session_id 这个传递过来的变量装载 SESSION 数据。

    if($_GET['session_id']) session_id($_GET['session_id']);

    属性设置

    SESSION 中有很多属性信息的获取和设置操作,这些功能其实我们在日常的开发中使用到的比较少,一般我们都会走默认的配置,而且大部分开发框架也会帮我们准备好这些内容,所以我们只是了解下就可以了。

    状态信息

    echo session_status(), "<br/>"; // 1
    
    session_start();
    
    echo session_status(), "<br/>"; // 2

    首先是当前 SESSION 的状态信息,比如在 session_start() 前后,session_status() 返回的结果就是不同的,分别代表着 SESSION 未启用和启用的状态信息。

    echo session_cache_expire(), "<br/>"; // 180
    echo session_cache_limiter(), "<br/>"; // nocache

    session_cache_expire() 表示 SESSION 缓存的过期时间,默认情况下是 180 分钟。而 session_cache_limiter() 表示的是缓存限制器的状态信息。这两个函数都是和 HTTP 中的头部缓存信息有关的。比如我们可以在 session_start() 之前设置 session_cache_limiter() 。

    session_cache_limiter('private');
    session_start();
    session_cache_limiter(); // private

    再通过浏览器工具查看请求信息的头时,就可以看到响应头信息中的 Cache-Control: private 这条信息了。关于这个头信息的内容及作用,大家可以自行查阅 HTTP 相关的知识材料。

    Cookie 设置

    同样的,对于使用 Cookie 传输模式来说,我们可以修改和获取 SESSION 中的 Cookie 设置信息。

    session_set_cookie_params(3600,'', '', true);
    session_start();
    print_r(session_get_cookie_params());
    // 未设置前
    // Array
    // (
    //     [lifetime] => 0
    //     [path] => /
    //     [domain] => 
    //     [secure] => 
    //     [httponly] => 
    //     [samesite] => 
    // )
    // 设置之后
    // Array
    // (
    //     [lifetime] => 3600
    //     [path] => 
    //     [domain] => 
    //     [secure] => 1
    //     [httponly] => 
    //     [samesite] => 
    // )

    分别就是使用 session_set_cookie_params() 函数和 session_get_cookie_params() 来进行设置及获取。对于数据传输的安全来说,secure 和 httponly 设置为 true 对我们的系统安全是非常有好处的,另外也要注意域和路径的设置。如果是默认情况下,它们都是打开的也就是没有任何安全机制的。

    重新生成 SESSION ID

    echo session_id(), "<br/>"; // i3tdf5bmmdq8eduh56ja7s87ka
    echo session_create_id(), "<br/>"; // bc9gkjbrepi3g8ku6udmk15588
    echo session_id(), "<br/>"; // i3tdf5bmmdq8eduh56ja7s87ka
    
    session_regenerate_id();
    echo session_name(), "=", session_id(), "<br/>"; // PHPSESSID=ei23t1pu0lr20o64qajl52m1na

    这两个函数 session_create_id() 和 session_regenerate_id() 都是用于重新生成 session id 的,不过不同的是,session_regenerate_id() 是重新生成并使用这个 session id ,可以看到它是没有返回值的,而我们当前的 session_id() 也变成了这个新的值。而 session_create_id() 只是创建一个 session id ,并不会将当前会话的 session id 重置为这个值。注意它们两个的区别哦。其实 session_regenerate_id() 就相当于是 session_id(session_create_id()) 这样一个操作。

    模块与保存路径设置

    默认情况下,PHP 的 SESSION 使用的是文件并保存在 /tmp 目录下,我们通过 php.ini 来修改它们,比如使用 memcache 或者 redis 来保存 SESSION 数据。这些信息我们在系统中也是可以查看到的。

    echo session_module_name(), "<br/>"; // files

    session_module_name() 对应的就是 php.ini 中的 session.save_handler 的配置,也就是可以通过它设置当前的 SESSION 方式。

    echo session_save_path(), "<br/>"; // /tmp

    session_save_path() 对应的就是 session.save_path 的信息,同样的,它返回的就是 SESSION 保存的路径信息。


    我们尝试在程序动态运行时修改这些信息,在 session_start() 之前将它们修改为使用 redis 保存 SESSION 数据。

    session_module_name('redis');
    session_save_path('tcp://127.0.0.1:6379');
    
    session_start();
    
    echo session_module_name(), "<br/>"; // redis
    echo session_save_path(), "<br/>"; // tcp://127.0.0.1:6379

    目测是设置成功了,接下来去 redis 看一眼。

    127.0.0.1:6379> keys *
    1) "PHPREDIS_SESSION:sntcu903o1s1qneok4v7tjac44"
    2) "PHPREDIS_SESSION:7l8kd4cf7pmg3jl31k5cf03uoj"
    127.0.0.1:6379> get PHPREDIS_SESSION:7l8kd4cf7pmg3jl31k5cf03uoj
    "user1|a:2:{s:2:\"id\";i:1;s:4:\"name\";s:10:\"test_user1\";}"

    redis 中也有了对应的 SESSION ID 的信息,之所以有两条数据,是因为我们的测试代码中重新生成过 session id ,也就是前面那一段代码中的 session_regenerate_id() 函数的作用,这个就不多做解释了。


    至于为什么会有这种配置,难道使用文件来保存 SESSION 不好吗?这个就牵涉到其它问题了,这里就简单地说明一下。使用 redis 或者 memcache 这种外部缓存系统来保存 SESSION 的好处第一就是在负载均衡应用中,使用统一管理的 SESSION 可以解决用户登录的问题。也就是说,如果是文件保存的话,用户第一次在 A 机器登录,数据被保存在了 A 机器的 /tmp 目录下,第二次请求如果被负载到了 B 服务器上,那么这个用户的用户登录信息是获取不到的,毕竟在 B 服务器上他没有登录过,/tmp 目录下没有这个 session id 对应的数据。另外一点也是更直观的好处,内存缓存数据库的速度可是比硬件文件读写快得多了。

    SESSION 编码解码

    SESSION 函数中还有两个编解码相关的函数,我们可以来看看它们的作用。

    echo session_encode(), "<br/>"; // user|a:2:{s:2:"id";i:1;s:4:"name";s:9:"test_user";}
    
    echo session_decode('user1|a:2:{s:2:"id";i:1;s:4:"name";s:10:"test_user1";}'), "<br/>";
    print_r($_SESSION);
    // Array
    // (
    //     [user] => Array
    //         (
    //             [id] => 1
    //             [name] => test_user
    //         )
    
    //     [user1] => Array
    //         (
    //             [id] => 1
    //             [name] => test_user1
    //         )
    
    // )

    session_encode() 获得编码后的 SESSION 数据信息,它非常像序列化后的数据,但又不是完全相同。因为它会以 SESSION 键名加上一个 | 进行分割。session_decode() 则是将一个字符串的编码数据反解回 SESSION 数据中。比如我们这里随便修改了一点 user 的数据增加了一个 user1 ,然后再打印 $_SESSION 就会发现 user1 的数据也添加到了系统的 SESSION 中。

    SESSION 删除销毁

    最后要学习的是 SESSION 的删除销毁。其实一般情况下,我们会这样来销毁一个 SESSION 数据。

    $_SESSION['user'] = null;
    
    session_unregister('user');  // 5.4后已移除,很老的项目中可能会见到
    // 也可以 unset($_SEESION['user']);

    对于单个的键的数据来说,这两种操作方式其实都是 OK 的,很多同学或者框架中会把这两种方式都写上。当然,PHP 也提供了可以全局删除销毁的函数。

    session_destroy();
    print_r($_SESSION);
    echo $a, "<br/>";
    
    session_unset();
    print_r($_SESSION);
    echo $a, "<br/>";

    session_destroy() 和 session_unset() 函数,它们是有区别的哦,大家可以分别注释来测试一下。


    session_destroy() 销毁当前会话中的全部数据, 但是不会重置当前会话所关联的全局变量, 也不会重置会话 cookie。 如果需要再次使用会话变量, 必须重新调用 session_start() 函数。通常情况下,在你的代码中不必调用 session_destroy() 函数, 可以直接清除 $_SESSION 数组中的数据来实现会话数据清理。


    session_unset() 会释放当前会话注册的所有会话变量,等同于 $_SESSION=[]

    总结

    关于 SESSION 使用相关的函数其实就是这些,另外还有一些函数和类确实是极少使用也并不常见,这里就不多说了。对于 SESSION 来说,安全性是非常重要的内容,所以下一篇文章我们将根据手册中的说明来深入的探讨学习一下 PHP 中 SESSION 相关的安全信息方面的内容,大家可不要错过哦。


    测试代码:


    https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/03/source/4.%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0PHP%E4%B8%AD%E7%9A%84SESSION%EF%BC%88%E4%B8%80%EF%BC%89.php


    https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/03/source/41.php


    参考文档:


    https://www.php.net/manual/zh/book.session.php

    视频链接

    微信文章地址:https://mp.weixin.qq.com/s/S9PdI1efpCw3f7px7hfT2Q

    微信文章地址:https://mp.weixin.qq.com/s/w1lF-NV48QKAAryGz1IPgA

    搜索
    关注