Laravel集合探學系列——添加擴展macro策略(一)

用過laravel的人,一定都用過其集合,各類對數據庫查詢出來的數據,進行隨意變換,來知足複雜的業務需求。那每一個集合的方法,又是如何實現的? 很好奇,裏面必定有各類奇思妙想,因此我想一探究竟。php

這裏直接按照 laravel-china 的官網文檔開搞~laravel

具體的代碼,就不一一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來執行匿名函數閉包

相關文章
相關標籤/搜索