• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

PHP框架之简单的路由器

php 搞代码 3年前 (2022-01-22) 11次浏览 已收录 0个评论

路由的功能就是分发请求到不同的控制器,基于的原理就是正则匹配。接下来呢,我们实现一个简单的路由器,实现的能力是对于静态的路由(没占位符的),正确调用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 {    /<a style="color:transparent">本@文来源gao($daima.com搞@代@#码(网5</a><strong>搞gaodaima代码</strong>** @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.php

location / {        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实现简单路由器,有更清楚的认识吧,后期我们还会继续推出相关文章,大家有任何问题都可以踊跃发言,

相关推荐:

JS实现简单路由器功能的方法

怎样修改无线路由器密码 MySQL修改密码方法总结

php yaf框架中路由器问题

以上就是PHP框架之简单的路由器的详细内容,更多请关注搞代码gaodaima其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:PHP框架之简单的路由器

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址