3
丑话说在前面,这一章难点,但也算是框架的核心了,大家静下心学吧。

分发器

看一个概念分发器,英文叫dispather,它的主要作用是管理所有类的实例化操作(本质上跟 laravel 的容器差不多,当我们触发各种服务类的时候,就会通过Dispatcher获取)。之前,我们都是通过 new 关键字创建对象,如入口文件的 new Application(),以及 Application 类的构造器中 new Bootstrap()。如果有分发器就简单多了。

core 目录下创建目录 dispatcher,并创建文件 Dispatcher.php,Container.php,Box.php

$ cd origin/core
$ mkdir dispatcher
$ cd dispatcher
$ touch Dispatcher.php Container.php Box.php

Dispatcher 是一个抽象类,定义了一个抽象方法 getInstance(),用于获取子类实例。子类 Container 和 Box 分别继承 Dispatcher 并实现 getInstance() 方法,不同点在于,容器中该方法需要存放子类实例,盒子中直接进行实例化操作,说白了,每次被触发调用的类实例之后,会讲该实例压入数组,下次调用的时候就不用重新实例化了。

  • 容器 Container。容器中的实例可以重复使用
  • 盒子 Box。盒子中的实例不能重复使用,每次会创建新的实例

Dispatcher 类

Dispatcher 类主要实现:

  • 定义抽象方法 getInstance()
abstract function getInstance();

注意抽象方法必须使用关键字 abstract 声明,并且不能包含函数体(花括号包括的部分)

  • 接收所有静态方式请求__callStatic()
public static function __callstatic(string $method, array $args)
{
    //1
    $instance = static::getInstance();
    //2
    return call_user_func_array(array($instance, $method), $args);
}

__callstatic 该方法的作用是,你在其他文件调用的不可见的静态方法都会执行该方法,所以它也是分发器入口。所有通过静态方式调用的不可见的类方法都会执行该函数,如 Config::get('db')

1,第一行 static::getInstance(); 获取执行类的实例,需要在 ContainerBox 中分别实现该方法。

2,通过 call_user_func_array() 调用自身存在的方法,该方法需要声明为 protected

  • 使用延迟静态绑定实例化子类
public static function newObject()
{
    return new Static;
}

该函数的作用是实例化操作,在子类 ContainerBox 中调用,new Static 返回创建具体执行操作的该子类实例,如 Config::get() 那么返回 Config 类的实例,Router::start() 返回 Router 类的实例。

关于延迟静态绑定 很重要 我下面会继续讲 请耐心看

  • 定义获取类实例函数 register()
public static function register()
{
    return static::getInstance();
}

该函数获取子类实例。

register 实质上就是利用 new Static, 不过 register 一般对外,比如我们在控制器中获取某个类的实例, newObject 则是对内,在容器中具体地对类进行实例化操作

Container 类

Container 类继承 Dispatcher,同时实现 getInstance() 方法。
该类使用一个静态变量 $container 来保存所有类实例,当再次调用该实例时直接返回,无需在实例化操作。

编辑 Container.php

<?php

namespace dispatcher;

/**
 * 继承 Dispatcher 必须实现 getInstance() 方法
 *
 */
class Container extends Dispatcher
{
    //存储子类实例
    public static $container = [];

    /**
     * 实现父类抽象方法
     *
     * 如果容器中已存在子类实例,直接返回
     */
    public function getInstance()
    {
        //获取子类名称
        $class = get_called_class();

        if (!isset(self::$container[$class]) ||
                !self::$container[$class] instanceof $class) {

            self::$container[$class] = self::newObject();
        }
        return self::$container[$class];
    }
}

get_called_class() 获取调用类的名称。

Box 类

Container 类一样,Box 继承 Dispatcher,同时实现 getInstance() 方法

编辑 Box.php

<?php

namespace dispatcher;

class Box extends Dispatcher
{
    /**
     * 该类的子类都会重新实例化
     *
     */
    public function getInstance()
    {
        return self::newObject();
    }
}

使用分发器

使用规则

  • 一个类要使用分发器,必须继承 ContainerBox 类。
  • 类方法通过静态方式调用,同时::符号后面直接接的方法必须声明为 protected
  • 通过 register() 方法获取类实例。

一个简单的例子

<?php

use core\Application;

// error_reporting(0);

define('APP_PATH',dirname(__DIR__));

require APP_PATH.'/core/Autoload.php';

(Application::register())::dispatcher();
<?php

namespace core;

use dispatcher\Container;

class Application extends Container
{
    public function run()
    {
        echo 'hello world';
    }

    protected function dispatcher()
    {
        echo 'dispatcher';
    }

  
}

可以通过 Application::dispatcher(); 调用类方法(非静态方法)。通过 Application::register(); 可以获取 Application 类实例。

执行 index.php 会输出 dispatcher

理理思路

1.先不管分发器怎么实现的,当你使用时必须得 use 吧
2.当我们从 index 出发,去调用了一个 静态的 dispatcher 方法时,Application 本身没有这样一个静态的方法啊,怎么办? 只能通过自动加载到 container.php 这个文件了,还是没找到这个方法,怎么办? Container 继承了 Dispatcher,好了 有 __callStatic ,这时候会自动进入这个函数,接下来,请注意,这里利用了 static 这个关键字来延迟绑定(呼应上文),简单来说,就是哪个类触发的 callStatic,static 就代表哪个类,这里就是 Application 这个类,会通过 Container 下 getInstance 方法获取 Application 实例,最后 通过 call_user_func_array 回调,输出 dispatcher 完。

关于延迟静态绑定

没搞懂的,看看这篇文章吧 https://www.cnblogs.com/codeAB/p/5560631.html

总结

本结主要介绍了分发器的概念,实现了一个简单的类管理机制,可以通过静态方式调用对象方法。分发器类 ContainerBox 的区别在于是否保存子类实例

下期见


entner
751 声望187 粉丝

青年 ---------------