php框架 路由_PHP框架之简单的路由器

路由的功能就是分发请求到不同的控制器,基于的原理就是正则匹配。接下来呢,我们实现一个简单的路由器,实现的能力是对于静态的路由(没占位符的),正确调用callback。

对于有占位符的路由,正确调用callback时传入占位符参数,譬如对于路由:/user/{id},当请求为/user/23时,传入参数$args结构为[ 'id' => '23'

]

大致思路

我们需要把每个路由的信息管理起来:http方法($method),路由字符串($route),回调($callback),因此需要一个addRoute方法,另外提供短方法get,post(就是把$method写好)

对于/user/{id}这样的有占位符的路由字符串,把占位符要提取出来,然后占位符部分变成正则字符串

实现

Route.php类<?phpnamespace SalamanderRoute;class Route { /** @var string */

public $httpMethod; /** @var string */

public $regex; /** @var array */

public $variables; /** @var mixed */

public $handler; /**

* Constructs a route (value object).

*

* @param string $httpMethod

* @param mixed $handler

* @param string $regex

* @param array $variables

*/

public function __construct($httpMethod, $handler, $regex, $variables) { $this->httpMethod = $httpMethod; $this->handler = $handler;

$this->regex = $regex; $this->variables = $variables;

} /**

* Tests whether this route matches the given string.

*

* @param string $str

*

* @return bool

*/

public function matches($str) {

$regex = '~^' . $this->regex . '$~'; return (bool) preg_match($regex, $str);

}

}

Dispatcher.php<?php /**

* User: salamander

* Date: 2017/11/12

* Time: 13:43

*/namespace SalamanderRoute;class Dispatcher { /** @var mixed[][] */

protected $staticRoutes = []; /** @var Route[][] */

private $methodToRegexToRoutesMap = []; const NOT_FOUND = 0; const FOUND = 1; const METHOD_NOT_ALLOWED = 2; /**

* 提取占位符

* @param $route

* @return array

*/

private function parse($route) {

$regex = '~^(?:/[a-zA-Z0-9_]*|/\{([a-zA-Z0-9_]+?)\})+/?$~'; if(preg_match($regex, $route, $matches)) { // 去掉full match

array_shift($matches); return [

preg_replace('~{[a-zA-Z0-9_]+?}~', '([a-zA-Z0-9_]+)', $route),

$matches,

];

} throw new \LogicException('register route failed, pattern is illegal');

} /**

* 注册路由

* @param $httpMethod string | string[]

* @param $route

* @param $handler

*/

public function addRoute($httpMethod, $route, $handler) {

$routeData = $this->parse($route); foreach ((array) $httpMethod as $method) { if ($this->isStaticRoute($routeData)) { $this->addStaticRoute($httpMethod, $routeData, $handler);

} else { $this->addVariableRoute($httpMethod, $routeData, $handler);

}

}

} private function isStaticRoute($routeData) { return count($routeData[1]) === 0;

} private function addStaticRoute($httpMethod, $routeData, $handler) {

$routeStr = $routeData[0]; if (isset($this->staticRoutes[$httpMethod][$routeStr])) { throw new \LogicException(sprintf( 'Cannot register two routes matching "%s" for method "%s"',

$routeStr, $httpMethod

));

} if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { if ($route->matches($routeStr)) { throw new \LogicException(sprintf( 'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',

$routeStr, $route->regex, $httpMethod

));

}

}

} $this->staticRoutes[$httpMethod][$routeStr] = $handler;

} private function addVariableRoute($httpMethod, $routeData, $handler) { list($regex, $variables) = $routeData; if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { throw new \LogicException(sprintf( 'Cannot register two routes matching "%s" for method "%s"',

$regex, $httpMethod

));

} $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route(

$httpMethod, $handler, $regex, $variables

);

} public function get($route, $handler) { $this->addRoute('GET', $route, $handler);

} public function post($route, $handler) { $this->addRoute('POST', $route, $handler);

} public function put($route, $handler) { $this->addRoute('PUT', $route, $handler);

} public function delete($route, $handler) { $this->addRoute('DELETE', $route, $handler);

} public function patch($route, $handler) { $this->addRoute('PATCH', $route, $handler);

} public function head($route, $handler) { $this->addRoute('HEAD', $route, $handler);

} /**

* 分发

* @param $httpMethod

* @param $uri

*/

public function dispatch($httpMethod, $uri) {

$staticRoutes = array_keys($this->staticRoutes[$httpMethod]); foreach ($staticRoutes as $staticRoute) { if($staticRoute === $uri) { return [self::FOUND, $this->staticRoutes[$httpMethod][$staticRoute], []];

}

}

$routeLookup = [];

$index = 1;

$regexes = array_keys($this->methodToRegexToRoutesMap[$httpMethod]); foreach ($regexes as $regex) {

$routeLookup[$index] = [ $this->methodToRegexToRoutesMap[$httpMethod][$regex]->handler, $this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables,

];

$index += count($this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables);

}

$regexCombined = '~^(?:' . implode('|', $regexes) . ')$~'; if(!preg_match($regexCombined, $uri, $matches)) { return [self::NOT_FOUND];

} for ($i = 1; '' === $matches[$i]; ++$i); list($handler, $varNames) = $routeLookup[$i];

$vars = []; foreach ($varNames as $varName) {

$vars[$varName] = $matches[$i++];

} return [self::FOUND, $handler, $vars];

}

}

配置

nginx.conf重写到index.phplocation / { try_files $uri $uri/ /index.php$is_args$args; # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

# location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }

composer.json自动载入

{ "name": "salmander/route", "require": {}, "autoload": { "psr-4":

{ "SalamanderRoute\\": "SalamanderRoute/" }

}}

composer.json自动载入{ "name": "salmander/route", "require": {}, "autoload": { "psr-4":

{ "SalamanderRoute\\": "SalamanderRoute/" } }

最终使用

index.php<?phpinclude_once 'vendor/autoload.php';use SalamanderRoute\Dispatcher;

$dispatcher = new Dispatcher();

$dispatcher->get('/', function () { echo 'hello world';

});

$dispatcher->get('/user/{id}', function ($args) { echo "user {$args['id']} visit";

});// Fetch method and URI from somewhere$httpMethod = $_SERVER['REQUEST_METHOD'];

$uri = $_SERVER['REQUEST_URI'];// 去掉查询字符串if (false !== $pos = strpos($uri, '?')) {

$uri = substr($uri, 0, $pos);

}

$routeInfo = $dispatcher->dispatch($httpMethod, $uri);switch ($routeInfo[0]) { case Dispatcher::NOT_FOUND: echo '404 not found'; break; case Dispatcher::FOUND:

$handler = $routeInfo[1];

$vars = $routeInfo[2];

$handler($vars); break;

}

看了上面的这个案例大家应该对PHP实现简单路由器,有更清楚的认识吧,后期我们还会继续推出相关文章,大家有任何问题都可以踊跃发言,

相关推荐:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值