1. 程式人生 > >TP5.1的核心程式碼解析之Facade

TP5.1的核心程式碼解析之Facade

Facade是什麼呢,目前看是一個用來方便實現原有類(未定義靜態方法)的靜態方式呼叫對應的方法的類。
具體Facade的實現如下:
首先看核心檔案:

\thinkphp\base.php

中的:

// 註冊核心類的靜態代理
Facade::bind([
    facade\App::class      => App::class,
    facade\Build::class    => Build::class,
    facade\Cache::class    => Cache::class,
    facade\Config::class   =>
Config::class, facade\Cookie::class => Cookie::class, facade\Debug::class => Debug::class, facade\Env::class => Env::class, facade\Hook::class => Hook::class, facade\Lang::class => Lang::class, facade\Log::class => Log::class, facade\
Request::class => Request::class, facade\Response::class => Response::class, facade\Route::class => Route::class, facade\Session::class => Session::class, facade\Url::class => Url::class, facade\Validate::class => Validate::class, facade\View::class
=> View::class, ]);

可以看到類似容器中的繫結方式,找到對應的繫結方法如下:

   /**
     * 繫結類的靜態代理
     * @static
     * @access public
     * @param  string|array  $name    類標識
     * @param  string        $class   類名
     * @return object
     */
    public static function bind($name, $class = null)
    {
        if (__CLASS__ != static::class) {
            return self::__callStatic('bind', func_get_args());
        }

        if (is_array($name)) {
            self::$bind = array_merge(self::$bind, $name);
        } else {
            self::$bind[$name] = $class;
        }
    }

可以看到同樣是放到了一個數組$bind中。
這裡注意一下下面的在\thinkphp\base.php檔案中的註冊類別名的方法,可以實現類別名直接訪問類(這裡自己看)

// 註冊類庫別名
Loader::addClassAlias([
    'App'      => facade\App::class,
    'Build'    => facade\Build::class,
    'Cache'    => facade\Cache::class,
    'Config'   => facade\Config::class,
    'Cookie'   => facade\Cookie::class,
    'Db'       => Db::class,
    'Debug'    => facade\Debug::class,
    'Env'      => facade\Env::class,
    'Facade'   => Facade::class,
    'Hook'     => facade\Hook::class,
    'Lang'     => facade\Lang::class,
    'Log'      => facade\Log::class,
    'Request'  => facade\Request::class,
    'Response' => facade\Response::class,
    'Route'    => facade\Route::class,
    'Session'  => facade\Session::class,
    'Url'      => facade\Url::class,
    'Validate' => facade\Validate::class,
    'View'     => facade\View::class,
]);

那麼具體如何呼叫的靜態方法呢,比如

\Cache::set('name','value');

這個方法,具體時間如何呼叫到set方法呢,首先先用上面的類的別名機制直接找到對應的facade\Cache::class這個類,也就是實際上是呼叫的這個類。然後我們看這個類的內容:

namespace think\facade;

use think\Facade;

class Cache extends Facade
{
}

可以看到沒有內容,那set方法哪來的呢,這裡利用了一個PHP的靜態方法呼叫失敗會呼叫的方法,在Facade類中有:

 // 呼叫實際類的方法
    public static function __callStatic($method, $params)
    {
        return call_user_func_array([static::createFacade(), $method], $params);
    }
//在靜態上下文中呼叫一個不可訪問方法時,__callStatic() 會被呼叫。    

也就是當呼叫不到set方法時,這個方法會被呼叫,也就是實際上呼叫了

call_user_func_array([static::createFacade(), $method], $params)

實際上就是:

/**
     * 建立Facade例項
     * @static
     * @access protected
     * @param  string    $class          類名或標識
     * @param  array     $args           變數
     * @param  bool      $newInstance    是否每次建立新的例項
     * @return object
     */
    protected static function createFacade($class = '', $args = [], $newInstance = false)
    {
        $class       = $class ?: static::class;
        $facadeClass = static::getFacadeClass();
        if ($facadeClass) {
            $class = $facadeClass;
        } elseif (isset(self::$bind[$class])) {
            $class = self::$bind[$class];
        }

        if (static::$alwaysNewInstance) {
            $newInstance = true;
        }
        return Container::getInstance()->make($class, $args, $newInstance);
    }

可以看到最後一句,實際上也就是是呼叫了Cache::class的例項(通過容器獲取)的set方法。

以上