PHP手寫MVC(三) —— 分發器

醜話說在前面,這一章難點,但也算是框架的核心了,你們靜下心學吧。

分發器

看一個概念分發器,英文叫dispather,它的主要做用是管理全部類的實例化操做。在程序中,有時須要大量建立對象實例,有時須要使用相同的實例,所以若是重複建立實例會額外給程序帶來開銷和管理的負擔。因此咱們使用了分發器的概念,用於專門管理全部類的實例化操做。以前,咱們都是經過 new 關鍵字建立對象,如入口文件的 new Application(),以及 Application 類的構造器中 new Bootstrap()。若是有分發器就簡單多了。php

core 目錄下建立目錄 dispatcher,並建立文件 Dispatcher.php,Container.php,Box.phphtml

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

Dispatcher 是一個抽象類,定義了一個抽象方法 getInstance(),用於獲取子類實例。子類 Container 和 Box 分別繼承 Dispatcher 並實現 getInstance() 方法,不一樣點在於,容器中該方法須要存放子類實例,盒子中直接進行實例化操做。linux

  • 容器 Container。容器中的實例能夠重複使用
  • 盒子 Box。盒子中的實例不能重複使用,每次會建立新的實例

容器就是存東西的變量 在這裏咱們存放全部調用類的實例,固然也能夠存服務,接口取決於你 everything框架

Dispatcher 類

Dispatcher 類主要實現:函數

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

注意抽象方法必須使用關鍵字 abstract 聲明,而且不能包含函數體(中括號包括的部分)ui

  • 接收全部靜態方式請求__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')spa

1,第一行 static::getInstance(); 執行子類的 getInstance() 方法,所以須要在 ContainerBox 中分別實現該方法。code

2,經過 call_user_func_array() 調用自身存在的方法,該方法須要聲明爲 protectedhtm

  • 使用延遲靜態綁定實例化子類
public static function newObject()
{
    return new Static;
}

該函數的做用是實例化操做,在子類 ContainerBox 中調用,new Static 返回建立具體執行操做的該子類實例,如 Config::get() 那麼返回 Config 類的實例,Router::start() 返回 Router 類的實例。對象

關於延遲靜態綁定 很重要 我下面會繼續講 請耐心看

  • 定義獲取類實例函數 register()
public static function register()
{
    return static::getInstance();
}

該函數獲取子類實例。

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 就表明哪一個類,若是你換成self 就會報錯,好了接着說,調用類,這裏是 Application緊接着調用了 dispatcher 子類的getInstance 方法去得到了該類的實例,最後回調,輸出 dispatcher 完。

關於延遲靜態綁定

沒搞懂的,看看這篇文章吧 http://www.javashuo.com/article/p-tfvtgghu-ht.html

總結

本結主要介紹了分發器的概念,實現了一個簡單的類管理機制,能夠經過靜態方式調用對象方法。分發器類 ContainerBox 的區別在因而否保存子類實例

下期見

相關文章
相關標籤/搜索