在入口文件里可以看到
$app = require_once __DIR__.'/../bootstrap/app.php';
$app這個是laravel的全局變量,在里面可以看到實例化了這個類
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
里面的構造函數做了4件事
1,注冊基本的綁定,把當前類的對象綁定到app和Illuminate\Container\Container中
$this->registerBaseBindings();
2,注冊服務提供器,里面注冊了EventServiceProvider和RoutingServiceProvider,詳細的沒研究過,以后有機會在研究
$this->registerBaseServiceProviders()
3,注冊別名
$this->registerCoreContainerAliases();
4,綁定一些項目內有可能用到的路徑
if ($basePath) {
$this->setBasePath($basePath);
}
接下來就一個一個的分析一下里面是用來做什么的
protected function registerBaseBindings()
{
static::setInstance($this);//獲取當前類的實例,規定了要實現ContainerContract接口的類才可以實例化
$this->instance('app', $this);
$this->instance('Illuminate\Container\Container', $this);
}
第一句
public static function setInstance(ContainerContract $container)
{
static::$instance = $container;
}
就把當前類的對象賦值給當前類的$instance變量,方便以后調用
剩下的就是調用instance方法來綁定對象到容器了
看看laravel是怎么綁定對象到容器的
public function instance($abstract, $instance)
{
$abstract = $this->normalize($abstract);//如果是字符串,就把前面的斜線去掉
if (is_array($abstract)) {
list($abstract, $alias) = $this->extractAlias($abstract);
$this->alias($abstract, $alias);
}//如果是數組,例如$this->instance(['app'=>'a'],$this),就存進$this->aliases中,變成$this->aliases['app'] = 'a';沒用過這個綁定方法,查看其他資料看到的
unset($this->aliases[$abstract]);
$bound = $this->bound($abstract);//返回一個布爾值,用於下面判斷是否執行回調函數
$this->instances[$abstract] = $instance;//這里就是把變量綁定到instance數組中,例如傳進來的是($app,$this),所以綁定成$this->instance['app'] = new a();a代表傳進來的類
if ($bound) {//如果為真,就調用$this->reboundCallback[]里有沒有回調函數,有的話就調用
$this->rebound($abstract);
}
}
就是這樣綁定了兩個變量到容器中了
第二件事:
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
注冊了兩個服務提供器,一個是事件服務提供器,一個是路由服務提供器
先看看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();//注冊provider
foreach ($options as $key => $value) {
$this[$key] = $value;
}
$this->markAsRegistered($provider);//記錄provider為已注冊
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
首先判斷里面有沒有曾經注冊過,有的話直接返回
分析一下getProvider()方法
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;
});
}
public static function first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
return empty($array) ? value($default) : reset($array);
}
foreach ($array as $key => $value) {
if (call_user_func($callback, $key, $value)) {
return $value;
}
}
return value($default);
}
這里Arr::first主要是循環$this->serviceProviders這個數組,在里面找到已經注冊過的提供器,即如果$this->serviceProviders的鍵值是當前服務提供器的實例,就返回一個布爾值,然后register方法里面返回當前服務提供器的實例,找不到的話執行下面的代碼
if (is_string($provider)) {
$provider = $this->resolveProviderClass($provider);
}
$provider->register();//注冊provider
如果是字符串,直接實例化,然后調用這個類重寫之后的register()方法
$this->markAsRegistered($provider);//記錄provider為已注冊
里面調用了剛注冊了的event服務提供器里的fire方法,以后有機會在研究,然后就把當前類記錄為已注冊,就是把當前服務提供器的類名放進loadedProviders數組里
$this->serviceProviders[] = $provider;
$this->loadedProviders[$class] = true;
然后就調用提供器里的boot方法
if ($this->booted) {
$this->bootProvider($provider);
}
里面用了call方法來解決boot里面的依賴問題,laravel學習之IOC容器分析(二)在這里面解釋了,然后這樣就把一個服務提供器注冊成功了。
剩下的第三件事和第四件事就是把別名數組和laravel里面要用到的路徑注入到容器中。
到了這里,$app這個全局變量就完成了最基本的工作。
完....
