漫談php框架之中間件

市面上常見的php框架有不少,最近由於有技術需求,因此對常見的php框架的中間件進行了一些瞭解。各個框架儘管在目標上對php框架的定義大同小異,可是在實現方式上卻各有不一樣,且看下文:php

定義

首先什麼是php的中間件?nginx

根據zend-framework中的定義:git

所謂中間件是指提供在請求和響應之間的,可以截獲請求,並在其基礎上進行邏輯處理,與此同時可以完成請求的響應或傳遞到下一個中間件的代碼。github

這一介紹十分的簡潔,但卻略顯抽象,接下來咱們經過例子來一個個看。api

處在原始時代的CI

首先來看CI框架,php star數 12830.
做爲一款很是簡潔的框架,CI被吐槽的很多,可是也有不少人喜歡。首先來看它官方給出的一張請求時序圖:跨域

CI%u6846%u67B6%u8BF7%u6C42%u65F6%u5E8F
CI框架請求時序

根據上文中對中間件的定義,那麼對於CI框架來講,惟一稱得上是內置中間件的:Security模塊瀏覽器

Security模塊是在請求進入controller以前實現的邏輯:緩存

  • 請求在完成路由以後,進入controller以前;
  • CI框架支持經過配置的方式,決定是否啓用包括「URI安全、XSS過濾、CSRF保護」在內的功能模塊;
  • 一旦框架初始化時探測到模塊啓用,那麼優先進行模塊邏輯;
  • 觸發安全模塊,請求即了結止。

乍看起來,CI框架的中間件十分的侷限,可是其實它卻提供了無限的可能性。。由於CI中還提供了一個叫作Hooks的功能。即鉤子。php框架

下面來看兩個個hooks的例子:安全

定義一個在controller邏輯以前的鉤子,並指定鉤子的參數、類名或函數名信息:

$hook['pre_controller'] = array( 'class' => 'MyClass', 'function' => 'Myfunction', 'filename' => 'Myclass.php', 'filepath' => 'hooks', 'params' => array('beer', 'wine', 'snacks') );

定義一個在controller邏輯以後的鉤子,並直接給出其實現:

$hook['post_controller'] = function() { /* do something here */ };

爲何說CI沒提供什麼像樣的中間件可是又很靈活呢,就是由於它能夠在以下的多個階段進行掛鉤子的操做。細數過來有7種之多。

從後文中能夠看出,不少其餘的框架可能也就會涵蓋兩三種階段,所以,從這個角度上來講,CI的鉤子組合而成的中間件的確很靈活。

  • pre_system階段: 在系統執行的早期調用,這個時候只有 基準測試類 和 鉤子類 被加載了, 尚未執行到路由或其餘的流程;
  • pre_controller階段: 在你的控制器調用以前執行,全部的基礎類都已加載,路由和安全檢查也已經完成;
  • post_controller_constructor階段: 在你的控制器實例化以後當即執行,控制器的任何方法都還還沒有調用;
  • post_controller階段: 在你的控制器徹底運行結束時執行;
  • display_override階段: 覆蓋 _display() 方法,該方法用於在系統執行結束時向瀏覽器發送最終的頁面結果; 這可讓你有本身的顯示頁面的方法。注意你可能須要使用 $this->CI =& get_instance()方法來獲取 CI 超級對象,以及使用 $this->CI->output->get_output()方法來 獲取最終的顯示數據;
  • cache_override階段: 使用你本身的方法來替代 輸出類 中的 _display_cache() 方法,這讓你有本身的緩存顯示機制。
  • post_system 在最終的頁面發送到瀏覽器以後、在系統的最後期被調用。

總結來看,CI中的中間件:

  • 有很大的自由度
  • 同時支持在多個階段對請求進行嵌入(對比下來是最全面的)
  • 鉤子函數的使用成本高;
  • 支持各類diy:
    • 請求來時http校驗、權限校驗、額外的安全策略
    • 請求去時上報數據

大紅大紫的Laravel

github star 24997
做爲最近兩年大紅大紫的Laravel,的確也是有必要對其中間件機制進行了解:

首先Laravel提供了一個很好的中間件自動生成工具:
php artisan make:middleware OldMiddleware
由Laravel的命令行完成,這種看似簡單的命令行工具其實能夠對框架的擴展起到很是重要的做用。

再來看一個Laravel中典型的請求過濾器:

<?php namespace App\Http\Middleware; use Closure; class OldMiddleware { /** * 運行請求過濾器。 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->input('age') <= 200) { return redirect('home'); } return $next($request); } }

過濾器,filter,是中間件中使用最普遍的一種,不少框架裏甚至filter就等同於中間件。意如其名,便是對請求Request進行某種過濾,這個過濾能夠是參數上的限制、安全策略的限制、http協議的限制,只要是請求中帶來的屬性,均可以據此進行過濾。

同時這裏也能夠看到,Laravel使用閉包的方式進行請求的傳遞,真正踐行的優雅的中間件串聯的方式,只須要調用next函數,請求便可被按照預先定義的規則傳遞到下一個中間件中。

Laravel支持全局的中間件和根據具體路由規定的中間件兩種,同時優先級又以定義順序爲準。作到全局與具體狀況的兼顧。同時它顯示的支持前置、後置和Terminable三種中間件,覆蓋了大部分的中間件場景,是一種相對不錯的設計。

但美中不足或者說場景覆蓋不夠友好的地方在於它以路由的方式組織中間件,會與controller有些脫節,每次定義controller中action行爲的時候,還須要轉換爲路由進行配置,略有些不方便。

總結來看

  • Laravel踐行了讓controller更純粹的思想,中間件交給路由,controller只作它該作的事;
  • 中間件與路由組靈活結合,可以知足應用場景;
  • 前置、後置與Terminable支持了現有大部分的中間件需求;
  • 自動生成十分方便擴展中間件,開發友好;
  • 但對一個controller內多個action須要統一加入或統一不加入中間件的場景,支持不友好。

老生常談yii 2.0

github star 4668

yii框架首先是中國人開發的,star數雖然不是不少,可是功能也算豐富。
yii框架從1.1到2.0,通過了一個比較大的升級,支持了不少新的特性,若是不支持,只怕是要落伍了。

在yii框架1.1中,中間件乾脆就叫filters了,十分的直白,分爲pre-filter和post-fiter兩種,即前文中說的,在進入controller以前的過濾邏輯,和完成controller處理以後的過濾邏輯。

可是到了yii2.0以後,filters通過了一層升級,到了behaviors,明確了一點:重心放在了每個controller的行爲上,而不是像Laravel同樣controller很傻很單純。

yii框架的behaviors能夠在controller或application中配置。

這裏是一個訪問控制的filter,具體進行什麼樣的訪問控制由className定義,同時對controller中的action支持「only」關鍵字,還有「」關鍵字,可以支持排除法的功能,這個在一些場景下仍是頗有用的。同時「roles」也可以支持你預先定義好的角色的概念,好比學生沒法訪問教師後臺,而教師沒法訪問學生論壇等。

use yii\filters\AccessControl; public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'only' => ['create', 'update'], 'rules' => [ // allow authenticated users [ 'allow' => true, 'roles' => ['@'], ], // everything else is denied by default ], ], ]; }

固然,在Yii中你也能夠自定義filter:

namespace app\components; use Yii; use yii\base\ActionFilter; class ActionTimeFilter extends ActionFilter { private $_startTime; public function beforeAction($action) { $this->_startTime = microtime(true); return parent::beforeAction($action); } public function afterAction($action, $result) { $time = microtime(true) - $this->_startTime; Yii::trace("Action '{$action->uniqueId}' spent $time second."); return parent::afterAction($action, $result); } }

這裏明顯能夠看出,這個filter針對action,分別在「beforeAction」和「afterAction」兩個階段進行了邏輯處理,完成了請求的計時工做。

因此總的來看,Yii框架中的中間件:

  • 支持前置和後置兩個階段的自定義;
  • 提供了基本的訪問控制中間件;
  • 配置侵入到controller中,完成對controller行爲的深度控制;
  • 沒法自動生成中間件,自定義成本略高。

你們夥 ZendFramework

ZendFramework是由zend公司推出的php框架,其目標就是創建一套大而全的php框架。以知足企業應用開發的目標。
ZendFramework由不少不一樣的模塊構成,使用者能夠經過相互組合的方式來實現本身想要的功能,同時也可以不一次加載大而全的框架,十分的靈活。
好比有負責受權的"zend-authentication",或者是負責驗證碼的"zend-captcha"等等。

其中"zend-stratigility" 負責提供中間件以及中間件執行流的功能。

use Zend\Stratigility\MiddlewarePipe; use Zend\Diactoros\Server; require __DIR__ . '/../vendor/autoload.php'; $app = new MiddlewarePipe(); $server = Server::createServer($app, $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); // Landing page $app->pipe('/', function ($req, $res, $next) { if (! in_array($req->getUri()->getPath(), ['/', ''], true)) { return $next($req, $res); } return $res->end('Hello world!'); }); // Another page $app->pipe('/foo', function ($req, $res, $next) { return $res->end('FOO!'); }); $server->listen();

這裏的代碼給出了兩個中間件的例子。第一個是落地頁,監聽了root路徑,若是命中了這一路由規則,那麼請求會被提早結束,返回給用戶「Hello world!」。
而第二個中間件去匹配foo這一路徑,模糊匹配的方式,若是命中了,會返回FOO並結束請求。

與Laravel相似,這裏一樣支持使用next(可調用的變量)的方式將請求繼續向下傳遞。而這裏中間件配置的方式也跟Laravel比較像,是統一在一個地方根據路由進行配置的,這樣徹底能夠按照以下的方式根據不一樣的路由定義不一樣的中間件處理邏輯:

$app->pipe('/api', $apiMiddleware); $app->pipe('/docs', $apiDocMiddleware); $app->pipe('/files', $filesMiddleware);

總結來看,ZendFramework的中間件:

  • 主要側重在請求前置階段,淡化了請求後置或其餘階段
  • 經過路由的方式統一配置中間件,支持串行
  • 並未預先定義中間件

我心目中的中間件設計

首先按照不一樣的類別列舉一下常見的中間件:

  • 前置中間件:
    • cookie驗證:驗證用戶的cookie
    • 用戶角色驗證:定義不一樣的用戶角色並驗證
    • 用戶權限驗證:配置不一樣的用戶權限,並驗證
    • 安全相關,如CSRF校驗:CSRF校驗中間件
    • http方法過濾:過濾特定的GET POST請求
    • http或者page cache:對指定路徑的頁面進行緩存
    • 跨域中間件:不用在nginx配置,而是經過框架的方式,針對某些域名或某些請求,提供跨域的服務。
  • 後置中間件:
    • 共同數據輸出:針對統一業務的公共數據,在後置中統一輸出
  • 請求返回瀏覽器以後的中間件:
    • 打印日誌
    • 更新session(Laravel)

因此一個php框架最好可以:

  • 定義核心可用中間件;
  • 提供在不一樣階段擴展中間件的能力,不能太多,支持前置和後置便可覆蓋大部分場景;
  • 統一配置中間件,方便管理全部的中間件,讓controller單純一些;
  • 提供中間件自動生成與方便擴展功能。
相關文章
相關標籤/搜索