lavarel之容器Application构造函数分析

在lavarel的入口文件index.php中包含了

$app = require_once __DIR__.'/../bootstrap/app.php';

在app.php中有

$app = new Illuminate\Foundation\Application(//Vendor/laravel/framework/src/Illuminate。
    realpath(__DIR__.'/../')#为lavarel的根路径
);

上方代码即建立了一个lavarel的基本容器,它是如何建立的,我们看代码

class Application extends Container implements ApplicationContract, HttpKernelInterface {
  protected $basePath;
  protected $bindings = [];#存放注册的服务
  protected static $instance;#存放当前对象(Application)实例
  protected static $instances#存放全局共享实例
  protected $resolved = [];#注册服务调用成功时如#$this->resolved[$abstract] = true;
  protected $aliases = [];#别名数组,键为类或接口路径,值为别名
  public function __construct($basePath = null) {
        $this->registerBaseBindings();
        $this->registerBaseServiceProviders();
        $this->registerCoreContainerAliases();
          if ($basePath) {
            $this->setBasePath($basePath);
        }
    }
    }

此构造函数需要一个路径参数,所以我们在实例化的时候传入了一个路径,为根路径,存入$basePath。
此构造函数的第一行中registerBaseBindings()函数展开为

static::setInstance($this);
/* 此处会以当前对象(application)作为参数,此时的对象是空 的,只有几个空的成员属性,调用父类的setInstance方法,将返回的对象赋值给静态变量$intanse , */
$this->instance('app', $this);
 /* 上方代码的作用是将app和当前对象以键值对的形式放入$instances数组.即[app=>当前对象][Illuminate\Container\Container=>当前对象] */
$this->instance('Illuminate\Container\Container', $this);

构造函数调用的第二个函数展开为

protected function registerBaseServiceProviders() {   
$this->register(new EventServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
    }

当前对象调用register方法为当前对象注册两个最基本的服务,我们以EventServiceProvider为例,首先看下此类是如何构造的,

class EventServiceProvider extends ServiceProvider {   /* 此类继承自ServiceProvider,自身只有register方法,构造函数属于父类,为了方便查看,放在这里 */
        public function __construct($app) {        $this->app = $app;
    }

    public function register() {
  $this->app->singleton('events', function ($app) {    return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
     return $app->make('Illuminate\Contracts\Queue\Factory');
            });
        });
    }
}

下面我们看下register方法,

public function register($provider, $options = [], $force = false) {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }
        if (is_string($provider)) {
            $provider = $this->resolveProviderClass($provider);
        }
        $provider->register();
        foreach ($options as $key => $value) {
            $this[$key] = $value;
        }

        $this->markAsRegistered($provider);
        if ($this->booted) {
            $this->bootProvider($provider);
        }
        return $provider;
    }

register方法第一行的if判断里调用了getProvider方法,将传递给register的参数再传给getProvider,我们已经知道$provider为一个EventServiceProvider,打印一下为
这里写图片描述

 public function getProvider($provider) {   
 $name = is_string($provider) ? $provider : get_class($provider);
return Arr::first($this->serviceProviders, function ($key, $value) use ($name) {
  return $value instanceof $name;
        });
    }
    #getProvider方法调用了first()方法,现在一并贴出
      public static function first($array, callable $callback, $default = null) {   
  foreach ($array as $key => $value) {
            if (call_user_func($callback, $key, $value)) {
                return $value;
            }
        }
  return value($default);#如果$default属于闭包,返回闭包
    }

getProvider首先判断$provider,如果$provider是字符串则返回自身,如果是对象的话,则返回该对象类的完整路径,我们以EventServiceProvider对象为例,返回
"Illuminate\Events\EventServiceProvider",下面调用了first方法,传递了serviceProviders数组和一个闭包,此时serviceProviders为空所以first方法返回空,如果serviceProviders不为空的话,我们将循环把serviceProviders的键值对当作参数传入闭包中,如果serviceProviders的值属于"Illuminate\Events\EventServiceProvider",将返回true;当然,此时是返回空的,所以getProvider函数也返回null,回到register函数,第一次if判断为false继续向下,第二次if也为false,继续向下,$provider->register();此时执行EventServiceProvider对象的register方法,关于EventServiceProvider的register方法可以理解为将EventServiceProvider自身注册到当前对象application中,这里不再详述,下面执行的代码为 $this->markAsRegistered($provider);此方法代码为:

   protected function markAsRegistered($provider) {
       $this['events']->fire($class = get_class($provider), [$provider]);
        $this->serviceProviders[] = $provider;
        $this->loadedProviders[$class] = true;
    }

因为application的父类container实现了ArrayAccess接口,$this[‘events’]就相当于调用下方代码

 public function offsetGet($key) {
        return $this->make($key);
    }

在这里我不会对make方法进行详解,只需要知道此时返回值为:
lavarel之容器Application构造函数分析_第1张图片
此对象执行了fire方法,此方法返回一个空数组,然后将已产生的EventServiceProvider对象分别存入serviceProviders和loadedProviders数组。最后register方法返回EventServiceProvider对象,
第三个方法比较简单registerCoreContainerAliases,即将定义好的$aliases数组转换成路径为键,别名为值的形式,如下所示
lavarel之容器Application构造函数分析_第2张图片
$this->setBasePath($basePath);此函数代码展开为

   public function setBasePath($basePath) {
        $this->basePath = rtrim($basePath, '\/');
        $this->bindPathsInContainer();
        return $this;
    }
     protected function bindPathsInContainer() {   
        $this->instance('path', $this->path());
        foreach (['base', 'config', 'database', 'lang', 'public', 'storage'] as $path) {
     $this->instance('path.'.$path, $this->{$path.'Path'}());
        }
    }

这两个函数只要理解了foreach,则一目了然,循环的目的是将一些基本配置信息,存入instances数组中,在instance方法中大多代码是不执行的关键的只有这一句$this->instances[$abstract] = $instance;,执行完后,如下图所示lavarel之容器Application构造函数分析_第3张图片

你可能感兴趣的:(laravel,容器)