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

nodejs的koa能夠說是很是受歡迎的,特別是其「洋蔥模型」應該用過的人印象都比較深,下面就嘗試用php來實現一個。php

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

指望用法

先來看看我要怎麼用「這個框架」?node

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方法添加中間件,支持閉包或外部文件。git

$ctx支持注入所需的各類參數,方便各中間件共用。github

完整代碼

//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方法。app

_add對傳進來的參數做判斷,若是是字符串,表示外部加載,則去判斷文件和處理函數是否存在和有效,而後將處理函數加到中間件隊列。框架

這裏面若是use()傳遞空參數,表示忽略後面的中間件。

run方法

添加完中間件後,執行$app -> run()方法運行,來看看是怎麼執行的:koa

  1. 調用私有的_gen來生成一個生成器,該生成器能夠迭代返回隊列中的中間件處理函數。
  2. 調用next方法執行下一個中間件(這裏即第一個入口)

    2.1 調用生成器的current方法得到當前的處理函數函數

    2.2 執行該函數(傳遞$this做爲參數,即$ctx),並調用生成器的next方法後移到下一個處理函數ui

  3. 直到生成器沒有返回時結束。
中間件中需調用$ctx-> next()將控制權交到下一個中間件,從而迭代完全部的中間件。

__get和__set方法

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

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

That is all

相關文章
相關標籤/搜索