用過laravel的人,一定都用過其集合,各類對數據庫查詢出來的數據,進行隨意變換,來知足複雜的業務需求。那每一個集合的方法,又是如何實現的? 很好奇,裏面必定有各類奇思妙想,因此我想一探究竟。php
這裏直接按照 laravel-china
的官網文檔開搞~laravel
具體的代碼,就不一一copy了。能夠參照着集合 collection.php
對着看。數據庫
這是集合類裏 use 的 trait Macroable,主要用來自定義擴展集合類的方法的,下面仔細分析一下。數組
// 這裏得命名空間和依賴的類就省略了
trait Macroable
{
/** * 定義數組用來存儲註冊的擴展。 * * @var array */
protected static $macros = [];
/** * 註冊自定製的方法macro。 * * @param string $name * @param object|callable $macro * @return void */
public static function macro($name, $macro) {
// 把方法存到定義的$macros裏。
static::$macros[$name] = $macro;
}
/** * 將其餘的類混合mix到這個Collection集合裏。 * * @param object $mixin * @return void */
public static function mixin($mixin) {
// 經過反射 獲取全部傳過來的類 $mixin 裏的方法,並保留public 和 protected 方法
$methods = (new ReflectionClass($mixin))->getMthods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
// 將方法進行循環, 將全部都方法都設置爲能夠訪問執行
foreach($methods as $method) {
$method->setAccessible(true);
// 利用invoke方法 來調用$mixin裏的 $method,而且執行靜態方法
// macro 來儲存在$macros數組裏。
static::macro($method->name, $method->invoke($mixin));
}
}
/** * 根據某個方法名判斷方法是否已註冊 * * @param string $name * @return bool */
public static function hasMacro($name) {
return isset(static::$macros[$name]);
}
/** * 處理Collection類裏不存在的靜態方法調用 * 也就是這裏自定義擴充的方法 * * @param string $method * @param array $parameters * @return mixed * * @throws \BadMethodCallException */
public static function __callStatic($method, $parameters) {
// 若是要調用的方法並不存在,就拋出錯誤
if (! static::hasMacro($method)) {
// 這個錯誤類 其實就是繼承了Exception類,沒別的啥
throw new BadMethodCallException("Method {$method} does not exist.");
}
// 若是存在,而且是匿名函數
if (static::$macros[$method] instanceof Closure) {
// 首先用匿名函數類Closure::bind 這靜態方法來複制這個匿名函數,而且從新定義做用域和上下文$this
// 由於是靜態方法,因此 $this 傳 null
// 而後再用call_user_func_array 來調用執行匿名函數。
return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
}
// 若是不屬於匿名函數,那麼就直接調用。
return call_user_func_array(static::$macros[$method], $parameters);
}
/** * 處理Collection裏不存在的成員方法 * 也就是擴充的方法 * * @param string $method * @param array $parameters * @return mixed * * @throws \BadMethodCallException */
public function __call($method, $parameters) {
// 若是要調用的方法並不存在,就拋出錯誤
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
// 若存在,將其賦值給$macro
$macro = static::$macros[$method];
// 若是是屬於匿名函數
if ($macro instanceof Closure) {
// 複製該閉包,併爲其從新定義上下文$this,和做用域。
return call_user_func_array($macro->bindTo($this, static::class), $parameters);
}
// 若是不是匿名函數,直接調用
return call_user_func_array($macro, $parameters);
}
}
複製代碼
小結:
1.定義一個靜態變量來存儲方法。
2.mixin
靜態方法,傳入類的實例,利用RelationClass
類來獲取想要的類型方法,而且利用RelationMethod
類裏的setAccessible
方法使其能夠執行。再用invoke
方法進行執行,並存入到靜態變量裏。
3.利用魔術方法__call
和__callStatic
來執行匿名函數閉包