用過laravel的人,必定都用過其集合,各種對資料庫查詢出來的資料,進行隨意變換,來滿足複雜的業務需求。那每個集合的方法,又是如何實現的? 很好奇,裡面一定有各種奇思妙想,所以我想一探究竟。
這裡直接按照 laravel-china
的官網文件開搞~
具體的程式碼,就不一一copy了。可以參照著集合 collection.php
對著看。
1. Macroable
這是集合類裡 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
來執行匿名函式