丑话说在前面,这一章难点,但也算是框架的核心了,大家静下心学吧。
分发器
看一个概念分发器,英文叫dispather,它的主要作用是管理所有类的实例化操作。在程序中,有时需要大量创建对象实例,有时需要使用相同的实例,因此如果重复创建实例会额外给程序带来开销和管理的负担。所以我们使用了分发器的概念,用于专门管理所有类的实例化操作。之前,我们都是通过 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。盒子中的实例不能重复使用,每次会创建新的实例
容器就是存东西的变量 在这里我们存放所有调用类的实例,当然也可以存服务,接口取决于你 everything
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(); 执行子类的 getInstance() 方法,因此需要在 Container 和 Box 中分别实现该方法。
2,通过 call_user_func_array() 调用自身存在的方法,该方法需要声明为 protected
使用延迟静态绑定实例化子类
public static function newObject()
{
return new Static;
}
该函数的作用是实例化操作,在子类 Container 和 Box 中调用,new Static 返回创建具体执行操作的该子类实例,如 Config::get() 那么返回 Config 类的实例,Router::start() 返回 Router 类的实例。
关于延迟静态绑定 很重要 我下面会继续讲 请耐心看
定义获取类实例函数 register()
public static function register()
{
return static::getInstance();
}
该函数获取子类实例。
Container 类
Container 类继承 Dispatcher,同时实现 getInstance() 方法。
该类使用一个静态变量 $container 来保存所有类实例,当再次调用该实例时直接返回,无需在实例化操作。
编辑 Container.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
namespace dispatcher;
class Box extends Dispatcher
{
/**
* 该类的子类都会重新实例化
*
*/
public function getInstance()
{
return self::newObject();
}
}
使用分发器
使用规则
一个类要使用分发器,必须继承 Container 或 Box 类。
类方法通过静态方式调用,同时::符号后面直接接的方法必须声明为 protected。
通过 register() 方法获取类实例。
一个简单的例子
use core\Application;
// error_reporting(0);
define('APP_PATH',dirname(__DIR__));
require APP_PATH.'/core/Autoload.php';
(Application::register())::dispatcher();
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 就代表哪个类,如果你换成self 就会报错,好了接着说,调用类,这里是 Application紧接着调用了 dispatcher 子类的getInstance 方法去获得了该类的实例,最后回调,输出 dispatcher 完。
关于延迟静态绑定
总结
本结主要介绍了分发器的概念,实现了一个简单的类管理机制,可以通过静态方式调用对象方法。分发器类 Container 和 Box 的区别在于是否保存子类实例
下期见
作者:entner