接着上篇文章走下去,在引入文件的时候的时候涉及到了路由 \think\Route::get('captcha/[:id]', "\\think\\captcha\\CaptchaController@index"); 所以就跟着这个代码,简单的把路由学习一下

    public static function get($rule, $route = '', $option = [], $pattern = [])
    {
        self::rule($rule, $route, 'GET', $option, $pattern);
    }
    public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = [])
    {
        //获得分组信息
        $group = self::getGroup('name');

        if (!is_null($group)) {
            // 路由分组
            $option  = array_merge(self::getGroup('option'), $option);
            $pattern = array_merge(self::getGroup('pattern'), $pattern);
        }

        $type = strtolower($type);

        //如果$type中含有'|',则将其赋值到$option['method'],并将$type赋值为*,这个在后面的处理中有用
        if (strpos($type, '|')) {
            $option['method'] = $type;
            $type             = '*';
        }
        if (is_array($rule) && empty($route)) {
            foreach ($rule as $key => $val) {
                if (is_numeric($key)) {
                    $key = array_shift($val);
                }
                if (is_array($val)) {
                    $route    = $val[0];
                    $option1  = array_merge($option, $val[1]);
                    $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
                } else {
                    $option1  = null;
                    $pattern1 = null;
                    $route    = $val;
                }
                self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group);
            }
        } else {
            self::setRule($rule, $route, $type, $option, $pattern, $group);
        }

    }

首先$group = self::getGroup('name');

    public static function getGroup($type)
    {
        if (isset(self::$group[$type])) {
            return self::$group[$type];
        } else {
            return 'name' == $type ? null : [];
        }
    }

由于我们的路由并没有分组,self::$group[$type]不存在,且$type传入的值是name,所以最后返回null值到$group

经过一堆判断,接下来就直接执行了最后一句self::setRule($rule, $route, $type, $option, $pattern, $group); 来看看setRule这个函数是怎么设置路由的

    /**
     * 设置路由规则
     * @access public
     * @param string|array $rule    路由规则
     * @param string       $route   路由地址
     * @param string       $type    请求类型
     * @param array        $option  路由参数
     * @param array        $pattern 变量规则
     * @param string       $group   所属分组
     * @return void
     */
    protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '')
    {
        if (is_array($rule)) {
            $name = $rule[0];
            $rule = $rule[1];
        } elseif (is_string($route)) {
            $name = $route;
        }
        if (!isset($option['complete_match'])) {

            if (Config::get('route_complete_match')) {
                $option['complete_match'] = true;
            } elseif ('$' == substr($rule, -1, 1)) {
                // 是否完整匹配
                $option['complete_match'] = true;
            }
        } elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) {
            // 是否完整匹配
            $option['complete_match'] = true;
        }


        //如果是完整匹配,则去掉最后面表示完全匹配的$
        if ('$' == substr($rule, -1, 1)) {
            $rule = substr($rule, 0, -1);
        }

        //如果路由路径不是根目录,那么去除末尾的/
        if ('/' != $rule || $group) {
            $rule = trim($rule, '/');
        }

        //获取路由里的变量
        $vars = self::parseVar($rule);
        if (isset($name)) {
            $key    = $group ? $group . ($rule ? '/' . $rule : '') : $rule;
            $suffix = isset($option['ext']) ? $option['ext'] : null;
            //设置self::rules['name']的参数
            self::name($name, [$key, $vars, self::$domain, $suffix]);
        }
        if (isset($option['modular'])) {
            $route = $option['modular'] . '/' . $route;
        }
        if ($group) {
            if ('*' != $type) {
                $option['method'] = $type;
            }
            if (self::$domain) {
                self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            } else {
                self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            }
        } else {
            if ('*' != $type && isset(self::$rules['*'][$rule])) {
                unset(self::$rules['*'][$rule]);
            }
            // 注册域名下的路由规则
            if (self::$domain) {
                self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            } else {
                //注册路由规则
                self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            }
            if ('*' == $type) {
                // 注册路由快捷方式
                foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
                    if (self::$domain && !isset(self::$rules['domain'][self::$domain][$method][$rule])) {
                        self::$rules['domain'][self::$domain][$method][$rule] = true;
                    } elseif (!self::$domain && !isset(self::$rules[$method][$rule])) {
                        self::$rules[$method][$rule] = true;
                    }
                }
            }
        }
    }

首先$route被赋值给了$name再往下走的时候,调用了Config类里的get方法

    /**
     * 获取配置参数 为空则获取所有配置
     * @access public
     * @param  string $name 配置参数名(支持二级配置 . 号分割)
     * @param  string $range  作用域
     * @return mixed
     */
    public static function get($name = null, $range = '')
    {
        $range = $range ?: self::$range;

        // 无参数时获取所有
        if (empty($name) && isset(self::$config[$range])) {
            return self::$config[$range];
        }

        // 非二级配置时直接返回
        if (!strpos($name, '.')) {
            $name = strtolower($name);
            return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
        }

        // 二维数组设置和获取支持
        $name    = explode('.', $name, 2);
        $name[0] = strtolower($name[0]);

        if (!isset(self::$config[$range][$name[0]])) {
            // 动态载入额外配置
            $module = Request::instance()->module();
            $file   = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;

            is_file($file) && self::load($file, $name[0]);
        }

        return isset(self::$config[$range][$name[0]][$name[1]]) ?
            self::$config[$range][$name[0]][$name[1]] :
            null;
    }

这个由于没有二级配置,且isset(self::$config[$range][$name])为false 所以直接返回了null 接下来就进入了这个分支

elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) {
    // 是否完整匹配
    $option['complete_match'] = true;
}

我们的规则是captcha/[:id]不带’$‘所以接着往下走 $vars = self::parseVar($rule); parseVar()这个函数把我们的路由规则按’/‘划分,获取路由里的变量

    private static function parseVar($rule)
    {
        // 提取路由规则中的变量
        $var = [];
        // 将
        foreach (explode('/', $rule) as $val) {
            $optional = false;
            //实现组合变量规则
            if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
                foreach ($matches[1] as $name) {
                    if (strpos($name, '?')) {
                        $name     = substr($name, 0, -1);
                        $optional = true;
                    } else {
                        $optional = false;
                    }
                    //$var[$name]由上面的optional决定,true为2 false为1,判断是否为可选参数
                    $var[$name] = $optional ? 2 : 1;
                }
            }

            if (0 === strpos($val, '[:')) {
                // 可选参数
                $optional = true;
                $val      = substr($val, 1, -1);
            }
            if (0 === strpos($val, ':')) {
                // URL变量
                $name       = substr($val, 1);
                $var[$name] = $optional ? 2 : 1;
            }
        }
        return $var;
    }

首先将$rule按’/‘分割后 进入这个判断 if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) 这一行判断的它是判断它是不是组合变量规则(如:’item-<name>-<id>‘) 但是我们这里就是一个普通的规则,所以往下走接下来两个判断第一个标记是不是可选参数,第二个则记录变量的名字 通过foreach则能够获取到所有的变量,最后返回一个数组类似这样

mark

接着进这个判断

   if (isset($name)) {
            $key    = $group ? $group . ($rule ? '/' . $rule : '') : $rule;
            $suffix = isset($option['ext']) ? $option['ext'] : null;
            //设置self::rules['name']的参数
            self::name($name, [$key, $vars, self::$domain, $suffix]);
        }

$name是路由的第二个参数

    public static function name($name = '', $value = null)
    {
        if (is_array($name)) {
            return self::$rules['name'] = $name;
        } elseif ('' === $name) {
            return self::$rules['name'];
        } elseif (!is_null($value)) {
            self::$rules['name'][strtolower($name)][] = $value;
        } else {
            $name = strtolower($name);
            return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null;
        }
    }

简单来说这里设置了self::rules[‘name’]的参数

mark

接下来再往下走就进入最后的这串代码

if ($group) {
            if ('*' != $type) {
                $option['method'] = $type;
            }
            if (self::$domain) {
                self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            } else {
                self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            }
        } else {
            if ('*' != $type && isset(self::$rules['*'][$rule])) {
                unset(self::$rules['*'][$rule]);
            }
            // 注册域名下的路由规则
            if (self::$domain) {
                self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            } else {
                //注册路由规则
                self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
            }
            if ('*' == $type) {
                // 注册路由快捷方式
                foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
                    if (self::$domain && !isset(self::$rules['domain'][self::$domain][$method][$rule])) {
                        self::$rules['domain'][self::$domain][$method][$rule] = true;
                    } elseif (!self::$domain && !isset(self::$rules[$method][$rule])) {
                        self::$rules[$method][$rule] = true;
                    }
                }
            }
        }

看着有点乱,我们直接跳到else来看 1.判断type的类型,这里是get 2.注册域名下的路由规则,如果没有域名下的路由规则直接注册路由规则 mark

3.注册路由快捷方式,但这里并没有进入这个分支

好了一个最简单的路由注册的流程就是这样,虽然对于之前没有读过别的源码的我还是感觉非常复杂233,至于其他类型的路由,还有访问的过程留到后面再分析好了