路由是一个框架中必不可少的组件,其做用是把 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的高级特性反射来实现。