How to implement session in php, how to implement session in itself, and how to implement session in laravel

Keywords: PHP Session Laravel Redis

1. How to implement session in php

Execute the PHP script, session_start() will read the configuration item from php.ini, and put the generated unique value sessionID save file to the save path and location in the configuration item. And return the response header setCookie through HTTP protocol
Cookie name = cookie value sent to client.

The client receives the set cookie and writes the cookie value to the cookie file

In the next visit, the client will carry a cookie to access the browser. The browser receives the cookie value, generates the http request header and sends the included cookie to PHP. PHP recognizes the cookie value and finds the corresponding file from the save path.

After finding the file, check whether it is within the validity period, read the file within the validity period, and empty the file if it is no longer within the validity period.

2. Several issues to be considered in realizing session by yourself

1) algorithm for generating unique value

2) what data should be stored in the file or redis or memcache

3) accept the cookie value and find the corresponding file or data from the save location

4) session garbage collection mechanism, deleting session files and data

5) session synchronization in distributed mode

3. Analyze how laravel implements session

vendor/laravel/framework/src/Illuminate/Contracts definitions are all interface classes (contracts).
Take a look at what the session interface defines?

<?php

namespace Illuminate\Contracts\Session;

interface Session
{
    public function getName();//Get session name

    public function getId();//Get the current sessionID

    public function setId($id);//Modify sessionID

    public function start();//Start session and read data from configuration file

    public function save();//Save session data

    public function all();//Get all session s

    public function exists($key);//Check if the session name exists

    public function has($key);//Check if the session name exists and is not empty

    public function get($key, $default = null);//Get session value by session name

    public function put($key, $value = null);//Save session name and session value to session file or database

    public function token(); //Get the value of csrf token
 
    public function remove($key);//Delete session information according to session name

    public function forget($keys);//Delete session information according to session name

    public function flush();//Clear all session contents

    public function migrate($destroy = false); //Create a new session for the session session

    public function isStarted();//Determine if the session starts

    public function previousUrl();//Get the url of the session from the session

    public function setPreviousUrl($url);//Set the previous url to save to session

    public function getHandler();//Get session processing instance

    public function handlerNeedsRequest();//Determine if the session program requires a request

    public function setRequestOnHandler($request);//Set request on processing request instance
}

There is only one vendor/laravel/framework/src/Illuminate/Session/Store.php

<?php

namespace Illuminate\Session;

use Closure;
use stdClass;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use SessionHandlerInterface;
use Illuminate\Contracts\Session\Session;

class Store implements Session
{
    protected $id;

    protected $name;

    protected $attributes = [];

    protected $handler;

    protected $started = false;

    public function __construct($name, SessionHandlerInterface $handler, $id = null)
    {
        $this->setId($id);
        $this->name = $name;
        $this->handler = $handler;
    }

    public function start()
    {
        $this->loadSession();

        if (! $this->has('_token')) {
            $this->regenerateToken();
        }

        return $this->started = true;
    }

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

    protected function readFromHandler()
    {
        if ($data = $this->handler->read($this->getId())) {
            $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', []));

        $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;
        });
    }


    public function has($key)
    {
        return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) {
            return is_null($this->get($key));
        });
    }

    public function get($key, $default = null)
    {
        return Arr::get($this->attributes, $key, $default);
    }


    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);
    }

    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);
        });
    }


    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);
    }

    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();
        });
    }

    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;
    }


    public function getName()
    {
        return $this->name;
    }


    public function setName($name)
    {
        $this->name = $name;
    }

    public function getId()
    {
        return $this->id;
    }

    public function setId($id)
    {
        $this->id = $this->isValidId($id) ? $id : $this->generateSessionId();
    }

    public function isValidId($id)
    {
        return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
    }

    protected function generateSessionId()
    {
        return Str::random(40);
    }

    public function setExists($value)
    {
        if ($this->handler instanceof ExistenceAwareInterface) {
            $this->handler->setExists($value);
        }
    }


    public function token()
    {
        return $this->get('_token');
    }


    public function regenerateToken()
    {
        $this->put('_token', Str::random(40));
    }


    public function previousUrl()
    {
        return $this->get('_previous.url');
    }


    public function setPreviousUrl($url)
    {
        $this->put('_previous.url', $url);
    }


    public function getHandler()
    {
        return $this->handler;
    }

    public function handlerNeedsRequest()
    {
        return $this->handler instanceof CookieSessionHandler;
    }

    public function setRequestOnHandler($request)
    {
        if ($this->handlerNeedsRequest()) {
            $this->handler->setRequest($request);
        }
    }
}

Posted by Mchl on Sat, 02 Nov 2019 03:03:47 -0700