资讯专栏INFORMATION COLUMN

php如何实现session,自己实现session,laravel如何实现session

silvertheo / 3023人阅读

摘要:如何实现执行脚本,会从中读取配置项,将生成的唯一值保存文件放到配置项中的保存路径和地点。并通过协议返回响应消息头名值发送给客户端。

1、php如何实现session
执行php脚本,session_start()会从php.ini中读取配置项,将生成的唯一值sessionID保存文件放到配置项中的保存路径和地点。并通过HTTP协议返回响应消息头setCookie
Cookie名=Cookie值发送给客户端。
客户端接受到Set-Cookie,将cookie值写入cookie文件
在接下来的访问中,客户端会携带cookie访问浏览器,浏览器接受到cookie值,生成http请求头将包含的COOKIE发送给Php,php识别cookie值,从保存路径中找对应的文件。
找到文件之后,检验是否在有效期内,在有效期内就读取文件,不再有效期内就清空文件
2、自己实现session需要考虑的几个问题 1)、生成唯一值的算法 2)、将session存到文件里还是存到redis还是memcache,应该存什么数据 3)、接受cookie值,从保存位置找到对应的文件或数据 4)、session垃圾回收机制,删除session文件和数据 5)、分布式的话,session同步问题 3、分析一下laravel如何实现session.
vendor/laravel/framework/src/Illuminate/Contracts定义都是接口类(契约)。向开发者提供了统一的接口类访问Session数据
看一下session接口定义的是什么?

实现接口类的具体实现类只有一个,也被称为驱动器
vendor/laravel/framework/src/Illuminate/Session/Store.php

class Store implements Session
{
    protected $id;//session id

    protected $name;//session name

    protected $attributes = [];//session

    protected $handler;//会话处理程序实现,使用了sessionHandlerInterface接口

    protected $started = false;//会话存储处理状态
    public function __construct($name, SessionHandlerInterface $handler, $id = null)//创建一个新的session实例
    {
        $this->setId($id);
        $this->name = $name;
        $this->handler = $handler;
       //setId
       //$this->id = $this->isValidId($id) ? $id : $this->generateSessionId();
       //1、 $this->isValidId($id)?
              //return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
              //is_string($id)检查sessionid是不是字符串,ctype_alnum检查sessionid所有的字符全是字母和数字,是为true,否为false;strlen检查字符串长度是否等于40
       //2、$this->generateSessionId();?
             //return Str::random(40); 这个使用Str门面类生成长度为40的唯一值
       //handler使用的是php预留接口类SessionnHandlerInterface,这个接口是为了将session存储到数据库中,(难怪在laravel查半天都没查到)。该类的回调方法是在php内部调用。
     }
来源于vendorlaravelframeworksrcIlluminateSupportStr.php 
laravel实现生成session唯一值算法函数,重点是使用了random_bytes函数和base64_encode函数
    public static function random($length = 16)
        {
          $string = "";
    
            while (($len = strlen($string)) < $length) {//当它的长度小于$length时
                $size = $length - $len;//长度差
    
                $bytes = random_bytes($size);//根据size生成加密安全的伪随机字节字符串
    
                $string .= substr(str_replace(["/", "+", "="], "", base64_encode($bytes)), 0, $size);//base64_encode对其进行编码,目的时为了使二进制数据可以通过非纯8比特的传输层传输,通过str_replace函数去掉字符串的/+=3种符号,在用substr截取$size大小的字符串。
            }
    
            return $string;
        }
来源于php手册中的关于SessionHandlerInterface接口的定义
SessionHandlerInterface {
/* 方法 */
    abstract public close ( void ) : bool
    abstract public destroy ( string $session_id ) : bool
    abstract public gc ( int $maxlifetime ) : int
    abstract public open ( string $save_path , string $session_name ) : bool
    abstract public read ( string $session_id ) : string
    abstract public write ( string $session_id , string $session_data ) :bool
}
看看都有那些类实现了SessionHandler,在编程软件中全局搜索implements SessionHandlerInterface

//CacheBasedSessionHandler 使用的实例化对象是
Illuminate/Contracts/Cache/Repository
"cache.store" => [IlluminateCacheRepository::class,
IlluminateContractsCacheRepository::class],

//CookieSessionHandler
//DatabaseSessionHandler
//FileSessionHandler
//NullSessionHandler

   //根据session id读取数据
    public function start()
    {
        $this->loadSession();
        //验证csrf_token
        if (! $this->has("_token")) {
            $this->regenerateToken();
        }

        return $this->started = true;
    }
    protected function loadSession()
    {
        //array_merge合并两个数组
        //readFromHandler() 从驱动器中根据session id得到session信息
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());
    }
    protected function readFromHandler()
    {
        if ($data = $this->handler->read($this->getId())) {
             //@阻止警告输出
             //unserialize反序列化读出来的session的id信息
            $data = @unserialize($this->prepareForUnserialize($data));
            //如果数组不是错误,不为空,是数组就返回数据
            if ($data !== false && ! is_null($data) && is_array($data)) {
                return $data;
            }
        }

        return [];
    }
    //返回数据
    protected function prepareForUnserialize($data)
    {
        return $data;
    }
public function save()
{
    $this->ageFlashData();

    $this->handler->write($this->getId(), $this->prepareForStorage(
        serialize($this->attributes)
    ));

    $this->started = false;
}
    protected function prepareForStorage($data)
    {
        return $data;
    }
    //使会话闪存存数据老化
    public function ageFlashData()
    {

        $this->forget($this->get("_flash.old", []));
        //其他文件定义的forget方法
>  public static function forget(&$array, $keys)
>     {
>         $original = &$array;
> 
>         $keys = (array) $keys;
> 
>         if (count($keys) === 0) {
>             return;
>         }
> 
>         foreach ($keys as $key) {
>             // if the exact key exists in the top-level, remove it
>             if (static::exists($array, $key)) {
>                 unset($array[$key]);
> 
>                 continue;
>             }
> 
>             $parts = explode(".", $key);
> 
>             // clean up before each pass
>             $array = &$original;
> 
>             while (count($parts) > 1) {
>                 $part = array_shift($parts);
> 
>                 if (isset($array[$part]) && is_array($array[$part])) {
>                     $array = &$array[$part];
>                 } else {
>                     continue 2;
>                 }
>             }
> 
>             unset($array[array_shift($parts)]);
>         }
>     }

        $this->put("_flash.old", $this->get("_flash.new", []));

        $this->put("_flash.new", []);
    }
    public function all()
    {
        return $this->attributes;
    }
    public function exists($key)
    {
        $placeholder = new stdClass;

        return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) use ($placeholder) {
            return $this->get($key, $placeholder) === $placeholder;
        });
    }
    //检查是否存在Key值
    public function has($key)
    {
        return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) {
            return is_null($this->get($key));
        });
    }
//获取session信息
    public function get($key, $default = null)
    {
        return Arr::get($this->attributes, $key, $default);
    }
//删除,类似pop
    public function pull($key, $default = null)
    {
        return Arr::pull($this->attributes, $key, $default);
    }
    public function hasOldInput($key = null)
    {
        $old = $this->getOldInput($key);

        return is_null($key) ? count($old) > 0 : ! is_null($old);
    }
    public function getOldInput($key = null, $default = null)
    {
        return Arr::get($this->get("_old_input", []), $key, $default);
    }
    public function replace(array $attributes)
    {
        $this->put($attributes);
    }
//session永久保存,在不过期范围内
    public function put($key, $value = null)
    {
        if (! is_array($key)) {
            $key = [$key => $value];
        }

        foreach ($key as $arrayKey => $arrayValue) {
            Arr::set($this->attributes, $arrayKey, $arrayValue);
        }
    }
    public function remember($key, Closure $callback)
    {
        if (! is_null($value = $this->get($key))) {
            return $value;
        }

        return tap($callback(), function ($value) use ($key) {
            $this->put($key, $value);
        });
    }
//类似于push
    public function push($key, $value)
    {
        $array = $this->get($key, []);

        $array[] = $value;

        $this->put($key, $array);
    }
    public function increment($key, $amount = 1)
    {
        $this->put($key, $value = $this->get($key, 0) + $amount);

        return $value;
    }
    public function decrement($key, $amount = 1)
    {
        return $this->increment($key, $amount * -1);
    }
//快闪保存,只保存两次请求
    public function flash(string $key, $value = true)
    {
        $this->put($key, $value);

        $this->push("_flash.new", $key);

        $this->removeFromOldFlashData([$key]);
    }
    public function now($key, $value)
    {
        $this->put($key, $value);

        $this->push("_flash.old", $key);
    }
    public function reflash()
    {
        $this->mergeNewFlashes($this->get("_flash.old", []));

        $this->put("_flash.old", []);
    }
//刷新快闪数据时间,保持到下次请求
    public function keep($keys = null)
    {
        $this->mergeNewFlashes($keys = is_array($keys) ? $keys : func_get_args());

        $this->removeFromOldFlashData($keys);
    }
    protected function mergeNewFlashes(array $keys)
    {
        $values = array_unique(array_merge($this->get("_flash.new", []), $keys));

        $this->put("_flash.new", $values);
    }
    protected function removeFromOldFlashData(array $keys)
    {
        $this->put("_flash.old", array_diff($this->get("_flash.old", []), $keys));
    }
    public function flashInput(array $value)
    {
        $this->flash("_old_input", $value);
    }
    public function remove($key)
    {
        return Arr::pull($this->attributes, $key);
    }
    public function forget($keys)
    {
        Arr::forget($this->attributes, $keys);
    }
   //每次flush将数组置空
    public function flush()
    {
        $this->attributes = [];
    }
    public function invalidate()
    {
        $this->flush();

        return $this->migrate(true);
    }
    public function regenerate($destroy = false)
    {
        return tap($this->migrate($destroy), function () {
            $this->regenerateToken();
        });
    }
//给session生成一个新的sessionID
    public function migrate($destroy = false)
    {
        //如果
        if ($destroy) {
            $this->handler->destroy($this->getId());
        }

        $this->setExists(false);

        $this->setId($this->generateSessionId());

        return true;
    }
     //是否启动
    public function isStarted()
    {
        return $this->started;
    }

    //获取session名
    public function getName()
    {
        return $this->name;
    }

   //设置session 名
    public function setName($name)
    {
        $this->name = $name;
    }
    //获取session id
    public function getId()
    {
        return $this->id;
    }
    //如果sessionid有效,就返回有效id,如果失效就生成session id
    public function setId($id)
    {
        $this->id = $this->isValidId($id) ? $id : $this->generateSessionId();
    }
    //检查session id
    public function isValidId($id)
    {
        return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
    }
    //获取sessionID 唯一的40长度值
    protected function generateSessionId()
    {
        return Str::random(40);
    }
    //如果适用,在处理程序上设置会话的存在性
    public function setExists($value)
    {
        if ($this->handler instanceof ExistenceAwareInterface) {
            $this->handler->setExists($value);
        }
    }

   //获取token值
    public function token()
    {
        return $this->get("_token");
    }

   //生成唯一值40,存入_token
    public function regenerateToken()
    {
        $this->put("_token", Str::random(40));
    }

   //获取当前url
    public function previousUrl()
    {
        return $this->get("_previous.url");
    }

    //存储当前url
    public function setPreviousUrl($url)
    {
        $this->put("_previous.url", $url);
    }

     //获取当前使用的驱动器
    public function getHandler()
    {
        return $this->handler;
    }
    //返回驱动器实例是否使CokieSessionHandler
    public function handlerNeedsRequest()
    {
        return $this->handler instanceof CookieSessionHandler;
    }
    //如果驱动器是CookieSessionHandler,那么执行setRequest方法
    public function setRequestOnHandler($request)
    {
        if ($this->handlerNeedsRequest()) {
            $this->handler->setRequest($request);
        }
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31633.html

相关文章

  • Laravel深入学习7 - 框架的扩展

    摘要:组件扩展通常有两种方法向容器中绑定自己的接口实现痛过使用工厂模式实现的类注册自己的扩展。类库管理类以工厂模式实现,负责诸如缓存等驱动的实例化。闭包须要传入继承自和容器的实例化对象。当完成扩展之后要记住中替换成自己的扩展名称。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证9...

    yuanxin 评论0 收藏0
  • LaravelSession机制简介

    摘要:我们在这个类中的方法看到如下代码,一个典型的过滤器,在这个中获取的方法是。,整个初始化的过程总结下巧妙的使用了面向对象的接口方式,为我们提供了各种各样不同的存储方式,一旦我们了解了存储方式和加密规则,让不同的容器进行共享的目的也可以达到 前些天,为了解答一个问题,就去研究了Laravel的源码,讲讲我的收获:这个是问题源:http://segmentfault.com/q/101000...

    kelvinlee 评论0 收藏0
  • Laravel学习笔记之Decorator Pattern

    摘要:把和拼接在一起的场所是,所以需要造一个类,在其内部实现对的操作中实现了把原有的进过个的装饰后得到的新的,新的还是的实现,还是原来的物种。 说明:Laravel中Middleware的实现主要利用了Decorator Pattern的设计,本文主要先学习下Decorator Pattern如何实现,为后面学习Middleware的设计做个铺垫。Decorator Pattern和Adap...

    dendoink 评论0 收藏0
  • 详解 Laravel 中的依赖注入和 IoC

    摘要:依赖注入依赖注入一词是由提出的术语,它是将组件注入到应用程序中的一种行为。就像说的依赖注入是敏捷架构中关键元素。类依赖于,所以我们的代码可能是这样的创建一个这是一种经典的方法,让我们从使用构造函数注入开始。 showImg(https://segmentfault.com/img/remote/1460000018806800); 文章转自:https://learnku.com/la...

    haitiancoder 评论0 收藏0
  • Laravel学习笔记之Session源码解析(上)

    摘要:然后中间件使用方法来启动获取实例,使用类来管理主要分为两步获取实例,主要步骤是通过该实例从存储介质中读取该次请求所需要的数据,主要步骤是。 说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助。Laravel在web middleware中定义了session中间件IlluminateSess...

    NervosNetwork 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<