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()方法執行,來看看是怎麼執行的:
- 呼叫私有的_gen來生成一個生成器,該生成器可以迭代返回佇列中的中介軟體處理函式。
- 呼叫next方法執行下一個中介軟體(這裡即第一個入口)
2.1 呼叫生成器的current方法獲得當前的處理函式
2.2 執行該函式(傳遞$this作為引數,即$ctx),並呼叫生成器的next方法後移到下一個處理函式
- 直到生成器沒有返回時結束。
中介軟體中需呼叫$ctx-> next()將控制權交到下一個中介軟體,從而迭代完所有的中介軟體。
__get和__set方法
提供了__get和__set方法,是方便在中介軟體中使用$ctx直接設定和訪問未經定義的值。如:
$ctx -> body = `hello`;
$ctx -> tplName = `a.tpl`;