laravel的facade原理以及static介绍
之前就说过new static 和new self的区别( php的newstatic和self )。今天在laravel中又看到了后期绑定的介绍,深入学习了一下static的用法。
今天看了laravel的官方文档,介绍了facade,说实话,以前一直用,但总是知其然不知其所以然。今天看了代码,了解了一下原理,同时也了解了static的用法。
那就从facade说起。
在laravel中有很多的facade可供我们使用,均在lluminate\Support\Facades中,说白了facade就是一个静态代理。就拿Cache来说,有时会这样用:
Cache::get('key')
看一下这个类:
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}
这个类本身没有get方法,那怎么实现的呢?
这就要归功于Facade抽象类了。先把整个抽象类的代码拿出来:
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;
/**
* Convert the facade into a Mockery spy.
*
* @return void
*/
public static function spy()
{
if (! static::isMock()) {
$class = static::getMockableClass();
static::swap($class ? Mockery::spy($class) : Mockery::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);
}
}
入口是__callStatic。当你调用一个不存在的静态方法时,这个方法会被调用。
然后再看内部的:
$instance = static::getFacadeRoot();
它是为了获取实例,看一下内部;
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
这里面的static::getFacadeAccessor()很重要。它调用的是当前子类的getFacadeAccessor()方法,即Cache重构的方法。PHP官方文档把这个叫做后期状态绑定。这个和self完全不同。
resolveFacadeInstance方法其实就是根据容器名称从注册的容器中获取对应的类的实例。随后就调用实际实例的方法。
好了,关于Facades说了这么多,那么,我们为什么需要它呢?直接调用对应的类不好吗?
其实,最最前面的Cache的例子已经说明了,这让我们的调用变得非常的方便。
Cache的getFacadeAccessor返回的是一个字符串cache.
其实cache是一个已经注册到Container中的Provider。其原型:
class CacheServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});
$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
}
关于laravel的容器,服务注册等介绍非常号的一篇文章:
微信分享/微信扫码阅读