Facade在Laravel中的工作机制

编程

我们知道Facade门面模式是一个中介类,对子系统或者类对象进行封装和代理,为子系统的一组接口提供一个统一的高层接口。它对外暴露子系统的功能,但是不暴露子系统对象,用户要使用子系统的某个功能(方法)时,先调用门店,由于门店代理了子系统,所以委托子系统去处理相应的功能请求,从而达到将用户和子系统解耦的目的,用户只需要和门店打交道,不需要知道所有的子系统及其内部构造。

我们接下来通过在Laravel中最常用的DB-Facade,来看看Facade在Laravel中是如何工作的。

1,代码如下

<?php

use Exception;

use IlluminateSupportFacadesDB;

Class A {

function a() {

try {

DB::beginTransaction();

//do something

DB::commit();

} catch (Exception $e) {

DB::rollback();

// handle exception

}

}

}

这里,我们看到use了一个DB的Facades,为什么写了DB两个字母,就会自动use DB的Facade呢?这就涉及到 Laravel的自动加载和依赖注入机制,这里略过。

我们来看看这个类文件的代码:

2, DB Facades

<?php

namespace IlluminateSupportFacades;

/**

* @method static IlluminateDatabaseConnectionInterface connection(string $name = null)

* @method static string getDefaultConnection()

* @method static void setDefaultConnection(string $name)

* @method static IlluminateDatabaseQueryBuilder table(string $table)

* @method static IlluminateDatabaseQueryExpression raw($value)

* @method static mixed selectOne(string $query, array $bindings = [])

* @method static array select(string $query, array $bindings = [])

* @method static bool insert(string $query, array $bindings = [])

* @method static int update(string $query, array $bindings = [])

* @method static int delete(string $query, array $bindings = [])

* @method static bool statement(string $query, array $bindings = [])

* @method static int affectingStatement(string $query, array $bindings = [])

* @method static bool unprepared(string $query)

* @method static array prepareBindings(array $bindings)

* @method static mixed transaction(Closure $callback, int $attempts = 1)

* @method static void beginTransaction()

* @method static void commit()

* @method static void rollBack()

* @method static int transactionLevel()

* @method static array pretend(Closure $callback)

*

* @see IlluminateDatabaseDatabaseManager

* @see IlluminateDatabaseConnection

*/

class DB extends Facade

{

/**

* Get the registered name of the component.

*

* @return string

*/

protected static function getFacadeAccessor()

{

return "db";

}

}

发现它只有一个getFacadeAccessor静态方法,返回一个"db"的字符串,其他方法都没有。

那它到底是怎么执行的其他方法呢,比如这里的beginTranscation()方法?我们知道,当调用一个不存在的静态方法时,PHP会自动调用__callStatic()魔术方法,而这里没有,那我们只能从它继承的Facade父类中看看。

3,Facade父类 / 基类

<?php

namespace IlluminateSupportFacades;

use Closure;

use Mockery;

use RuntimeException;

use MockeryMockInterface;

abstract class Facade

{

/**

* The application instance being facaded.

*

* @var IlluminateContractsFoundationApplication

*/

protected static $app;

/**

* The resolved object instances.

*

* @var array

*/

protected static $resolvedInstance;

/**

* Run a Closure when the facade has been resolved.

*

* @param Closure $callback

* @return void

*/

public static function resolved(Closure $callback)

{

static::$app->afterResolving(static::getFacadeAccessor(), function ($service) use ($callback) {

$callback($service);

});

}

/**

* Convert the facade into a Mockery spy.

*

* @return MockeryMockInterface

*/

public static function spy()

{

if (! static::isMock()) {

$class = static::getMockableClass();

return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {

static::swap($spy);

});

}

}

/**

* Initiate a mock expectation on the facade.

*

* @return MockeryExpectation

*/

public static function shouldReceive()

{

$name = static::getFacadeAccessor();

$mock = static::isMock()

? static::$resolvedInstance[$name]

: static::createFreshMockInstance();

return $mock->shouldReceive(...func_get_args());

}

/**

* Create a fresh mock instance for the given class.

*

* @return MockeryExpectation

*/

protected static function createFreshMockInstance()

{

return tap(static::createMock(), function ($mock) {

static::swap($mock);

$mock->shouldAllowMockingProtectedMethods();

});

}

/**

* Create a fresh mock instance for the given class.

*

* @return MockeryMockInterface

*/

protected static function createMock()

{

$class = static::getMockableClass();

return $class ? Mockery::mock($class) : Mockery::mock();

}

/**

* Determines whether a mock is set as the instance of the facade.

*

* @return bool

*/

protected static function isMock()

{

$name = static::getFacadeAccessor();

return isset(static::$resolvedInstance[$name]) &&

static::$resolvedInstance[$name] instanceof MockInterface;

}

/**

* Get the mockable class for the bound instance.

*

* @return string|null

*/

protected static function getMockableClass()

{

if ($root = static::getFacadeRoot()) {

return get_class($root);

}

}

/**

* Hotswap the underlying instance behind the facade.

*

* @param mixed $instance

* @return void

*/

public static function swap($instance)

{

static::$resolvedInstance[static::getFacadeAccessor()] = $instance;

if (isset(static::$app)) {

static::$app->instance(static::getFacadeAccessor(), $instance);

}

}

/**

* Get the root object behind the facade.

*

* @return mixed

*/

public static function getFacadeRoot()

{

return static::resolveFacadeInstance(static::getFacadeAccessor());

}

/**

* Get the registered name of the component.

*

* @return string

*

* @throws RuntimeException

*/

protected static function getFacadeAccessor()

{

throw new RuntimeException("Facade does not implement getFacadeAccessor method.");

}

/**

* Resolve the facade root instance from the container.

*

* @param string|object $name

* @return mixed

*/

protected static function resolveFacadeInstance($name)

{

if (is_object($name)) {

return $name;

}

if (isset(static::$resolvedInstance[$name])) {

return static::$resolvedInstance[$name];

}

return static::$resolvedInstance[$name] = static::$app[$name];

}

/**

* Clear a resolved facade instance.

*

* @param string $name

* @return void

*/

public static function clearResolvedInstance($name)

{

unset(static::$resolvedInstance[$name]);

}

/**

* Clear all of the resolved instances.

*

* @return void

*/

public static function clearResolvedInstances()

{

static::$resolvedInstance = [];

}

/**

* Get the application instance behind the facade.

*

* @return IlluminateContractsFoundationApplication

*/

public static function getFacadeApplication()

{

return static::$app;

}

/**

* Set the application instance.

*

* @param IlluminateContractsFoundationApplication $app

* @return void

*/

public static function setFacadeApplication($app)

{

static::$app = $app;

}

/**

* Handle dynamic, static calls to the object.

*

* @param string $method

* @param array $args

* @return mixed

*

* @throws RuntimeException

*/

public static function __callStatic($method, $args)

{

$instance = static::getFacadeRoot();

if (! $instance) {

throw new RuntimeException("A facade root has not been set.");

}

return $instance->$method(...$args);

}

}

这里的基类是给所有Facade子类继承的,很明显,是不可能有子类的静态方法的。所以只能看__callStatic()方法,也就是最后这个。

    public static function __callStatic($method, $args)

{

$instance = static::getFacadeRoot();

if (! $instance) {

throw new RuntimeException("A facade root has not been set.");

}

return $instance->$method(...$args);

}

它的第一步,是获取在Facade后面的 根对象 (root object)。

   /**

* Get the root object behind the facade.

*

* @return mixed

*/

public static function getFacadeRoot()

{

return static::resolveFacadeInstance(static::getFacadeAccessor());

}

这里的 static::getFacadeAccessor()方法是不是很眼熟,对,它就是DB-Facade类里面唯一的一个方法,返回字符串"db"。OK,我们看它的主方法resolveFacadeInstance,代码如下。它会从容器中解析 facade 根实例。

   /**

* Resolve the facade root instance from the container.

*

* @param string|object $name

* @return mixed

*/

protected static function resolveFacadeInstance($name)

{

if (is_object($name)) {

return $name;

}

if (isset(static::$resolvedInstance[$name])) {

return static::$resolvedInstance[$name];

}

return static::$resolvedInstance[$name] = static::$app[$name];

}

如果是第一次解析,它就会从 static::$app这个数组中,去获取key 为 字符串 "db" 的值。

那我们看看 static::$app是什么。

    /**

* The application instance being facaded.

*

* @var IlluminateContractsFoundationApplication

*/

protected static $app;

是正在被门店代理的应用实例,来自于 IlluminateContractsFoundationApplication.

这是一个契约,有契约就会有实现契约的类,全局搜一下整个框架,发现,唯一的实现,是在 namespace IlluminateFoundationApplication 下 (vendor/laravel/framework/src/Illuminate/Foundation/Application.php),我们来看看

<?php

namespace IlluminateFoundation;

.

.

use SymfonyComponentHttpKernelHttpKernelInterface;

.

use SymfonyComponentHttpKernelExceptionNotFoundHttpException;

use IlluminateContractsFoundationApplication as ApplicationContract;

class Application extends Container implements ApplicationContract, HttpKernelInterface

{

/**

* Create a new Illuminate application instance.

*

* @param string|null $basePath

* @return void

*/

public function __construct($basePath = null)

{

if ($basePath) {

$this->setBasePath($basePath);

}

$this->registerBaseBindings();

$this->registerBaseServiceProviders();

$this->registerCoreContainerAliases();

}

}

代码太多,就截取了最关键的部分。可以看到这个类实现了上面的 ApplicationContract。

看到构造方法,其实很熟悉了,是在将各种基本的绑定,基本的服务提供者,还有核心容器别名,注入到 服务容器中,也就是 $app中。

也就是说,我们要在服务容器$app中,找一个名为 "db" 的 facade 根实例。

2,$app

我们知道,在 Laravel 的入口文件 index.php文件中,第一步是注册自动加载器,第二步实例化出服务容器 $app,第三步是将内核启动,处理请求。

其中在处理请求中,有一个步骤是 bootstrap(),为HTTP请求运行起应用。

    /**

* Bootstrap the application for HTTP requests.

*

* @return void

*/

public function bootstrap()

{

if (! $this->app->hasBeenBootstrapped()) {

$this->app->bootstrapWith($this->bootstrappers());

}

}

这里会让服务容器 $app 将 很多需要启动的组件给启动起来,如下:

    /**

* The bootstrap classes for the application.

*

* @var array

*/

protected $bootstrappers = [

IlluminateFoundationBootstrapLoadEnvironmentVariables::class,

IlluminateFoundationBootstrapLoadConfiguration::class,

IlluminateFoundationBootstrapHandleExceptions::class,

IlluminateFoundationBootstrapRegisterFacades::class,

IlluminateFoundationBootstrapRegisterProviders::class,

IlluminateFoundationBootstrapBootProviders::class,

];

而 $this->app->bootstrapWith() 是怎么处理这些 bootstrap 类呢?

这里的bootstrapWith() 方法,点进去,它是在  vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php 文件中,

namespace IlluminateContractsFoundation;

use Closure;

use IlluminateContractsContainerContainer;

interface Application extends Container

{

/**

* Run the given array of bootstrap classes.

*

* @param array $bootstrappers

* @return void

*/

public function bootstrapWith(array $bootstrappers);

}

又是一个接口,那谁实现的呢?就是上面提到的vendor/laravel/framework/src/Illuminate/Foundation/Application.php,代码如下

   /**

* Run the given array of bootstrap classes.

*

* @param string[] $bootstrappers

* @return void

*/

public function bootstrapWith(array $bootstrappers)

{

$this->hasBeenBootstrapped = true;

foreach ($bootstrappers as $bootstrapper) {

$this["events"]->dispatch("bootstrapping: ".$bootstrapper, [$this]);

$this->make($bootstrapper)->bootstrap($this);

$this["events"]->dispatch("bootstrapped: ".$bootstrapper, [$this]);

}

}

这里传入一个数组,然后实例化他们,获取到对象后,分别调用了各自的bootstrap方法。

我们看两个:RegisterFacades 和 RegisterProviders。

2.1 RegisterFacades 的 bootstrap()

文件: vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php

<?php

namespace IlluminateFoundationBootstrap;

use IlluminateFoundationAliasLoader;

use IlluminateSupportFacadesFacade;

use IlluminateFoundationPackageManifest;

use IlluminateContractsFoundationApplication;

class RegisterFacades

{

/**

* Bootstrap the given application.

*

* @param IlluminateContractsFoundationApplication $app

* @return void

*/

public function bootstrap(Application $app)

{

Facade::clearResolvedInstances();

Facade::setFacadeApplication($app);

AliasLoader::getInstance(array_merge(

$app->make("config")->get("app.aliases", []),

$app->make(PackageManifest::class)->aliases()

))->register();

}

}

它关键的一步,是将config/app.php文件中的aliases这些 类的别名 和 具体的 Facades,加上PackageManifest包列表的别名,一起打包, 将这些映射关系 调用 register() 方法,进行注册。注意以下的注释部分,这些别名是被 懒加载 处理的,也就是说,等到程序要用的时候才会真正被加载进来,所以不会影响性能。

/*

|--------------------------------------------------------------------------

| Class Aliases

|--------------------------------------------------------------------------

|

| This array of class aliases will be registered when this application

| is started. However, feel free to register as many as you wish as

| the aliases are "lazy" loaded so they don"t hinder performance.

|

*/

"aliases" => [

"App" => IlluminateSupportFacadesApp::class,

.

"DB" => IlluminateSupportFacadesDB::class,

],

这是RegisterFacades部分。我们再看 RegisterProviders。

2.2 RegisterProviders

目录: vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php

<?php

namespace IlluminateFoundationBootstrap;

use IlluminateContractsFoundationApplication;

class RegisterProviders

{

/**

* Bootstrap the given application.

*

* @param IlluminateContractsFoundationApplication $app

* @return void

*/

public function bootstrap(Application $app)

{

$app->registerConfiguredProviders();

}

}

这里的registerConfiguredProviders()方法点进去,又是一个接口:

目录: vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php

<?php

namespace IlluminateContractsFoundation;

use IlluminateContractsContainerContainer;

interface Application extends Container

{

/**

     * Register all of the configured providers.

     *

     * @return void

     */

    public function registerConfiguredProviders();

}

具体的实现,还是在 vendor/laravel/framework/src/Illuminate/Foundation/Application.php

<?php

namespace IlluminateFoundation;

use IlluminateSupportServiceProvider;

use IlluminateEventsEventServiceProvider;

use IlluminateRoutingRoutingServiceProvider;

use SymfonyComponentHttpKernelHttpKernelInterface;

.

use IlluminateContractsHttpKernel as HttpKernelContract;

.

use IlluminateContractsFoundationApplication as ApplicationContract;

class Application extends Container implements ApplicationContract, HttpKernelInterface

{

/**

* Register all of the configured providers.

*

* @return void

*/

public function registerConfiguredProviders()

{

$providers = Collection::make($this->config["app.providers"])

->partition(function ($provider) {

return Str::startsWith($provider, "Illuminate\");

});

$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))

->load($providers->collapse()->toArray());

}

}

它会将config/app.php配置文件的providers, 以 "Illuminate\" 开头的类 当做第一部分,其他的为第二部分。然后将 packageManifest插入到中间,最后,providers就是这个三个元素的数组,如下:

object(IlluminateSupportCollection)#32 (1) {

["items":protected]=>

array(3) {

[0]=>

object(IlluminateSupportCollection)#22 (1) {

["items":protected]=>

array(22) {

[0]=>

string(35) "IlluminateAuthAuthServiceProvider"

[1]=>

string(48) "IlluminateBroadcastingBroadcastServiceProvider"

[2]=>

string(33) "IlluminateBusBusServiceProvider"

[3]=>

string(37) "IlluminateCacheCacheServiceProvider"

[4]=>

string(61) "IlluminateFoundationProvidersConsoleSupportServiceProvider"

[5]=>

string(39) "IlluminateCookieCookieServiceProvider"

[6]=>

string(43) "IlluminateDatabaseDatabaseServiceProvider"

[7]=>

string(47) "IlluminateEncryptionEncryptionServiceProvider"

[8]=>

string(47) "IlluminateFilesystemFilesystemServiceProvider"

[9]=>

string(57) "IlluminateFoundationProvidersFoundationServiceProvider"

[10]=>

string(38) "IlluminateHashingHashServiceProvider"

[11]=>

string(35) "IlluminateMailMailServiceProvider"

[12]=>

string(52) "IlluminateNotificationsNotificationServiceProvider"

[13]=>

string(47) "IlluminatePaginationPaginationServiceProvider"

[14]=>

string(43) "IlluminatePipelinePipelineServiceProvider"

[15]=>

string(37) "IlluminateQueueQueueServiceProvider"

[16]=>

string(37) "IlluminateRedisRedisServiceProvider"

[17]=>

string(54) "IlluminateAuthPasswordsPasswordResetServiceProvider"

[18]=>

string(41) "IlluminateSessionSessionServiceProvider"

[19]=>

string(49) "IlluminateTranslationTranslationServiceProvider"

[20]=>

string(47) "IlluminateValidationValidationServiceProvider"

[21]=>

string(35) "IlluminateViewViewServiceProvider"

}

}

[1]=>

array(9) {

[0]=>

string(43) "FideloperProxyTrustedProxyServiceProvider"

[1]=>

string(37) "JacobcylAliOSSAliOssServiceProvider"

[2]=>

string(52) "IlluminateNotificationsNexmoChannelServiceProvider"

[3]=>

string(52) "IlluminateNotificationsSlackChannelServiceProvider"

[4]=>

string(36) "LaravelTinkerTinkerServiceProvider"

[5]=>

string(38) "MaatwebsiteExcelExcelServiceProvider"

[6]=>

string(30) "CarbonLaravelServiceProvider"

[7]=>

string(45) "SimpleSoftwareIOQrCodeQrCodeServiceProvider"

[8]=>

string(43) "SpatiePermissionPermissionServiceProvider"

}

[2]=>

object(IlluminateSupportCollection)#30 (1) {

["items":protected]=>

array(8) {

[22]=>

string(32) "AppProvidersAppServiceProvider"

[23]=>

string(33) "AppProvidersAuthServiceProvider"

[24]=>

string(34) "AppProvidersEventServiceProvider"

[25]=>

string(34) "AppProvidersRouteServiceProvider"

[26]=>

string(39) "AppProvidersApiRequestServiceProvider"

[27]=>

string(37) "JacobcylAliOSSAliOssServiceProvider"

[28]=>

string(38) "MaatwebsiteExcelExcelServiceProvider"

[29]=>

string(33) "AppProvidersTestServiceProvider"

}

}

}

}

然后用 vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php 这个服务提供者仓库(ProviderRepository),将这些服务提供者,逐个load 加载注册 到 $app 中。

<?php

namespace IlluminateFoundation;

use Exception;

use IlluminateFilesystemFilesystem;

use IlluminateContractsFoundationApplication as ApplicationContract;

class ProviderRepository

{

/**

* Register the application service providers.

*

* @param array $providers

* @return void

*/

public function load(array $providers)

{

$manifest = $this->loadManifest();

// First we will load the service manifest, which contains information on all

// service providers registered with the application and which services it

// provides. This is used to know which services are "deferred" loaders.

if ($this->shouldRecompile($manifest, $providers)) {

$manifest = $this->compileManifest($providers);

}

// Next, we will register events to load the providers for each of the events

// that it has requested. This allows the service provider to defer itself

// while still getting automatically loaded when a certain event occurs.

foreach ($manifest["when"] as $provider => $events) {

$this->registerLoadEvents($provider, $events);

}

// We will go ahead and register all of the eagerly loaded providers with the

// application so their services can be registered with the application as

// a provided service. Then we will set the deferred service list on it.

foreach ($manifest["eager"] as $provider) {

$this->app->register($provider);

}

$this->app->addDeferredServices($manifest["deferred"]);

}

}

执行顺序如下:

2.2.1 如果存在服务提供者缓存清单,则直接读取「服务提供者」集合;

2.2.2 否则,将从 config/app.php 配置中的服务提供者编译到缓存清单中;

2.2.2.1 这个处理由 compileManifest() 方法完成,之后$manifest是一个数组,含 providers, eager, when, deferred四个字段,如下

array(4) {

["when"]=>

array(14) {

["IlluminateBroadcastingBroadcastServiceProvider"]=>

array(0) {

}

["IlluminateBusBusServiceProvider"]=>

array(0) {

}

["IlluminateCacheCacheServiceProvider"]=>

array(0) {

}

.

}

["providers"]=>

array(39) {

[0]=>

string(35) "IlluminateAuthAuthServiceProvider"

[1]=>

string(48) "IlluminateBroadcastingBroadcastServiceProvider"

[2]=>

string(33) "IlluminateBusBusServiceProvider"

[3]=>

string(37) "IlluminateCacheCacheServiceProvider"

[4]=>

string(61) "IlluminateFoundationProvidersConsoleSupportServiceProvider"

[5]=>

string(39) "IlluminateCookieCookieServiceProvider"

[6]=>

string(43) "IlluminateDatabaseDatabaseServiceProvider"

.

[31]=>

string(32) "AppProvidersAppServiceProvider"

[32]=>

string(33) "AppProvidersAuthServiceProvider"

[33]=>

string(34) "AppProvidersEventServiceProvider"

[34]=>

string(34) "AppProvidersRouteServiceProvider"

[35]=>

string(39) "AppProvidersApiRequestServiceProvider"

[36]=>

string(37) "JacobcylAliOSSAliOssServiceProvider"

[37]=>

string(38) "MaatwebsiteExcelExcelServiceProvider"

[38]=>

string(33) "AppProvidersTestServiceProvider"

}

["eager"]=>

array(25) {

[0]=>

string(35) "IlluminateAuthAuthServiceProvider"

[1]=>

string(39) "IlluminateCookieCookieServiceProvider"

[2]=>

string(43) "IlluminateDatabaseDatabaseServiceProvider"

[3]=>

string(47) "IlluminateEncryptionEncryptionServiceProvider"

[4]=>

string(47) "IlluminateFilesystemFilesystemServiceProvider"

[5]=>

string(57) "IlluminateFoundationProvidersFoundationServiceProvider"

.

[17]=>

string(32) "AppProvidersAppServiceProvider"

[18]=>

string(33) "AppProvidersAuthServiceProvider"

[19]=>

string(34) "AppProvidersEventServiceProvider"

[20]=>

string(34) "AppProvidersRouteServiceProvider"

[21]=>

string(39) "AppProvidersApiRequestServiceProvider"

[22]=>

string(37) "JacobcylAliOSSAliOssServiceProvider"

[23]=>

string(38) "MaatwebsiteExcelExcelServiceProvider"

[24]=>

string(33) "AppProvidersTestServiceProvider"

}

["deferred"]=>

array(103) {

["IlluminateBroadcastingBroadcastManager"]=>

string(48) "IlluminateBroadcastingBroadcastServiceProvider"

["IlluminateContractsBroadcastingFactory"]=>

string(48) "IlluminateBroadcastingBroadcastServiceProvider"

["IlluminateContractsBroadcastingBroadcaster"]=>

string(48) "IlluminateBroadcastingBroadcastServiceProvider"

["IlluminateBusDispatcher"]=>

string(33) "IlluminateBusBusServiceProvider"

["IlluminateContractsBusDispatcher"]=>

string(33) "IlluminateBusBusServiceProvider"

["IlluminateContractsBusQueueingDispatcher"]=>

string(33) "IlluminateBusBusServiceProvider"

["cache"]=>

string(37) "IlluminateCacheCacheServiceProvider"

["cache.store"]=>

string(37) "IlluminateCacheCacheServiceProvider"

["memcached.connector"]=>

string(37) "IlluminateCacheCacheServiceProvider"

["command.cache.clear"]=>

string(61) "IlluminateFoundationProvidersConsoleSupportServiceProvider"

.

string(61) "IlluminateFoundationProvidersConsoleSupportServiceProvider"

["migrator"]=>

.

}

}

我们看到 config/app.php中的providers都被加定义在eager字段下,也就是饥渴加载。

2.2.2.2 编译缓存清单时将处理 贪婪/饥渴加载(eager)和 延迟加载(deferred)的服务提供者;

2.2.3 对于贪婪加载的提供者直接执行服务容器的 register 方法完成服务注册;

2.2.4 将延迟加载提供者加入到服务容器中,按需注册和引导启动。

这边关注需要被饥渴加载的服务提供则,它调用了 register()方法,同样是 vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php 这个 契约接口,仍然是由 IlluminateFoundationApplication 来实现的,具体如下:

/**

* Register a service provider with the application.

*

* @param IlluminateSupportServiceProvider|string $provider

* @param bool $force

* @return IlluminateSupportServiceProvider

*/

public function register($provider, $force = false)

{

if (($registered = $this->getProvider($provider)) && ! $force) {

return $registered;

}

// If the given "provider" is a string, we will resolve it, passing in the

// application instance automatically for the developer. This is simply

// a more convenient way of specifying your service provider classes.

if (is_string($provider)) {

$provider = $this->resolveProvider($provider);

}

if (method_exists($provider, "register")) {

$provider->register();

}

// If there are bindings / singletons set as properties on the provider we

// will spin through them and register them with the application, which

// serves as a convenience layer while registering a lot of bindings.

if (property_exists($provider, "bindings")) {

foreach ($provider->bindings as $key => $value) {

$this->bind($key, $value);

}

}

if (property_exists($provider, "singletons")) {

foreach ($provider->singletons as $key => $value) {

$this->singleton($key, $value);

}

}

$this->markAsRegistered($provider);

// If the application has already booted, we will call this boot method on

// the provider class so it has an opportunity to do its boot logic and

// will be ready for any usage by this developer"s application logic.

if ($this->booted) {

$this->bootProvider($provider);

}

return $provider;

}

关键是这一步,如果provider的控制器中有register方法,则执行该方法。

if (method_exists($provider, "register")) {

$provider->register();

}

我们在上面的eager字段中包含了 IlluminateDatabaseDatabaseServiceProvider 这个服务提供者,让我们来看看它的注册方法。

<?php

namespace IlluminateDatabase;

.

.

class DatabaseServiceProvider extends ServiceProvider

{

/**

* Bootstrap the application events.

*

* @return void

*/

public function boot()

{

Model::setConnectionResolver($this->app["db"]);

Model::setEventDispatcher($this->app["events"]);

}

/**

* Register the service provider.

*

* @return void

*/

public function register()

{

Model::clearBootedModels();

$this->registerConnectionServices();

$this->registerEloquentFactory();

$this->registerQueueableEntityResolver();

}

/**

* Register the primary database bindings.

*

* @return void

*/

protected function registerConnectionServices()

{

// The connection factory is used to create the actual connection instances on

// the database. We will inject the factory into the manager so that it may

// make the connections while they are actually needed and not of before.

$this->app->singleton("db.factory", function ($app) {

return new ConnectionFactory($app);

});

// The database manager is used to resolve various connections, since multiple

// connections might be managed. It also implements the connection resolver

// interface which may be used by other components requiring connections.

$this->app->singleton("db", function ($app) {

return new DatabaseManager($app, $app["db.factory"]);

});

$this->app->bind("db.connection", function ($app) {

return $app["db"]->connection();

});

}

.

.

.

}

我们看到它的register()方法调用了registerConnectionServices,而在这个方法里面,我们看到第二步,把 DatabaseManager对象以 "db" 的别名 注册到了 $app 容器中。

百转千回,山重水复,至此,真相终于大白。

原来 Facade 要解析的根对象,就来自于被注册到服务容器$app中的 服务提供者。

   /**

* Resolve the facade root instance from the container.

*

* @param string|object $name

* @return mixed

*/

protected static function resolveFacadeInstance($name)

{

if (is_object($name)) {

return $name;

}

if (isset(static::$resolvedInstance[$name])) {

return static::$resolvedInstance[$name];

}

return static::$resolvedInstance[$name] = static::$app[$name];

}

在 调用 DB Facade 中的 beginTransaction() 方法时,实际是由 IlluminateDatabaseDatabaseServiceProvider 这个服务提供者 中的 DatabaseManager 去执行的,他们通过 Facade的别名 "db",以及 服务提供者下的DatabaseManager的别名 "db" 进行关联的。

 

额外说一点,我们在说 将应用启动时,只讲了 RegisterFacades 和 RegisterProviders,其实下面还有一步 BootProviders. 它的步骤和 RegisterProviders差不多,也是通过应用契约,最后让应用去调用每个Provider的方法,只不过RegisterProviders调用的是register()方法,而BootProviders调用的是boot()方法。一个是注册,一个是启动。

    /**

* The bootstrap classes for the application.

*

* @var array

*/

protected $bootstrappers = [

IlluminateFoundationBootstrapLoadEnvironmentVariables::class,

IlluminateFoundationBootstrapLoadConfiguration::class,

IlluminateFoundationBootstrapHandleExceptions::class,

IlluminateFoundationBootstrapRegisterFacades::class,

IlluminateFoundationBootstrapRegisterProviders::class,

IlluminateFoundationBootstrapBootProviders::class,

];

至此,我们把Facade在Laravel中的工作机制跑通了一遍,顺带着把服务提供者的注册和启动也过了一遍,让我们对Laravel的请求生命周期也有了更深的了解。

以上是 Facade在Laravel中的工作机制 的全部内容, 来源链接: utcz.com/z/512454.html

回到顶部