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接口定义的是什么?
<?php
namespace Illuminate\Contracts\Session;
interface Session
{
public function getName();//获得session名字
public function getId();//获得当前sessionID
public function setId($id);//修改sessionID
public function start();//启动session,从配置文件中读取数据
public function save();//将session数据保存
public function all();//获得所有的session
public function exists($key);//检查session名称是否存在
public function has($key);//检查session名称是否存在和不为空
public function get($key, $default = null);//通过session名称获取session值
public function put($key, $value = null);//将session名称和session值存入session文件或数据库
public function token(); //获得csrf token的值
public function remove($key);//根据session名称删除session信息
public function forget($keys);//根据session名称删除session信息
public function flush();//清空所有的session内容
public function migrate($destroy = false); //为session会话创建一个新的session会话
public function isStarted();//确定会话是否启动
public function previousUrl();//从会话中获取session的url
public function setPreviousUrl($url);//设置previous的url存入session
public function getHandler();//获得session处理实例
public function handlerNeedsRequest();//确定会话程序是否需要请求
public function setRequestOnHandler($request);//在处理请求实例上设置请求
}
实现接口类的具体实现类只有一个,也被称为驱动器
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;//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);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。