中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Laravel中Container如何使用

發布時間:2021-07-19 13:50:30 來源:億速云 閱讀:135 作者:Leah 欄目:web開發

Laravel中Container如何使用,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

PHPUnit測試下綁定

在聊解析過程前,先測試下\Illuminate\Container\Container中綁定的源碼,這里測試下bind(),singleton(),instance()三個綁定方式:

<?php  namespace MyRightCapital\Container\Tests;  use MyRightCapital\Container\Container;  class ContainerBindTest extends \PHPUnit_Framework_TestCase {     /**      * @var Container $container      */     protected $container;      public function setUp()     {         $this->container = new Container();     }      public function testBindClosure()     {         // Arrange         $expected = 'Laravel is a PHP Framework.';         $this->container->bind('PHP', function () use ($expected) {             return $expected;         });          // Actual         $actual = $this->container->make('PHP');          // Assert         $this->assertEquals($expected, $actual);     }      public function testBindInterfaceToImplement()     {         // Arrange         $this->container->bind(IContainerStub::class, ContainerImplementationStub::class);          // Actual         $actual = $this->container->make(IContainerStub::class);          // Assert         $this->assertInstanceOf(IContainerStub::class, $actual);     }      public function testBindDependencyResolution()     {         // Arrange         $this->container->bind(IContainerStub::class, ContainerImplementationStub::class);          // Actual         $actual = $this->container->make(ContainerNestedDependentStub::class);          // Assert         $this->assertInstanceOf(ContainerDependentStub::class, $actual->containerDependentStub);         $this->assertInstanceOf(ContainerImplementationStub::class, $actual->containerDependentStub->containerStub);     }      public function testSingleton()     {         // Arrange         $this->container->singleton(ContainerConcreteStub::class);         $expected = $this->container->make(ContainerConcreteStub::class);          // Actual         $actual = $this->container->make(ContainerConcreteStub::class);          // Assert         $this->assertSame($expected, $actual);     }      public function testInstanceExistingObject()     {         // Arrange         $expected = new ContainerImplementationStub();         $this->container->instance(IContainerStub::class, $expected);          // Actual         $actual = $this->container->make(IContainerStub::class);          // Assert         $this->assertSame($expected, $actual);     } }  class ContainerConcreteStub {  }  interface IContainerStub {  }  class ContainerImplementationStub implements IContainerStub {  }  class ContainerDependentStub {     /**      * @var \MyRightCapital\Container\Tests\IContainerStub      */     public $containerStub;      public function __construct(IContainerStub $containerStub)     {         $this->containerStub = $containerStub;     } }  class ContainerNestedDependentStub {     /**      * @var \MyRightCapital\Container\Tests\ContainerDependentStub      */     public $containerDependentStub;      public function __construct(ContainerDependentStub $containerDependentStub)     {         $this->containerDependentStub = $containerDependentStub;     } }

這里測試了bind()綁定閉包,綁定接口和對應實現,依賴解析這三個feature,singleton()測試了是否為單例綁定一個feature,instance()測試了已存在對象綁定這個feature,測試結果5個tests都通過:

Laravel中Container如何使用

關于在PHPStorm中配置PHPUnit可參考這篇:Laravel學習筆記之基于PHPStorm編輯器的Laravel開發

make()源碼解析

從以上testcase知道,make()是負責從Container中解析出service的,而且在testBindDependencyResolution()這個test中,還能發現當ContainerNestedDependentStub::class有構造依賴時,Container也會自動去解析這個依賴并注入ContainerNestedDependentStub::class的構造函數中,這個依賴是ContainerDependentStub::class,而這個依賴又有自己的依賴IContainerStub::class,從斷言語句$this->assertInstanceOf(ContainerImplementationStub::class,  $actual->containerDependentStub->containerStub);知道,Container又自動解析了這個依賴,所有這一切都不需要我們手動去解析,全都是Container自動化解析的。

這一切Container是怎么做到的?實際上并不復雜,解決依賴只是用了PHP的Reflector反射機制來實現的。先看下make()源碼:

/**      * Resolve the given type from the container.      *      * @param  string  $abstract      * @param  array   $parameters      * @return mixed      */     public function make($abstract, array $parameters = [])     {         $abstract = $this->getAlias($this->normalize($abstract));          // 如果是instance()綁定的方式,就直接解析返回綁定的service         if (isset($this->instances[$abstract])) {             return $this->instances[$abstract];         }          // 獲取$abstract對應綁定的$concrete         $concrete = $this->getConcrete($abstract);          if ($this->isBuildable($concrete, $abstract)) {             $object = $this->build($concrete, $parameters);         } else {             $object = $this->make($concrete, $parameters);         }          foreach ($this->getExtenders($abstract) as $extender) {             $object = $extender($object, $this);         }          if ($this->isShared($abstract)) {             $this->instances[$abstract] = $object;         }          $this->fireResolvingCallbacks($abstract, $object);          $this->resolved[$abstract] = true;          return $object;     }          protected function getConcrete($abstract)     {         if (! is_null($concrete = $this->getContextualConcrete($abstract))) {             return $concrete;         }          // 如果是$this->container->singleton(ContainerConcreteStub::class);這種方式綁定,即$concrete = null         // 則 $abstract = $concrete,可看以上PHPUnit的testSingleton()這個test         // 這種方式稱為'自動補全'綁定         if (! isset($this->bindings[$abstract])) {             return $abstract;         }          return $this->bindings[$abstract]['concrete'];     }          protected function isBuildable($concrete, $abstract)     {         return $concrete === $abstract || $concrete instanceof Closure;     }

從以上源碼可知道如果綁定的是閉包或者'自動補全'綁定($concrete =  null),則需要build()這個閉包或類名,轉換成對應的實例。如果是'接口實現'這種方式綁定,則需要再一次調用make()并經過getConcrete后$abstract  =  $concrete,然后符合isBuildable()的條件,進入build()函數內。所以以上的PHPUnit的測試用例中不管什么方式的綁定,都要進入build()函數內編譯出相應對象實例。當編譯出對象后,檢查是否是共享的,以及是否要觸發回調,以及標記該對象已經被解析。OK,看下build()的源碼:

/**      * Instantiate a concrete instance of the given type.      *      * @param  string  $concrete      * @param  array   $parameters      * @return mixed      *      * @throws \Illuminate\Contracts\Container\BindingResolutionException      */     public function build($concrete, array $parameters = [])     {         // 如果是閉包直接執行閉包并返回,e.g. PHPUnit的這個test:testBindClosure()         if ($concrete instanceof Closure) {             return $concrete($this, $parameters);         }                  // 如這個test:testBindInterfaceToImplement(),這里的$concrete = ContainerImplementationStub::class類名稱,         // 則使用反射ReflectionClass來探測ContainerImplementationStub這個類的構造函數和構造函數的依賴         $reflector = new ReflectionClass($concrete);          // 如果ContainerImplementationStub不能實例化,這應該是接口或抽象類,再或者就是ContainerImplementationStub的構造函數是private的         if (! $reflector->isInstantiable()) {             if (! empty($this->buildStack)) {                 $previous = implode(', ', $this->buildStack);                  $message = "Target [$concrete] is not instantiable while building [$previous].";             } else {                 $message = "Target [$concrete] is not instantiable.";             }              throw new BindingResolutionException($message);         }          $this->buildStack[] = $concrete;          // 獲取構造函數的反射         $constructor = $reflector->getConstructor();          // 如果構造函數是空,說明沒有任何依賴,直接new返回         if (is_null($constructor)) {             array_pop($this->buildStack);              return new $concrete;         }                  // 獲取構造函數的依賴,返回ReflectionParameter[]         $dependencies = $constructor->getParameters();          $parameters = $this->keyParametersByArgument(             $dependencies, $parameters         );          // 然后就是獲取相關依賴,如testBindDependencyResolution()這個test中,         // ContainerNestedDependentStub::class是依賴于ContainerDependentStub::class的         $instances = $this->getDependencies(             $dependencies, $parameters         );          array_pop($this->buildStack);          return $reflector->newInstanceArgs($instances);     }

從源碼可知道,比較麻煩的是當ContainerNestedDependentStub::class的構造函數有依賴ContainerDependentStub::class時,通過getDependencies()來解決的,看下getDependencies()的源碼:

// 這里$parameters = ReflectionParameter[]     protected function getDependencies(array $parameters, array $primitives = [])     {         $dependencies = [];          foreach ($parameters as $parameter) {             $dependency = $parameter->getClass();              // 如果某一依賴值已給,就賦值             if (array_key_exists($parameter->name, $primitives)) {                 $dependencies[] = $primitives[$parameter->name];             }              // 如果類名為null,說明是基本類型,如'int','string' and so on.             elseif (is_null($dependency)) {                 $dependencies[] = $this->resolveNonClass($parameter);             }              // 如果是類名,如ContainerDependentStub::class,則resolveClass去解析成對象             else {                 $dependencies[] = $this->resolveClass($parameter);             }         }          return $dependencies;     }

通過上面注釋,看下resolveClass()的源碼:

protected function resolveClass(ReflectionParameter $parameter)    {        try {            // $parameter->getClass()->name返回的是類名,如ContainerNestedDependentStub依賴于$containerDependentStub            // $containerDependentStub的typehint是ContainerDependentStub,所以類名是'ContainerDependentStub'            // 然后遞歸繼續make(ContainerDependentStub::class)            // 又和PHPUnit中這個測試$this->container->make(ContainerNestedDependentStub::class)相類似了            // ContainerNestedDependentStub又依賴于IContainerStub::class,            // IContainerStub::class是綁定于ContainerImplementationStub::class            // 直到ContainerImplementationStub沒有依賴或者是構造函數是基本屬性,            // ***build()結束            return $this->make($parameter->getClass()->name);        } catch (BindingResolutionException $e) {            if ($parameter->isOptional()) {                return $parameter->getDefaultValue();            }             throw $e;        }    }

從以上代碼注釋直到build()是個遞歸過程,A類依賴于B類,B類依賴于C類和D類,那就從A類開始build,發現依賴于B類,再從Container中解析make()即再build()出B類,發現依賴于C類,再make()  and build(),發現B類又同時依賴于D類,再make() and  build(),以此類推直到沒有依賴或依賴基本屬性,解析結束。這樣一步步解析完后,發現Container的解析make()并不是很神秘很復雜中的過程。

從以上源碼發現PHP的反射Reflector是個很好用的技術,這里給出個test,看下Reflector能干些啥:

<?php  class ConstructorParameter {  }  class ReflectorTest {     private $refletorProperty1;      protected $refletorProperty2;      public $refletorProperty3;      /**      * @var int      */     private $request;      public function __construct(int $request = 10, string $response, ConstructorParameter $constructorParameter, Closure $closure)     {          $this->request = $request;     }      private function reflectorMethod1()     {     }      protected function reflectorMethod2()     {     }      public function reflectorMethod3()     {     } }  $reflector_class        = new ReflectionClass(ReflectorTest::class); $methods                = $reflector_class->getMethods(); $properties             = $reflector_class->getProperties(); $constructor            = $reflector_class->getConstructor(); $constructor_parameters = $constructor->getParameters();  foreach ($constructor_parameters as $constructor_parameter) {     $dependency = $constructor_parameter->getClass();     var_dump($dependency);      if ($constructor_parameter->isDefaultValueAvailable()) {         var_dump($constructor_parameter->getDefaultValue());     } }  var_dump($methods); var_dump($properties); var_dump($constructor); var_dump($constructor_parameters);

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

巫溪县| 新平| 宁海县| 保德县| 徐州市| 嘉荫县| 阿巴嘎旗| 屯门区| 福泉市| 方正县| 藁城市| 宁乡县| 游戏| 集安市| 富顺县| 绥阳县| 隆化县| 罗江县| 卓尼县| 泽普县| 姚安县| 昌吉市| 汉中市| 阿合奇县| 银川市| 萨嘎县| 天峻县| 巴塘县| 桂林市| 通许县| 雅江县| 墨竹工卡县| 宜宾市| 新丰县| 化德县| 定结县| 长泰县| 和田县| 公主岭市| 子长县| 集安市|