TP5.1 源码窥探之初始化

前面了解到了自动加载,容器,以及Facade门面,下面就看一下,容器实例化以后App类都做了些什么吧。

入口文件index.php执行应用并响应

Container::get('app')->run()->send();

然后看一下容器中的APP实例是怎么执行run方法,进行一步一步的初始化的。,首先是初始化应用,类似AOP切面思想的监听app_init应用初始化钩子行为等等,一直得到response响应信息。

public function run()
{
    try {
        // 初始化应用
        $this->initialize();

        // 监听app_init
        $this->hook->listen('app_init');

        if ($this->bindModule) {
            // 模块/控制器绑定
            $this->route->bind($this->bindModule);
        } elseif ($this->config('app.auto_bind_module')) {
            // 入口自动绑定
            $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
            if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
                $this->route->bind($name);
            }
        }

        // 监听app_dispatch
        $this->hook->listen('app_dispatch');

        $dispatch = $this->dispatch;

        if (empty($dispatch)) {
            // 路由检测
            $dispatch = $this->routeCheck()->init();
        }

        // 记录当前调度信息
        $this->request->dispatch($dispatch);

        // 记录路由和请求信息
        if ($this->appDebug) {
            $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
            $this->log('[ HEADER ] ' . var_export($this->request->header(), true));
            $this->log('[ PARAM ] ' . var_export($this->request->param(), true));
        }

        // 监听app_begin
        $this->hook->listen('app_begin');

        // 请求缓存检查
        $this->checkRequestCache(
            $this->config('request_cache'),
            $this->config('request_cache_expire'),
            $this->config('request_cache_except')
        );

        $data = null;
    } catch (HttpResponseException $exception) {
        $dispatch = null;
        $data     = $exception->getResponse();
    }

    $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
        return is_null($data) ? $dispatch->run() : $data;
    });

    $response = $this->middleware->dispatch($this->request);

    // 监听app_end
    $this->hook->listen('app_end', $response);

    return $response;
}

下面看一下初始化应用流程,

public function initialize()
{
    if ($this->initialized) {
        return;
    }

    $this->initialized = true;
    $this->beginTime   = microtime(true);
    $this->beginMem    = memory_get_usage();

    $this->rootPath    = dirname($this->appPath) . DIRECTORY_SEPARATOR;
    $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
    $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
    $this->configPath  = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;

    static::setInstance($this);

    $this->instance('app', $this);

    //var_dump(is_file($this->rootPath . '.env'));exit;
    // 加载环境变量配置文件
    if (is_file($this->rootPath . '.env')) {
        $this->env->load($this->rootPath . '.env');
    }

    $this->configExt = $this->env->get('config_ext', '.php');

    // 加载惯例配置文件
    $this->config->set(include $this->thinkPath . 'convention.php');

    // 设置路径环境变量
    $this->env->set([
        'think_path'   => $this->thinkPath,
        'root_path'    => $this->rootPath,
        'app_path'     => $this->appPath,
        'config_path'  => $this->configPath,
        'route_path'   => $this->routePath,
        'runtime_path' => $this->runtimePath,
        'extend_path'  => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
        'vendor_path'  => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
    ]);
    /*$test = $this->env->get('THINK_PATH');
    var_dump($test);;exit;*/
    $this->namespace = $this->env->get('app_namespace', $this->namespace);
    $this->env->set('app_namespace', $this->namespace);

    // 注册应用命名空间
    Loader::addNamespace($this->namespace, $this->appPath);

    // 初始化应用
    $this->init();

    // 开启类名后缀
    $this->suffix = $this->config('app.class_suffix');

    // 应用调试模式
    $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
    $this->env->set('app_debug', $this->appDebug);

    if (!$this->appDebug) {
        ini_set('display_errors', 'Off');
    } elseif (PHP_SAPI != 'cli') {
        //重新申请一块比较大的buffer
        if (ob_get_level() > 0) {
            $output = ob_get_clean();
        }
        ob_start();
        if (!empty($output)) {
            echo $output;
        }
    }

    // 注册异常处理类
    if ($this->config('app.exception_handle')) {
        Error::setExceptionHandler($this->config('app.exception_handle'));
    }

    // 注册根命名空间
    if (!empty($this->config('app.root_namespace'))) {
        Loader::addNamespace($this->config('app.root_namespace'));
    }

    // 加载composer autofile文件
    Loader::loadComposerAutoloadFiles();

    // 注册类库别名
    Loader::addClassAlias($this->config->pull('alias'));

    // 数据库配置初始化
    Db::init($this->config->pull('database'));

    // 设置系统时区
    date_default_timezone_set($this->config('app.default_timezone'));

    // 读取语言包
    $this->loadLangPack();

    // 路由初始化
    $this->routeInit();
}

initialized属性表示应用舒适化只能初始化一次,然后记录应用初始化时间以及初始内存使用情况,项目根目录,路由目录,以及配置文件目录,然后将以上属性记录到当前实例,然后绑定自身实例到容器。加载Env环境变量,设置配置文件后缀,加载惯例配置,然后设置env路径环境变量,注册应用命名空间,初始化应用,开启类名后缀,应用调试模式,注册异常处理,注册跟命名空间,加载composer autofile文件,注册类库别名,数据库配置初始化,设置系统时区,读取语言包,路由初始化。下面看一下初始化应用或者模块。

public function init($module = '')
{
    // 定位模块目录
    $module = $module ? $module . DIRECTORY_SEPARATOR : '';
    $path   = $this->appPath . $module;

    // 加载初始化文件
    if (is_file($path . 'init.php')) {
        include $path . 'init.php';
    } elseif (is_file($this->runtimePath . $module . 'init.php')) {
        include $this->runtimePath . $module . 'init.php';
    } else {
        // 加载行为扩展文件
        if (is_file($path . 'tags.php')) {
            $tags = include $path . 'tags.php';
            if (is_array($tags)) {
                $this->hook->import($tags);
            }
        }

        // 加载公共文件
        if (is_file($path . 'common.php')) {
            include_once $path . 'common.php';
        }

        if ('' == $module) {
            // 加载系统助手函数
            include $this->thinkPath . 'helper.php';
        }

        // 加载中间件
        if (is_file($path . 'middleware.php')) {
            $middleware = include $path . 'middleware.php';
            if (is_array($middleware)) {
                $this->middleware->import($middleware);
            }
        }

        // 注册服务的容器对象实例
        if (is_file($path . 'provider.php')) {
            $provider = include $path . 'provider.php';
            if (is_array($provider)) {
                $this->bindTo($provider);
            }
        }

        // 自动读取配置文件
        if (is_dir($path . 'config')) {
            $dir = $path . 'config' . DIRECTORY_SEPARATOR;
        } elseif (is_dir($this->configPath . $module)) {
            $dir = $this->configPath . $module;
        }

        $files = isset($dir) ? scandir($dir) : [];

        foreach ($files as $file) {
            if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
                $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
            }
        }
    }

    $this->setModulePath($path);

    if ($module) {
        // 对容器中的对象实例进行配置更新
        $this->containerConfigUpdate($module);
    }
}

加载模块下的初始化文件init.php,在家模块下的行为扩展文件tags.php,加载模块下的公共文件common.php,加载系统助手函数helper.php,加载中间件middleware.php,注册服务的容器对象实例provider.php,自动读取配置文件,设置模块路径,对容器中的对象实例进行配置更新。

本作品采用《CC 协议》,转载必须注明作者和本文链接
今年不学习,明天惨唧唧。
zs4336
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!