Facade在Laravel中的工作机制
我们知道Facade门面模式是一个中介类,对子系统或者类对象进行封装和代理,为子系统的一组接口提供一个统一的高层接口。它对外暴露子系统的功能,但是不暴露子系统对象,用户要使用子系统的某个功能(方法)时,先调用门店,由于门店代理了子系统,所以委托子系统去处理相应的功能请求,从而达到将用户和子系统解耦的目的,用户只需要和门店打交道,不需要知道所有的子系统及其内部构造。
我们接下来通过在Laravel中最常用的DB-Facade,来看看Facade在Laravel中是如何工作的。
1,代码如下
<?phpuse 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
<?phpnamespace 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父类 / 基类
<?phpnamespace 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),我们来看看
<?phpnamespace 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
<?phpnamespace 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
<?phpnamespace 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
<?phpnamespace 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
<?phpnamespace 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 中。
<?phpnamespace 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 这个服务提供者,让我们来看看它的注册方法。
<?phpnamespace 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