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能夠加入閉包或外部文件,且php5不支持use做爲方法名,這裏用__call來實現重載,當調用use時由__call來調用私有的_add方法。app
_add對傳進來的參數做判斷,若是是字符串,表示外部加載,則去判斷文件和處理函數是否存在和有效,而後將處理函數加到中間件隊列。框架
這裏面若是use()傳遞空參數,表示忽略後面的中間件。
添加完中間件後,執行$app -> run()方法運行,來看看是怎麼執行的:koa
2.1 調用生成器的current方法得到當前的處理函數函數
2.2 執行該函數(傳遞$this做爲參數,即$ctx),並調用生成器的next方法後移到下一個處理函數ui
中間件中需調用$ctx-> next()將控制權交到下一個中間件,從而迭代完全部的中間件。
提供了__get和__set方法,是方便在中間件中使用$ctx直接設置和訪問未經定義的值。如:
$ctx -> body = 'hello'; $ctx -> tplName = 'a.tpl';