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}
先记到这里,下次有空继续写