| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992 | <?php// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK ]// +----------------------------------------------------------------------// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st <liu21st@gmail.com>// +----------------------------------------------------------------------namespace think;use think\exception\RouteNotFoundException;use think\route\AliasRule;use think\route\Dispatch;use think\route\dispatch\Url as UrlDispatch;use think\route\Domain;use think\route\Resource;use think\route\Rule;use think\route\RuleGroup;use think\route\RuleItem;class Route{    /**     * REST定义     * @var array     */    protected $rest = [        'index'  => ['get', '', 'index'],        'create' => ['get', '/create', 'create'],        'edit'   => ['get', '/<id>/edit', 'edit'],        'read'   => ['get', '/<id>', 'read'],        'save'   => ['post', '', 'save'],        'update' => ['put', '/<id>', 'update'],        'delete' => ['delete', '/<id>', 'delete'],    ];    /**     * 请求方法前缀定义     * @var array     */    protected $methodPrefix = [        'get'    => 'get',        'post'   => 'post',        'put'    => 'put',        'delete' => 'delete',        'patch'  => 'patch',    ];    /**     * 应用对象     * @var App     */    protected $app;    /**     * 请求对象     * @var Request     */    protected $request;    /**     * 当前HOST     * @var string     */    protected $host;    /**     * 当前域名     * @var string     */    protected $domain;    /**     * 当前分组对象     * @var RuleGroup     */    protected $group;    /**     * 配置参数     * @var array     */    protected $config = [];    /**     * 路由绑定     * @var array     */    protected $bind = [];    /**     * 域名对象     * @var array     */    protected $domains = [];    /**     * 跨域路由规则     * @var RuleGroup     */    protected $cross;    /**     * 路由别名     * @var array     */    protected $alias = [];    /**     * 路由是否延迟解析     * @var bool     */    protected $lazy = true;    /**     * 路由是否测试模式     * @var bool     */    protected $isTest;    /**     * (分组)路由规则是否合并解析     * @var bool     */    protected $mergeRuleRegex = true;    /**     * 路由解析自动搜索多级控制器     * @var bool     */    protected $autoSearchController = true;    public function __construct(App $app, array $config = [])    {        $this->app     = $app;        $this->request = $app['request'];        $this->config  = $config;        $this->host = $this->request->host(true) ?: $config['app_host'];        $this->setDefaultDomain();    }    public function config($name = null)    {        if (is_null($name)) {            return $this->config;        }        return isset($this->config[$name]) ? $this->config[$name] : null;    }    /**     * 配置     * @access public     * @param  array $config     * @return void     */    public function setConfig(array $config = [])    {        $this->config = array_merge($this->config, array_change_key_case($config));    }    public static function __make(App $app, Config $config)    {        $config = $config->pull('app');        $route  = new static($app, $config);        $route->lazy($config['url_lazy_route'])            ->autoSearchController($config['controller_auto_search'])            ->mergeRuleRegex($config['route_rule_merge']);        return $route;    }    /**     * 设置路由的请求对象实例     * @access public     * @param  Request     $request   请求对象实例     * @return void     */    public function setRequest($request)    {        $this->request = $request;    }    /**     * 设置路由域名及分组(包括资源路由)是否延迟解析     * @access public     * @param  bool     $lazy   路由是否延迟解析     * @return $this     */    public function lazy($lazy = true)    {        $this->lazy = $lazy;        return $this;    }    /**     * 设置路由为测试模式     * @access public     * @param  bool     $test   路由是否测试模式     * @return void     */    public function setTestMode($test)    {        $this->isTest = $test;    }    /**     * 检查路由是否为测试模式     * @access public     * @return bool     */    public function isTest()    {        return $this->isTest;    }    /**     * 设置路由域名及分组(包括资源路由)是否合并解析     * @access public     * @param  bool     $merge   路由是否合并解析     * @return $this     */    public function mergeRuleRegex($merge = true)    {        $this->mergeRuleRegex = $merge;        $this->group->mergeRuleRegex($merge);        return $this;    }    /**     * 设置路由自动解析是否搜索多级控制器     * @access public     * @param  bool     $auto   是否自动搜索多级控制器     * @return $this     */    public function autoSearchController($auto = true)    {        $this->autoSearchController = $auto;        return $this;    }    /**     * 初始化默认域名     * @access protected     * @return void     */    protected function setDefaultDomain()    {        // 默认域名        $this->domain = $this->host;        // 注册默认域名        $domain = new Domain($this, $this->host);        $this->domains[$this->host] = $domain;        // 默认分组        $this->group = $domain;    }    /**     * 设置当前域名     * @access public     * @param  RuleGroup    $group 域名     * @return void     */    public function setGroup(RuleGroup $group)    {        $this->group = $group;    }    /**     * 获取当前分组     * @access public     * @return RuleGroup     */    public function getGroup()    {        return $this->group;    }    /**     * 注册变量规则     * @access public     * @param  string|array  $name 变量名     * @param  string        $rule 变量规则     * @return $this     */    public function pattern($name, $rule = '')    {        $this->group->pattern($name, $rule);        return $this;    }    /**     * 注册路由参数     * @access public     * @param  string|array  $name  参数名     * @param  mixed         $value 值     * @return $this     */    public function option($name, $value = '')    {        $this->group->option($name, $value);        return $this;    }    /**     * 注册域名路由     * @access public     * @param  string|array  $name 子域名     * @param  mixed         $rule 路由规则     * @param  array         $option 路由参数     * @param  array         $pattern 变量规则     * @return Domain     */    public function domain($name, $rule = '', $option = [], $pattern = [])    {        // 支持多个域名使用相同路由规则        $domainName = is_array($name) ? array_shift($name) : $name;        if ('*' != $domainName && false === strpos($domainName, '.')) {            $domainName .= '.' . $this->request->rootDomain();        }        if (!isset($this->domains[$domainName])) {            $domain = (new Domain($this, $domainName, $rule, $option, $pattern))                ->lazy($this->lazy)                ->mergeRuleRegex($this->mergeRuleRegex);            $this->domains[$domainName] = $domain;        } else {            $domain = $this->domains[$domainName];            $domain->parseGroupRule($rule);        }        if (is_array($name) && !empty($name)) {            $root = $this->request->rootDomain();            foreach ($name as $item) {                if (false === strpos($item, '.')) {                    $item .= '.' . $root;                }                $this->domains[$item] = $domainName;            }        }        // 返回域名对象        return $domain;    }    /**     * 获取域名     * @access public     * @return array     */    public function getDomains()    {        return $this->domains;    }    /**     * 设置路由绑定     * @access public     * @param  string     $bind 绑定信息     * @param  string     $domain 域名     * @return $this     */    public function bind($bind, $domain = null)    {        $domain = is_null($domain) ? $this->domain : $domain;        $this->bind[$domain] = $bind;        return $this;    }    /**     * 读取路由绑定     * @access public     * @param  string    $domain 域名     * @return string|null     */    public function getBind($domain = null)    {        if (is_null($domain)) {            $domain = $this->domain;        } elseif (true === $domain) {            return $this->bind;        } elseif (false === strpos($domain, '.')) {            $domain .= '.' . $this->request->rootDomain();        }        $subDomain = $this->request->subDomain();        if (strpos($subDomain, '.')) {            $name = '*' . strstr($subDomain, '.');        }        if (isset($this->bind[$domain])) {            $result = $this->bind[$domain];        } elseif (isset($name) && isset($this->bind[$name])) {            $result = $this->bind[$name];        } elseif (!empty($subDomain) && isset($this->bind['*'])) {            $result = $this->bind['*'];        } else {            $result = null;        }        return $result;    }    /**     * 读取路由标识     * @access public     * @param  string    $name 路由标识     * @param  string    $domain 域名     * @return mixed     */    public function getName($name = null, $domain = null, $method = '*')    {        return $this->app['rule_name']->get($name, $domain, $method);    }    /**     * 读取路由     * @access public     * @param  string    $rule 路由规则     * @param  string    $domain 域名     * @return array     */    public function getRule($rule, $domain = null)    {        if (is_null($domain)) {            $domain = $this->domain;        }        return $this->app['rule_name']->getRule($rule, $domain);    }    /**     * 读取路由     * @access public     * @param  string    $domain 域名     * @return array     */    public function getRuleList($domain = null)    {        return $this->app['rule_name']->getRuleList($domain);    }    /**     * 批量导入路由标识     * @access public     * @param  array    $name 路由标识     * @return $this     */    public function setName($name)    {        $this->app['rule_name']->import($name);        return $this;    }    /**     * 导入配置文件的路由规则     * @access public     * @param  array     $rules 路由规则     * @param  string    $type  请求类型     * @return void     */    public function import(array $rules, $type = '*')    {        // 检查域名部署        if (isset($rules['__domain__'])) {            foreach ($rules['__domain__'] as $key => $rule) {                $this->domain($key, $rule);            }            unset($rules['__domain__']);        }        // 检查变量规则        if (isset($rules['__pattern__'])) {            $this->pattern($rules['__pattern__']);            unset($rules['__pattern__']);        }        // 检查路由别名        if (isset($rules['__alias__'])) {            foreach ($rules['__alias__'] as $key => $val) {                $this->alias($key, $val);            }            unset($rules['__alias__']);        }        // 检查资源路由        if (isset($rules['__rest__'])) {            foreach ($rules['__rest__'] as $key => $rule) {                $this->resource($key, $rule);            }            unset($rules['__rest__']);        }        // 检查路由规则(包含分组)        foreach ($rules as $key => $val) {            if (is_numeric($key)) {                $key = array_shift($val);            }            if (empty($val)) {                continue;            }            if (is_string($key) && 0 === strpos($key, '[')) {                $key = substr($key, 1, -1);                $this->group($key, $val);            } elseif (is_array($val)) {                $this->rule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);            } else {                $this->rule($key, $val, $type);            }        }    }    /**     * 注册路由规则     * @access public     * @param  string    $rule       路由规则     * @param  mixed     $route      路由地址     * @param  string    $method     请求类型     * @param  array     $option     路由参数     * @param  array     $pattern    变量规则     * @return RuleItem     */    public function rule($rule, $route, $method = '*', array $option = [], array $pattern = [])    {        return $this->group->addRule($rule, $route, $method, $option, $pattern);    }    /**     * 设置跨域有效路由规则     * @access public     * @param  Rule      $rule      路由规则     * @param  string    $method    请求类型     * @return $this     */    public function setCrossDomainRule($rule, $method = '*')    {        if (!isset($this->cross)) {            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);        }        $this->cross->addRuleItem($rule, $method);        return $this;    }    /**     * 批量注册路由规则     * @access public     * @param  array     $rules      路由规则     * @param  string    $method     请求类型     * @param  array     $option     路由参数     * @param  array     $pattern    变量规则     * @return void     */    public function rules($rules, $method = '*', array $option = [], array $pattern = [])    {        $this->group->addRules($rules, $method, $option, $pattern);    }    /**     * 注册路由分组     * @access public     * @param  string|array      $name       分组名称或者参数     * @param  array|\Closure    $route      分组路由     * @param  array             $option     路由参数     * @param  array             $pattern    变量规则     * @return RuleGroup     */    public function group($name, $route, array $option = [], array $pattern = [])    {        if (is_array($name)) {            $option = $name;            $name   = isset($option['name']) ? $option['name'] : '';        }        return (new RuleGroup($this, $this->group, $name, $route, $option, $pattern))            ->lazy($this->lazy)            ->mergeRuleRegex($this->mergeRuleRegex);    }    /**     * 注册路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function any($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, '*', $option, $pattern);    }    /**     * 注册GET路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function get($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, 'GET', $option, $pattern);    }    /**     * 注册POST路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function post($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, 'POST', $option, $pattern);    }    /**     * 注册PUT路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function put($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, 'PUT', $option, $pattern);    }    /**     * 注册DELETE路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function delete($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, 'DELETE', $option, $pattern);    }    /**     * 注册PATCH路由     * @access public     * @param  string    $rule 路由规则     * @param  mixed     $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleItem     */    public function patch($rule, $route = '', array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, 'PATCH', $option, $pattern);    }    /**     * 注册资源路由     * @access public     * @param  string    $rule 路由规则     * @param  string    $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return Resource     */    public function resource($rule, $route = '', array $option = [], array $pattern = [])    {        return (new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest))            ->lazy($this->lazy);    }    /**     * 注册控制器路由 操作方法对应不同的请求前缀     * @access public     * @param  string    $rule 路由规则     * @param  string    $route 路由地址     * @param  array     $option 路由参数     * @param  array     $pattern 变量规则     * @return RuleGroup     */    public function controller($rule, $route = '', array $option = [], array $pattern = [])    {        $group = new RuleGroup($this, $this->group, $rule, null, $option, $pattern);        foreach ($this->methodPrefix as $type => $val) {            $group->addRule('<action>', $val . '<action>', $type);        }        return $group->prefix($route . '/');    }    /**     * 注册视图路由     * @access public     * @param  string|array $rule 路由规则     * @param  string       $template 路由模板地址     * @param  array        $vars 模板变量     * @param  array        $option 路由参数     * @param  array        $pattern 变量规则     * @return RuleItem     */    public function view($rule, $template = '', array $vars = [], array $option = [], array $pattern = [])    {        return $this->rule($rule, $template, 'GET', $option, $pattern)->view($vars);    }    /**     * 注册重定向路由     * @access public     * @param  string|array $rule 路由规则     * @param  string       $route 路由地址     * @param  array        $status 状态码     * @param  array        $option 路由参数     * @param  array        $pattern 变量规则     * @return RuleItem     */    public function redirect($rule, $route = '', $status = 301, array $option = [], array $pattern = [])    {        return $this->rule($rule, $route, '*', $option, $pattern)->redirect()->status($status);    }    /**     * 注册别名路由     * @access public     * @param  string  $rule 路由别名     * @param  string  $route 路由地址     * @param  array   $option 路由参数     * @return AliasRule     */    public function alias($rule, $route, array $option = [])    {        $aliasRule = new AliasRule($this, $this->group, $rule, $route, $option);        $this->alias[$rule] = $aliasRule;        return $aliasRule;    }    /**     * 获取别名路由定义     * @access public     * @param  string    $name 路由别名     * @return string|array|null     */    public function getAlias($name = null)    {        if (is_null($name)) {            return $this->alias;        }        return isset($this->alias[$name]) ? $this->alias[$name] : null;    }    /**     * 设置不同请求类型下面的方法前缀     * @access public     * @param  string|array  $method 请求类型     * @param  string        $prefix 类型前缀     * @return $this     */    public function setMethodPrefix($method, $prefix = '')    {        if (is_array($method)) {            $this->methodPrefix = array_merge($this->methodPrefix, array_change_key_case($method));        } else {            $this->methodPrefix[strtolower($method)] = $prefix;        }        return $this;    }    /**     * 获取请求类型的方法前缀     * @access public     * @param  string    $method 请求类型     * @param  string    $prefix 类型前缀     * @return string|null     */    public function getMethodPrefix($method)    {        $method = strtolower($method);        return isset($this->methodPrefix[$method]) ? $this->methodPrefix[$method] : null;    }    /**     * rest方法定义和修改     * @access public     * @param  string        $name 方法名称     * @param  array|bool    $resource 资源     * @return $this     */    public function rest($name, $resource = [])    {        if (is_array($name)) {            $this->rest = $resource ? $name : array_merge($this->rest, $name);        } else {            $this->rest[$name] = $resource;        }        return $this;    }    /**     * 获取rest方法定义的参数     * @access public     * @param  string        $name 方法名称     * @return array|null     */    public function getRest($name = null)    {        if (is_null($name)) {            return $this->rest;        }        return isset($this->rest[$name]) ? $this->rest[$name] : null;    }    /**     * 注册未匹配路由规则后的处理     * @access public     * @param  string    $route 路由地址     * @param  string    $method 请求类型     * @param  array     $option 路由参数     * @return RuleItem     */    public function miss($route, $method = '*', array $option = [])    {        return $this->group->addMissRule($route, $method, $option);    }    /**     * 注册一个自动解析的URL路由     * @access public     * @param  string    $route 路由地址     * @return RuleItem     */    public function auto($route)    {        return $this->group->addAutoRule($route);    }    /**     * 检测URL路由     * @access public     * @param  string    $url URL地址     * @param  bool      $must 是否强制路由     * @return Dispatch     * @throws RouteNotFoundException     */    public function check($url, $must = false)    {        // 自动检测域名路由        $domain = $this->checkDomain();        $url    = str_replace($this->config['pathinfo_depr'], '|', $url);        $completeMatch = $this->config['route_complete_match'];        $result = $domain->check($this->request, $url, $completeMatch);        if (false === $result && !empty($this->cross)) {            // 检测跨域路由            $result = $this->cross->check($this->request, $url, $completeMatch);        }        if (false !== $result) {            // 路由匹配            return $result;        } elseif ($must) {            // 强制路由不匹配则抛出异常            throw new RouteNotFoundException();        }        // 默认路由解析        return new UrlDispatch($this->request, $this->group, $url, [            'auto_search' => $this->autoSearchController,        ]);    }    /**     * 检测域名的路由规则     * @access protected     * @return Domain     */    protected function checkDomain()    {        // 获取当前子域名        $subDomain = $this->request->subDomain();        $item = false;        if ($subDomain && count($this->domains) > 1) {            $domain  = explode('.', $subDomain);            $domain2 = array_pop($domain);            if ($domain) {                // 存在三级域名                $domain3 = array_pop($domain);            }            if ($subDomain && isset($this->domains[$subDomain])) {                // 子域名配置                $item = $this->domains[$subDomain];            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {                // 泛三级域名                $item      = $this->domains['*.' . $domain2];                $panDomain = $domain3;            } elseif (isset($this->domains['*']) && !empty($domain2)) {                // 泛二级域名                if ('www' != $domain2) {                    $item      = $this->domains['*'];                    $panDomain = $domain2;                }            }            if (isset($panDomain)) {                // 保存当前泛域名                $this->request->setPanDomain($panDomain);            }        }        if (false === $item) {            // 检测当前完整域名            $item = $this->domains[$this->host];        }        if (is_string($item)) {            $item = $this->domains[$item];        }        return $item;    }    /**     * 清空路由规则     * @access public     * @return void     */    public function clear()    {        $this->app['rule_name']->clear();        $this->group->clear();    }    /**     * 设置全局的路由分组参数     * @access public     * @param  string    $method     方法名     * @param  array     $args       调用参数     * @return RuleGroup     */    public function __call($method, $args)    {        return call_user_func_array([$this->group, $method], $args);    }    public function __debugInfo()    {        $data = get_object_vars($this);        unset($data['app'], $data['request']);        return $data;    }}
 |