Laravel5.3之Session源碼解析(中)

說明:在上篇中學習了session的啓動過程,主要分爲兩步,一是session的實例化,即\Illuminate\Session\Store的實例化;二是從session存儲介質redis中讀取id = laravel_session*的數據。Laravel5.3把session垃圾回收放在了啓動過程當中,儘管Laravel5.1是放在session關閉過程的,本篇聊下垃圾回收,這也是session第一步啓動session的過程。session第二步就是操做session,包括對session數據的CRUD增刪改查操做,本文也主要聊下相關操做源碼。php

開發環境:Laravel5.3 + PHP7laravel

Session垃圾回收

首先看下session中間件的源碼\Illuminate\Session\Middleware\StartSession::classredis

public function handle($request, Closure $next)
    {
        $this->sessionHandled = true;

        if ($this->sessionConfigured()) {
            $session = $this->startSession($request);

            // 把session對象存儲到Request中
            // 因此能夠在控制器Controller中使用Request實例獲取session對象:$request->session()
            $request->setSession($session);

            $this->collectGarbage($session);
        }   
        
        ...
        
     }  
     
     protected function collectGarbage(SessionInterface $session)
    {
        // 讀取config/session.php中的配置
        $config = $this->manager->getSessionConfig();

        if ($this->configHitsLottery($config)) {
            // CacheBasedSessionHandler::gc(60) 60 minutes
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    } 
    
    protected function configHitsLottery(array $config)
    {
        // session.php中'lottery'默認配置是[2, 100],這裏就是取機率2/100 = 2%
        // 也就是100次請求有2次會觸發過時session的垃圾回收
        return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
    }

這裏假設session的存儲介質是經常使用的redis,則$session->getHandler()返回的就是\Illuminate\Session\CacheBasedSessionHandler實例,該handler就是負責從redis這個存儲介質中CRUD數據,OK,看下該handler的gc()源碼:數據庫

public function gc($lifetime)
    {
        return true;
    }

其實什麼都沒作。這是固然的,redis對於過時的key會自動清除,因此這裏就讓redis來負責垃圾回收過時數據。固然,對於database這種handler,能夠看下它的垃圾回收\Illuminate\Session\DatabaseSessionHandler:數組

public function gc($lifetime)
    {
        $this->getQuery()->where('last_activity', '<=', time() - $lifetime)->delete();
    }

以數據庫做爲存儲session的介質,垃圾回收就是從sessions表裏刪除掉對應字段。session

操做Session

操做Session就是對從存儲介質如redis中取出的數據進行CRUD增刪改查操做,包括:數據讀取;數據存儲;數據刪除;數據暫存。固然,在對session進行CRUD操做前,首先得獲取session對象即\Illuminate\Session\Store實例,有三種方法:經過Request實例;經過Session Facade方法;經過helper函數session(),代碼以下:app

// 由於在中間件StartSesstion前置操做中有把session實例存入到$request中,$request->setSession($session);
    $session = $request->session(); 
    // 經過Session Facade直接獲取到$session對象,並進行CRUD操做
    Session::put('session', 'Store'); 
    // 經過helper函數來獲取session實例,其實是經過app('session')從Container中解析出名爲'session'的服務即Store實例
    $session = session()->driver(); 

    function session($key = null, $default = null)
    {
        if (is_null($key)) {
            return app('session');
        }

        if (is_array($key)) {
            return app('session')->put($key);
        }

        return app('session')->get($key, $default);
    }

session數據讀取

session數據讀取方法包括:dom

// 'Store'是默認數據,讀取key爲'session:store'的數據
    $value = Session::get('session.store', 'Store'); 
    // Illuminate\Session\Store
    public function get($name, $default = null)
    {
        return Arr::get($this->attributes, $name, $default);
    }
    
    // 'Store'是默認數據,讀取key爲'session:store'的數據,並刪除key爲'session'的數據
    $value = Session::pull('session', 'Store'); 
    // Illuminate\Session\Store
    public function pull($key, $default = null)
    {
        return Arr::pull($this->attributes, $key, $default);
    }
    
    // 返回全部數據
    $value = Session:all();
    public function all()
    {
        return $this->attributes;
    }

在Session啓動過程當中,就包含了把session數據從存儲介質如redis中取出來,並存放在Store的$attributes屬性中,可看Store::loadSession()源代碼:函數

protected function loadSession()
    {
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());

        foreach (array_merge($this->bags, [$this->metaBag]) as $bag) {
            $this->initializeLocalBag($bag);

            $bag->initialize($this->bagData[$bag->getStorageKey()]);
        }
    }

因此,使用Arr類的一些數組操做函數從Store的$attributes屬性中讀取session數據。Laravel提供了\Illuminate\Support\Arr輔助類來操做數組,支持.語法來操做數組,同時還提供了\Illuminate\Support\Str輔助類來操做字符串。學習

總之,Laravel提供了三種方法來讀取session數據:

Session::get();
Session::pull();
Session::all();

session數據存儲

session數據存儲方法包括:

// '更新式存儲',即若是redis中有'session.store'數據,就使用'Store'來update舊數據
    Session::put('session.store', 'Store');
    public function put($key, $value = null)
    {
        if (! is_array($key)) {
            $key = [$key => $value];
        }

        foreach ($key as $arrayKey => $arrayValue) {
            $this->set($arrayKey, $arrayValue);
        }
    }
    
    // '壓入式存儲',即若是redis中有'session.store'數據,就使用'Store'和舊數據如'StoreOld'做爲新數組數據
    // 這時'session.store'新數據是['StoreOld', 'Store'];
    Session::push('session.store', 'Store');
    public function push($key, $value)
    {
        $array = $this->get($key, []);

        $array[] = $value;

        $this->put($key, $array);
    }

總之,Laravel提供了兩種方法來存儲數據:

Session::put('session.store', 'Store');
Session::push('session.store', 'StoreNew');

session數據刪除

session數據刪除方法包括:

// 刪除key爲'session.store'的數據
    Session::forget('session.store');
    public function forget($keys)
    {
        Arr::forget($this->attributes, $keys);
    }
    
    // 清空全部數據,$attributes爲空
    Session::flush();
    public function flush()
    {
        $this->clear();
    }
    public function clear()
    {
        $this->attributes = [];

        foreach ($this->bags as $bag) {
            $bag->clear();
        }
    }

總之,Laravel提供了兩種方法來刪除數據:

Session::forget('session.store');
Session::flush();

session數據暫存

數據暫存是把session中的數據保留到下一次請求中,下一次請求結束後則刪除數據,數據暫存方法包括:

// 把'session.store'數據刷到'_flash.new',等待下一次請求使用,而後再刪除
    Session::flash('session.store', 'Store');
    public function flash($key, $value)
    {
        // 更新式存儲'session.store' => 'Store'
        $this->put($key, $value);

        // 壓入式存儲'_flash.new' => ['session.store', XXX]
        $this->push('_flash.new', $key);

        // 刪除'session.store'這個value值
        $this->removeFromOldFlashData([$key]);
    }
    protected function removeFromOldFlashData(array $keys)
    {
        // 把'_flash.old'數組中不包含'session.store'的結果存儲到'_flash.old'中
        // 即刪除'session.store'這個value值
        $this->put('_flash.old', array_diff($this->get('_flash.old', []), $keys));
    }
    
    // 把全部本次須要刪除的數據所有刷到'_flash.new'中,等待下一次請求使用,而後再刪除
    Session::reflash();
    public function reflash()
    {
        $this->mergeNewFlashes($this->get('_flash.old', []));
        
        $this->put('_flash.old', []);
    }
    protected function mergeNewFlashes(array $keys)
    {
        // 把'_flash.old'中的value值合併到'_flash.new'中
        $values = array_unique(array_merge($this->get('_flash.new', []), $keys));

        $this->put('_flash.new', $values);
    }
    
    // 把要刪除的'session.store'從新激活,刷到'_flash.new'中,等待下一次使用
    Session::keep(['session.store' => 'Store']);
    public function keep($keys = null)
    {
        $keys = is_array($keys) ? $keys : func_get_args();
        
        // 把'session.store'刷到'_flash.new'中
        $this->mergeNewFlashes($keys);
        // 同時,把'session.store'從'_flash.old'中刪除
        $this->removeFromOldFlashData($keys);
    }

總之,就是把本次請求要刪除的數據放在'_flash.old',留到下一次請求中使用的就把它刷到'_flash.new'中。Laravel提供了三種方法來暫存數據:

Session::flash();
Session::reflash();
Session::keep();

總結:本文主要學習下Laravel的session的垃圾回收和CRUD增刪改查操做。下篇再學習下關閉session,到時見。

相關文章
相關標籤/搜索