这篇文章主要围绕LaravelFacade的详细解读和laravelfacades展开,旨在为您提供一份详细的参考资料。我们将全面介绍LaravelFacade的详细解读的优缺点,解答laravelf
这篇文章主要围绕Laravel Facade 的详细解读和laravel facades展开,旨在为您提供一份详细的参考资料。我们将全面介绍Laravel Facade 的详细解读的优缺点,解答laravel facades的相关问题,同时也会为您带来Facade 在 Laravel 中的工作机制、Facade---Laravel学习笔记、laravel Facade、Laravel Facade 解读的实用方法。
本文目录一览:- Laravel Facade 的详细解读(laravel facades)
- Facade 在 Laravel 中的工作机制
- Facade---Laravel学习笔记
- laravel Facade
- Laravel Facade 解读
Laravel Facade 的详细解读(laravel facades)
下面由Laravel教程栏目给大家介绍Laravel Facade 的详细解读,希望对需要的朋友有所帮助!
大家好,今天带来的内容是 Laravel 的 Facade 机制实现原理。
Facade的简单使用
数据库的使用:
$users = DB::connection('foo')->select(...);
IOC容器
众所周知,IOC容器是 Laravel 框架的最最重要的部分。它提供了两个功能,IOC和容器。
- IOC(Inversion of Control),也叫控制反转。说白了,就是控制对象的生成,使开发者不用关心对象的如何生成,只需要关心它的使用即可。
- 而通过IOC机制生成的对象实例需要一个存放的位置,以便之后继续使用,便是它的容器功能。
这次不准备讲解IOC容器的具体实现,之后会有文章详细解读它。关于IOC容器,读者只需要记住两点即可:
- 根据配置生成对象实例;
- 保存对象实例,方便随时取用;
简化后 Facade 类
<?PHP namespace facades; abstract class Facade { protected static $app; /** * Set the application instance. * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */ public static function setFacadeApplication($app) { static::$app = $app; } /** * Get the registered name of the component. * * @return string * * @throws \RuntimeException */ protected static function getFacadeAccessor() { throw new RuntimeException('Facade does not implement getFacadeAccessor method.'); } /** * Get the root object behind the facade. * * @return mixed */ public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); } /** * Resolve the facade root instance from the container. * * @param string|object $name * @return mixed */ protected static function resolveFacadeInstance($name) { return static::$app->instances[$name]; } public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } switch (count($args)) { case 0: return $instance->$method(); case 1: return $instance->$method($args[0]); case 2: return $instance->$method($args[0], $args[1]); case 3: return $instance->$method($args[0], $args[1], $args[2]); case 4: return $instance->$method($args[0], $args[1], $args[2], $args[3]); default: return call_user_func_array([$instance, $method], $args); } } }
代码说明:
- $app中存放的就是一个IOC容器实例,它是在框架初始化时,通过 setFacadeApplication() 这个静态方法设置的
- 它实现了 __callStatic 魔术方法
- getFacadeAccessor() 方法需要子类去继承,返回一个string的标识,通过这个标识,IOC容器便能返回它所绑定类(框架初始化或其它时刻绑定)的对象
- 通过 $instance 调用具体的方法
创建自己的Facade:
TEST1 的具体逻辑:
<?PHP class Test1{ public function hello() { print(hello world); }}
TEST1 类的Facade:
<?PHP namespace facades;/** * Class Test1 * @package facades * * @method static setoverRecommendInfo [设置播放完毕时的回调函数] * @method static setHandlerPlayer [明确指定下一首时的执行类] */class Test1Facade extends Facade{ protected static function getFacadeAccessor() { return 'test1'; } }
使用:
use facades\Test1Facade;Test1Facade::hello(); // 这是 Facade 调用
解释:
- facades\Test1Facade 调用静态方法 hello() 时,由于没有定义此方法,会调用 __callStatic;
- 在 __callStatic 中,首先是获取对应的实例,即
return static::$app->instances[$name];
。这其中的$name
,即为facades\Test1
里的 test1 - $app, 即为 IOC 容器,类的实例化过程,就交由它来处理。
Facade 在 Laravel 中的工作机制
我们知道 Facade 门面模式是一个中介类,对子系统或者类对象进行封装和代理,为子系统的一组接口提供一个统一的高层接口。它对外暴露子系统的功能,但是不暴露子系统对象,用户要使用子系统的某个功能 (方法) 时,先调用门店,由于门店代理了子系统,所以委托子系统去处理相应的功能请求,从而达到将用户和子系统解耦的目的,用户只需要和门店打交道,不需要知道所有的子系统及其内部构造。
我们接下来通过在 Laravel 中最常用的 DB-Facade,来看看 Facade 在 Laravel 中是如何工作的。
1,代码如下
<?php
use Exception;
use Illuminate\Support\Facades\DB;
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 Illuminate\Support\Facades;
/**
* @method static \Illuminate\Database\ConnectionInterface connection(string $name = null)
* @method static string getDefaultConnection()
* @method static void setDefaultConnection(string $name)
* @method static \Illuminate\Database\Query\Builder table(string $table)
* @method static \Illuminate\Database\Query\Expression 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 \Illuminate\Database\DatabaseManager
* @see \Illuminate\Database\Connection
*/
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 Illuminate\Support\Facades;
use Closure;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;
abstract class Facade
{
/**
* The application instance being facaded.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
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 \Mockery\MockInterface
*/
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 \Mockery\Expectation
*/
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 \Mockery\Expectation
*/
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 \Mockery\MockInterface
*/
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 \Illuminate\Contracts\Foundation\Application
*/
public static function getFacadeApplication()
{
return static::$app;
}
/**
* Set the application instance.
*
* @param \Illuminate\Contracts\Foundation\Application $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 \Illuminate\Contracts\Foundation\Application
*/
protected static $app;
是正在被门店代理的应用实例,来自于 \Illuminate\Contracts\Foundation\Application.
这是一个契约,有契约就会有实现契约的类,全局搜一下整个框架,发现,唯一的实现,是在 namespace Illuminate\Foundation\Application 下 (vendor/laravel/framework/src/Illuminate/Foundation/Application.php),我们来看看
<?php
namespace Illuminate\Foundation;
.
.
use Symfony\Component\HttpKernel\HttpKernelInterface;
.
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Contracts\Foundation\Application 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 = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
而 $this->app->bootstrapWith () 是怎么处理这些 bootstrap 类呢?
这里的 bootstrapWith () 方法,点进去,它是在 vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php 文件中,
namespace Illuminate\Contracts\Foundation;
use Closure;
use Illuminate\Contracts\Container\Container;
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 Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $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'' => Illuminate\Support\Facades\App::class,
.
''DB'' => Illuminate\Support\Facades\DB::class,
],
这是 RegisterFacades 部分。我们再看 RegisterProviders。
2.2 RegisterProviders
目录: vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Contracts\Foundation\Application;
class RegisterProviders
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
}
这里的 registerConfiguredProviders () 方法点进去,又是一个接口:
目录: vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php
<?php
namespace Illuminate\Contracts\Foundation;
use Illuminate\Contracts\Container\Container;
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 Illuminate\Foundation;
use Illuminate\Support\ServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;
use Symfony\Component\HttpKernel\HttpKernelInterface;
.
use Illuminate\Contracts\Http\Kernel as HttpKernelContract;
.
use Illuminate\Contracts\Foundation\Application 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(Illuminate\Support\Collection)#32 (1) {
["items":protected]=>
array(3) {
[0]=>
object(Illuminate\Support\Collection)#22 (1) {
["items":protected]=>
array(22) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
[2]=>
string(33) "Illuminate\Bus\BusServiceProvider"
[3]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
[4]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
[5]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[6]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
[7]=>
string(47) "Illuminate\Encryption\EncryptionServiceProvider"
[8]=>
string(47) "Illuminate\Filesystem\FilesystemServiceProvider"
[9]=>
string(57) "Illuminate\Foundation\Providers\FoundationServiceProvider"
[10]=>
string(38) "Illuminate\Hashing\HashServiceProvider"
[11]=>
string(35) "Illuminate\Mail\MailServiceProvider"
[12]=>
string(52) "Illuminate\Notifications\NotificationServiceProvider"
[13]=>
string(47) "Illuminate\Pagination\PaginationServiceProvider"
[14]=>
string(43) "Illuminate\Pipeline\PipelineServiceProvider"
[15]=>
string(37) "Illuminate\Queue\QueueServiceProvider"
[16]=>
string(37) "Illuminate\Redis\RedisServiceProvider"
[17]=>
string(54) "Illuminate\Auth\Passwords\PasswordResetServiceProvider"
[18]=>
string(41) "Illuminate\Session\SessionServiceProvider"
[19]=>
string(49) "Illuminate\Translation\TranslationServiceProvider"
[20]=>
string(47) "Illuminate\Validation\ValidationServiceProvider"
[21]=>
string(35) "Illuminate\View\ViewServiceProvider"
}
}
[1]=>
array(9) {
[0]=>
string(43) "Fideloper\Proxy\TrustedProxyServiceProvider"
[1]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[2]=>
string(52) "Illuminate\Notifications\NexmoChannelServiceProvider"
[3]=>
string(52) "Illuminate\Notifications\SlackChannelServiceProvider"
[4]=>
string(36) "Laravel\Tinker\TinkerServiceProvider"
[5]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[6]=>
string(30) "Carbon\Laravel\ServiceProvider"
[7]=>
string(45) "SimpleSoftwareIO\QrCode\QrCodeServiceProvider"
[8]=>
string(43) "Spatie\Permission\PermissionServiceProvider"
}
[2]=>
object(Illuminate\Support\Collection)#30 (1) {
["items":protected]=>
array(8) {
[22]=>
string(32) "App\Providers\AppServiceProvider"
[23]=>
string(33) "App\Providers\AuthServiceProvider"
[24]=>
string(34) "App\Providers\EventServiceProvider"
[25]=>
string(34) "App\Providers\RouteServiceProvider"
[26]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[27]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[28]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[29]=>
string(33) "App\Providers\TestServiceProvider"
}
}
}
}
然后用 vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php 这个服务提供者仓库(ProviderRepository),将这些服务提供者,逐个 load 加载注册 到 $app 中。
<?php
namespace Illuminate\Foundation;
use Exception;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Foundation\Application 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) {
["Illuminate\Broadcasting\BroadcastServiceProvider"]=>
array(0) {
}
["Illuminate\Bus\BusServiceProvider"]=>
array(0) {
}
["Illuminate\Cache\CacheServiceProvider"]=>
array(0) {
}
.
}
["providers"]=>
array(39) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
[2]=>
string(33) "Illuminate\Bus\BusServiceProvider"
[3]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
[4]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
[5]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[6]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
.
[31]=>
string(32) "App\Providers\AppServiceProvider"
[32]=>
string(33) "App\Providers\AuthServiceProvider"
[33]=>
string(34) "App\Providers\EventServiceProvider"
[34]=>
string(34) "App\Providers\RouteServiceProvider"
[35]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[36]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[37]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[38]=>
string(33) "App\Providers\TestServiceProvider"
}
["eager"]=>
array(25) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[2]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
[3]=>
string(47) "Illuminate\Encryption\EncryptionServiceProvider"
[4]=>
string(47) "Illuminate\Filesystem\FilesystemServiceProvider"
[5]=>
string(57) "Illuminate\Foundation\Providers\FoundationServiceProvider"
.
[17]=>
string(32) "App\Providers\AppServiceProvider"
[18]=>
string(33) "App\Providers\AuthServiceProvider"
[19]=>
string(34) "App\Providers\EventServiceProvider"
[20]=>
string(34) "App\Providers\RouteServiceProvider"
[21]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[22]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[23]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[24]=>
string(33) "App\Providers\TestServiceProvider"
}
["deferred"]=>
array(103) {
["Illuminate\Broadcasting\BroadcastManager"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Contracts\Broadcasting\Factory"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Contracts\Broadcasting\Broadcaster"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Bus\Dispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["Illuminate\Contracts\Bus\Dispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["Illuminate\Contracts\Bus\QueueingDispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["cache"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["cache.store"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["memcached.connector"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["command.cache.clear"]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
.
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
["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 这个 契约接口,仍然是由 Illuminate\Foundation\Application 来实现的,具体如下:
/**
* Register a service provider with the application.
*
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param bool $force
* @return \Illuminate\Support\ServiceProvider
*/
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 字段中包含了 Illuminate\Database\DatabaseServiceProvider 这个服务提供者,让我们来看看它的注册方法。
<?php
namespace Illuminate\Database;
.
.
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 () 方法时,实际是由 Illuminate\Database\DatabaseServiceProvider 这个服务提供者 中的 DatabaseManager 去执行的,他们通过 Facade 的别名 ''db'',以及 服务提供者下的 DatabaseManager 的别名 ''db'' 进行关联的。
额外说一点,我们在说 将应用启动时,只讲了 RegisterFacades 和 RegisterProviders,其实下面还有一步 BootProviders. 它的步骤和 RegisterProviders 差不多,也是通过应用契约,最后让应用去调用每个 Provider 的方法,只不过 RegisterProviders 调用的是 register () 方法,而 BootProviders 调用的是 boot () 方法。一个是注册,一个是启动。
/**
* The bootstrap classes for the application.
*
* @var array
*/
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
至此,我们把 Facade 在 Laravel 中的工作机制跑通了一遍,顺带着把服务提供者的注册和启动也过了一遍,让我们对 Laravel 的请求生命周期也有了更深的了解。
Facade---Laravel学习笔记
Laravel中的Facade提供了对于类的更简便的访问方法。在通过Facade访问类时,是不需要手动对类进行实例化的,对于类的所有可访问的方法(不管是是静态还是非静态方法),都可以采用访问静态方法的方式进行访问,即SomeObjectFacade::someMethod($parameters)
其实某个类的facade可以看作是具体的相关的类的实例的一个代理,而这个实例是在服务容器中注册的。
现在有一个类App\LearningTest\Bar:
<?php
namespace App\LearningTest;
class Bar
{
public function test ()
{
echo ''Mes from Bar::test'';
return ''Mes from Bar::test'';
}
}
以下是这个类的facade的实现:
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Bar extends Facade
{
protected static function getFacadeAccessor()
{
return ''bar'';
}
}
在通过App\Facades\Bar访问相应的类时,实际上是通过getFacadeAccessor返回的字符串''bar''去容器中查询该字符串绑定的所注册的类的实例。即
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
$this->app->bind(''bar'', function () {
return new \App\LearningTest\Bar();
});
$this->app->alias(''bar'', ''App\LearningTest\Bar'');
}
}
可见,字符串''bar''通过中间的容器将facade与具体的类的实例联系了起来。
最好,通过在config/app.php中将App\Facades\Bar注册到aliases数组中,即
''Bar'' => App\Facades\Bar::class
就可以在Laravel应用中通过该别名而不需要特别引入就可以访问App\LearningTest\Bar实例的相关方法了,比如在某个类中调用test方法:Bar::test()
,但不需要明显的引入App\LearningTest\Bar。
laravel Facade
Facade的调用原理和过程
创建一个类 CacheManager.PHP(默认所有组件都在同一目录下)
创建一个provider CacheServiceProvider
class CacheServiceProvider extends ServiceProvider { public function register() { $this->app->singleton('cache',function($app){ return new CacheManager($app); }); } }
服务容器($this->app)在服务提供者(ServiceProvider)内绑定服务(CacheManager)
singleton将类绑定到容器中
在config/app.PHP中注册服务提供者
'providers' => [ /* * Laravel Framework Service Providers... */ 'Illuminate\Cache\CacheServiceProvider', ]
创建facades,调用容器方法
class Cache extends Facade { protected static function getFacadeAccessor() { return 'cache'; } }
基类Facade
所有门脸类都是继承自Facade类,再该基类中定义了一个__callStatic方法,以至于我们可以轻松的使用facade
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); }
public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); }
getFacadeRoot方法将根据getFacadeAccessor()的返回值,从容器中取出对应的实例对象。
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]; }
Laravel Facade 解读
大家好,今天带来的内容是 Laravel 的 Facade 机制实现原理。
Facade的简单使用
数据库的使用:
$users = DB::connection('foo')->select(...);
IOC容器
众所周知,IOC容器是 Laravel 框架的最最重要的部分。它提供了两个功能,IOC和容器。
IOC(Inversion of Control),也叫控制反转。说白了,就是控制对象的生成,使开发者不用关心对象的如何生成,只需要关心它的使用即可。
而通过IOC机制生成的对象实例需要一个存放的位置,以便之后继续使用,便是它的容器功能。
这次不准备讲解IOC容器的具体实现,之后会有文章详细解读它。关于IOC容器,读者只需要记住两点即可:
根据配置生成对象实例;
保存对象实例,方便随时取用;
简化后 Facade 类
PHP
namespace facades;
/**
* Class Test1
* @package facades
*
* @method static setoverRecommendInfo [设置播放完毕时的回调函数]
* @method static setHandlerPlayer [明确指定下一首时的执行类]
*/
class Test1Facade extends Facade
{
protected static function getFacadeAccessor()
{
return 'test1';
}
}
使用:
use facadesTest1Facade;
Test1Facade::hello(); // 这是 Facade 调用
解释:
facadesTest1Facade 调用静态方法 hello() 时,由于没有定义此方法,会调用 __callStatic;
在 __callStatic 中,首先是获取对应的实例,即 return static::$app->instances[$name];。这其中的 $name,即为 facadesTest1 里的 test1
$app, 即为 IOC 容器,类的实例化过程,就交由它来处理。
今天的关于Laravel Facade 的详细解读和laravel facades的分享已经结束,谢谢您的关注,如果想了解更多关于Facade 在 Laravel 中的工作机制、Facade---Laravel学习笔记、laravel Facade、Laravel Facade 解读的相关知识,请在本站进行查询。
本文标签: