thinkphp 5.1フレームワーク解析(三):容器と依存注入


前の記事ではThinkPHPがどのように自動ロードを実現するかについてお話ししましたが、見たいならThinkPHP 5を見ることができます.1ソースコードの浅い分析(二)自動ロードメカニズム
この記事を読む前にIOC、DI、Facadeの基本的な知識を身につけていただきたいと思いますが、ご理解いただけない場合は、まずいくつかの記事をご覧ください.
制御反転(IoC)と依存注入(DI)の詳細な理解
では本題に入ります.
サービスコール
分析フレームワークに基づくエントリスクリプトファイルindex.php
//       
require __DIR__ . '/../thinkphp/base.php';

//             Request   Config  

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

上記のbase.phpの役割は、自動ロードメカニズムのロードと異常処理、およびログ機能のオンです.
//        
Container::get('app')->run()->send();

ここでこそIOCコンテナ機能を使って、appというコンテナ1を取得
Containerに入ってから彼のクラス属性を紹介します
protected static $instance; //           ,      ,      
protected $instances = [];    //         
protected $bind = [];        //       
protected $name = [];        //       
$instancesは、登録ツリーモード2が実現され、値が格納される
array (
  'think\\App' => App  ,
  'think\\Env' => Env  ,
  'think\\Config' => Config  ,
   ...
)
bindは、初期化時に初期データの山をロードし、クラス名とクラス名のマッピング関係を記録します.
protected $bind = [
      'app' => 'think\\App',
      'build' => 'think\\Build',
      'cache' => 'think\\Cache',
      'config' => 'think\\Config',
      'cookie' => 'think\\Cookie',
        ...
    ]
nameおよびbind属性レコードの値は、クラス別名とクラス名のマッピング関係で類似しています.違いは、nameがインスタンス化されたマッピング関係を記録していることである.
getメソッドへのアクセス
public static function get($abstract, $vars = [], $newInstance = false)
{
    return static::getInstance()->make($abstract, $vars, $newInstance);
}

このコードは、現在のコンテナのインスタンス(単一例)を取得し、インスタンス化することです.
makeメソッドへのアクセス
public function make($abstract, $vars = [], $newInstance = false)
{
    if (true === $vars) {
        //            
        $newInstance = true;
        $vars        = [];
    }
    //              ,         
    $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
    //        ,              ,            
    if (isset($this->instances[$abstract]) && !$newInstance) {
        return $this->instances[$abstract];
    }
    //           ,   'app' => 'think\\App',
    if (isset($this->bind[$abstract])) {
        $concrete = $this->bind[$abstract];
        //   ThinkPHP                   ,         
        if ($concrete instanceof Closure) {
            $object = $this->invokeFunction($concrete, $vars);
        } else {
            //        ,          ,  think\\App
            $this->name[$abstract] = $concrete;
            return $this->make($concrete, $vars, $newInstance);
        }
    } else {
        //         
        $object = $this->invokeClass($abstract, $vars);
    }

    if (!$newInstance) {
        $this->instances[$abstract] = $object;
    }
    //          
    return $object;
}

分割してみましょう
if (true === $vars) {
        //            
        $newInstance = true;
        $vars        = [];
}

このコードは、make($abstract, true)を使用して関数を呼び出すことができ、毎回新しいインスタンスを得ることができます.(この方式はあまりよくないと思いますが、変数ごとに意味が明確ではありません)
//              ,         
    $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
    //        ,              ,            
    if (isset($this->instances[$abstract]) && !$newInstance) {
        return $this->instances[$abstract];
    }

前述したように、nameにはインスタンス化された別名=>クラス名のマッピング関係が格納されており、ここでクラス名を取り出してみます.このクラスがインスタンス化されている場合は、直接戻ります.
//           ,   'app' => 'think\\App',
    if (isset($this->bind[$abstract])) {
        $concrete = $this->bind[$abstract];
        //   ThinkPHP                   ,         
        if ($concrete instanceof Closure) {
            $object = $this->invokeFunction($concrete, $vars);
        } else {
            //        ,          ,  think\\App
            $this->name[$abstract] = $concrete;
            return $this->make($concrete, $vars, $newInstance);
        }
    } else {
        //         
        $object = $this->invokeClass($abstract, $vars);
    }

ここでは、コンテナにロードするクラスが以前に別名をバインドしたかどうかを確認します(bind('classNickName')に直接設定することもできます).
  • バインドされている場合は、インスタンス化します.
  • がなければ、クラス名であると認定し、直接呼び出します.3

  • ファセットモード
    上のIOC容器では、$ioc->get('test'); testクラスを手に入れることができ、私たちの$user->hello()の方法で挨拶をします.顔ができたら、直接使ってもいいです.Test::hello()静的呼び出しについて説明します.
    コードを記述する際にfacadeパケットのクラスを用いてインタフェースの静的呼び出しを行うことが多いが,ここでは公式サイトの例を挙げる.app\common\Testクラスを定義すると、helloダイナミックメソッドがあります.

    helloメソッドを呼び出すコードは、次のようになります.
    $test = new \app\common\Test;
    echo $test->hello('thinkphp'); //    hello,thinkphp

    次に、このクラスに静的エージェントクラスapp\facade\Testを定義します(このクラス名は必ずしもTestクラスと一致する必要はありませんが、通常は管理を容易にするために、名前の統一を維持することをお勧めします).

    このクラスライブラリがthink\Facadeを継承する限り、動的クラスapp\common\Testの動的メソッドを静的に呼び出すことができ、例えば、上記のコードを変更することができる.
    //                    hello
    echo \app\facade\Test::hello('thinkphp');

    結果はhello,thinkphpも出力されます.
    率直に言って、Facade機能はクラスをインスタンス化せずに静的に呼び出すことができます.
    Facadeの動作原理
  • Facedeコア実装原理は、FacadeにIoC容器を事前に注入することである.
  • は、iocコンテナにバインドされたkeyと同様にクラスの変数を定義するサービスプロバイダの外観クラスを定義する.
  • 静的マジックによる方法_callStaticは、現在呼び出すhelloメソッド
  • を得ることができる.
  • static:$ioc->make('Test');

  • なぜFacadeを使うのか
    Facadesを使用する上で最も重要なのは、長いクラス名を手動で注入したり構成したりする必要がなく、簡単で覚えやすい構文を提供することです.さらに,PHP静的方法に対する独自の呼び出しにより,試験が非常に容易になった.
    ここではContainerクラスの場所が見つからないので、自動ロードメカニズムを実行してContainerの場所を探してロードします.↩
    実例の山を木に掛けて、必要なときに使っています.↩
    ダイレクトコールは反射を使用した結果で、反射に関する知識点は自分で見ています.↩