模仿KOA,用php來寫一個極簡的開發框架

tim1020發表於2019-02-16

nodejs的koa可以說是非常受歡迎的,特別是其“洋蔥模型”應該用過的人印象都比較深,下面就嘗試用php來實現一個。

注:本文是PHPec框架的最原始思路版本。PHPec是在此基礎上完善編寫出來的一個極簡的輕量級開發框架,除了提供中介軟體呼叫模式外,同時提供了常見的自動路由功能,目前 已在github上釋出了最初版本。歡迎感興趣的去了解和提出建議,也歡迎star. 地址:https://github.com/tim1020/PHPec

期望用法

先來看看我要怎麼用“這個框架”?

require `app.php`;
$app = new App();

$app -> use(function($ctx){
    $ctx -> body.= `>m1`;
    $ctx -> next();
    $ctx -> body .= `>m1 end`;
});

$app -> use(`Middleware2`);

$app -> run();

基本上跟koa類似,先new一個app物件,使用use方法新增中介軟體,支援閉包或外部檔案。

$ctx支援注入所需的各種引數,方便各中介軟體共用。

完整程式碼

//app.php
class App{
    private $m = array();
    private $ctx = array();
    function next(){
        $f = $this -> c -> current();
        if(!$f) return;
        $this -> c -> next();
        $f($this);
    }
    function run(){
        $this -> c = $this -> _gen();
        $this -> next();
    }
    private function _gen(){
        foreach($this -> m as $v){
            yield $v;
        }
    }
    private function _add($m){
        if(!empty($this->m) && $this -> m[count($this->m) -1] === false) return;
        if(!$m){
            $this -> m[] = false;
        }elseif(($m instanceof Closure)){
            $this -> m[] = $m;
        }else{
            $m = $this -> _load($m);
            if(!function_exists($m)){
               throw new Exception(`middleware error`);
            }
            else $this -> m[] = $m;
        }
    }
    //處理檔案載入,返回執行函式(如需要,可加入名稱空間處理)
    private function _load($m){
        $f = `./middleware/`.$m.".php";
        if(!file_exists($f)) throw new Exception(`middleware error`);
        require $f;
        return $m;
    }
    function __call($m,$v){
        if(`use` == $m){
            $p = isset($v[0]) ? $v[0] : ``;
            $this -> _add($p);
        }else{
            throw new Exception(`method not exists`);
        }
    }
    function __set($k,$v){
        $this -> ctx[$k] = $v;
    }
    function __get($k){
        return isset($this -> ctx[$k]) ? $this -> ctx[$k] : NULL;
    }
}

沒錯,這就是全部的程式碼。

程式碼講解

use方法

use可以加入閉包或外部檔案,且php5不支援use作為方法名,這裡用__call來實現過載,當呼叫use時由__call來呼叫私有的_add方法。

_add對傳進來的引數作判斷,如果是字串,表示外部載入,則去判斷檔案和處理函式是否存在和有效,然後將處理函式加到中介軟體佇列。

這裡面如果use()傳遞空引數,表示忽略後面的中介軟體。

run方法

新增完中介軟體後,執行$app -> run()方法執行,來看看是怎麼執行的:

  1. 呼叫私有的_gen來生成一個生成器,該生成器可以迭代返回佇列中的中介軟體處理函式。
  2. 呼叫next方法執行下一個中介軟體(這裡即第一個入口)

    2.1 呼叫生成器的current方法獲得當前的處理函式

    2.2 執行該函式(傳遞$this作為引數,即$ctx),並呼叫生成器的next方法後移到下一個處理函式

  3. 直到生成器沒有返回時結束。

中介軟體中需呼叫$ctx-> next()將控制權交到下一個中介軟體,從而迭代完所有的中介軟體。

__get和__set方法

提供了__get和__set方法,是方便在中介軟體中使用$ctx直接設定和訪問未經定義的值。如:

$ctx -> body = `hello`;
$ctx -> tplName = `a.tpl`;

That is all

相關文章