广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Laravel中Container如何使用
  • 342
分享到

Laravel中Container如何使用

2024-04-02 19:04:59 342人浏览 独家记忆
摘要

Laravel中Container如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。PHPUnit测试下绑定在聊解析过程前

Laravel中Container如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

PHPUnit测试下绑定

在聊解析过程前,先测试下\Illuminate\Container\Container中绑定的源码,这里测试下bind(),singleton(),instance()三个绑定方式:

<?PHP  namespace MyRightCapital\Container\Tests;  use MyRightCapital\Container\Container;  class ContainerBindTest extends \phpUnit_Framework_TestCase {          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 {          public $containerStub;      public function __construct(IContainerStub $containerStub)     {         $this->containerStub = $containerStub;     } }  class ContainerNestedDependentStub {          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()源码:

     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()的源码:

     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;           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);

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网JavaScript频道,感谢您对编程网的支持。

--结束END--

本文标题: Laravel中Container如何使用

本文链接: https://www.lsjlt.com/news/73697.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Laravel中Container如何使用
    Laravel中Container如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。PHPUnit测试下绑定在聊解析过程前...
    99+
    2022-10-19
  • laravel中的Service Container的概念及使用方法
    这篇文章主要讲解了“laravel中的Service Container的概念及使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“laravel中的Service Container的概...
    99+
    2023-06-20
  • Laravel中如何使用Typescript
    本篇内容介绍了“Laravel中如何使用Typescript”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么使用 TypeScriptT...
    99+
    2023-07-04
  • Laravel中Middleware如何使用
    今天就跟大家聊聊有关Laravel中Middleware如何使用,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。PHP内置函数array_revers...
    99+
    2022-10-19
  • 如何在 Laravel 中使用 Python?
    Laravel 是一个流行的 PHP 框架,它提供了一个简单而强大的平台,用于构建 Web 应用程序。Python 是一种高级编程语言,具有强大的数据处理和分析能力。在本文中,我们将介绍如何在 Laravel 中使用 Python,以及如何...
    99+
    2023-06-18
    面试 javascript laravel
  • Google Container Engine上如何申请和使用Docker容器
    本篇内容介绍了“Google Container Engine上如何申请和使用Docker容器”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!...
    99+
    2023-06-10
  • laravel中has方法如何使用
    这篇文章主要介绍“laravel中has方法如何使用”,在日常操作中,相信很多人在laravel中has方法如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”laravel中has方法如何使用”的疑惑有所...
    99+
    2023-06-22
  • laravel中如何使用with方法
    这篇文章主要为大家展示了“laravel中如何使用with方法”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“laravel中如何使用with方法”这篇文章吧。在laravel中,with()方法...
    99+
    2023-06-26
  • 如何在Laravel中使用Tailwind CSS?
    Tailwind Tailwind是新的CSS实用程序框架,它很快成为我最喜欢的构建界面的方法。通常,尝试一个新的框架、包或语言的最困难的部分是建立起来。建造Tailwind的人做了一项令人难以置信的工作,记录了这个过程,而且非常容易做到。...
    99+
    2022-11-10
  • AWS S3在Laravel中如何使用
    今天小编给大家分享一下AWS S3在Laravel中如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。AWS S3 为我...
    99+
    2023-06-29
  • laravel如何使用RabbitMQ
    这篇文章主要介绍“laravel如何使用RabbitMQ”,在日常操作中,相信很多人在laravel如何使用RabbitMQ问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”laravel如何使用RabbitMQ...
    99+
    2023-06-22
  • Laravel Livewire如何使用
    这篇文章主要讲解了“Laravel Livewire如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Laravel Livewire如何使用”吧!Laravel Livewire是一个...
    99+
    2023-07-04
  • laravel如何使用websocket
    什么是WebSocket? WebSocket是一种在单个TCP连接上进行全双工通信的协议。它使得浏览器和服务器之间的实时通信变得更加容易。与HTTP请求不同,WebSocket连接是持久的,这意味着...
    99+
    2023-08-31
    php laravel websocket
  • 如何在Laravel中使用Python函数?
    Laravel是一个流行的PHP框架,它提供了许多功能和工具来帮助开发人员构建高质量的Web应用程序。Python是一种强大的编程语言,可以用于多种应用领域,包括数据分析、机器学习和自然语言处理等。在本文中,我们将探讨如何在Laravel中...
    99+
    2023-06-05
    git 函数 laravel
  • 如何在php中使用Laravel接口
    如何在php中使用Laravel接口?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。php是什么语言php,一个嵌套的缩写名称,是英文超级文本预处理语言(PHP:Hypert...
    99+
    2023-06-14
  • Laravel队列如何使用
    这篇文章主要讲解了“Laravel队列如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Laravel队列如何使用”吧!什么情况使用队列?耗时的,比如上传一个文件后进行一些格式的转化等。...
    99+
    2023-06-29
  • Laravel的Tinker如何使用
    这篇文章主要介绍“Laravel的Tinker如何使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Laravel的Tinker如何使用”文章能帮助大家解决问题。Laravel Tinker 的使用...
    99+
    2023-07-04
  • 如何在 PHP Laravel 中使用 Spring 函数?
    PHP Laravel 是一款广受欢迎的 Web 开发框架,它提供了许多强大的功能和工具,使得开发者能够更加高效地构建优秀的 Web 应用。其中,Spring 函数是一个非常实用的工具,它可以帮助我们在 Laravel 中实现一些常见的编程...
    99+
    2023-07-20
    laravel 函数 spring
  • Laravel 中如何使用 Python 进行编程?
    Laravel 是一款流行的 PHP 开发框架,它具有简单易用、高效稳定的特点。Python 是一门广泛应用于科学计算、人工智能等领域的编程语言。那么,如何在 Laravel 中使用 Python 进行编程呢?本文将为你详细介绍。 一、Py...
    99+
    2023-07-19
    unix 编程算法 laravel
  • Laravel中如何使用Python打包数组?
    Laravel是一款流行的PHP框架,它提供了丰富的功能和便捷的开发体验。而Python作为一种强大的脚本语言,也有着广泛的应用场景。在Laravel中使用Python打包数组,可以方便地实现数据的传输和处理。下面我们来详细介绍一下具体的实...
    99+
    2023-06-03
    打包 数组 laravel
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作