laravel容器

laravel容器负责存放所需要的各种类,当需要的时候再从容器中解析。下面我们对容器进行分析,如果有谬误,欢迎指正。


入口文件

laravel入口文件在public目录下面的index.php。然后进入index.php。

require __DIR__.'/../bootstrap/autoload.php';


$app = require_once __DIR__.'/../bootstrap/app.php';
类的自动加载可以看我的另一篇博文:  http://blog.csdn.net/qq_16877261/article/details/77707453

查看app.php代码如下,然后进入laravel容器。

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

直接查看容器的构造函数如下:

注册路径

public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    //注册容器基本实例
    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    //注册核心的容器类别名
    $this->registerCoreContainerAliases();
}
首先我们先查看setBasePath 这个函数,最终发现通过bindPathsInContainer这个函数将一些常用的路径绑定到容器。关于绑定函数instance 请查看博文:http://blog.csdn.net/qq_16877261/article/details/78189673

instance 绑定其实就是将绑定的对象放入容器中的$instances 数组中,我打印这个数组结果如下,发现laravel绑定路径其实和其他框架里将路径定义为常量的效果是一样的。



绑定容器实例

接着我看下一个函数registerBaseBindings,代码如下:

在该方法中,首先将当前的类设置为全局的容器实例,然后将app与容器实例进行绑定,方便以后通过访问app属性来访问容器。最后将Container类的访问也绑定到容器实例。

绑定所谓的服务提供者

找到 registerBaseServiceProviders函数如下

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));
    $this->register(new LogServiceProvider($this));
    $this->register(new RoutingServiceProvider($this));
}
以EventServiceProvider 为例,然后查看register函数如下

public function register($provider, $options = [], $force = false)
{
    //检查是否已经注册
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }
    // If the given "provider" is a string, we will resolve it, passing in the
    // application instance automatically for the developer. This is simply
    // a more convenient way of specifying your service provider classes.
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
    //通过这一步实现服务提供者的绑定,然后查看具体服务提供者的
    //register 函数
if (method_exists($provider, 'register')) { $provider->register(); } $this->markAsRegistered($provider); // If the application has already booted, we will call this boot method on // the provider class so it has an opportunity to do its boot logic and // will be ready for any usage by this developer's application logic. if ($this->booted) { $this->bootProvider($provider); } return $provider;}

EventServiceProvider 类下的register函数如下

public function register()
{
    $this->app->singleton('events', function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make(QueueFactoryContract::class);
        });
    });
}
于是我们去查看容器中的singleton 函数如下,发现singleton函数其实只是bind函数最后一个参数为为true。

/**
 * Register a shared binding in the container.
 *  绑定到容器的对象只会被解析一次,之后的调用都返回相同的实例:
 * @param  string|array  $abstract
 * @param  \Closure|string|null  $concrete
 * @return void
 */
public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}


于是查看bind函数如下:

public function bind($abstract, $concrete = null, $shared = false)
{
    // 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.

    //移除旧的实例
    $this->dropStaleInstances($abstract);
    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) {

// 绑定的时候,如果$concrete 是具体的类名,通过下面的函数封装成闭包
闭包里面的内容一般是这样的            return  $this->make($concrete);或者$this->build($concrete);
 $concrete = $this->getClosure($abstract, $concrete);
    }
    // 创建一个包含变量与其值的数组。
    //对每个参数,compact() 在当前的符号表中查找该变量名并将它添加到输出的数组中,变量名成为键名而变量的内容成为该键的值。简单说,它做的事和 extract() 正好相反。返回将所有变量添加进去后的数组。
    $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); }}
由于 events 是第一次绑定,不会进行  $this->rebound($abstract); 最后singleton 函数执行后的结果是  打印$this->bindings 如下


singleton 函数 在对象第一次进行绑定时,只是进行对象的绑定,而不会执行额外的操作,例如 不会对绑定的匿名函数进行执行。

服务容器解析

下面我们分析下对象的解析,以入口文件这行代码进行讲解

dd(

Illuminate\Contracts\Http\Kernel::class
)   //   Illuminate\Contracts\Http\Kernel    绑定的类名为  
"App\Http\Kernel"

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
 make 函数如下

/**
 * Resolve the given type from the container.
 *
 *  从容器中解析实例
 * @param  string  $abstract
 * @return mixed
 */
public function make($abstract)
{
    $needsContextualBuild = ! is_null(
        $this->getContextualConcrete($abstract = $this->getAlias($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->bingdings 数组中获取,如果没有,直接返回类名
 $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);

    $this->resolved[$abstract] = true;

    return $object;
}
按照程序执行,由于是第一次解析,执行

 $this->build($concrete); 
于是我又去查看build函数

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.
    //是否是闭包(匿名函数),在本例中由于是闭包,就直接返回闭包执行的结果。由于这个闭包是自动封装的闭包,格式如下
// $this->make($concrete);  其实就是直接解析绑定的类,在本例中就是  App\Http\Kernel  于是又需要查看make函数

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

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




























Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐