Maison > cadre php > PensezPHP > le corps du texte

Analyse de l'initialisation de l'application ThinkPHP6

藏色散人
Libérer: 2021-07-14 08:59:58
avant
3121 Les gens l'ont consulté

Méthode runWithRequest ()

Dans la méthode Http de la classe run(), après avoir obtenu l'instance de la classe think\Request, le programme continue d'exécuter $response = $this->runWithRequest(request);. Parmi elles, les premières lignes de la méthode runWithRequest() sont les suivantes :

protected function runWithRequest(Request $request)
{
    $this->initialize();

    // 加载全局中间件
    $this->loadMiddleware();
    .
    .
    .
Copier après la connexion

La première ligne de cette méthode exécute $this->initialize(); pour initialiser l'application. Ensuite, analysez cette opération d'initialisation en détail.
La méthode Http de la classe initialize() :

protected function initialize()
{
    //如果还未初始化,则初始化之
    if (!$this->app->initialized()) {
        $this->app->initialize();
    }
}
Copier après la connexion

appelle en fait la méthode App de la classe initialize(). Code de cette méthode :

public function initialize()
{
    // 设置应用状态为已经初始化
    $this->initialized = true;

    //记录开始时间
    $this->beginTime = microtime(true);
    //记录起始内存使用量
    $this->beginMem  = memory_get_usage();

    // ==( A )== 加载环境变量
    // $this->env跟前面的(new App())->http和$this->config都是同样的套路
    if (is_file($this->rootPath . '.env')) {
        $this->env->load($this->rootPath . '.env');
    }
    //设置配置文件后缀
    $this->configExt = $this->env->get('config_ext', '.php');
    // ==( B )== 设置调试模式
    $this->debugModeInit();

    // ==( C )== 加载应用文件和配置等操作
    $this->load();

    // 加载框架默认语言包
    $langSet = $this->lang->defaultLangSet();
    // 框架目录下对应的语言包
    // 比如:D:\dev\tp6\vendor\topthink\framework\src\lang\zh-cn.php
    $this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');

    // 加载应用默认语言包
    // 这个会扫描「app/lang」里面,对应语言包文件夹的所有「.php」文件
    // 比如,app/lang/zh-cn/* 下的所有文件
    // 然后加载解析
    $this->loadLangPack($langSet);

    // 监听AppInit
    $this->event->trigger('AppInit');

    // 设置时区
    date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));

    // ==( D )== 初始化
    // 初始化错误和异常处理、注册系统服务和初始化系统服务
    foreach ($this->initializers as $initializer) {
        $this->make($initializer)->init($this);
    }

    return $this;
}
Copier après la connexion

L'initialisation de l'application effectue de nombreuses opérations. Ses principales opérations sont : le chargement des variables d'environnement, le chargement des fichiers de configuration, le chargement des packs de langue, la surveillance d'AppInit et l'initialisation des classes contenues dans les initialiseurs. tableau.

(A) Charger les variables d'environnement

L'instruction correspondante de

est : $this->env->load($this->rootPath . ‘.env’);, où $this->env est le même que le principe précédent de (new App())->http (voir le premier article), il peut obtenir une instance du thinkEnvclasse. Après avoir obtenu l'instance de classe Env, appelez la méthode load() et le paramètre transmis est l'adresse du fichier .env. La méthode load() est implémentée comme suit :

public function load(string $file): void
{
    $env = parse_ini_file($file, true) ?: [];
    $this->set($env);
}
Copier après la connexion

Après avoir lu la valeur du fichier .env, cette méthode appelle la méthode set() et enregistre la configuration dans la variable membre Env de la classe $data. set() Code de la méthode :

public function set($env, $value = null): void
{
    if (is_array($env)) {
        //全部KEY转为大写字母
        $env = array_change_key_case($env, CASE_UPPER);

        foreach ($env as $key => $val) {
            //有二级配置的,转为KEY1_KEY2 => $v 的形式
            if (is_array($val)) {
                foreach ($val as $k => $v) {
                    $this->data[$key . '_' . strtoupper($k)] = $v;
                }
            } else {
                $this->data[$key] = $val;
            }
        }
        //ENV的值不是数组的情况
    } else {
        $name = strtoupper(str_replace('.', '_', $env));

        $this->data[$name] = $value;
    }
}
Copier après la connexion

La valeur lue depuis .env est probablement comme ceci :
Analyse de linitialisation de lapplication ThinkPHP6

$this->set($env) Ce que vous obtenez après cela ressemble probablement à ceci :

Analyse de linitialisation de lapplication ThinkPHP6

(B) Paramètre du mode débogage h3>

$this->debugModeInit() Voir les notes pour plus de détails sur le principe de fonctionnement.

protected function debugModeInit(): void
{
    // 应用调试模式
    if (!$this->appDebug) {
        $this->appDebug = $this->env->get('app_debug') ? true : false;
        // 关闭错误显示
        ini_set('display_errors', 'Off');
    }
    // 如果不是命令行模式
    if (!$this->runningInConsole()) {
        // 重新申请一块比较大的buffer
        // php缓冲控制
        // 参考:https://www.php.net/manual/en/ref.outcontrol.php
        // https://www.cnblogs.com/saw2012/archive/2013/01/30/2882451.html
        // 新版PHP默认开启缓冲区ob_start(),ob_get_level() == 1
        if (ob_get_level() > 0) {
            // 相当于ob_get_contents() 和 ob_clean()
            // 获取缓冲区内容并删除缓冲区内容
            $output = ob_get_clean();
        }
        // 开启新的缓冲区控制
        ob_start();
        if (!empty($output)) {
            // 由于开启了新的缓冲区控制,
            // 这里不会直接输出$output
            // 而是等到依次执行了ob_flush()和flash()之后才将内容输出到浏览器
            echo $output;
        }
    }
}
Copier après la connexion

Il convient de noter qu'il semble y avoir un bug ici. Vous devez d'abord exécuter $this->appDebug = $this->env->get('app_debug') ? true : false; pour obtenir la configuration du mode débogage, puis juger : if(!$this->appDebug).

(C) Chargement des fichiers d'application et opérations de configuration

Ensuite, exécutez$this->load(); L'implémentation spécifique de la méthode "load" est la suivante :

protected function load(): void
{
    $appPath = $this->getAppPath();

    // 加载「app」目录下的「common.php」文件
    if (is_file($appPath . 'common.php')) {
        include_once $appPath . 'common.php';
    }
    // 加载核心目录下的「helper.php」文件
    // 可以看到,这里的加载顺序:先「common.php」,后「helper.php」
    // 且「helper.php」中的函数包裹在「if (!function_exists('xxx'))」下
    // 所以可以在「common.php」文件中覆盖系统定义的助手函数
    include_once $this->thinkPath . 'helper.php';

    $configPath = $this->getConfigPath();

    $files = [];

    // glob的作用是扫描给定路径模式下的文件,非常好用
    // 这里扫描「config」目录下的所有「.php」文件
    if (is_dir($configPath)) {
        $files = glob($configPath . '*' . $this->configExt);
    }

    foreach ($files as $file) {
        // $this->config 还是同样的套路获得了「Config」类的实例
        // 「load」的第二个参数为一级配置名,这里传入一个文件名,所有文件名作为一级配置
        // 比如「app.php」配置文件,一级配置为「app」
        // 在 「Config」类作用域下的操作:
        // 「load」加载文件后,通过「parse」方法解析文件内容
        // 最后,通过「set」方法将所有配置合并了「config」成员变量
        $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
    }

    // 加载「app」目录下的「event.php」文件
    if (is_file($appPath . 'event.php')) {
        $this->loadEvent(include $appPath . 'event.php');
    }
    // 注册自定义的服务
    if (is_file($appPath . 'service.php')) {
        $services = include $appPath . 'service.php';
        foreach ($services as $service) {
            $this->register($service);
        }
    }
}
Copier après la connexion

Il convient de mentionner que. le programme est chargé d'abord "common.php", puis "helper.php" est chargé, et les fonctions dans "helper.php" sont enveloppées sous "if (!function_exists ('xxx'))", donc si nous en avons besoin , nous pouvons utiliser le fichier "common. php" pour remplacer la fonction d'assistance définie par le système.

En plus de charger ces deux fichiers, cette méthode analyse également tous les fichiers de configuration dans le répertoire "config", les charge dans les variables membres Config de la classe $config et charge "l'événement dans l'application". " directory. php ", puis chargez et enregistrez les services personnalisés.

(D) Gestion des erreurs d'initialisation et des exceptions, enregistrement des services système et initialisation des services système

Ensuite, regardez le dernier paragraphe de la fonction d'initialisation :

foreach ($this->initializers as $initializer) {
    $this->make($initializer)->init($this);
}
Copier après la connexion

Comparez-les lignes de code Opérations multiples : instanciez séparément les classes qu'il contient, puis appelez sa méthode "init". initializers La valeur du tableau est la suivante :

protected $initializers = [
    Error::class,  //错误处理类
    RegisterService::class, //注册系统服务类
    BootService::class,  //启动系统服务
];
Copier après la connexion

Ignorez la classe de gestion des erreurs système et regardez d'abord la classe de service du système d'enregistrement. Il convient de noter que cette classe a une variable membre :

protected $services = [
    PaginatorService::class,
    ValidateService::class,
    ModelService::class,
];
Copier après la connexion

contient trois services système de base. Dans sa méthode init, ces services sont enregistrés dans le service système et fusionnés avec les services personnalisés précédents. Son code d'implémentation principal est :

foreach ($services as $service) {
    if (class_exists($service)) {
        // 注册到系统服务
        $app->register($service);
    }
}
Copier après la connexion

La dernière chose instanciée est la classe de service système de démarrage. La méthode init de cette classe. seule La méthode App de la classe boot est appelée. La fonction de cette méthode est d'initialiser chaque service système, c'est-à-dire d'appeler la méthode boot de chaque service.
La classe de service du système de démarrage est implémentée comme suit :

Méthode

de la classe

class BootService
{
    public function init(App $app)
    {
        $app->boot();
    }
}
Copier après la connexion
Appboot :
public function boot(): void
{
    array_walk($this->services, function ($service) {
        $this->bootService($service);
    });
}
Copier après la connexion

La clé est la méthode bootService :

public function bootService($service)
{
    if (method_exists($service, 'boot')) {
        return $this->invoke([$service, 'boot']);
    }
}
Copier après la connexion

Cette méthode appelle chaque service séparément boot méthode pour initialiser le service enregistré.
Comme vous pouvez le voir dans le code ci-dessus, il existe trois sources de services enregistrées par le système :

  1. Le système est fourni avec, tels que PaginatorService, ValidateService, ModelService ; li>
  2. Il est personnalisé dans le fichier "service.php" du répertoire de l'application
  3. Il est défini dans le fichier "service.php" du répertoire du fournisseur ;

Après l'initialisation, l'instance de la classe "App" ressemble probablement à ceci :

Analyse de linitialisation de lapplication ThinkPHP6

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!