TP5.1 源码窥探之门面代理

之前有过疑问在某个控制器或者模型中use引入一个全局的类,但使用编辑器的快捷跳转功能,又跳不过去,说是找不到该类,但实际上项目又可以使用,现在了解了自动加载和门面代理模式就知道为什么可以正常使用了。

自动加载类库别名

之前有介绍了框架是怎么自动加载类的,系统在注册自动加载后,有一个操作,那就是给Loader类添加类库别名。
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,
]);

而在框架进行Loader::autoload自动加载的步骤的时候,有一段逻辑是判断是否存在类库别名,如果存在就自动加载该类。

thinkphp/library/think/Loader.php

// 自动加载
public static function autoload($class)
{
    //class_alias 为一个类创建别名,并自动加载
    if (isset(self::$classAlias[$class])) {
        return class_alias(self::$classAlias[$class], $class);
    }
    //var_dump($class);exit; => think\Error
    if ($file = self::findFile($class)) {

        // Win环境严格区分大小写
        if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
            return false;
        }

        __include_file($file);
        return true;
    }
}

截止目前,终于知道它是怎么实现自动加载的了。

门面代理

门面为容器中的类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展性,说的直白一点,Facade功能可以让类无需实例化而直接进行静态方式调用。你可以为任何的非静态类库定义一个facade类。下面就看一下是怎么实现的,就以Config类别名为例吧。

当使用Config类别名的时候,

$middleware_config = \Config::get('middleware.');

系统实际上调用的是thinkphp/library/think/facade/Config.php

namespace think\facade;

use think\Facade;

class Config extends Facade
{
    /**
     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
     * @access protected
     * @return string
     */
    protected static function getFacadeClass()
    {
        return 'config';
    }
}

可见它继承了Facade类,当调用静态方法get的时候,此类并不存在该方法,则会自动调用魔术方法__callStatic

thinkphp/library/think/Facade.php

// 调用实际类的方法
public static function __callStatic($method, $params)
{
    return call_user_func_array([static::createFacade(), $method], $params);
}

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);
}

然后再调用Container容器类的make方法,然后再加载响应的绑定标识类。

thinkphp/library/think/Container.php

public function make($abstract, $vars = [], $newInstance = false)
{
    if (true === $vars) {
        // 总是创建新的实例化对象
        $newInstance = true;
        $vars        = [];
    }
    $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
    //var_dump($abstract,$this->name,$this->instances);exit;
    if (isset($this->instances[$abstract]) && !$newInstance) {
        return $this->instances[$abstract];
    }

    if (isset($this->bind[$abstract])) {
        $concrete = $this->bind[$abstract];
        //var_dump($concrete);exit;
        if ($concrete instanceof Closure) {
            $object = $this->invokeFunction($concrete, $vars);
        } else {
            $this->name[$abstract] = $concrete;//['app'=>'think\App']

            return $this->make($concrete, $vars, $newInstance);
        }
    } else {
        $object = $this->invokeClass($abstract, $vars);
    }

    if (!$newInstance) {
        $this->instances[$abstract] = $object;//['think\App' => 'App实例对象']
    }

    return $object;
}

源码显示实际上调用的是createFacade返回的代理类的$method相关方法,例如think\Configget方法。现在明白它是怎么运行的了吧。

本作品采用《CC 协议》,转载必须注明作者和本文链接
今年不学习,明天惨唧唧。
zs4336
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!