laravel 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.
     * 在解决facade时运行Closure
     * @param  \Closure  $callback
     * @return void
     */
    public static function resolved(Closure $callback)
    {
        static::$app->afterResolving(static::getFacadeAccessor(), function ($service) use ($callback) {
            $callback($service);
        });
    }

这里的afterResolving,来自Container.php接口里的方法

/**
     * Register a new after resolving callback.
     * 解析回调后注册一个新的
     * @param  \Closure|string  $abstract
     * @param  \Closure|null  $callback
     * @return void
     */
    public function afterResolving($abstract, Closure $callback = null);

第一个参数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.');
    }

第二个参数是一个匿名函数方法作为回调。

下一个方法

/**
     * 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);
            });
        }
    }

isMock方法返回一个布尔值

/**
     * Determines whether a mock is set as the instance of the facade.
     * 确定是否将模拟设置为facade的实例
     * @return bool
     */
    protected static function isMock()
    {
        $name = static::getFacadeAccessor();

        return isset(static::$resolvedInstance[$name]) &&
               static::$resolvedInstance[$name] instanceof MockInterface;
    }
MockInterface接口如下:
interface MockInterface extends LegacyMockInterface
{
    /**
     * @param mixed $something  String method name or map of method => return
     * @return self|\Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\HigherOrderMessage
     */
    public function allows($something = []);

    /**
     * @param mixed $something  String method name (optional)
     * @return \Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\ExpectsHigherOrderMessage
     */
    public function expects($something = null);
}

在isMock返回false后进入判断,首先获取实例类名getMockableClass,实现如下

/**
     * Get the mockable class for the bound instance.
     * 获取绑定实例的可模拟类
     * @return string|null
     */
    protected static function getMockableClass()
    {
        if ($root = static::getFacadeRoot()) {
            return get_class($root);
        }
    }

其中getFacadeRoot方法实现如下

/**
     * Get the root object behind the facade.
     * 获取facade背后的根对象
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
里面的resolveFacadeInstancef方法如下
/**
     * Resolve the facade root instance from the container.
     * 从容器解析facade根实例
     * @param  object|string  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

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

        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }
resolveFacadeInstance根据传入的参数判断是否是对象,是对象直接返回该参数,不是则判断static::$resolvedInstance[$name]是否设置了这个key,有则返回static::$resolvedInstance[$name],没有再判断static::$app是否存在,存在赋值给static::$resolvedInstance[$name]并返回。
回到getMockableClass,static::getFacadeRoot()赋值给$root,用get_class获取类名。
回到spy,获取类名后,调用tap方法,该方法在helpers.php里,实现如下
if (! function_exists('tap')) {
    /**
     * Call the given Closure with the given value then return the value.
     * 用给定的值调用给定的闭包,然后返回该值
     * @param  mixed  $value
     * @param  callable|null  $callback
     * @return mixed
     */
    function tap($value, $callback = null)
    {
        if (is_null($callback)) {
            return new HigherOrderTapProxy($value);
        }

        $callback($value);

        return $value;
    }
}
HigherOrderTapProxy类源码如下
class HigherOrderTapProxy
{
    /**
     * The target being tapped.
     *
     * @var mixed
     */
    public $target;

    /**
     * Create a new tap proxy instance.
     *
     * @param  mixed  $target
     * @return void
     */
    public function __construct($target)
    {
        $this->target = $target;
    }

    /**
     * Dynamically pass method calls to the target.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        $this->target->{$method}(...$parameters);

        return $this->target;
    }
}
回到spy()里的tap,第一个参数
Mockery::spy()实现在Mockery里
/**
     * Static and semantic shortcut for getting a mock from the container
     * and applying the spy's expected behavior into it.
     * 静态和语义快捷方式,用于从容器获取mock并将间谍的预期行为应用到其中
     * @param mixed ...$args
     *
     * @return \Mockery\MockInterface|\Mockery\LegacyMockInterface
     */
    public static function spy(...$args)
    {
        if (count($args) && $args[0] instanceof \Closure) {
            $args[0] = new ClosureWrapper($args[0]);
        }

        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
    }
/**
     * Lazy loader and getter for
     * the container property.
     * 容器属性的惰性加载器和getter
     * @return Mockery\Container
     */
    public static function getContainer()
    {
        if (is_null(self::$_container)) {
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
        }

        return self::$_container;
    }
/**
     * Lazy loader method and getter for
     * the $_loader property.
     * $_loader属性的惰性加载器方法和getter
     * @return Loader
     */
    public static function getLoader()
    {
        if (is_null(self::$_loader)) {
            self::$_loader = self::getDefaultLoader();
        }

        return self::$_loader;
    }
class ClosureWrapper
{
    private $closure;

    public function __construct(\Closure $closure)
    {
        $this->closure = $closure;
    }

    public function __invoke()
    {
        return call_user_func_array($this->closure, func_get_args());
    }
}

 

/**
     * Hotswap the underlying instance behind the facade.
     * Hotswap 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);
        }
    }

我试着调用spy(),

$facade = Facade::spy();
        dump($facade);

结果提示

RuntimeException
Facade does not implement getFacadeAccessor method.

为什么呢,因为我没有对这个方法进行重写,我们看看其他继承了facade抽象类的子类如何重写的,例如Redis.php

namespace Illuminate\Support\Facades;

/**
 * @method static \Illuminate\Redis\Connections\Connection connection(string $name = null)
 *
 * @see \Illuminate\Redis\RedisManager
 * @see \Illuminate\Contracts\Redis\Factory
 */
class Redis extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'redis';
    }
}

我们可以看到redis类继承了facade类,并且重写了getFacadeAccessor方法,返回一个叫redis的类名。

 于是再来打印spy()

$facade = Redis::spy();
        dump($facade);

得到一个对象

Mockery_0_Illuminate_Redis_RedisManager {#4552 ▼
  #_mockery_expectations: []
  #_mockery_expectations_count: 0
  #_mockery_ignoreMissing: true
  #_mockery_deferMissing: false
  #_mockery_verified: false
  #_mockery_name: null
  #_mockery_allocatedOrder: 0
  #_mockery_currentOrder: 0
  #_mockery_groups: []
  #_mockery_container: Mockery\Container {#4325 ▼
    #_mocks: array:1 [▼
      "Mockery_0_Illuminate_Redis_RedisManager" => Mockery_0_Illuminate_Redis_RedisManager {#4552}
    ]
    #_allocatedOrder: 0
    #_currentOrder: 0
    #_groups: []
    #_generator: Mockery\Generator\CachingGenerator {#4326 ▼
      #generator: Mockery\Generator\StringManipulationGenerator {#4327 ▼
        #passes: array:13 [▼
          0 => Mockery\Generator\StringManipulation\Pass\CallTypeHintPass {#4328}
          1 => Mockery\Generator\StringManipulation\Pass\MagicMethodTypeHintsPass {#4329 ▶}
          2 => Mockery\Generator\StringManipulation\Pass\ClassPass {#4330}
          3 => Mockery\Generator\StringManipulation\Pass\TraitPass {#4331}
          4 => Mockery\Generator\StringManipulation\Pass\ClassNamePass {#4332}
          5 => Mockery\Generator\StringManipulation\Pass\InstanceMockPass {#4333}
          6 => Mockery\Generator\StringManipulation\Pass\InterfacePass {#4334}
          7 => Mockery\Generator\StringManipulation\Pass\AvoidMethodClashPass {#4335}
          8 => Mockery\Generator\StringManipulation\Pass\MethodDefinitionPass {#4336}
          9 => Mockery\Generator\StringManipulation\Pass\RemoveUnserializeForInternalSerializableClassesPass {#4337}
          10 => Mockery\Generator\StringManipulation\Pass\RemoveBuiltinMethodsThatAreFinalPass {#4338 ▼
            #methods: array:1 [▼
              "__wakeup" => "/public function __wakeup\(\)\s+\{.*?\}/sm"
            ]
          }
          11 => Mockery\Generator\StringManipulation\Pass\RemoveDestructorPass {#4339}
          12 => Mockery\Generator\StringManipulation\Pass\ConstantsPass {#4340}
        ]
      }
      #cache: array:1 [▶]
    }
    #_loader: Mockery\Loader\EvalLoader {#4341}
    #_namedMocks: []
  }
  #_mockery_partial: null
  #_mockery_disableExpectationMatching: false
  #_mockery_mockableProperties: []
  #_mockery_mockableMethods: []
  #_mockery_allowMockingProtectedMethods: false
  #_mockery_receivedMethodCalls: null
  #_mockery_defaultReturnValue: null
  #_mockery_thrownExceptions: []
  #_mockery_instanceMock: false
  #app: null
  #driver: null
  #customCreators: []
  #config: null
  #connections: null
  #events: false}

先记到这里,下次有空继续写

posted @ 2021-08-30 15:41  骑着新浪狂奔  阅读(123)  评论(0编辑  收藏  举报