We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
容器的 singleton 和 bind 方法在整个 Laravel 框架或扩展中是调用的比较频繁的底层方法,掌握其原理能帮助我们加深 laravel 容器的理解。
singleton
bind
singleton()
单身狗模式
laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 338 to 348 in d081c91
bind()
分裂模式
Lines 206 to 240 in d081c91
初略看二者差异可能看不大出来。 我们从他们暴露的接口入手,先用,再啪(扒)源码。
祭出大杀器:php artisan tinker
php artisan tinker
>>> app()->bind('test', function(){dump(1); return new \StdClass();}); 1 => null >>> >>> app()->make('test') 1 => {#2895} >>> >>> app()->make('test') 1 => {#2896}
可以看出,调用 bind 时,每次取出被绑定的对象时,都会重新去构建一次。
如果用singleton 呢。
>>> app()->singleton('test', function(){dump(1); return new \StdClass();}); 1 => null >>> app()->make('test') => {#2915} >>> app()->make('test') => {#2915} >>>
我们回头再看下 bind() 方法
逻辑步骤为:
$abstract
$concrete
dropStaleInstances
$concret
Container::getClosure()
$container
$parameters
Container::$bindings
compact('concrete', 'shared')
["concrete" => $concrete, "shared" => $shared]
resolved
Container::rebound()
reboundCallbacks
到目前为止,暂时还没看出 $share 对 singleton 和 bind 的影响。只知道 bind 在执行过程中,将 $shared 和 $concrete 一起存到了 Container::$bindings 中。
$share
$shared
用句四川话,莫慌,我们马上就要揭开关键的面纱了。。。
Container::make()
Lines 592 to 602 in d081c91
其实 make 是直接透传给了 resolve laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 616 to 675 in d081c91 /** * Resolve the given type from the container. * * @param string $abstract * @param array $parameters * @return mixed */ protected function resolve($abstract, $parameters = []) { $abstract = $this->getAlias($abstract); $needsContextualBuild = ! empty($parameters) || ! is_null( $this->getContextualConcrete($abstract) ); // If an instance of the type is currently being managed as a singleton we'll // just return an existing instance instead of instantiating new instances // so the developer can keep using the same objects instance every time. if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } $this->with[] = $parameters; $concrete = $this->getConcrete($abstract); // We're ready to instantiate an instance of the concrete type registered for // the binding. This will instantiate the types, as well as resolve any of // its "nested" dependencies recursively until all have gotten resolved. if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } // If we defined any extenders for this type, we'll need to spin through them // and apply them to the object being built. This allows for the extension // of services, such as changing configuration or decorating the object. foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } // If the requested type is registered as a singleton we'll want to cache off // the instances in "memory" so we can return it later without creating an // entirely new instance of an object on each subsequent request for it. if ($this->isShared($abstract) && ! $needsContextualBuild) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); // Before returning, we will also set the resolved flag to "true" and pop off // the parameter overrides for this build. After those two things are done // we will be ready to return back the fully constructed class instance. $this->resolved[$abstract] = true; array_pop($this->with); return $object; } 第一步骤的 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Line 625 in d081c91 $abstract = $this->getAlias($abstract); 是做了森么呢? 还原用 Container::alias() 方法设置过别名的原始 $abstract。 下一步, laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 627 to 629 in d081c91 $needsContextualBuild = ! empty($parameters) || ! is_null( $this->getContextualConcrete($abstract) ); Container::getContextualConcrete() 其实是取这里设置进取的 上下文绑定 的数据 laravel/vendor/laravel/framework/src/Illuminate/Container/ContextualBindingBuilder.php Lines 56 to 67 in d081c91 /** * Define the implementation for the contextual binding. * * @param \Closure|string $implementation * @return void */ public function give($implementation) { $this->container->addContextualBinding( $this->concrete, $this->needs, $implementation ); } ContextualBindingBuilder::give() 暂时就不在这里暂开研究了。咱们继续往下看 $needsContextualBuild 值为 make() 方法是否传了第二个参数,也就是 $parameters。 needs contextual build 意思是 构建时,是否具有上下文关联性 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 634 to 636 in d081c91 if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } 判断 Container::$instances 数组中是否能通过 $abstract 找到对象。如果找得到且不具有上下文关联性,就用之前创建好的对象。 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 638 to 640 in d081c91 $this->with[] = $parameters; $concrete = $this->getConcrete($abstract); getConcrete() 代码为 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 680 to 697 in d081c91 * @param string $abstract * @return mixed $concrete */ protected function getConcrete($abstract) { if (! is_null($concrete = $this->getContextualConcrete($abstract))) { return $concrete; } // If we don't have a registered resolver or concrete for the type, we'll just // assume each type is a concrete name and will attempt to resolve it as is // since the container should be able to resolve concretes automatically. if (isset($this->bindings[$abstract])) { return $this->bindings[$abstract]['concrete']; } return $abstract; } 相信你能看出来 return $this->bindings[$abstract]['concrete'] ,就是根据 $abstract 把前面 bind 或 singleton 的数据返回。 这里的 isBuildable 是判断 $abstract 是否与 $concrete 一致或 $concrete 为闭包 如果不是,那么可能是还有递归调用,需要再走一次 make laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 645 to 649 in d081c91 if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } 645 行调用的 isBuildable() laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 745 to 748 in d081c91 protected function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } 接着调用了 Container::build() 方法 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 758 to 801 in d081c91 public function build($concrete) { // If the concrete type is actually a Closure, we will just execute it and // hand back the results of the functions, which allows functions to be // used as resolvers for more fine-tuned resolution of these objects. if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } $reflector = new ReflectionClass($concrete); // If the type is not instantiable, the developer is attempting to resolve // an abstract type such as an Interface of Abstract Class and there is // no binding registered for the abstractions so we need to bail out. if (! $reflector->isInstantiable()) { return $this->notInstantiable($concrete); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); // If there are no constructors, that means there are no dependencies then // we can just resolve the instances of the objects right away, without // resolving any other types or dependencies out of these containers. if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); } 其中的 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Line 767 in d081c91 $reflector = new ReflectionClass($concrete); 和 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 789 to 800 in d081c91 $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); 是通过反射解析待构建类所需参数中其他对象参数,从容器中取出填入。 再往下面就是执行 Container::extend 设置的扩充流程了。 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 654 to 656 in d081c91 foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } 再后面,才是对我们 singleton() 和 bind() 产生深远影响的代码: laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 661 to 663 in d081c91 if ($this->isShared($abstract) && ! $needsContextualBuild) { $this->instances[$abstract] = $object; } 我们在前面知道,如果为 singleton() 执行到 resolve() 的第二个参数 $shared 为 true, bind 为 false。 前面 $needsContextualBuild 判断的位置,判断了 Container::$instance 中是否有 $abstract, 所以在这里如果 resolve 没有将 $object 绑入 Container::$instance,那么下次 Container::make() 依旧会在去 build() 一次,这就产生了 singleton() 和 bind()的特性。 再接着,就是触发事件了。 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Line 665 in d081c91 $this->fireResolvingCallbacks($abstract, $object); 最后是一些收尾工作 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 670 to 674 in d081c91 $this->resolved[$abstract] = true; array_pop($this->with); return $object;
其实 make 是直接透传给了 resolve
make
resolve
Lines 616 to 675 in d081c91
第一步骤的
Line 625 in d081c91
Container::alias()
下一步,
Lines 627 to 629 in d081c91
Container::getContextualConcrete() 其实是取这里设置进取的 上下文绑定 的数据
Container::getContextualConcrete()
上下文绑定
laravel/vendor/laravel/framework/src/Illuminate/Container/ContextualBindingBuilder.php
Lines 56 to 67 in d081c91
ContextualBindingBuilder::give() 暂时就不在这里暂开研究了。咱们继续往下看
ContextualBindingBuilder::give()
$needsContextualBuild 值为 make() 方法是否传了第二个参数,也就是 $parameters。 needs contextual build 意思是
$needsContextualBuild
make()
needs contextual build
构建时,是否具有上下文关联性
laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 634 to 636 in d081c91 if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } 判断 Container::$instances 数组中是否能通过 $abstract 找到对象。如果找得到且不具有上下文关联性,就用之前创建好的对象。
Lines 634 to 636 in d081c91
Container::$instances
laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 638 to 640 in d081c91 $this->with[] = $parameters; $concrete = $this->getConcrete($abstract); getConcrete() 代码为 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 680 to 697 in d081c91 * @param string $abstract * @return mixed $concrete */ protected function getConcrete($abstract) { if (! is_null($concrete = $this->getContextualConcrete($abstract))) { return $concrete; } // If we don't have a registered resolver or concrete for the type, we'll just // assume each type is a concrete name and will attempt to resolve it as is // since the container should be able to resolve concretes automatically. if (isset($this->bindings[$abstract])) { return $this->bindings[$abstract]['concrete']; } return $abstract; } 相信你能看出来 return $this->bindings[$abstract]['concrete'] ,就是根据 $abstract 把前面 bind 或 singleton 的数据返回。
Lines 638 to 640 in d081c91
getConcrete() 代码为
getConcrete()
Lines 680 to 697 in d081c91
相信你能看出来 return $this->bindings[$abstract]['concrete'] ,就是根据 $abstract 把前面 bind 或 singleton 的数据返回。
return $this->bindings[$abstract]['concrete']
这里的 isBuildable 是判断 $abstract 是否与 $concrete 一致或 $concrete 为闭包
isBuildable
如果不是,那么可能是还有递归调用,需要再走一次 make
Lines 645 to 649 in d081c91
645 行调用的 isBuildable() laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 745 to 748 in d081c91 protected function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } 接着调用了 Container::build() 方法 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 758 to 801 in d081c91 public function build($concrete) { // If the concrete type is actually a Closure, we will just execute it and // hand back the results of the functions, which allows functions to be // used as resolvers for more fine-tuned resolution of these objects. if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } $reflector = new ReflectionClass($concrete); // If the type is not instantiable, the developer is attempting to resolve // an abstract type such as an Interface of Abstract Class and there is // no binding registered for the abstractions so we need to bail out. if (! $reflector->isInstantiable()) { return $this->notInstantiable($concrete); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); // If there are no constructors, that means there are no dependencies then // we can just resolve the instances of the objects right away, without // resolving any other types or dependencies out of these containers. if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); } 其中的 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Line 767 in d081c91 $reflector = new ReflectionClass($concrete); 和 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 789 to 800 in d081c91 $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); 是通过反射解析待构建类所需参数中其他对象参数,从容器中取出填入。
645 行调用的 isBuildable()
isBuildable()
Lines 745 to 748 in d081c91
接着调用了 Container::build() 方法
Container::build()
Lines 758 to 801 in d081c91
其中的 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Line 767 in d081c91 $reflector = new ReflectionClass($concrete); 和 laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php Lines 789 to 800 in d081c91 $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); 是通过反射解析待构建类所需参数中其他对象参数,从容器中取出填入。
其中的
Line 767 in d081c91
Lines 789 to 800 in d081c91
再往下面就是执行 Container::extend 设置的扩充流程了。
Container::extend
Lines 654 to 656 in d081c91
再后面,才是对我们 singleton() 和 bind() 产生深远影响的代码:
Lines 661 to 663 in d081c91
我们在前面知道,如果为 singleton() 执行到 resolve() 的第二个参数 $shared 为 true, bind 为 false。 前面 $needsContextualBuild 判断的位置,判断了 Container::$instance 中是否有 $abstract, 所以在这里如果 resolve 没有将 $object 绑入 Container::$instance,那么下次 Container::make() 依旧会在去 build() 一次,这就产生了 singleton() 和 bind()的特性。
resolve()
true
false
Container::$instance
$object
build()
再接着,就是触发事件了。
Line 665 in d081c91
最后是一些收尾工作
Lines 670 to 674 in d081c91
此章节为什么叫做单身狗呢?因为 Container::make() 严格遵守 $shared,如果声明了 true,他保证不搞一个分身(所以是单身)给你。
The text was updated successfully, but these errors were encountered:
bind的分析,'如果为闭包' 应该是 '如果不是闭包' 吧?因为程序是 if (! $concrete instanceof Closure)
Sorry, something went wrong.
是的,打错了
No branches or pull requests
初识
singleton()
代码laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 338 to 348 in d081c91
bind()
代码laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 206 to 240 in d081c91
初略看二者差异可能看不大出来。
我们从他们暴露的接口入手,先用,再啪(扒)源码。
调试
祭出大杀器:
php artisan tinker
bind
可以看出,调用
bind
时,每次取出被绑定的对象时,都会重新去构建一次。如果用
singleton
呢。singleton
分析
bind 的实质,容器中捆绑
我们回头再看下
bind()
方法laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 206 to 240 in d081c91
逻辑步骤为:
$abstract
、$concrete
dropStaleInstances
移除之前 bind 过的$abstract
的数据(旧的$concret
)$concrete
,则将$concrete
设置成$abstract
如果为闭包如果不为闭包(感谢 @HubQin 的指正),则通过Container::getClosure()
方法将闭包改成一个接受$container
、$parameters
的包裹后的闭包。此闭包实质就是在需要运行的时候,运行闭包。Container::$bindings
数组$abstract
为 key 的值设置为compact('concrete', 'shared')
,即["concrete" => $concrete, "shared" => $shared]
bind
的对象已经resolved
过了,通过Container::rebound()
触发reboundCallbacks
中的回调。到目前为止,暂时还没看出
$share
对singleton
和bind
的影响。只知道bind
在执行过程中,将$shared
和$concrete
一起存到了Container::$bindings
中。用句四川话,莫慌,我们马上就要揭开关键的面纱了。。。
揭开
Container::make()
神秘的面纱laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php
Lines 592 to 602 in d081c91
The text was updated successfully, but these errors were encountered: