xiaohuilam / laravel

Laravel 深入详解 —— 源代码解析,新手进阶指南
433 stars 80 forks source link

03. ServiceProvider Register 解析 #3

Open xiaohuilam opened 5 years ago

xiaohuilam commented 5 years ago

ServiceProvider Register 解析

前面 《02. Kernel Handle解析》 结尾,我们留下了 注册服务提供者启动服务提供者 是两个比较重要的步骤的悬念,接下来两篇文章,我们来分别解析这两大流程。

代码

https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php#L1-L19

解析

整个 RegisterProviders 都是粮衣,其没有任何功能功能代码,而是调用到了 Illuminate\Foundation\ ApplicationregisterConfiguredProviders

注意运行时实际并没有调用 Illuminate\Contracts\Foundation\Application 的 registerConfiguredProviders,而是调用了其具体实现类 Illuminate\Foundation\ Application 的 registerConfiguredProviders。不要被上面代码中的注解表象所迷惑

https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L533-L549

上面第 540-543 行是将 config/app.php 中配置的 providers https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/config/app.php#L122-L163 读取出来,判断是否为 Illuminate\ 开头,归为两组:

Illuminate\Support\Collection {#2825
     all: [
       Illuminate\Support\Collection {#2822
         all: [
            "Illuminate\Auth\AuthServiceProvider",
            "Illuminate\Broadcasting\BroadcastServiceProvider",
            "Illuminate\Bus\BusServiceProvider",
            "Illuminate\Cache\CacheServiceProvider",
            "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider",
            "Illuminate\Cookie\CookieServiceProvider",
            "Illuminate\Database\DatabaseServiceProvider",
            "Illuminate\Encryption\EncryptionServiceProvider",
            "Illuminate\Filesystem\FilesystemServiceProvider",
            "Illuminate\Foundation\Providers\FoundationServiceProvider",
            "Illuminate\Hashing\HashServiceProvider",
            "Illuminate\Mail\MailServiceProvider",
            "Illuminate\Notifications\NotificationServiceProvider",
            "Illuminate\Pagination\PaginationServiceProvider",
            "Illuminate\Pipeline\PipelineServiceProvider",
            "Illuminate\Queue\QueueServiceProvider",
            "Illuminate\Redis\RedisServiceProvider",
            "Illuminate\Auth\Passwords\PasswordResetServiceProvider",
            "Illuminate\Session\SessionServiceProvider",
            "Illuminate\Translation\TranslationServiceProvider",
            "Illuminate\Validation\ValidationServiceProvider",
            "Illuminate\View\ViewServiceProvider",
         ],
       },
       Illuminate\Support\Collection {#2823
         all: [
            "22" => "App\Providers\AppServiceProvider",
            "22" => "App\Providers\AuthServiceProvider",
            "22" => "App\Providers\EventServiceProvider",
            "22" => "App\Providers\RouteServiceProvider",
         ],
       },
     ],
   }

接着 $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]) 这一行会得到如下结果

Illuminate\Support\Collection {#33
  #items: array:3 [
    0 => Illuminate\Support\Collection {#21
      #items: array:22 [
        0 => "Illuminate\Auth\AuthServiceProvider"
        1 => "Illuminate\Broadcasting\BroadcastServiceProvider"
        2 => "Illuminate\Bus\BusServiceProvider"
        3 => "Illuminate\Cache\CacheServiceProvider"
        4 => "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
        5 => "Illuminate\Cookie\CookieServiceProvider"
        6 => "Illuminate\Database\DatabaseServiceProvider"
        7 => "Illuminate\Encryption\EncryptionServiceProvider"
        8 => "Illuminate\Filesystem\FilesystemServiceProvider"
        9 => "Illuminate\Foundation\Providers\FoundationServiceProvider"
        10 => "Illuminate\Hashing\HashServiceProvider"
        11 => "Illuminate\Mail\MailServiceProvider"
        12 => "Illuminate\Notifications\NotificationServiceProvider"
        13 => "Illuminate\Pagination\PaginationServiceProvider"
        14 => "Illuminate\Pipeline\PipelineServiceProvider"
        15 => "Illuminate\Queue\QueueServiceProvider"
        16 => "Illuminate\Redis\RedisServiceProvider"
        17 => "Illuminate\Auth\Passwords\PasswordResetServiceProvider"
        18 => "Illuminate\Session\SessionServiceProvider"
        19 => "Illuminate\Translation\TranslationServiceProvider"
        20 => "Illuminate\Validation\ValidationServiceProvider"
        21 => "Illuminate\View\ViewServiceProvider"
      ]
    }
    1 => array:5 [
      0 => "BeyondCode\DumpServer\DumpServerServiceProvider"
      1 => "Fideloper\Proxy\TrustedProxyServiceProvider"
      2 => "Laravel\Tinker\TinkerServiceProvider"
      3 => "Carbon\Laravel\ServiceProvider"
      4 => "NunoMaduro\Collision\Adapters\Laravel\CollisionServiceProvider"
    ]
    2 => Illuminate\Support\Collection {#31
      #items: array:4 [
        22 => "App\Providers\AppServiceProvider"
        23 => "App\Providers\AuthServiceProvider"
        24 => "App\Providers\EventServiceProvider"
        25 => "App\Providers\RouteServiceProvider"
      ]
    }
  ]
}

最后一句代码

(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) 
                     ->load($providers->collapse()->toArray()); 

第一步是将加载缓存好的 bootstrap/cache/services.php https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L855-L863

其实就是执行 ProviderRepository::load() $providers-> collapse() 得到的数组 (这个数组是前面 splice 拆分后再并入的)。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L47-L79

这句 loadManifest 即为加载前面传入的 bootstrap/cache/services.php https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L81-L98

第60行调用到的的 shouldRecompile 逻辑为判断是否 provider 不符合,代码 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L100-L110

如果需要重新编译此服务提供者缓存的 bootstrap/cache/services.php ,把数据 $manifest 提取到 bootstrap/cache/services.php ,具体过程为: https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L130-L166 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L179-L198

到这时,ProviderRepository::load() 的服务提供者缓存的判断和执行流程就结束了。我们回到 ProviderRepository::load() 的后面流程,接下来就是 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L64-L69

没错,触发 registerLoadEvents 方法了。感谢 laravel 代码做到了足够语义化,我们猜到了这里就是触发服务提供者注册事件的。具体流程为: https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L112-L128

再继续后面,就是 ProviderRepository::load() 触发容器的 register 的流程了 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php#L71-L76

Illuminate\Foundation\Container\Appplication 容器的 register 方法代码为 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L551-L600

步骤

  1. 先尝试取 getProvider 这个服务,如果能取到证明注册过了,除非要求强硬方式 $force 就不再注册
  2. 解析出 $provier 为具体的 ServiceProvider 对象
  3. 判断 $provider 有无 register 方法,有则运行
  4. 判断 $provider有无定义 $bindings 属性,若有,则依次 $key$value 为参,进入 Illuminate\Foundation\Container\Appplication::bind($key, $value) 执行
  5. 判断 $provider 有无定义 $singletons 属性,若有,则依次 $key$value 为参,进入 Illuminate\Foundation\Container\Appplication::singleton($key, $value) 执行
  6. 标记这个服务提供者已被注册过了 Illuminate\Foundation\Container\Appplication::$loadedProviders[ServiceProvider::class] = true
  7. 如果服务提供者已经被启动过了 ($booted == true), 调用 bootProvider($provider)