一個PHP的微型路由控制器

以前使用dispatch, 可是從4.0到如今的8.0 API變更比較大,特別是在最近兩次大的版本的升級,爲了保持代碼簡潔丟失了向下兼容的特性。javascript

感受做者的的核心思想不是很堅決。因此生出了本身造輪子的衝動。php

router.lua

這個是一個微型的能夠在openresty裏面運行的路由控制器,曾經幫做者作了一次重大改版,如今仍是這個項目第二貢獻者。java

其中的思想是很值得借鑑的:jquery

  1. 使用樹形結構來保存url和handler的映射關係。(按照樹形結構查找保證了查找回調函數的效率Olog(n),而傳統的以正則表達式作key映射handler方式,查找回調函數時間不穩定,最壞狀況須要執行一遍全部的正則表達式)git

  2. 將reqest method定義成是match函數的一個封裝形式。便於提供方便的形式來映射路由。github

Router

鑑於以上兩個很是好的特性,因此就把這個lua的庫在PHP下面重寫了一遍。同時在寫的過程當中加入了一些新的特性:正則表達式

  1. 增長error這個API,一個API提供兩種調用方式(這個借鑑了dispatch裏面的一些特性,有點像jquery的某些方法),能夠兼具定義error handler和觸發error handler的做用。json

  2. 增長hook API,一樣有兩種調用方式。數組

  3. 默認觸發「before」和「after」兩個hook。分別在執行真正的handler先後。app

  4. 在「before」這個hook後面執行用戶自定義的hook,這些hook是在定義回調函數的時候一塊兒給定當前url須要調用的hook列表。(固然這些hook所有都要用戶本身定義回調函數)

安裝

這個微型的路由控制器已經提交到packagist網站,能夠經過composer工具安裝

composer require lloydzhou/router

此處附README裏面的一個例子:

(new Router())
/* 定義錯誤處理函數 */
->error(401, function($message){
    header('Location: /login', true, 302);
    die($message);
})
->error(405, function($message){
    header('Location: /hello/world', true, 302);
})
->error(406, function($message){
    die($message);
})
/* 定義hook函數,除了內置默認調用的before和after,還定義了檢查登陸的auth */
->hook('auth', function($params){
    if ('lloyd' == $params['name'])
    return $params;
    $params['router']->error(401, 'Forbiden');
})
/* 定義after這個鉤子函數,支持json或者jsonp格式輸出 */
->hook('after', function($result, $router){
    if ($result) {
    header('Content-type: application/'. ($_GET['jsoncallback']?'javascript':'json'));
    if ($_GET['jsoncallback'])
        print $_GET['jsoncallback']. '('. json_encode($result). ')';
    else print json_encode($result);
    }
})
->hook('before', function($params){
    //$params['name'] = 'lloydzhou';
    return $params;
})
/* 定義url映射 */
->get('/', function(){
    echo "Hello world !!!";
})
->get('/hello/:name', function($name){
    echo "Hello $name !!!";
})
->get('/hello/:name/again', function($name){
    echo "Hello $name again !!!";
}, 'auth')
->get('/hello/:name.:ext', function($name, $ext){
    if ('js' == $ext || 'json' == $ext) return array('name'=>$name);
    return array('code'=>1, 'msg'=>'error message...');
}, 'auth')
/* 程序入口,以當前的url查找對應的處理函數,並獲取變量執行該函數 */
->execute();

啓動服務

php -S 0.0.0.0:8888 test.php

測試

curl -vvv 127.0.0.1:8888/hello/
url未能映射成功,觸發405錯誤處理函數, 自動跳轉向 URL: "/hello/world"

curl -vvv 127.0.0.1:8888/hello/lloyd 
返回 "Hello lloyd !!!"

curl -vvv 127.0.0.1:8888/hello/lloyd/again 
返回 "Hello lloyd again !!!"

curl -vvv 127.0.0.1:8888/hello/world/again 
在鉤子函數auth處理失敗觸發401錯誤處理函數, 自動跳轉到 URL: "/login"

curl -vvv 127.0.0.1:8888/hello/lloyd.json 
支持「/」和「.」做爲pathinfo的分隔符,而且和after鉤子函數配合,返回json格式文本 {"name": "lloyd"}

curl -vvv 127.0.0.1:8888/hello/lloyd.js?jsoncallback=test
返回jsonp格式文本 test({"name": "lloyd"})

curl -vvv 127.0.0.1:8888/hello/lloyd.jsx?jsoncallback=test
最後的後綴名不匹配,輸出錯誤jsonp格式的消息 test({"code":1,"msg":"error message..."})

編譯

開發環境使用CRouter替代Router能夠自動檢測文件修改時間,而且編譯成原生數組保存至router.inc.php

(new CRouter('router.inc.php', true))

發佈的時候只須要把路由映射的部分替換成如下兩行代碼便可跳過建立路由映射表的階段。以節省時間!!!

$router = include('router.inc.php');
$router->execute();

性能

  1. 使用樹形結構來保存url和handler的映射關係。查找URL映射函數的時候保證了查找回調函數的效率O(log n)。

  2. 而傳統的以正則表達式作key映射handler方式,查找回調函數時間不穩定,最壞狀況須要執行一遍全部的正則表達式。

  3. 支持編譯,將映射好的樹形路由映射數組直接編譯成源碼。不須要每次PHP請求的時候從新切割pathinfo再來生成樹形節點。以節省時間!

DEMO

爲了一邊測試,一邊完善這個庫。因此使用這個庫結合另一個ActiveRecordMicroTpl 寫了一個簡單的博客,裏面基本涵蓋了這幾個庫的API。

相關文章
相關標籤/搜索