丑话说在前面,这一章难点,但也算是框架的核心了,大家静下心学吧。
分发器
看一个概念分发器
,英文叫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();
获取执行类的实例,需要在 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();
}
该函数获取子类实例。
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();
}
}
使用分发器
使用规则
- 一个类要使用分发器,必须继承
Container
或Box
类。 - 类方法通过静态方式调用,同时
::
符号后面直接接的方法必须声明为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
总结
本结主要介绍了分发器的概念,实现了一个简单的类管理机制,可以通过静态方式调用对象方法。分发器类 Container
和 Box
的区别在于是否保存子类实例
下期见
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。