基於 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';
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章