Heim >PHP-Framework >Laravel >Detaillierte Erklärung von Laravel – IOC-Container

Detaillierte Erklärung von Laravel – IOC-Container

藏色散人
藏色散人nach vorne
2020-12-21 09:13:352378Durchsuche

Die folgende Tutorial-Kolumne von Laravel Framework wird Ihnen eine detaillierte Erklärung des Laravel-IOC-Containers geben. Ich hoffe, dass es für Freunde hilfreich sein wird, die es brauchen!

Detaillierte Erklärung von Laravel – IOC-Container

1. Abhängigkeit

IOC (Inversion of Controller) wird als Inversion des Steuermodus bezeichnet, der auch als Abhängigkeitsinjektionsmodus (Abhängigkeitsinjektion) bezeichnet werden kann. Um das Konzept der Abhängigkeitsinjektion zu verstehen, verstehen wir zunächst, was Abhängigkeiten sind

//支付宝支付
class Alipay {
      public function __construct(){}

      public function pay()
      {
          echo 'pay bill by alipay';
      }
}
//微信支付
class Wechatpay {
      public function __construct(){}

      public function pay()
      {
          echo 'pay bill by wechatpay';
      }
}
//银联支付
class Unionpay{
      public function __construct(){}

      public function pay()
      {
          echo 'pay bill by unionpay';
      }
}

//支付账单
class PayBill {

      private $payMethod;

      public function __construct( )
      {
          $this->payMethod= new Alipay ();
      }

      public function  payMyBill()
      {
           $this->payMethod->pay();
      }
}


$pb = new PayBill ();
$pb->payMyBill();

Durch den obigen Code wissen wir, dass der Konstruktor von PayBill beim Erstellen einer Instanz der Klasse PayBill { $this->payMethod= new Alipay ( ); }, das heißt, eine Klasse Alipay wird zu diesem Zeitpunkt generiert. Dies kann so verstanden werden, dass ich zuerst eine Instanz von Alipay erhalten muss Alipay-Instanz. Wenn wir mit der Verwendung des neuen Schlüsselworts fertig sind, wurde die Abhängigkeit tatsächlich aufgelöst, da wir die Instanz von Alipay erhalten haben. Tatsächlich befand sich der größte Teil meines Codes darin Muster ~ _ ~ Was ist das Problem? Um es einfach auszudrücken: Was kann ich tun, wenn ich WeChat anstelle von Alipay verwenden möchte? Wechatpay.

Wenn unser Programm nicht sehr groß ist, spüren wir vielleicht nichts, aber wenn Ihr Code sehr komplex und umfangreich ist und sich unsere Anforderungen häufig ändern, wird es sehr mühsam sein, den Code zu ändern. Daher besteht die Idee von ioc nicht darin, neue Abhängigkeiten in der Klasse Payment zu instanziieren und aufzulösen, sondern sie in eine externe Verantwortung umzuwandeln. Vereinfacht gesagt gibt es intern keinen neuen Schritt, und die Zahlung kann erfolgen Erhalten durch Abhängigkeitsinjektion.

2. Abhängigkeitsinjektion

Wir wissen, was Abhängigkeit bedeutet. Was bedeutet Abhängigkeitsinjektion? Erweitern wir den obigen Code im Vergleich zum vorherigen Eine Pay-Schnittstelle, und dann erben alle Zahlungsmethoden diese Schnittstelle und implementieren die Pay-Funktion. Sie fragen sich vielleicht, warum Sie eine Schnittstelle verwenden müssen, worüber wir später sprechen werden.

Bevor wir PayBill instanziieren, instanziieren wir zunächst eine Alipay. In diesem Schritt müssen wir diese Abhängigkeit in die PayBill-Instanz einfügen. Über den Code können wir sehen, dass wir diese Abhängigkeit in PayBill einfügen Konstruktor. Auf diese Weise hat die PayBill-Instanz die Möglichkeit, mit Alipay zu bezahlen.

Injizieren Sie die Instanz der Klasse PayBill. Hier handelt es sich um eine manuelle Injektion, nicht um eine automatische Implementierung ist die automatische Injektion.

3. Bevor wir den IOC-Container vorstellen, wollen wir zunächst das Konzept der Reflexion (Reflexion) verstehen, da der IOC-Container auch einen Absatz aus dem Internet kopiert hat, um zu erklären, was Reflexion ist bedeutet: „Reflexion bezieht sich auf die Erweiterung der Analyse von PHP-Programmen im laufenden Zustand von PHP und das Exportieren oder Extrahieren detaillierter Informationen zu Klassen, Methoden, Eigenschaften, Parametern usw., einschließlich des dynamischen Abrufens von Informationen und des dynamischen Aufrufs.“ Methoden von Objekten werden als Reflection-API bezeichnet. Sie ist sehr leistungsfähig und kann uns dabei helfen, komplexe und erweiterbare Objekte zu erstellen. Dokumente werden automatisch generiert und können sogar zum Erweitern der PHP-Sprache verwendet werden der Konstruktor der Klasse A sowie die Klassen, von denen der Konstruktor abhängt. Hier verlassen wir uns auf eine Größe namens „args“, und durch TypeHint können wir erkennen, dass es sich um den Typ Klasse B handelt Eine Klasse kann die Attribute, Methoden, Konstruktoren und Parameter abrufen, die der Konstruktor in der Klasse benötigt. Nur so können wir den IOC-Container von Laravel realisieren. Als nächstes stellen wir das Konzept des IOC-Service-Containers vor Die gesamten Systemfunktionen und die Konfiguration und der Aufruf von Diensten sind Dinge, die wir beispielsweise im Kühlschrank aufbewahren können Container kann auch auf diese Weise verstanden werden. Wenn das Programm gestartet wird, legen oder registrieren (binden) wir einige Dienste, die wir benötigen, in den Container, und wenn ich sie brauche, nehmen wir sie einfach heraus (make). oben erwähnt sind die beiden Aktionen Registrierung und Entfernung

5. IOC 容器代码

好了,说了这么多,下面要上一段容器的代码了. 下面这段代码不是laravel 的源码, 而是来自一本书《laravel 框架关键技术解析》. 这段代码很好的还原了laravel 的服务容器的核心思想. 代码有点长, 小伙伴们要耐心看. 当然小伙伴完全可以试着运行一下这段代码,然后调试一下,这样会更有助于理解.

<?php 

//容器类装实例或提供实例的回调函数
class Container {

    //用于装提供实例的回调函数,真正的容器还会装实例等其他内容
    //从而实现单例等高级功能
    protected $bindings = [];

    //绑定接口和生成相应实例的回调函数
    public function bind($abstract, $concrete=null, $shared=false) {
        
        //如果提供的参数不是回调函数,则产生默认的回调函数
        if(!$concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }
        
        $this->bindings[$abstract] = compact(&#39;concrete&#39;, &#39;shared&#39;);
    }

    //默认生成实例的回调函数
    protected function getClosure($abstract, $concrete) {
        
        return function($c) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? &#39;build&#39; : &#39;make&#39;;
            return $c->$method($concrete);
        };
        
    }

    public function make($abstract) {
        $concrete = $this->getConcrete($abstract);

        if($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
        
        return $object;
    }

    protected function isBuildable($concrete, $abstract) {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    //获取绑定的回调函数
    protected function getConcrete($abstract) {
        if(!isset($this->bindings[$abstract])) {
            return $abstract;
        }

        return $this->bindings[$abstract][&#39;concrete&#39;];
    }

    //实例化对象
    public function build($concrete) {

        if($concrete instanceof Closure) {
            return $concrete($this);
        }

        $reflector = new ReflectionClass($concrete);
        if(!$reflector->isInstantiable()) {
            echo $message = "Target [$concrete] is not instantiable";
        }

        $constructor = $reflector->getConstructor();
        if(is_null($constructor)) {
            return new $concrete;
        }

        $dependencies = $constructor->getParameters();
        $instances = $this->getDependencies($dependencies);

        return $reflector->newInstanceArgs($instances);
    }

    //解决通过反射机制实例化对象时的依赖
    protected function getDependencies($parameters) {
        $dependencies = [];
        foreach($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if(is_null($dependency)) {
                $dependencies[] = NULL;
            } else {
                $dependencies[] = $this->resolveClass($parameter);
            }
        }

        return (array)$dependencies;
    }

    protected function resolveClass(ReflectionParameter $parameter) {
        return $this->make($parameter->getClass()->name);
    }

}

上面的代码就生成了一个容器,下面是如何使用容器

$app = new Container();
$app->bind("Pay", "Alipay");//Pay 为接口, Alipay 是 class Alipay
$app->bind("tryToPayMyBill", "PayBill"); //tryToPayMyBill可以当做是Class PayBill 的服务别名

//通过字符解析,或得到了Class PayBill 的实例
$paybill = $app->make("tryToPayMyBill"); 

//因为之前已经把Pay 接口绑定为了 Alipay,所以调用pay 方法的话会显示 &#39;pay bill by alipay &#39;
$paybill->payMyBill();

当我们实例化一个Container得到 $app 后, 我们就可以向其中填充东西了

$app->bind("Pay", "Alipay");
$app->bind("tryToPayMyBill", "PayBill");

当执行完这两行绑定码后, $app 里面的属性 $bindings 就已经有了array 值,是啥样的呢,我们来看下

array:2 [
 "App\Http\Controllers\Pay" => array:2 [
     "concrete" => Closure {#355 
       class: "App\Http\Controllers\Container" 
       this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …} 
       parameters: array:1 [
         "$c" => []
       ] 
       use: array:2 [
         "$abstract" => "App\Http\Controllers\Pay"
        "$concrete" => "App\Http\Controllers\Alipay"
       ] 
       file: "C:\project\test\app\Http\Controllers\IOCController.php" line:       "119 to 122"
   } 
   "shared" => false 
 ]

"tryToPayMyBill" => array:2 [
     "concrete" => Closure {#359 
         class: "App\Http\Controllers\Container" 
         this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …} 
         parameters: array:1 [
               "$c" => []
         ] 
         use: array:2 [
               "$abstract" => "tryToPayMyBill" 
               "$concrete" => "\App\Http\Controllers\PayBill"
         ] 
         file: "C:\project\test\app\Http\Controllers\IOCController.php" line: "119 to 122"
   } 
     "shared" => false 
 ]
]

当执行 $paybill = $app->make(“tryToPayMyBill”); 的时候, 程序就会用make方法通过闭包函数的回调开始解析了.

解析’tryToPayBill’ 这个字符串, 程序通过闭包函数 和build方法会得到 ‘PayBill’ 这个字符串,该字符串保存在$concrete 上. 这个是第一步. 然后程序还会以类似于递归方式 将$concrete 传入 build() 方法. 这个时候build里面就获取了$concrete = ‘PayBill’. 这个时候反射就派上了用场, 大家有没有发现,PayBill 不就是 class PayBill 吗? 然后在通过反射的方法ReflectionClass(‘PayBill’) 获取PayBill 的实例. 之后通过getConstructor(),和getParameters() 等方法知道了 Class PayBill 和 接口Pay 存在依赖

//$constructor = $reflector->getConstructor();
ReflectionMethod {#374 
    +name: "__construct" 
    +class: "App\Http\Controllers\PayBill" 
    parameters: array:1 [
          "$payMethod" => ReflectionParameter {#371 
              +name: "payMethod" 
              position: 0 typeHint: "App\Http\Controllers\Pay"
          }
    ]
     extra: array:3 [
          "file" => "C:\project\test\app\Http\Controllers\IOCController.php"
          "line" => "83 to 86" 
          "isUserDefined" => true 
      ] 
    modifiers: "public"
}


//$dependencies = $constructor->getParameters();
array:1 [
    0 => ReflectionParameter {#370 
        +name: "payMethod" 
        position: 0 
        typeHint: "App\Http\Controllers\Pay"
        }
]

接着,我们知道了有’Pay’这个依赖之后呢,我们要做的就是解决这个依赖,通过 getDependencies($parameters), 和 resolveClass(ReflectionParameter $parameter) ,还有之前的绑定$app->bind(“Pay”, “Alipay”); 在build 一次的时候,通过 return new $concrete;到这里我们得到了这个Alipay 的实例

        if(is_null($constructor)) {
            return new $concrete;
        }

到这里我们总算结局了这个依赖, 这个依赖的结果就是实例化了一个 Alipay. 到这里还没结束

        $instances = $this->getDependencies($dependencies);

上面的$instances 数组只有一个element 那就是 Alipay 实例

  array:1 [0 =>Alipay
      {#380}
 ]

最终通过 newInstanceArgs() 方法, 我们获取到了 PayBill 的实例。

 return $reflector->newInstanceArgs($instances);

到这里整个流程就结束了, 我们通过 bind 方式绑定了一些依赖关系, 然后通过make 方法 获取到到我们想要的实例. 在make中有牵扯到了闭包函数,反射等概念.

好了,当我们把容器的概念理解了之后,我们就可以理解下为什么要用接口这个问题了. 如果说我不想用支付宝支付,我要用微信支付怎么办,too easy.

$app->bind("Pay", "Wechatpay");
$app->bind("tryToPayMyBill", "PayBill");
$paybill = $app->make("tryToPayMyBill"); 
$paybill->payMyBill();

是不是很简单呢, 只要把绑定从’Alipay’ 改成 ‘Wechatpay’ 就行了,其他的都不用改. 这就是为什么我们要用接口. 只要你的支付方式继承了pay 这个接口,并且实现pay 这个方法,我们就能够通过绑定正常的使用. 这样我们的程序就非常容易被拓展,因为以后可能会出现成百上千种的支付方式.

好了,到这里不知道小伙伴有没有理解呢,我建议大家可以试着运行下这些代码, 这样理解起来会更快.同时推荐大家去看看 《laravel 框架关键技术解析》这本书,写的还是不错的.

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung von Laravel – IOC-Container. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen