添加网站文件

This commit is contained in:
2025-12-22 13:59:40 +08:00
commit 117aaf83d1
19468 changed files with 2111999 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
<?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\route;
use think\Route;
class AliasRule extends Domain
{
/**
* 架构函数
* @access public
* @param Route $router 路由实例
* @param RuleGroup $parent 上级对象
* @param string $name 路由别名
* @param string $route 路由绑定
* @param array $option 路由参数
*/
public function __construct(Route $router, RuleGroup $parent, $name, $route, $option = [])
{
$this->router = $router;
$this->parent = $parent;
$this->name = $name;
$this->route = $route;
$this->option = $option;
}
/**
* 检测路由别名
* @access public
* @param Request $request 请求对象
* @param string $url 访问地址
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
public function check($request, $url, $completeMatch = false)
{
if ($dispatch = $this->checkCrossDomain($request)) {
// 允许跨域
return $dispatch;
}
// 检查参数有效性
if (!$this->checkOption($this->option, $request)) {
return false;
}
list($action, $bind) = array_pad(explode('|', $url, 2), 2, '');
if (isset($this->option['allow']) && !in_array($action, $this->option['allow'])) {
// 允许操作
return false;
} elseif (isset($this->option['except']) && in_array($action, $this->option['except'])) {
// 排除操作
return false;
}
if (isset($this->option['method'][$action])) {
$this->option['method'] = $this->option['method'][$action];
}
// 匹配后执行的行为
$this->afterMatchGroup($request);
if ($this->parent) {
// 合并分组参数
$this->mergeGroupOptions();
}
if (isset($this->option['ext'])) {
// 路由ext参数 优先于系统配置的URL伪静态后缀参数
$bind = preg_replace('/\.(' . $request->ext() . ')$/i', '', $bind);
}
$this->parseBindAppendParam($this->route);
if (0 === strpos($this->route, '\\')) {
// 路由到类
return $this->bindToClass($request, $bind, substr($this->route, 1));
} elseif (0 === strpos($this->route, '@')) {
// 路由到控制器类
return $this->bindToController($request, $bind, substr($this->route, 1));
} else {
// 路由到模块/控制器
return $this->bindToModule($request, $bind, $this->route);
}
}
/**
* 设置允许的操作方法
* @access public
* @param array $action 操作方法
* @return $this
*/
public function allow($action = [])
{
return $this->option('allow', $action);
}
/**
* 设置排除的操作方法
* @access public
* @param array $action 操作方法
* @return $this
*/
public function except($action = [])
{
return $this->option('except', $action);
}
}

View File

@@ -0,0 +1,366 @@
<?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\route;
use think\App;
use think\Container;
use think\exception\ValidateException;
use think\Request;
use think\Response;
abstract class Dispatch
{
/**
* 应用对象
* @var App
*/
protected $app;
/**
* 请求对象
* @var Request
*/
protected $request;
/**
* 路由规则
* @var Rule
*/
protected $rule;
/**
* 调度信息
* @var mixed
*/
protected $dispatch;
/**
* 调度参数
* @var array
*/
protected $param;
/**
* 状态码
* @var string
*/
protected $code;
/**
* 是否进行大小写转换
* @var bool
*/
protected $convert;
public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null)
{
$this->request = $request;
$this->rule = $rule;
$this->app = Container::get('app');
$this->dispatch = $dispatch;
$this->param = $param;
$this->code = $code;
if (isset($param['convert'])) {
$this->convert = $param['convert'];
}
}
public function init()
{
// 执行路由后置操作
if ($this->rule->doAfter()) {
// 设置请求的路由信息
// 设置当前请求的参数
$this->request->setRouteVars($this->rule->getVars());
$this->request->routeInfo([
'rule' => $this->rule->getRule(),
'route' => $this->rule->getRoute(),
'option' => $this->rule->getOption(),
'var' => $this->rule->getVars(),
]);
$this->doRouteAfter();
}
return $this;
}
/**
* 检查路由后置操作
* @access protected
* @return void
*/
protected function doRouteAfter()
{
// 记录匹配的路由信息
$option = $this->rule->getOption();
$matches = $this->rule->getVars();
// 添加中间件
if (!empty($option['middleware'])) {
$this->app['middleware']->import($option['middleware']);
}
// 绑定模型数据
if (!empty($option['model'])) {
$this->createBindModel($option['model'], $matches);
}
// 指定Header数据
if (!empty($option['header'])) {
$header = $option['header'];
$this->app['hook']->add('response_send', function ($response) use ($header) {
$response->header($header);
});
}
// 指定Response响应数据
if (!empty($option['response'])) {
foreach ($option['response'] as $response) {
$this->app['hook']->add('response_send', $response);
}
}
// 开启请求缓存
if (isset($option['cache']) && $this->request->isGet()) {
$this->parseRequestCache($option['cache']);
}
if (!empty($option['append'])) {
$this->request->setRouteVars($option['append']);
}
}
/**
* 执行路由调度
* @access public
* @return mixed
*/
public function run()
{
$option = $this->rule->getOption();
// 检测路由after行为
if (!empty($option['after'])) {
$dispatch = $this->checkAfter($option['after']);
if ($dispatch instanceof Response) {
return $dispatch;
}
}
// 数据自动验证
if (isset($option['validate'])) {
$this->autoValidate($option['validate']);
}
$data = $this->exec();
return $this->autoResponse($data);
}
protected function autoResponse($data)
{
if ($data instanceof Response) {
$response = $data;
} elseif (!is_null($data)) {
// 默认自动识别响应输出类型
$isAjax = $this->request->isAjax();
$type = $isAjax ? $this->rule->getConfig('default_ajax_return') : $this->rule->getConfig('default_return_type');
$response = Response::create($data, $type);
} else {
$data = ob_get_clean();
$content = false === $data ? '' : $data;
$status = '' === $content && $this->request->isJson() ? 204 : 200;
$response = Response::create($content, '', $status);
}
return $response;
}
/**
* 检查路由后置行为
* @access protected
* @param mixed $after 后置行为
* @return mixed
*/
protected function checkAfter($after)
{
$this->app['log']->notice('路由后置行为建议使用中间件替代!');
$hook = $this->app['hook'];
$result = null;
foreach ((array) $after as $behavior) {
$result = $hook->exec($behavior);
if (!is_null($result)) {
break;
}
}
// 路由规则重定向
if ($result instanceof Response) {
return $result;
}
return false;
}
/**
* 验证数据
* @access protected
* @param array $option
* @return void
* @throws ValidateException
*/
protected function autoValidate($option)
{
list($validate, $scene, $message, $batch) = $option;
if (is_array($validate)) {
// 指定验证规则
$v = $this->app->validate();
$v->rule($validate);
} else {
// 调用验证器
$v = $this->app->validate($validate);
if (!empty($scene)) {
$v->scene($scene);
}
}
if (!empty($message)) {
$v->message($message);
}
// 批量验证
if ($batch) {
$v->batch(true);
}
if (!$v->check($this->request->param())) {
throw new ValidateException($v->getError());
}
}
/**
* 处理路由请求缓存
* @access protected
* @param Request $request 请求对象
* @param string|array $cache 路由缓存
* @return void
*/
protected function parseRequestCache($cache)
{
if (is_array($cache)) {
list($key, $expire, $tag) = array_pad($cache, 3, null);
} else {
$key = str_replace('|', '/', $this->request->url());
$expire = $cache;
$tag = null;
}
$cache = $this->request->cache($key, $expire, $tag);
$this->app->setResponseCache($cache);
}
/**
* 路由绑定模型实例
* @access protected
* @param array|\Clousre $bindModel 绑定模型
* @param array $matches 路由变量
* @return void
*/
protected function createBindModel($bindModel, $matches)
{
foreach ($bindModel as $key => $val) {
if ($val instanceof \Closure) {
$result = $this->app->invokeFunction($val, $matches);
} else {
$fields = explode('&', $key);
if (is_array($val)) {
list($model, $exception) = $val;
} else {
$model = $val;
$exception = true;
}
$where = [];
$match = true;
foreach ($fields as $field) {
if (!isset($matches[$field])) {
$match = false;
break;
} else {
$where[] = [$field, '=', $matches[$field]];
}
}
if ($match) {
$query = strpos($model, '\\') ? $model::where($where) : $this->app->model($model)->where($where);
$result = $query->failException($exception)->find();
}
}
if (!empty($result)) {
// 注入容器
$this->app->instance(get_class($result), $result);
}
}
}
public function convert($convert)
{
$this->convert = $convert;
return $this;
}
public function getDispatch()
{
return $this->dispatch;
}
public function getParam()
{
return $this->param;
}
abstract public function exec();
public function __sleep()
{
return ['rule', 'dispatch', 'convert', 'param', 'code', 'controller', 'actionName'];
}
public function __wakeup()
{
$this->app = Container::get('app');
$this->request = $this->app['request'];
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app'], $data['request'], $data['rule']);
return $data;
}
}

View File

@@ -0,0 +1,236 @@
<?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\route;
use think\Container;
use think\Loader;
use think\Route;
use think\route\dispatch\Callback as CallbackDispatch;
use think\route\dispatch\Controller as ControllerDispatch;
use think\route\dispatch\Module as ModuleDispatch;
class Domain extends RuleGroup
{
/**
* 架构函数
* @access public
* @param Route $router 路由对象
* @param string $name 路由域名
* @param mixed $rule 域名路由
* @param array $option 路由参数
* @param array $pattern 变量规则
*/
public function __construct(Route $router, $name = '', $rule = null, $option = [], $pattern = [])
{
$this->router = $router;
$this->domain = $name;
$this->option = $option;
$this->rule = $rule;
$this->pattern = $pattern;
}
/**
* 检测域名路由
* @access public
* @param Request $request 请求对象
* @param string $url 访问地址
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
public function check($request, $url, $completeMatch = false)
{
// 检测别名路由
$result = $this->checkRouteAlias($request, $url);
if (false !== $result) {
return $result;
}
// 检测URL绑定
$result = $this->checkUrlBind($request, $url);
if (!empty($this->option['append'])) {
$request->setRouteVars($this->option['append']);
unset($this->option['append']);
}
if (false !== $result) {
return $result;
}
// 添加域名中间件
if (!empty($this->option['middleware'])) {
Container::get('middleware')->import($this->option['middleware']);
unset($this->option['middleware']);
}
return parent::check($request, $url, $completeMatch);
}
/**
* 设置路由绑定
* @access public
* @param string $bind 绑定信息
* @return $this
*/
public function bind($bind)
{
$this->router->bind($bind, $this->domain);
return $this;
}
/**
* 检测路由别名
* @access private
* @param Request $request
* @param string $url URL地址
* @return Dispatch|false
*/
private function checkRouteAlias($request, $url)
{
$alias = strpos($url, '|') ? strstr($url, '|', true) : $url;
$item = $this->router->getAlias($alias);
return $item ? $item->check($request, $url) : false;
}
/**
* 检测URL绑定
* @access private
* @param Request $request
* @param string $url URL地址
* @return Dispatch|false
*/
private function checkUrlBind($request, $url)
{
$bind = $this->router->getBind($this->domain);
if (!empty($bind)) {
$this->parseBindAppendParam($bind);
// 记录绑定信息
Container::get('app')->log('[ BIND ] ' . var_export($bind, true));
// 如果有URL绑定 则进行绑定检测
$type = substr($bind, 0, 1);
$bind = substr($bind, 1);
$bindTo = [
'\\' => 'bindToClass',
'@' => 'bindToController',
':' => 'bindToNamespace',
];
if (isset($bindTo[$type])) {
return $this->{$bindTo[$type]}($request, $url, $bind);
}
}
return false;
}
protected function parseBindAppendParam(&$bind)
{
if (false !== strpos($bind, '?')) {
list($bind, $query) = explode('?', $bind);
parse_str($query, $vars);
$this->append($vars);
}
}
/**
* 绑定到类
* @access protected
* @param Request $request
* @param string $url URL地址
* @param string $class 类名(带命名空间)
* @return CallbackDispatch
*/
protected function bindToClass($request, $url, $class)
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
$param = [];
if (!empty($array[1])) {
$this->parseUrlParams($request, $array[1], $param);
}
return new CallbackDispatch($request, $this, [$class, $action], $param);
}
/**
* 绑定到命名空间
* @access protected
* @param Request $request
* @param string $url URL地址
* @param string $namespace 命名空间
* @return CallbackDispatch
*/
protected function bindToNamespace($request, $url, $namespace)
{
$array = explode('|', $url, 3);
$class = !empty($array[0]) ? $array[0] : $this->router->config('default_controller');
$method = !empty($array[1]) ? $array[1] : $this->router->config('default_action');
$param = [];
if (!empty($array[2])) {
$this->parseUrlParams($request, $array[2], $param);
}
return new CallbackDispatch($request, $this, [$namespace . '\\' . Loader::parseName($class, 1), $method], $param);
}
/**
* 绑定到控制器类
* @access protected
* @param Request $request
* @param string $url URL地址
* @param string $controller 控制器名 (支持带模块名 index/user
* @return ControllerDispatch
*/
protected function bindToController($request, $url, $controller)
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
$param = [];
if (!empty($array[1])) {
$this->parseUrlParams($request, $array[1], $param);
}
return new ControllerDispatch($request, $this, $controller . '/' . $action, $param);
}
/**
* 绑定到模块/控制器
* @access protected
* @param Request $request
* @param string $url URL地址
* @param string $controller 控制器类名(带命名空间)
* @return ModuleDispatch
*/
protected function bindToModule($request, $url, $controller)
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
$param = [];
if (!empty($array[1])) {
$this->parseUrlParams($request, $array[1], $param);
}
return new ModuleDispatch($request, $this, $controller . '/' . $action, $param);
}
}

View File

@@ -0,0 +1,126 @@
<?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\route;
use think\Route;
class Resource extends RuleGroup
{
// 资源路由名称
protected $resource;
// REST路由方法定义
protected $rest = [];
/**
* 架构函数
* @access public
* @param Route $router 路由对象
* @param RuleGroup $parent 上级对象
* @param string $name 资源名称
* @param string $route 路由地址
* @param array $option 路由参数
* @param array $pattern 变量规则
* @param array $rest 资源定义
*/
public function __construct(Route $router, RuleGroup $parent = null, $name = '', $route = '', $option = [], $pattern = [], $rest = [])
{
$this->router = $router;
$this->parent = $parent;
$this->resource = $name;
$this->route = $route;
$this->name = strpos($name, '.') ? strstr($name, '.', true) : $name;
$this->setFullName();
// 资源路由默认为完整匹配
$option['complete_match'] = true;
$this->pattern = $pattern;
$this->option = $option;
$this->rest = $rest;
if ($this->parent) {
$this->domain = $this->parent->getDomain();
$this->parent->addRuleItem($this);
}
if ($router->isTest()) {
$this->buildResourceRule();
}
}
/**
* 生成资源路由规则
* @access protected
* @return void
*/
protected function buildResourceRule()
{
$origin = $this->router->getGroup();
$this->router->setGroup($this);
$rule = $this->resource;
$option = $this->option;
if (strpos($rule, '.')) {
// 注册嵌套资源路由
$array = explode('.', $rule);
$last = array_pop($array);
$item = [];
foreach ($array as $val) {
$item[] = $val . '/<' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id') . '>';
}
$rule = implode('/', $item) . '/' . $last;
}
$prefix = substr($rule, strlen($this->name) + 1);
// 注册资源路由
foreach ($this->rest as $key => $val) {
if ((isset($option['only']) && !in_array($key, $option['only']))
|| (isset($option['except']) && in_array($key, $option['except']))) {
continue;
}
if (isset($last) && strpos($val[1], '<id>') && isset($option['var'][$last])) {
$val[1] = str_replace('<id>', '<' . $option['var'][$last] . '>', $val[1]);
} elseif (strpos($val[1], '<id>') && isset($option['var'][$rule])) {
$val[1] = str_replace('<id>', '<' . $option['var'][$rule] . '>', $val[1]);
}
$this->addRule(trim($prefix . $val[1], '/'), $this->route . '/' . $val[2], $val[0]);
}
$this->router->setGroup($origin);
}
/**
* 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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,601 @@
<?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\route;
use think\Container;
use think\Exception;
use think\Request;
use think\Response;
use think\Route;
use think\route\dispatch\Response as ResponseDispatch;
use think\route\dispatch\Url as UrlDispatch;
class RuleGroup extends Rule
{
// 分组路由(包括子分组)
protected $rules = [
'*' => [],
'get' => [],
'post' => [],
'put' => [],
'patch' => [],
'delete' => [],
'head' => [],
'options' => [],
];
// MISS路由
protected $miss;
// 自动路由
protected $auto;
// 完整名称
protected $fullName;
// 所在域名
protected $domain;
/**
* 架构函数
* @access public
* @param Route $router 路由对象
* @param RuleGroup $parent 上级对象
* @param string $name 分组名称
* @param mixed $rule 分组路由
* @param array $option 路由参数
* @param array $pattern 变量规则
*/
public function __construct(Route $router, RuleGroup $parent = null, $name = '', $rule = [], $option = [], $pattern = [])
{
$this->router = $router;
$this->parent = $parent;
$this->rule = $rule;
$this->name = trim($name, '/');
$this->option = $option;
$this->pattern = $pattern;
$this->setFullName();
if ($this->parent) {
$this->domain = $this->parent->getDomain();
$this->parent->addRuleItem($this);
}
if (!empty($option['cross_domain'])) {
$this->router->setCrossDomainRule($this);
}
if ($router->isTest()) {
$this->lazy(false);
}
}
/**
* 设置分组的路由规则
* @access public
* @return void
*/
protected function setFullName()
{
if (false !== strpos($this->name, ':')) {
$this->name = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $this->name);
}
if ($this->parent && $this->parent->getFullName()) {
$this->fullName = $this->parent->getFullName() . ($this->name ? '/' . $this->name : '');
} else {
$this->fullName = $this->name;
}
}
/**
* 获取所属域名
* @access public
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* 检测分组路由
* @access public
* @param Request $request 请求对象
* @param string $url 访问地址
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
public function check($request, $url, $completeMatch = false)
{
// 跨域OPTIONS请求
if ($dispatch = $this->checkCrossDomain($request)) {
return $dispatch;
}
// 检查分组有效性
if (!$this->checkOption($this->option, $request) || !$this->checkUrl($url)) {
return false;
}
// 检查前置行为
if (isset($this->option['before'])) {
if (false === $this->checkBefore($this->option['before'])) {
return false;
}
unset($this->option['before']);
}
// 解析分组路由
if ($this instanceof Resource) {
$this->buildResourceRule();
} elseif ($this->rule) {
if ($this->rule instanceof Response) {
return new ResponseDispatch($request, $this, $this->rule);
}
$this->parseGroupRule($this->rule);
}
// 获取当前路由规则
$method = strtolower($request->method());
$rules = $this->getMethodRules($method);
if ($this->parent) {
// 合并分组参数
$this->mergeGroupOptions();
// 合并分组变量规则
$this->pattern = array_merge($this->parent->getPattern(), $this->pattern);
}
if (isset($this->option['complete_match'])) {
$completeMatch = $this->option['complete_match'];
}
if (!empty($this->option['merge_rule_regex'])) {
// 合并路由正则规则进行路由匹配检查
$result = $this->checkMergeRuleRegex($request, $rules, $url, $completeMatch);
if (false !== $result) {
return $result;
}
}
// 检查分组路由
foreach ($rules as $key => $item) {
$result = $item->check($request, $url, $completeMatch);
if (false !== $result) {
return $result;
}
}
if ($this->auto) {
// 自动解析URL地址
$result = new UrlDispatch($request, $this, $this->auto . '/' . $url, ['auto_search' => false]);
} elseif ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) {
// 未匹配所有路由的路由规则处理
$result = $this->miss->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->mergeGroupOptions());
} else {
$result = false;
}
return $result;
}
/**
* 获取当前请求的路由规则(包括子分组、资源路由)
* @access protected
* @param string $method
* @return array
*/
protected function getMethodRules($method)
{
return array_merge($this->rules[$method], $this->rules['*']);
}
/**
* 分组URL匹配检查
* @access protected
* @param string $url
* @return bool
*/
protected function checkUrl($url)
{
if ($this->fullName) {
$pos = strpos($this->fullName, '<');
if (false !== $pos) {
$str = substr($this->fullName, 0, $pos);
} else {
$str = $this->fullName;
}
if ($str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
return false;
}
}
return true;
}
/**
* 延迟解析分组的路由规则
* @access public
* @param bool $lazy 路由是否延迟解析
* @return $this
*/
public function lazy($lazy = true)
{
if (!$lazy) {
$this->parseGroupRule($this->rule);
$this->rule = null;
}
return $this;
}
/**
* 解析分组和域名的路由规则及绑定
* @access public
* @param mixed $rule 路由规则
* @return void
*/
public function parseGroupRule($rule)
{
$origin = $this->router->getGroup();
$this->router->setGroup($this);
if ($rule instanceof \Closure) {
Container::getInstance()->invokeFunction($rule);
} elseif (is_array($rule)) {
$this->addRules($rule);
} elseif (is_string($rule) && $rule) {
$this->router->bind($rule, $this->domain);
}
$this->router->setGroup($origin);
}
/**
* 检测分组路由
* @access public
* @param Request $request 请求对象
* @param array $rules 路由规则
* @param string $url 访问地址
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
protected function checkMergeRuleRegex($request, &$rules, $url, $completeMatch)
{
$depr = $this->router->config('pathinfo_depr');
$url = $depr . str_replace('|', $depr, $url);
foreach ($rules as $key => $item) {
if ($item instanceof RuleItem) {
$rule = $depr . str_replace('/', $depr, $item->getRule());
if ($depr == $rule && $depr != $url) {
unset($rules[$key]);
continue;
}
$complete = null !== $item->getOption('complete_match') ? $item->getOption('complete_match') : $completeMatch;
if (false === strpos($rule, '<')) {
if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) {
return $item->checkRule($request, $url, []);
}
unset($rules[$key]);
continue;
}
$slash = preg_quote('/-' . $depr, '/');
if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) {
if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
unset($rules[$key]);
continue;
}
}
if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
unset($rules[$key]);
$pattern = array_merge($this->getPattern(), $item->getPattern());
$option = array_merge($this->getOption(), $item->getOption());
$regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key);
$items[$key] = $item;
}
}
}
if (empty($regex)) {
return false;
}
try {
$result = preg_match('/^(?:' . implode('|', $regex) . ')/u', $url, $match);
} catch (\Exception $e) {
throw new Exception('route pattern error');
}
if ($result) {
$var = [];
foreach ($match as $key => $val) {
if (is_string($key) && '' !== $val) {
list($name, $pos) = explode('_THINK_', $key);
$var[$name] = $val;
}
}
if (!isset($pos)) {
foreach ($regex as $key => $item) {
if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) {
$pos = $key;
break;
}
}
}
$rule = $items[$pos]->getRule();
$array = $this->router->getRule($rule);
foreach ($array as $item) {
if (in_array($item->getMethod(), ['*', strtolower($request->method())])) {
$result = $item->checkRule($request, $url, $var);
if (false !== $result) {
return $result;
}
}
}
}
return false;
}
/**
* 获取分组的MISS路由
* @access public
* @return RuleItem|null
*/
public function getMissRule()
{
return $this->miss;
}
/**
* 获取分组的自动路由
* @access public
* @return string
*/
public function getAutoRule()
{
return $this->auto;
}
/**
* 注册自动路由
* @access public
* @param string $route 路由规则
* @return void
*/
public function addAutoRule($route)
{
$this->auto = $route;
}
/**
* 注册MISS路由
* @access public
* @param string $route 路由地址
* @param string $method 请求类型
* @param array $option 路由参数
* @return RuleItem
*/
public function addMissRule($route, $method = '*', $option = [])
{
// 创建路由规则实例
$ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method), $option);
$this->miss = $ruleItem;
return $ruleItem;
}
/**
* 添加分组下的路由规则或者子分组
* @access public
* @param string $rule 路由规则
* @param string $route 路由地址
* @param string $method 请求类型
* @param array $option 路由参数
* @param array $pattern 变量规则
* @return $this
*/
public function addRule($rule, $route, $method = '*', $option = [], $pattern = [])
{
// 读取路由标识
if (is_array($rule)) {
$name = $rule[0];
$rule = $rule[1];
} elseif (is_string($route)) {
$name = $route;
} else {
$name = null;
}
$method = strtolower($method);
if ('/' === $rule || '' === $rule) {
// 首页自动完整匹配
$rule .= '$';
}
// 创建路由规则实例
$ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method, $option, $pattern);
if (!empty($option['cross_domain'])) {
$this->router->setCrossDomainRule($ruleItem, $method);
}
$this->addRuleItem($ruleItem, $method);
return $ruleItem;
}
/**
* 批量注册路由规则
* @access public
* @param array $rules 路由规则
* @param string $method 请求类型
* @param array $option 路由参数
* @param array $pattern 变量规则
* @return void
*/
public function addRules($rules, $method = '*', $option = [], $pattern = [])
{
foreach ($rules as $key => $val) {
if (is_numeric($key)) {
$key = array_shift($val);
}
if (is_array($val)) {
$route = array_shift($val);
$option = $val ? array_shift($val) : [];
$pattern = $val ? array_shift($val) : [];
} else {
$route = $val;
}
$this->addRule($key, $route, $method, $option, $pattern);
}
}
public function addRuleItem($rule, $method = '*')
{
if (strpos($method, '|')) {
$rule->method($method);
$method = '*';
}
$this->rules[$method][] = $rule;
return $this;
}
/**
* 设置分组的路由前缀
* @access public
* @param string $prefix
* @return $this
*/
public function prefix($prefix)
{
if ($this->parent && $this->parent->getOption('prefix')) {
$prefix = $this->parent->getOption('prefix') . $prefix;
}
return $this->option('prefix', $prefix);
}
/**
* 设置资源允许
* @access public
* @param array $only
* @return $this
*/
public function only($only)
{
return $this->option('only', $only);
}
/**
* 设置资源排除
* @access public
* @param array $except
* @return $this
*/
public function except($except)
{
return $this->option('except', $except);
}
/**
* 设置资源路由的变量
* @access public
* @param array $vars
* @return $this
*/
public function vars($vars)
{
return $this->option('var', $vars);
}
/**
* 合并分组的路由规则正则
* @access public
* @param bool $merge
* @return $this
*/
public function mergeRuleRegex($merge = true)
{
return $this->option('merge_rule_regex', $merge);
}
/**
* 获取完整分组Name
* @access public
* @return string
*/
public function getFullName()
{
return $this->fullName;
}
/**
* 获取分组的路由规则
* @access public
* @param string $method
* @return array
*/
public function getRules($method = '')
{
if ('' === $method) {
return $this->rules;
}
return isset($this->rules[strtolower($method)]) ? $this->rules[strtolower($method)] : [];
}
/**
* 清空分组下的路由规则
* @access public
* @return void
*/
public function clear()
{
$this->rules = [
'*' => [],
'get' => [],
'post' => [],
'put' => [],
'patch' => [],
'delete' => [],
'head' => [],
'options' => [],
];
}
}

View File

@@ -0,0 +1,292 @@
<?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\route;
use think\Container;
use think\Exception;
use think\Route;
class RuleItem extends Rule
{
protected $hasSetRule;
/**
* 架构函数
* @access public
* @param Route $router 路由实例
* @param RuleGroup $parent 上级对象
* @param string $name 路由标识
* @param string|array $rule 路由规则
* @param string|\Closure $route 路由地址
* @param string $method 请求类型
* @param array $option 路由参数
* @param array $pattern 变量规则
*/
public function __construct(Route $router, RuleGroup $parent, $name, $rule, $route, $method = '*', $option = [], $pattern = [])
{
$this->router = $router;
$this->parent = $parent;
$this->name = $name;
$this->route = $route;
$this->method = $method;
$this->option = $option;
$this->pattern = $pattern;
$this->setRule($rule);
if (!empty($option['cross_domain'])) {
$this->router->setCrossDomainRule($this, $method);
}
}
/**
* 路由规则预处理
* @access public
* @param string $rule 路由规则
* @return void
*/
public function setRule($rule)
{
if ('$' == substr($rule, -1, 1)) {
// 是否完整匹配
$rule = substr($rule, 0, -1);
$this->option['complete_match'] = true;
}
$rule = '/' != $rule ? ltrim($rule, '/') : '';
if ($this->parent && $prefix = $this->parent->getFullName()) {
$rule = $prefix . ($rule ? '/' . ltrim($rule, '/') : '');
}
if (false !== strpos($rule, ':')) {
$this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $rule);
} else {
$this->rule = $rule;
}
// 生成路由标识的快捷访问
$this->setRuleName();
}
/**
* 检查后缀
* @access public
* @param string $ext
* @return $this
*/
public function ext($ext = '')
{
$this->option('ext', $ext);
$this->setRuleName(true);
return $this;
}
/**
* 设置别名
* @access public
* @param string $name
* @return $this
*/
public function name($name)
{
$this->name = $name;
$this->setRuleName(true);
return $this;
}
/**
* 设置路由标识 用于URL反解生成
* @access protected
* @param bool $first 是否插入开头
* @return void
*/
protected function setRuleName($first = false)
{
if ($this->name) {
$vars = $this->parseVar($this->rule);
$name = strtolower($this->name);
if (isset($this->option['ext'])) {
$suffix = $this->option['ext'];
} elseif ($this->parent->getOption('ext')) {
$suffix = $this->parent->getOption('ext');
} else {
$suffix = null;
}
$value = [$this->rule, $vars, $this->parent->getDomain(), $suffix, $this->method];
Container::get('rule_name')->set($name, $value, $first);
}
if (!$this->hasSetRule) {
Container::get('rule_name')->setRule($this->rule, $this);
$this->hasSetRule = true;
}
}
/**
* 检测路由
* @access public
* @param Request $request 请求对象
* @param string $url 访问地址
* @param array $match 匹配路由变量
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
public function checkRule($request, $url, $match = null, $completeMatch = false)
{
// 检查参数有效性
if (!$this->checkOption($this->option, $request)) {
return false;
}
// 合并分组参数
$option = $this->mergeGroupOptions();
$url = $this->urlSuffixCheck($request, $url, $option);
if (is_null($match)) {
$match = $this->match($url, $option, $completeMatch);
}
if (false !== $match) {
if (!empty($option['cross_domain'])) {
if ($dispatch = $this->checkCrossDomain($request)) {
// 允许跨域
return $dispatch;
}
$option['header'] = $this->option['header'];
}
// 检查前置行为
if (isset($option['before']) && false === $this->checkBefore($option['before'])) {
return false;
}
return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match);
}
return false;
}
/**
* 检测路由(含路由匹配)
* @access public
* @param Request $request 请求对象
* @param string $url 访问地址
* @param string $depr 路径分隔符
* @param bool $completeMatch 路由是否完全匹配
* @return Dispatch|false
*/
public function check($request, $url, $completeMatch = false)
{
return $this->checkRule($request, $url, null, $completeMatch);
}
/**
* URL后缀及Slash检查
* @access protected
* @param Request $request 请求对象
* @param string $url 访问地址
* @param array $option 路由参数
* @return string
*/
protected function urlSuffixCheck($request, $url, $option = [])
{
// 是否区分 / 地址访问
if (!empty($option['remove_slash']) && '/' != $this->rule) {
$this->rule = rtrim($this->rule, '/');
$url = rtrim($url, '|');
}
if (isset($option['ext'])) {
// 路由ext参数 优先于系统配置的URL伪静态后缀参数
$url = preg_replace('/\.(' . $request->ext() . ')$/i', '', $url);
}
return $url;
}
/**
* 检测URL和规则路由是否匹配
* @access private
* @param string $url URL地址
* @param array $option 路由参数
* @param bool $completeMatch 路由是否完全匹配
* @return array|false
*/
private function match($url, $option, $completeMatch)
{
if (isset($option['complete_match'])) {
$completeMatch = $option['complete_match'];
}
$pattern = array_merge($this->parent->getPattern(), $this->pattern);
$depr = $this->router->config('pathinfo_depr');
// 检查完整规则定义
if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) {
return false;
}
$var = [];
$url = $depr . str_replace('|', $depr, $url);
$rule = $depr . str_replace('/', $depr, $this->rule);
if ($depr == $rule && $depr != $url) {
return false;
}
if (false === strpos($rule, '<')) {
if (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr)))) {
return $var;
}
return false;
}
$slash = preg_quote('/-' . $depr, '/');
if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) {
if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
return false;
}
}
if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
$regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch);
try {
if (!preg_match('/^' . $regex . ($completeMatch ? '$' : '') . '/u', $url, $match)) {
return false;
}
} catch (\Exception $e) {
throw new Exception('route pattern error');
}
foreach ($match as $key => $val) {
if (is_string($key)) {
$var[$key] = $val;
}
}
}
// 成功匹配后返回URL中的动态变量数组
return $var;
}
}

View File

@@ -0,0 +1,147 @@
<?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\route;
class RuleName
{
protected $item = [];
protected $rule = [];
/**
* 注册路由标识
* @access public
* @param string $name 路由标识
* @param array $value 路由规则
* @param bool $first 是否置顶
* @return void
*/
public function set($name, $value, $first = false)
{
if ($first && isset($this->item[$name])) {
array_unshift($this->item[$name], $value);
} else {
$this->item[$name][] = $value;
}
}
/**
* 注册路由规则
* @access public
* @param string $rule 路由规则
* @param RuleItem $route 路由
* @return void
*/
public function setRule($rule, $route)
{
$this->rule[$route->getDomain()][$rule][$route->getMethod()] = $route;
}
/**
* 根据路由规则获取路由对象(列表)
* @access public
* @param string $name 路由标识
* @param string $domain 域名
* @return array
*/
public function getRule($rule, $domain = null)
{
return isset($this->rule[$domain][$rule]) ? $this->rule[$domain][$rule] : [];
}
/**
* 获取全部路由列表
* @access public
* @param string $domain 域名
* @return array
*/
public function getRuleList($domain = null)
{
$list = [];
foreach ($this->rule as $ruleDomain => $rules) {
foreach ($rules as $rule => $items) {
foreach ($items as $item) {
$val['domain'] = $ruleDomain;
foreach (['method', 'rule', 'name', 'route', 'pattern', 'option'] as $param) {
$call = 'get' . $param;
$val[$param] = $item->$call();
}
$list[$ruleDomain][] = $val;
}
}
}
if ($domain) {
return isset($list[$domain]) ? $list[$domain] : [];
}
return $list;
}
/**
* 导入路由标识
* @access public
* @param array $name 路由标识
* @return void
*/
public function import($item)
{
$this->item = $item;
}
/**
* 根据路由标识获取路由信息用于URL生成
* @access public
* @param string $name 路由标识
* @param string $domain 域名
* @return array|null
*/
public function get($name = null, $domain = null, $method = '*')
{
if (is_null($name)) {
return $this->item;
}
$name = strtolower($name);
$method = strtolower($method);
if (isset($this->item[$name])) {
if (is_null($domain)) {
$result = $this->item[$name];
} else {
$result = [];
foreach ($this->item[$name] as $item) {
if ($item[2] == $domain && ('*' == $item[4] || $method == $item[4])) {
$result[] = $item;
}
}
}
} else {
$result = null;
}
return $result;
}
/**
* 清空路由规则
* @access public
* @return void
*/
public function clear()
{
$this->item = [];
$this->rule = [];
}
}

View File

@@ -0,0 +1,26 @@
<?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\route\dispatch;
use think\route\Dispatch;
class Callback extends Dispatch
{
public function exec()
{
// 执行回调方法
$vars = array_merge($this->request->param(), $this->param);
return $this->app->invoke($this->dispatch, $vars);
}
}

View File

@@ -0,0 +1,30 @@
<?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\route\dispatch;
use think\route\Dispatch;
class Controller extends Dispatch
{
public function exec()
{
// 执行控制器的操作方法
$vars = array_merge($this->request->param(), $this->param);
return $this->app->action(
$this->dispatch, $vars,
$this->rule->getConfig('url_controller_layer'),
$this->rule->getConfig('controller_suffix')
);
}
}

View File

@@ -0,0 +1,138 @@
<?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\route\dispatch;
use ReflectionMethod;
use think\exception\ClassNotFoundException;
use think\exception\HttpException;
use think\Loader;
use think\Request;
use think\route\Dispatch;
class Module extends Dispatch
{
protected $controller;
protected $actionName;
public function init()
{
parent::init();
$result = $this->dispatch;
if (is_string($result)) {
$result = explode('/', $result);
}
if ($this->rule->getConfig('app_multi_module')) {
// 多模块部署
$module = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module')));
$bind = $this->rule->getRouter()->getBind();
$available = false;
if ($bind && preg_match('/^[a-z]/is', $bind)) {
// 绑定模块
list($bindModule) = explode('/', $bind);
if (empty($result[0])) {
$module = $bindModule;
}
$available = true;
} elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) {
$available = true;
} elseif ($this->rule->getConfig('empty_module')) {
$module = $this->rule->getConfig('empty_module');
$available = true;
}
// 模块初始化
if ($module && $available) {
// 初始化模块
$this->request->setModule($module);
$this->app->init($module);
} else {
throw new HttpException(404, 'module not exists:' . $module);
}
}
// 是否自动转换控制器和操作名
$convert = is_bool($this->convert) ? $this->convert : $this->rule->getConfig('url_convert');
// 获取控制器名
$controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));
$this->controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
$this->actionName = strip_tags($result[2] ?: $this->rule->getConfig('default_action'));
// 设置当前请求的控制器、操作
$this->request
->setController(Loader::parseName($this->controller, 1))
->setAction($this->actionName);
return $this;
}
public function exec()
{
// 监听module_init
$this->app['hook']->listen('module_init');
try {
// 实例化控制器
$instance = $this->app->controller($this->controller,
$this->rule->getConfig('url_controller_layer'),
$this->rule->getConfig('controller_suffix'),
$this->rule->getConfig('empty_controller'));
} catch (ClassNotFoundException $e) {
throw new HttpException(404, 'controller not exists:' . $e->getClass());
}
$this->app['middleware']->controller(function (Request $request, $next) use ($instance) {
// 获取当前操作名
$action = $this->actionName . $this->rule->getConfig('action_suffix');
if (is_callable([$instance, $action])) {
// 执行操作方法
$call = [$instance, $action];
// 严格获取当前操作方法名
$reflect = new ReflectionMethod($instance, $action);
$methodName = $reflect->getName();
$suffix = $this->rule->getConfig('action_suffix');
$actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
$this->request->setAction($actionName);
// 自动获取请求变量
$vars = $this->rule->getConfig('url_param_type')
? $this->request->route()
: $this->request->param();
$vars = array_merge($vars, $this->param);
} elseif (is_callable([$instance, '_empty'])) {
// 空操作
$call = [$instance, '_empty'];
$vars = [$this->actionName];
$reflect = new ReflectionMethod($instance, '_empty');
} else {
// 操作不存在
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}
$this->app['hook']->listen('action_begin', $call);
$data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
return $this->autoResponse($data);
});
return $this->app['middleware']->dispatch($this->request, 'controller');
}
}

View File

@@ -0,0 +1,23 @@
<?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\route\dispatch;
use think\Response;
use think\route\Dispatch;
class Redirect extends Dispatch
{
public function exec()
{
return Response::create($this->dispatch, 'redirect')->code($this->code);
}
}

View File

@@ -0,0 +1,23 @@
<?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\route\dispatch;
use think\route\Dispatch;
class Response extends Dispatch
{
public function exec()
{
return $this->dispatch;
}
}

View File

@@ -0,0 +1,169 @@
<?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\route\dispatch;
use think\exception\HttpException;
use think\Loader;
use think\route\Dispatch;
class Url extends Dispatch
{
public function init()
{
// 解析默认的URL规则
$result = $this->parseUrl($this->dispatch);
return (new Module($this->request, $this->rule, $result))->init();
}
public function exec()
{}
/**
* 解析URL地址
* @access protected
* @param string $url URL
* @return array
*/
protected function parseUrl($url)
{
$depr = $this->rule->getConfig('pathinfo_depr');
$bind = $this->rule->getRouter()->getBind();
if (!empty($bind) && preg_match('/^[a-z]/is', $bind)) {
$bind = str_replace('/', $depr, $bind);
// 如果有模块/控制器绑定
$url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
}
list($path, $var) = $this->rule->parseUrlPath($url);
if (empty($path)) {
return [null, null, null];
}
// 解析模块
$module = $this->rule->getConfig('app_multi_module') ? array_shift($path) : null;
if ($this->param['auto_search']) {
$controller = $this->autoFindController($module, $path);
} else {
// 解析控制器
$controller = !empty($path) ? array_shift($path) : null;
}
if ($controller && !preg_match('/^[A-Za-z0-9][\w|\.]*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller);
}
// 解析操作
$action = !empty($path) ? array_shift($path) : null;
// 解析额外参数
if ($path) {
if ($this->rule->getConfig('url_param_type')) {
$var += $path;
} else {
preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
$var[$match[1]] = strip_tags($match[2]);
}, implode('|', $path));
}
}
$panDomain = $this->request->panDomain();
if ($panDomain && $key = array_search('*', $var)) {
// 泛域名赋值
$var[$key] = $panDomain;
}
// 设置当前请求的参数
$this->request->setRouteVars($var);
// 封装路由
$route = [$module, $controller, $action];
if ($this->hasDefinedRoute($route, $bind)) {
throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
}
return $route;
}
/**
* 检查URL是否已经定义过路由
* @access protected
* @param string $route 路由信息
* @param string $bind 绑定信息
* @return bool
*/
protected function hasDefinedRoute($route, $bind)
{
list($module, $controller, $action) = $route;
// 检查地址是否被定义过路由
$name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action);
$name2 = '';
if (empty($module) || $module == $bind) {
$name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action);
}
$host = $this->request->host(true);
$method = $this->request->method();
if ($this->rule->getRouter()->getName($name, $host, $method) || $this->rule->getRouter()->getName($name2, $host, $method)) {
return true;
}
return false;
}
/**
* 自动定位控制器类
* @access protected
* @param string $module 模块名
* @param array $path URL
* @return string
*/
protected function autoFindController($module, &$path)
{
$dir = $this->app->getAppPath() . ($module ? $module . '/' : '') . $this->rule->getConfig('url_controller_layer');
$suffix = $this->app->getSuffix() || $this->rule->getConfig('controller_suffix') ? ucfirst($this->rule->getConfig('url_controller_layer')) : '';
$item = [];
$find = false;
foreach ($path as $val) {
$item[] = $val;
$file = $dir . '/' . str_replace('.', '/', $val) . $suffix . '.php';
$file = pathinfo($file, PATHINFO_DIRNAME) . '/' . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . '.php';
if (is_file($file)) {
$find = true;
break;
} else {
$dir .= '/' . Loader::parseName($val);
}
}
if ($find) {
$controller = implode('.', $item);
$path = array_slice($path, count($item));
} else {
$controller = array_shift($path);
}
return $controller;
}
}

View File

@@ -0,0 +1,26 @@
<?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\route\dispatch;
use think\Response;
use think\route\Dispatch;
class View extends Dispatch
{
public function exec()
{
// 渲染模板输出
$vars = array_merge($this->request->param(), $this->param);
return Response::create($this->dispatch, 'view')->assign($vars);
}
}