連接
https://github.com/nikic/Fast...
這個庫提供了基於正則表達式的快速路由實現。這篇文章解釋了 FastRoute 是如何工做的和它爲何很快。php
經過 composer 安裝html
composer require nikic/fast-route
要求 PHP 5.4 及更高的版本git
這是一個基本的使用示例github
<?php require '/path/to/vendor/autoload.php'; $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/users', 'get_all_users_handler'); // {id} 必須是一個數字 (\d+) $r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler'); // /{title} 後綴是可選的 $r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler'); }); // 獲取請求的方法和 URI $httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; // 去除查詢字符串( ? 後面的內容) 和 解碼 URI if (false !== $pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } $uri = rawurldecode($uri); $routeInfo = $dispatcher->dispatch($httpMethod, $uri); switch ($routeInfo[0]) { case FastRoute\Dispatcher::NOT_FOUND: // ... 404 Not Found 沒找到對應的方法 break; case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: $allowedMethods = $routeInfo[1]; // ... 405 Method Not Allowed 方法不容許 break; case FastRoute\Dispatcher::FOUND: // 找到對應的方法 $handler = $routeInfo[1]; // 得到處理函數 $vars = $routeInfo[2]; // 獲取請求參數 // ... call $handler with $vars // 調用處理函數 break; }
經過調用 FastRoute\simpleDispatcher()
函數來定義路由,該函數接受一個以 FastRoute\RouteCollector
實例爲參數的閉包做爲參數。經過在 collector 實例裏面調用 addRoute()
增長路由。web
$r->addRoute($method, $routePattern, $handler);
$method
是大寫的 HTTP 方法,可以被某個路由匹配,能夠使用數組指定多個有效的 $method 。正則表達式
// 這裏兩行調用 $r->addRoute('GET', '/test', 'handler'); $r->addRoute('POST', '/test', 'handler'); // 等同於這一行調用 $r->addRoute(['GET', 'POST'], '/test', 'handler');
默認狀況下 $routePattern
使用一種語法,好比 {foo}
是指定名稱爲 foo
的佔位符,能夠匹配正則表達式 [^/]+.
。要調整佔位符匹配的模式,能夠經過編寫 {bar:[0-9] +}
來指定自定義模式。一些例子express
// 匹配 /user/42,不匹配 /user/xyx $r->addRoute('GET', '/user/{id:\d+}', 'handler'); // 匹配 /user/foobar,不匹配 /user/foo/bar $r->addRoute('GET', '/user/{name}', 'handler'); // 匹配 /user/foobar,也匹配 /user/foo/bar $r->addRoute('GET', '/user/{name:.+}', 'handler');
路由佔位符的自定義模式不能使用捕獲組,例如 {lang:(en|de)} 不是有效的佔位符,由於 ()
是一個捕獲組,能夠使用 {lang:en|de}
或者 {lang:(?:en|de)}
代替。數組
另外,在路由 [...]
中定義的部分是可選匹配的,因此 /foo[bar]
將匹配 /foo
和 /foobar
。路由可選部分只支持在定義的末尾,而不能在定義的中間。緩存
// 這個路由有,[/{name}] 可選擇匹配部分 $r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler'); // 等同於這兩個路由 $r->addRoute('GET', '/user/{id:\d+}', 'handler'); $r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler'); // 多層嵌套可選路由,也是支持的 $r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler'); // 這個路由定義無效,由於可選部分只能在定義的末尾 $r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler');
$handler
參數不必定必須是回調函數,它也能夠是控制器類名或任何其餘類型的數據。FastRoute 只告訴你哪一個 handler 對應 URI,如何解釋它取決於你。服務器
對於 GET、POST、PUT、PATCH、DELETE 和 HEAD 請求方法,可以使用快捷方式。
$r->get('/get-route', 'get_handler'); $r->post('/post-route', 'post_handler'); // 等同於 $r->addRoute('GET', '/get-route', 'get_handler'); $r->addRoute('POST', '/post-route', 'post_handler');
你能夠在一個組內定義路由,同一組內的路由有相同的前綴。
$r->addGroup('/admin', function (RouteCollector $r) { $r->addRoute('GET', '/do-something', 'handler'); $r->addRoute('GET', '/do-another-thing', 'handler'); $r->addRoute('GET', '/do-something-else', 'handler'); }); // 等同於 $r->addRoute('GET', '/admin/do-something', 'handler'); $r->addRoute('GET', '/admin/do-another-thing', 'handler'); $r->addRoute('GET', '/admin/do-something-else', 'handler');
能夠定義多層嵌套組結構。
使用 simpleDispatcher
定義路由的回調函數能夠無縫緩存。經過使用 cachedDispatcher
而不是 simpleDispatcher
,能夠緩存生成的路由數據並從緩存的信息構建調度。
<?php $dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); $r->addRoute('GET', '/user/{name}', 'handler2'); }, [ 'cacheFile' => __DIR__ . '/route.cache', /* required 緩存文件路徑,必須設置 */ 'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default 是否緩存,可選參數,默認狀況下開啓 */ ]);
該函數的第二個參數是一個選項數組,可用於指定緩存文件路徑等等。
經過調用 dispatch()
調度 URI。這個方法接受 HTTP 方法 和一個 URI 做爲參數。得到這兩個信息是你本身的工做,這個庫並不綁定到 PHP web SAPIs 。dispatch()
返回一個數組,第一個元素是一個狀態碼,狀態碼是 Dispatcher::NOT_FOUND
、Dispatcher::METHOD_NOT_ALLOWED
、Dispatcher::FOUND
其中之一。對於 Dispatcher::METHOD_NOT_ALLOWED
狀態,第二個數組元素包含容許提供的 URI 的 HTTP 方法列表。
[FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']]
對於 Dispatcher::FOUND
狀態,第二個數組元素是 $handler ,第三個數組元素是是一個包含全部佔位符的數組
/* Routing against GET /user/nikic/42 */ [FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']]
這個庫使用三個組件,一個路由解析器,一個數據生成器,一個調度器。這個三個組件實現如下接口
<?php namespace FastRoute; interface RouteParser { public function parse($route); } interface DataGenerator { public function addRoute($httpMethod, $routeData, $handler); public function getData(); } interface Dispatcher { const NOT_FOUND = 0, FOUND = 1, METHOD_NOT_ALLOWED = 2; public function dispatch($httpMethod, $uri); }
路由解析器獲取路由模式字符串並將其轉換爲路由信息數組,其中每一個路線信息又是它的部分數組。
/* The route /user/{id:\d+}[/{name}] converts to the following array: */ [ [ '/user/', ['id', '\d+'], ], [ '/user/', ['id', '\d+'], '/', ['name', '[^/]+'], ], ]
而後能夠將該數組傳遞給數據生成器的 addRoute()
方法,在添加了全部路由以後,調用生成器的 getData()
,它將返回調度器所需的全部路由數據。
調度程序經過構造函數接受路由數據,並提供 dispatch()
方法。
路由解析器能夠被單獨覆蓋,然而數據生成器和調度器應該老是一塊兒修改,由於前者的輸出與後者的輸入緊密耦合。
當使用 simpleDispatcher / cachedDispatcher
時,能夠經過傳入額外的參數,進行覆蓋
<?php $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { /* ... */ }, [ 'routeParser' => 'FastRoute\\RouteParser\\Std', 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', ]);
上面給出了默認的設置,經過把 GroupCountBased
替換成 GroupPosBased
能夠使用徹底不一樣的調度策略
HTTP 規範要求服務器 同時支持 GET 和 HEAD 方法
GET和HEAD方法必須獲得全部通用服務器的支持
爲避免強制用戶爲每一個資源手動註冊 HEAD 路由,將使用一個匹配的 GET 路由響應請求。PHP web SAPI 透明地從 HEAD 響應中移除實體主體,因此這種行爲對絕大多數用戶沒有影響。
可是,在 Web SAPI 環境外部使用 FastRoute ,毫不能發送響應 HEAD 請求而生成的實體主體,若是你是非 SAPI 用戶,這是你的責任;在這種狀況下,FastRoute 無權限制你破壞 HTTP 。
最後,請注意,應用程序能夠始終爲給定資源指定其本身的 HEAD 方法路由以徹底繞過此行爲。
文檔仍是很好理解,下次就要看源碼了。
原創文章,歡迎轉載。轉載請註明出處,謝謝。
原文連接地址: http://dryyun.com/2018/04/20/...
做者: dryyun 發表日期: 2018-04-21 10:14:21