在Support/Facades中的大部份類別,都是繼承此Facade類別、只實作getFacadeAccessor函式。
產生少打很多字的效果:developer只要呼叫facade類別的靜態函式,Laravel就會自動去對應到某個物件的某個方法。
當然了,不少工程師討厭這種特效,參見:讓你少打很多字:Facades。
那,這種特效又是如何做到的呢?
instance(static::getFacadeAccessor(), $instance); } /** * Initiate a mock expectation on the facade. * * @param mixed * @return \Mockery\Expectation */ public static function shouldReceive() { $name = static::getFacadeAccessor(); if (static::isMock()) { $mock = static::$resolvedInstance[$name]; } else { $mock = static::createFreshMockInstance($name); } return call_user_func_array(array($mock, 'shouldReceive'), func_get_args()); } /** * Create a fresh mock instance for the given class. * * @param string $name * @return \Mockery\Expectation */ protected static function createFreshMockInstance($name) { static::$resolvedInstance[$name] = $mock = static::createMockByName($name); if (isset(static::$app)) { static::$app->instance($name, $mock); } return $mock; } /** * Create a fresh mock instance for the given class. * * @param string $name * @return \Mockery\Expectation */ protected static function createMockByName($name) { $class = static::getMockableClass($name); 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 */ protected static function getMockableClass() { if ($root = static::getFacadeRoot()) return get_class($root); } /** * 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 $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 = array(); } /** * Get the application instance behind the facade. * * @return \Illuminate\Foundation\Application */ public static function getFacadeApplication() { return static::$app; } /** * Set the application instance. * * @param \Illuminate\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 */ public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); 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(array($instance, $method), $args); } } }
最重要的函式只是這樣而已。使用PHP的magic method「__callStatic」,在類別呼叫靜態函式而找不到時,使用getFacadeRoot找到對應的物件,然後根據參數的數量去呼叫物件的方法。
注意這邊的設計:參數在4個以下都會直接呼叫方法,超過的話則用call_user_func_array呼叫方法。
這是因為call_user_func_array執行時有一點點慢的關係。