1.1 Make singleton
use App\Services\TestService;
app()->singleton(TestService::class, function ($app) {
return new TestService;
});
1.2 Call to helpers
+vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
use Illuminate\Container\Container;
function app($abstract = null, array $parameters = [])
{
if (is_null($abstract)) {
return Container::getInstance();
}
return Container::getInstance()
->make($abstract, $parameters);
}
1.3 Bind TestService object with container
D:\Projects\laravel70\vendor\laravel\framework\src\Illuminate\Container\Container.php
/**
* Register a shared binding in the container.
*
* @param string $abstract
* @param \Closure|string|null $concrete
* @return void
*/
public function singleton($abstract,
$concrete = null)
{
$this->bind($abstract, $concrete, true);
}
/**
* Register a binding with the container.
*
* @param string $abstract
* @param \Closure|string|null $concrete
* @param bool $shared
* @return void
*/
public function bind($abstract,
$concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
// If no concrete type was given,
we will simply set the concrete type to the
// abstract type. After that, the concrete
type to be registered as shared
// without being forced to state their classes
in both of the parameters.
if (is_null($concrete)) {
$concrete = $abstract;
}
// If the factory is not a Closure,
it means it is just a class name which is
// bound into this container to the abstract type
and we will just wrap it
// up inside its own Closure to give us
more convenience when extending.
if (! $concrete instanceof Closure) {
if (! is_string($concrete)) {
throw new \TypeError(self::class.'::bind():
Argument #2 ($concrete) must
be of type Closure|string|null');
}
$concrete = $this->getClosure(
$abstract, $concrete);
}
$this->bindings[$abstract]
= compact('concrete', 'shared');
// If the abstract type was already resolved
in this container we'll fire the
// rebound listener so that any objects
which have already gotten resolved
// can have their copy of the object updated
via the listener callbacks.
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
1.1 You can check Container>bindings before register singleton TestService with container
1.2 you can check Container>bindings after register singleton TestService
In this step, TestService is bound in container as Closure.
3.1 Make TestService from container
public function singleton()
{
// Step 1, check bindings in container when
// TestService haven't bind
//dd('Step1: ContainerController:container', app());
app()->singleton(TestService::class,
function ($app) {
return new TestService;
}
);
// Step 2, check bindings in container when
// TestService haven't bind
// dd('Step2: ContainerController:bind', app());
$testService1 = app()->make(TestService::class);
// Step 3, get TestService from container
// because of TestService in step 2, that's only
// Closure, this step will relove it
dd('Step3: ContainerController:resolve', app());
}
3.2 Make TestService
D:\Projects\laravel70\vendor\laravel\framework\src\Illuminate\Container\Container.php
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
3.3 Build TestService obj in Container
protected function resolve($abstract, $parameters = [],
$raiseEvents = true)
{
$abstract = $this->getAlias($abstract);
$concrete = $this->getContextualConcrete($abstract);
$needsContextualBuild = ! empty($parameters)
|| ! is_null($concrete);
// 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;
if (is_null($concrete)) {
$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);
// Just to test
if ($abstract=='App\Services\TestService') {
var_dump('build', $abstract, $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)
{
// 3.3.1 assign TestService object for $this->instances
of container
$this->instances[$abstract] = $object;
}
if ($raiseEvents) {
$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.
// 3.3.2 Notify container TestService object is resolved
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
4.1 Build object with Closure
/**
* Instantiate a concrete instance of the given type.
*
* @param \Closure|string $concrete
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
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) {
if (@$this->bindings['App\Services\TestService']) {
var_dump('build closure', $concrete);
}
return $concrete($this, $this->getLastParameterOverride());
}
try {
$reflector = new ReflectionClass($concrete);
if ($concrete=='App\Services\TestService') {
var_dump(3344, $concrete, $reflector, $this->buildStack);
}
} catch (ReflectionException $e) {
throw new BindingResolutionException("Target class
[$concrete] does not exist.", 0, $e);
}
// If the type is not instantiable,
the developer is attempting to resolve
// an abstract type such as an Interface or 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.
try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);
throw $e;
}
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
4.2 Assign TestService object for $this->instances in container
view step 3.3.1
4.3 Notify to container TestService object is resolved
view step 3.3.2
Code Reference: https://github.com/truemenews/laravel70/pull/5/files