环境创建:

  1. 需要在全局文件开启Session:

/app/middleware.php

<?php
// 全局中间件定义文件
return [
    // 全局请求缓存
    // \think\middleware\CheckRequestCache::class,
    // 多语言加载
    // \think\middleware\LoadLangPack::class,
    // Session初始化
    // \think\middleware\SessionInit::class
    \think\middleware\SessionInit::class
];

/app/controller/index.php:

class Index extends BaseController
{
    public function index()
    {
        session("demo","domo");
    }
}

问题:

外面的分析文章已经很多了,但是我还是有一些不解之处。

  1. 从哪里写入了文件操作?为什么会调用save函数?
  2. session是从哪里判断已什么方式储存的?

解决问题:

要解决问题一,我们需要同时在自己调用的session以及sava函数同时下断,看下谁先断下:

Snipaste_2020-01-16_17-13-20.png
Snipaste_2020-01-16_17-14-00.png

先被断下的是自己的session函数。我们直接跟吧:

    function session($name = '', $value = '')
    {
        if (is_null($name)) {
            // 清除
            Session::clear();
        } elseif ('' === $name) {
            return Session::all();
        } elseif (is_null($value)) {
            // 删除
            Session::delete($name);
        } elseif ('' === $value) {
            // 判断或获取
            return 0 === strpos($name, '?') ? Session::has(substr($name, 1)) : Session::get($name);
        } else {
            // 设置
            Session::set($name, $value);
        }
    }

这里调用了Session::set这个静态函数.

然后是TP的路由寻找,一直找到了这里:

vendor/topthink/framework/src/think/Manager.php:

    public function __call($method, $parameters)
    {
        return $this->driver()->$method(...$parameters);
    }

他在这里调用了本类的driver,我们先跟进driver:

    protected function driver(string $name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        if (is_null($name)) {
            throw new InvalidArgumentException(sprintf(
                'Unable to resolve NULL driver for [%s].', static::class
            ));
        }

        return $this->drivers[$name] = $this->getDriver($name);
    }

第一行获取了默认的driver,跟进看看:

    public function getDefaultDriver()
    {
        return $this->app->config->get('session.type', 'file');
    }

到这里我的第二个问题就解决了,他是从config中获取到的存储方式。我们接着跟下去:

return $this->drivers[$name] = $this->getDriver($name);

跟进他的getDriver函数:

    protected function getDriver(string $name)
    {
        return $this->drivers[$name] ?? $this->createDriver($name);
    }

然后这里说一下PHP7中的一个特性,就是双问号。双问号的意思就是:

isset($this->drivers[$name])?$this->drivers[$name]:$this->createDriver($name)

所以这里会直接返回dirver,可以看下此时driver的值:
Snipaste_2020-01-16_17-58-23.png

之后会调用这个:Arr::set($this->data, $name, $value);

这个函数就是对$this->data赋值.

之后函数直接返回了。这时候我们是在index中的哪个函数里面呢?
打开文件:public/index.php 如图下断:
Snipaste_2020-01-16_18-14-31.png
第一次断下:

$http = (new App())->http;

我们按F9,看下第二次在哪里断下:

$response = $http->run();

再次F9看下第三次在哪里断下:

session("demo","domo");

然后再次F9,第四次断下位置:

$response->send();

再次F9,第五次断下位置:

$http->end($response);

再次F9,第六次断下位置:

    public function save(): void
    {
        $this->clearFlashData();

        $sessionId = $this->getId();

        if (!empty($this->data)) {
            $data = $this->serialize($this->data);

            $this->handler->write($sessionId, $data);
        } else {
            $this->handler->delete($sessionId);
        }

        $this->init = false;
    }

现在才到了save函数,此时看堆栈:

Store.php:256, think\session\Store->save()
Manager.php:174, think\Session->__call()
SessionInit.php:78, think\Session->save()
SessionInit.php:78, think\middleware\SessionInit->end()
Middleware.php:165, think\Middleware->end()
Http.php:279, think\Http->end()
index.php:24, {main}()

Http.php:279, thinkHttp->end():

    public function end(Response $response): void
    {
        $this->app->event->trigger(HttpEnd::class, $response);

        //执行中间件
        $this->app->middleware->end($response);

        // 写入日志
        $this->app->log->save();
    }

通过堆栈得知在低一级为:$this->app->middleware->end($response);
此时我们需要直到middleware是从哪里来的,直接从新来在end下断查看变量:

middleware = think\Middleware

也就是说end是thinkMiddleware对象中的,跟入就行:

    public function end(Response $response)
    {
        foreach ($this->queue as $queue) {
            foreach ($queue as $middleware) {
                [$call] = $middleware;
                if (is_array($call) && is_string($call[0])) {
                    $instance = $this->app->make($call[0]);
                    if (method_exists($instance, 'end')) {
                        $instance->end($response);
                    }
                }
            }
        }
    }

此时会获取到我们之前写入的中间件类,也就是thinkmiddlewareSessionInit::class,然后调用thinkmiddlewareSessionInit::class的End函数,跟进end函数:

    public function end(Response $response)
    {
        $this->session->save();
    }

发现它调用了save函数。自此我的两个疑惑点都解决完毕了。

总结:

  1. 从哪里写入了文件操作?为什么会调用save函数?
在程序退出时调用了save函数。
  1. session是从哪里判断已什么方式储存的?
从配置项中获取,在设置Session时获取