摘要:调用了的可以看出,所有服务提供器都在配置文件文件的数组中。启动的启动由类负责引导应用的属性中记录的所有服务提供器,就是依次调用这些服务提供器的方法,引导完成后就代表应用正式启动了,可以开始处理请求了。
服务提供器是所有 Laravel 应用程序引导中心。你的应用程序自定义的服务、第三方资源包提供的服务以及 Laravel 的所有核心服务都是通过服务提供器进行注册(register)和引导(boot)的。
拿一个Laravel框架自带的服务提供器来举例子
class BroadcastServiceProvider extends ServiceProvider { protected $defer = true; public function register() { $this->app->singleton(BroadcastManager::class, function ($app) { return new BroadcastManager($app); }); $this->app->singleton(BroadcasterContract::class, function ($app) { return $app->make(BroadcastManager::class)->connection(); }); //将BroadcastingFactory::class设置为BroadcastManager::class的别名 $this->app->alias( BroadcastManager::class, BroadcastingFactory::class ); } public function provides() { return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class, ]; } }
在服务提供器BroadcastServiceProvider的register中, 为BroadcastingFactory的类名绑定了类实现BroadcastManager,这样就能通过服务容器来make出通过BroadcastingFactory::class绑定的服务BroadcastManger对象供应用程序使用了。
本文主要时来梳理一下laravel是如何注册、和初始化这些服务的,关于如何编写自己的服务提供器,可以参考官方文档
BootStrap首先laravel注册和引导应用需要的服务是发生在寻找路由处理客户端请求之前的Bootstrap阶段的,在框架的入口文件里我们可以看到,框架在实例化了Application对象后从服务容器中解析出了HTTP Kernel对象
$kernel = $app->make(IlluminateContractsHttpKernel::class); $response = $kernel->handle( $request = IlluminateHttpRequest::capture() );
在Kernel处理请求时会先让请求通过中间件然后在发送请求给路由对应的控制器方法, 在这之前有一个BootStrap阶段来引导启动Laravel应用程序,如下面代码所示。
public function handle($request) { ...... $response = $this->sendRequestThroughRouter($request); ...... return $response; }
protected function sendRequestThroughRouter($request) { $this->app->instance("request", $request); Facade::clearResolvedInstance("request"); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } //引导启动Laravel应用程序 public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { /**依次执行$bootstrappers中每一个bootstrapper的bootstrap()函数 $bootstrappers = [ "IlluminateFoundationBootstrapDetectEnvironment", "IlluminateFoundationBootstrapLoadConfiguration", "IlluminateFoundationBootstrapConfigureLogging", "IlluminateFoundationBootstrapHandleExceptions", "IlluminateFoundationBootstrapRegisterFacades", "IlluminateFoundationBootstrapRegisterProviders", "IlluminateFoundationBootstrapBootProviders", ];*/ $this->app->bootstrapWith($this->bootstrappers()); } }
上面bootstrap中会分别执行每一个bootstrapper的bootstrap方法来引导启动应用程序的各个部分
1. DetectEnvironment 检查环境 2. LoadConfiguration 加载应用配置 3. ConfigureLogging 配置日至 4. HandleException 注册异常处理的Handler 5. RegisterFacades 注册Facades 6. RegisterProviders 注册Providers 7. BootProviders 启动Providers
启动应用程序的最后两部就是注册服务提供这和启动提供者,如果对前面几个阶段具体时怎么实现的可以参考这篇文章。在这里我们主要关注服务提供器的注册和启动。
先来看注册服务提供器,服务提供器的注册由类 IlluminateFoundationBootstrapRegisterProviders::class 负责,该类用于加载所有服务提供器的 register 函数,并保存延迟加载的服务的信息,以便实现延迟加载。
class RegisterProviders { public function bootstrap(Application $app) { //调用了Application的registerConfiguredProviders() $app->registerConfiguredProviders(); } } class Application extends Container implements ApplicationContract, HttpKernelInterface { public function registerConfiguredProviders() { (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) ->load($this->config["app.providers"]); } public function getCachedServicesPath() { return $this->bootstrapPath()."/cache/services.php"; } }
可以看出,所有服务提供器都在配置文件 app.php 文件的 providers 数组中。类 ProviderRepository 负责所有的服务加载功能:
class ProviderRepository { public function load(array $providers) { $manifest = $this->loadManifest(); if ($this->shouldRecompile($manifest, $providers)) { $manifest = $this->compileManifest($providers); } foreach ($manifest["when"] as $provider => $events) { $this->registerLoadEvents($provider, $events); } foreach ($manifest["eager"] as $provider) { $this->app->register($provider); } $this->app->addDeferredServices($manifest["deferred"]); } }
loadManifest()会加载服务提供器缓存文件services.php,如果框架是第一次启动时没有这个文件的,或者是缓存文件中的providers数组项与config/app.php里的providers数组项不一致都会编译生成services.php。
//判断是否需要编译生成services文件 public function shouldRecompile($manifest, $providers) { return is_null($manifest) || $manifest["providers"] != $providers; } //编译生成文件的具体过程 protected function compileManifest($providers) { $manifest = $this->freshManifest($providers); foreach ($providers as $provider) { $instance = $this->createProvider($provider); if ($instance->isDeferred()) { foreach ($instance->provides() as $service) { $manifest["deferred"][$service] = $provider; } $manifest["when"][$provider] = $instance->when(); } else { $manifest["eager"][] = $provider; } } return $this->writeManifest($manifest); } protected function freshManifest(array $providers) { return ["providers" => $providers, "eager" => [], "deferred" => []]; }
缓存文件中 providers 放入了所有自定义和框架核心的服务。
如果服务提供器是需要立即注册的,那么将会放入缓存文件中 eager 数组中。
如果服务提供器是延迟加载的,那么其函数 provides() 通常会提供服务别名,这个服务别名通常是向服务容器中注册的别名,别名将会放入缓存文件的 deferred 数组中,与真正要注册的服务提供器组成一个键值对。
延迟加载若由 event 事件激活,那么可以在 when 函数中写入事件类,并写入缓存文件的 when 数组中。
生成的缓存文件内容如下:
array ( "providers" => array ( 0 => "IlluminateAuthAuthServiceProvider", 1 => "IlluminateBroadcastingBroadcastServiceProvider", ... ), "eager" => array ( 0 => "IlluminateAuthAuthServiceProvider", 1 => "IlluminateCookieCookieServiceProvider", ... ), "deferred" => array ( "IlluminateBroadcastingBroadcastManager" => "IlluminateBroadcastingBroadcastServiceProvider", "IlluminateContractsBroadcastingFactory" => "IlluminateBroadcastingBroadcastServiceProvider", ... ), "when" => array ( "IlluminateBroadcastingBroadcastServiceProvider" => array ( ), ... )事件触发时注册延迟服务提供器
延迟服务提供器除了利用 IOC 容器解析服务方式激活,还可以利用 Event 事件来激活:
protected function registerLoadEvents($provider, array $events) { if (count($events) < 1) { return; } $this->app->make("events")->listen($events, function () use ($provider) { $this->app->register($provider); }); }即时注册服务提供器
需要即时注册的服务提供器的register方法由Application的register方法里来调用:
class Application extends Container implements ApplicationContract, HttpKernelInterface { public function register($provider, $options = [], $force = false) { if (($registered = $this->getProvider($provider)) && ! $force) { return $registered; } if (is_string($provider)) { $provider = $this->resolveProvider($provider); } if (method_exists($provider, "register")) { $provider->register(); } $this->markAsRegistered($provider); if ($this->booted) { $this->bootProvider($provider); } return $provider; } public function getProvider($provider) { $name = is_string($provider) ? $provider : get_class($provider); return Arr::first($this->serviceProviders, function ($value) use ($name) { return $value instanceof $name; }); } public function resolveProvider($provider) { return new $provider($this); } protected function markAsRegistered($provider) { //这个属性在稍后booting服务时会用到 $this->serviceProviders[] = $provider; $this->loadedProviders[get_class($provider)] = true; } protected function bootProvider(ServiceProvider $provider) { if (method_exists($provider, "boot")) { return $this->call([$provider, "boot"]); } } }
可以看出,服务提供器的注册过程:
判断当前服务提供器是否被注册过,如注册过直接返回对象
解析服务提供器
调用服务提供器的 register 函数
标记当前服务提供器已经注册完毕
若框架已经加载注册完毕所有的服务容器,那么就启动服务提供器的 boot 函数,该函数由于是 call 调用,所以支持依赖注入。
服务解析时注册延迟服务提供器延迟服务提供器首先需要添加到 Application 中
public function addDeferredServices(array $services) { $this->deferredServices = array_merge($this->deferredServices, $services); }
我们之前说过,延迟服务提供器的激活注册有两种方法:事件与服务解析。
当特定的事件被激发后,就会调用 Application 的 register 函数,进而调用服务提供器的 register 函数,实现服务的注册。
当利用 Ioc 容器解析服务名时,例如解析服务名 BroadcastingFactory:
class BroadcastServiceProvider extends ServiceProvider { protected $defer = true; public function provides() { return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class, ]; } }
在Application的make方法里会通过别名BroadcastingFactory查找是否有对应的延迟注册的服务提供器,如果有的话那么
就先通过registerDeferredProvider方法注册服务提供器。
class Application extends Container implements ApplicationContract, HttpKernelInterface { public function make($abstract) { $abstract = $this->getAlias($abstract); if (isset($this->deferredServices[$abstract])) { $this->loadDeferredProvider($abstract); } return parent::make($abstract); } public function loadDeferredProvider($service) { if (! isset($this->deferredServices[$service])) { return; } $provider = $this->deferredServices[$service]; if (! isset($this->loadedProviders[$provider])) { $this->registerDeferredProvider($provider, $service); } } }
由 deferredServices 数组可以得知,BroadcastingFactory 为延迟服务,接着程序会利用函数 loadDeferredProvider 来加载延迟服务提供器,调用服务提供器的 register 函数,若当前的框架还未注册完全部服务。那么将会放入服务启动的回调函数中,以待服务启动时调用:
public function registerDeferredProvider($provider, $service = null) { if ($service) { unset($this->deferredServices[$service]); } $this->register($instance = new $provider($this)); if (! $this->booted) { $this->booting(function () use ($instance) { $this->bootProvider($instance); }); } }
还是拿服务提供器BroadcastServiceProvider来举例:
class BroadcastServiceProvider extends ServiceProvider { protected $defer = true; public function register() { $this->app->singleton(BroadcastManager::class, function ($app) { return new BroadcastManager($app); }); $this->app->singleton(BroadcasterContract::class, function ($app) { return $app->make(BroadcastManager::class)->connection(); }); //将BroadcastingFactory::class设置为BroadcastManager::class的别名 $this->app->alias( BroadcastManager::class, BroadcastingFactory::class ); } public function provides() { return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class, ]; } }
函数 register 为类 BroadcastingFactory 向 服务容器绑定了特定的实现类 BroadcastManager,Application中的 make 函数里执行parent::make($abstract) 通过服务容器的make就会正确的解析出服务 BroadcastingFactory。
因此函数 provides() 返回的元素一定都是 register() 向 服务容器中绑定的类名或者别名。这样当我们利用App::make() 解析这些类名的时候,服务容器才会根据服务提供器的 register() 函数中绑定的实现类,正确解析出服务功能。
启动ApplicationApplication的启动由类 IlluminateFoundationBootstrapBootProviders 负责:
class BootProviders { public function bootstrap(Application $app) { $app->boot(); } } class Application extends Container implements ApplicationContract, HttpKernelInterface { public function boot() { if ($this->booted) { return; } $this->fireAppCallbacks($this->bootingCallbacks); array_walk($this->serviceProviders, function ($p) { $this->bootProvider($p); }); $this->booted = true; $this->fireAppCallbacks($this->bootedCallbacks); } protected function bootProvider(ServiceProvider $provider) { if (method_exists($provider, "boot")) { return $this->call([$provider, "boot"]); } } }
引导应用Application的serviceProviders属性中记录的所有服务提供器,就是依次调用这些服务提供器的boot方法,引导完成后$this->booted = true 就代表应用Application正式启动了,可以开始处理请求了。这里额外说一句,之所以等到所有服务提供器都注册完后再来进行引导是因为有可能在一个服务提供器的boot方法里调用了其他服务提供器注册的服务,所以需要等到所有即时注册的服务提供器都register完成后再来boot。
本文已经收录在系列文章Laravel源码学习里,欢迎访问阅读。
本文参考链接:
[1] [2],这两篇文章让我在学习服务提供器时提供了不少帮助
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/28138.html
摘要:扩展用户认证系统上一节我们介绍了系统实现的一些细节知道了是如何应用看守器和用户提供器来进行用户认证的,但是针对我们自己开发的项目或多或少地我们都会需要在自带的看守器和用户提供器基础之上做一些定制化来适应项目,本节我会列举一个在做项目时遇到的 扩展用户认证系统 上一节我们介绍了Laravel Auth系统实现的一些细节知道了Laravel是如何应用看守器和用户提供器来进行用户认证的,但是...
摘要:一个服务提供器必须包含至少一种方法。服务提供器一旦被注册,就可被用于程序的各个地方。注意服务提供器的变量来自类中。启动服务当所有的服务提供器注册之后,他们就变成了已启动状态。再次提示,把服务提供器作为一种组织工具来使用。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%...
摘要:官网源码解读号外号外欢迎大家我们开发组定了一个就线下聚一次的小目标里面的框架算是非常重的了这里的重先不具体到性能层面主要是框架的设计思想和框架集成的服务让框架可以既可以快速解决很多问题又可以轻松扩展中的框架有在应该无出其右了这次解读的源码 官网: https://www.swoft.org/源码解读: http://naotu.baidu.com/file/8... 号外号外, 欢迎大...
摘要:可以为服务提供者的方法设置类型提示。方法将在所有其他服务提供者均已注册之后调用。所有服务提供者都在配置文件中注册。可以选择推迟服务提供者的注册,直到真正需要注册绑定时,这样可以提供应用程序的性能。 本文最早发布于 Rootrl的Blog 导言 Laravel是一款先进的现代化框架,里面有一些概念非常重要。在上手Laravel之前,我认为先弄懂这些概念是很有必要的。你甚至需要重温下PHP...
摘要:系统的核心是由的认证组件的看守器和提供器组成。使用的认证系统,几乎所有东西都已经为你配置好了。其配置文件位于,其中包含了用于调整认证服务行为的注释清晰的选项配置。 用户认证系统(基础介绍) 使用过Laravel的开发者都知道,Laravel自带了一个认证系统来提供基本的用户注册、登录、认证、找回密码,如果Auth系统里提供的基础功能不满足需求还可以很方便的在这些基础功能上进行扩展。这篇...
阅读 2985·2021-11-23 09:51
阅读 2797·2021-11-11 16:55
阅读 2907·2021-10-14 09:43
阅读 1393·2021-09-23 11:22
阅读 1034·2019-08-30 11:04
阅读 1663·2019-08-29 11:10
阅读 955·2019-08-27 10:56
阅读 3101·2019-08-26 12:01