在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方法进行详解,只需要知道此时返回值为:
此对象执行了fire方法,此方法返回一个空数组,然后将已产生的EventServiceProvider对象分别存入serviceProviders和loadedProviders数组。最后register方法返回EventServiceProvider对象,
第三个方法比较简单registerCoreContainerAliases,即将定义好的$aliases数组转换成路径为键,别名为值的形式,如下所示
$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;
,执行完后,如下图所示