基于 Thinkphp 6 实现禅道路由调度

一切的不能突破阈值,都是因为轮子造得不够。所以最好的情况大致是这样的 - 工作中出于成本和进度的考虑要,适量少造轮子,但是业余一定要多造轮子。不造出自己的轮子,永远在表层游走,没法击穿。

个人当前阶段,最感兴趣是高效的研发管理体系和工具打造,所以研究了市面上众多产品后,还是会选择以禅道的功能体系为蓝本进行学习。因此标题所表达的内容不属于研究的主题范围,纯粹因为个人在企业级应用开发领域所涉及的一些问题的额外探索。

禅道中的伪静态路由格式是这样的:

/product-browse-27.html

/product-browse-27-0-allstory-0-story.html

即除了控制器和动作,后面的参数部分,是不带参数名的,框架需要将这些参数按顺序赋值给参数。看起来会比较简洁,有一定的吸引力。因此针对这一项特性解构出来独立实现。

PHP这个玩意虽然一直在快速发展,但是似乎关注的人和应用的空间越来越少。支持国产,因此尝试在 ThinkPHP 上来研究这个问题。

tp的路由机制当前有了比较大的灵活度,由于这项特性需要使用反射机制读取方法的参数定义,因此使用 路由到调度对象(V6.0.3+)这些特性。

// 路由到自定义调度对象
Route::get('blog/:id',\app\route\BlogDispatch::class);

路由调度类 app\route\ZentaoDispatch

<?php

namespace app\route;

use ReflectionMethod;
use think\facade\Route;

class ZentaoDispatch extends \think\route\dispatch\Url
{

    const CTRL_KEY = '_control';
    const ACT_KEY = '_action';
    const PARAM_KEY = '_params';
    const SEPR_KEY = '_sepr';

    const SEPR = '-';

    public static function rule()
    {
        $rule = vsprintf("<%s>-<%s><%s?><%s?>", [
            self::CTRL_KEY,
            self::ACT_KEY,
            self::SEPR_KEY,
            self::PARAM_KEY
        ]);

        return $rule;
    }

    public static function addRouteRule()
    {
        Route::rule(ZentaoDispatch::rule(), ZentaoDispatch::class)->pattern([
                self::SEPR_KEY => self::SEPR,
                self::PARAM_KEY => '[\w|-]+',
            ])->ext('html|json');
    }

    /**
     * 解析URL地址,看该方法的主要目的是解析出控制器和动作 将第一截参数的-换为/应该就可以
     * @access protected
     * @param  string $url URL app\route\ZentaoDispatch 没有意义了
     * @return array
     */
    protected function parseUrl(string $url): array
    {
        $pathinfo = $this->buildPathinfo();

        $this->request->setPathinfo($pathinfo);

        $route = parent::parseUrl($pathinfo);

        return $route;
    }

    protected function buildPathinfo()
    {
        $pathinfo_depr = $this->rule->config('pathinfo_depr');

        $vars = $this->rule->getVars();
        $paths = [
            $vars[self::CTRL_KEY] => $vars[self::ACT_KEY],
            self::SEPR_KEY => $vars[self::SEPR_KEY],
            self::PARAM_KEY => $vars[self::PARAM_KEY],
        ];

        $pathinfo = '';
        foreach ($paths as $key => $path) {
            $pathinfo .= $pathinfo_depr . $key . $pathinfo_depr . $path;
        }
        $pathinfo = trim($pathinfo, '/');

        return $pathinfo;
    }

    /**
     * 似乎这里是最佳时机,不必重复初始化控制器对象造成性能损失
     *
     * @param string $name
     * @return mixed|object
     */
    public function controller(string $name)
    {
        $instance = parent::controller($name);

        // 获取当前操作名
        $suffix = $this->rule->config('action_suffix');
        $action = $this->actionName . $suffix;

        $paramValue = $this->request->param(self::PARAM_KEY);
        $items = explode(self::SEPR, $paramValue);

        $reflect = new ReflectionMethod($instance, $action);
        $i = 0;
        $params = [];
        foreach($reflect->getParameters() as $param) {
            if (isset($items[$i])) {
                $params[$param->getName()] = $items[$i];
            }

            $i ++;
        }

        $requestReflect = new \ReflectionClass($this->request);
        $paramProperty = $requestReflect->getProperty('param');
        $paramProperty->setAccessible(true);

        $params = array_merge($this->request->param(), $params);
        $paramProperty->setValue($this->request, $params);

        return $instance;
    }
}

注册路由

route/app.php

use app\route\ZentaoDispatch;

ZentaoDispatch::addRouteRule();

测试

路由:/index.php/Abc-do1-27-0-allstory-0-story.json

文件:app/controller/AbcController.php

<?php

namespace app\controller;

class AbcController
{
    public function do1($p1, $p2, $p3, $p4, $p5, $p6 = '')
    {
        var_dump(func_get_args());
        echo 'here do1';
    }
}
posted @ 2021-08-08 15:34  x3d  阅读(251)  评论(0编辑  收藏  举报