路由是一個框架中必不可少的組件,其做用是把 URL 按照預約規則解析到特定控制器中。php
咱們在這裏定義了兩種路由規則:linux
&
分隔。在配置文件使用 querystring
表示#控制器/方法?參數1=值1&參數2=值2 http://domain/user/info?name=php&chapter=10
/
分隔。配置中使用 restful
#控制器/方法/參數1/值1/參數2/值2 https://domain/user/info/name/php/chapter/100
在目錄 core
建立 Controller.php
,該類繼承 Container
瀏覽器
<?php namespace core; class Controller extends Container { }
主控制器能夠添加控制器公共方法,如頁面渲染 render()
,錯誤代碼等,全部控制器必須繼承主控制器。因爲主控制器繼承 Container
,所以,控制器也是分發器的子類,能夠經過 register()
獲取實例。服務器
控制器命名遵循大寫開頭的駝峯命名規則,而且默認添加後綴 Controller
,控制器文件命名和類命名同樣,如控制器類 UserController
,其文件命名爲 UserController.php
。restful
方法命名遵循小寫開頭的駝峯命名規則,而且默認添加請求方式
(如,get,post,put等)前綴,如 getIndex()
,postUpdate()
。app
以上例 UserController
爲例框架
<?php namespace controller; use core\Controller; class UserController extends Controller { /** * HTTP 請求方式爲 GET 時有效 * url 爲 /user/info * */ public function getInfo() { } /** * HTTP 請求方式爲 POST 時有效 * url 爲 /user/update * */ public function postUpdate() { } }
在 core
目錄下建立 Router.php
dom
$ cd tinyphp/core $ touch Router.php
在構造函數中定義變量函數
<?php namespace core; use dispatcher\Container; class Router extends Container { public $method; public $uri; public $path; public function __construct() { $this->method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $this->uri = $_SERVER['REQUEST_URI']; $this->path = $_SERVER['PATH_INFO']; } }
常見 $_SERVER
字段post
$_SERVER['PATH_INFO']
URL的路徑信息,如 /user/info$_SERVER['REQUEST_METHOD']
請求方法,如 POST,GET$_SERVER['REQUEST_URI']
完整 URL,如 /user/info?id=1&name=Lucy在 start()
方法中解析 URL
protected function start() { /** * 也能夠寫成 Config::get('default.route','querystring'); * */ $route = Config::get('default.route') ?? 'querystring'; //解析 controller 和 action $path = explode('/',trim($this->path,'/')); if (empty($path[0])) { $path[0] = Config::get('default.controller','index'); } $controller = ucfirst($path[0]).'Controller'; //獲取請求方法 $method = strtolower($this->method); $action = $method.ucfirst($path[1] ?? Config::get('default.action','index')); //獲取參數 $args = []; if (method_exists($this,$route)) { $args = call_user_func_array([$this,$route],[$this->uri]); } return ['controller'=>$controller,'action'=>$action,'args'=>$args]; }
querystring()
參數解析
private function querystring($url) { $urls = explode('?', $url); if (empty($urls[1])) { return []; } $param_arr = []; $param_tmp = explode('&', $urls[1]); if (empty($param_tmp)) { return []; } foreach ($param_tmp as $param) { if (strpos($param, '=')) { list($key,$value) = explode('=', $param); //變量名是否複合規則 if (preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $key)) { $param_arr[$key] = $value; } } } return $param_arr; }
querystring 的參數爲 ?
後面的部分,多個參數用 &
分隔。
restful()
參數解析
private function restful($url) { $path = explode('/', trim(explode('?', $url)[0], '/')); $params = []; $i = 2; while (1) { if (!isset($path[$i])) { break; } $params[$path[$i]] = $path[$i+1] ?? ''; $i = $i+2; } return $params; }
restful 的參數爲方法後面的路徑。
完整代碼以下:
<?php namespace core; use dispatcher\Container; class Router extends Container { public $method; public $uri; public $path; public function __construct() { $this->method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $this->uri = $_SERVER['REQUEST_URI']; $this->path = $_SERVER['PATH_INFO']; } protected function start() { $route = Config::get('default.route') ?? 'querystring'; //解析 controller 和 action $path = explode('/',trim($this->path,'/')); if (empty($path[0])) { $path[0] = Config::get('default.controller','index'); } $controller = ucfirst($path[0]).'Controller'; //獲取請求方法 $method = strtolower($this->method); $action = $method.ucfirst($path[1] ?? Config::get('default.action','index')); //獲取參數 $args = []; if (method_exists($this,$route)) { $args = call_user_func_array([$this,$route],[$this->uri]); } return ['controller'=>$controller,'action'=>$action,'args'=>$args]; } /** * 查詢字符串參數 * ?後,參數經過&&分隔 * */ private function querystring($url) { $urls = explode('?', $url); if (empty($urls[1])) { return []; } $param_arr = []; $param_tmp = explode('&', $urls[1]); if (empty($param_tmp)) { return []; } foreach ($param_tmp as $param) { if (strpos($param, '=')) { list($key,$value) = explode('=', $param); //變量名是否複合規則 if (preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $key)) { $param_arr[$key] = $value; } } } return $param_arr; } /** * 路徑參數 * 控制器/方法/參數1/值1/參數2/值2 * */ http://domain/user/info/name/entner?name=php&chapter=10 private function restful($url) { $path = explode('/', trim(explode('?', $url)[0], '/')); $params = []; $i = 2; while (1) { if (!isset($path[$i])) { break; } $params[$path[$i]] = $path[$i+1] ?? ''; $i = $i+2; } return $params; } }
路由調用方式爲
<?php $router = Rouer::start();
在配置文件 app/conf/config.php
中設置默認路由爲 querystring
,
<?php return [ 'default' => [ 'controller' => 'index', 'action' => 'index', 'route' => 'querystring',//還能夠設置爲 restful ], 'view' => [ 'dir' => 'layout', 'file' => 'base', ] ];
在 core/Application.php
文件中 run()
方法實現路由調用
<?php ... public function run() { $router = Router::start(); echo '<pre>'; print_r($router); } ...
啓動 PHP 內置服務器
$ cd tinyphp/public $ php -S localhost:8080
在瀏覽器中輸入 http://localhost:8080/course/document?name=php&&chapter=10
輸出結果爲
Array ( [controller] => CourseController [action] => getDocument [args] => Array ( [name] => php [chapter] => 10 ) )
同理能夠測試 restful
路由規則。
路由解析後,得到須要調用的控制器名,方法和參數。因爲控制器繼承分發器後,能夠經過 register()
獲取實例,編輯 core/Applicaiton.php
<?php ... public function run() { $router = Router::start(); //注意使用命名空間 $controller = "controller\\".$router['controller']; $action = $router['action']; $args = $router['args']; echo call_user_func_array([$controller::register(),$action],$args); } ...
經過這種方式能夠實現方法調用,可是沒法控制方法參數,好比,有時候咱們須要在方法參數中使用某個對象實例,術語稱爲依賴注入,即把須要使用的實例注入到方法中,那麼能夠經過PHP的高級特性反射來實現。