diff --git a/thinkphp/base.php b/thinkphp/base.php index b668120..5bd1b40 100644 --- a/thinkphp/base.php +++ b/thinkphp/base.php @@ -9,7 +9,7 @@ // | Author: liu21st // +---------------------------------------------------------------------- -define('THINK_VERSION', '5.0.2'); +define('THINK_VERSION', '5.0.3'); define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); diff --git a/thinkphp/convention.php b/thinkphp/convention.php index 2ef29dc..8eb30f4 100644 --- a/thinkphp/convention.php +++ b/thinkphp/convention.php @@ -103,6 +103,10 @@ return [ 'var_ajax' => '_ajax', // 表单pjax伪装变量 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, // +---------------------------------------------------------------------- // | 模板设置 diff --git a/thinkphp/helper.php b/thinkphp/helper.php index bcb52ea..b81970c 100644 --- a/thinkphp/helper.php +++ b/thinkphp/helper.php @@ -117,7 +117,7 @@ if (!function_exists('input')) { * @param string $filter 过滤方法 * @return mixed */ - function input($key = '', $default = null, $filter = null) + function input($key = '', $default = null, $filter = '') { if (0 === strpos($key, '?')) { $key = substr($key, 1); @@ -125,10 +125,9 @@ if (!function_exists('input')) { } if ($pos = strpos($key, '.')) { // 指定参数来源 - $method = substr($key, 0, $pos); - if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { - $key = substr($key, $pos + 1); - } else { + list($method, $key) = explode('.', $key); + if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { + $key = $method . '.' . $key; $method = 'param'; } } else { @@ -359,12 +358,17 @@ if (!function_exists('cache')) { // 缓存初始化 return Cache::connect($name); } - if ('' === $value) { + if (is_null($name)) { + return Cache::clear($value); + } elseif ('' === $value) { // 获取缓存 return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name); } elseif (is_null($value)) { // 删除缓存 return Cache::rm($name); + } elseif (0 === strpos($name, '?') && '' !== $value) { + $expire = is_numeric($options) ? $options : null; + return Cache::remember(substr($name, 1), $value, $expire); } else { // 缓存数据 if (is_array($options)) { diff --git a/thinkphp/library/think/App.php b/thinkphp/library/think/App.php index ddb147f..4eddc7c 100644 --- a/thinkphp/library/think/App.php +++ b/thinkphp/library/think/App.php @@ -127,6 +127,8 @@ class App // 监听app_begin Hook::listen('app_begin', $dispatch); + // 请求缓存检查 + $request->cache($config['request_cache'], $config['request_cache_expire']); switch ($dispatch['type']) { case 'redirect': @@ -377,12 +379,14 @@ class App // 获取当前操作名 $action = $actionName . $config['action_suffix']; + $vars = []; if (is_callable([$instance, $action])) { // 执行操作方法 $call = [$instance, $action]; } elseif (is_callable([$instance, '_empty'])) { // 空操作 $call = [$instance, '_empty']; + $vars = [$action]; } else { // 操作不存在 throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); @@ -390,9 +394,7 @@ class App Hook::listen('action_begin', $call); - $data = self::invokeMethod($call); - - return $data; + return self::invokeMethod($call, $vars); } /** diff --git a/thinkphp/library/think/Config.php b/thinkphp/library/think/Config.php index d7c3dc5..fb5340d 100644 --- a/thinkphp/library/think/Config.php +++ b/thinkphp/library/think/Config.php @@ -59,6 +59,7 @@ class Config self::$config[$range] = []; } if (is_file($file)) { + $name = strtolower($name); $type = pathinfo($file, PATHINFO_EXTENSION); if ('php' == $type) { return self::set(include $file, $name, $range); diff --git a/thinkphp/library/think/Db.php b/thinkphp/library/think/Db.php index b00aee6..c67cba6 100644 --- a/thinkphp/library/think/Db.php +++ b/thinkphp/library/think/Db.php @@ -42,6 +42,9 @@ use think\paginator\Collection as PaginatorCollection; * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 * @method PaginatorCollection paginate(integer $listRows = 15, mixed $simple = false, array $config = []) static 分页查询 * @method mixed transaction(callable $callback) static 执行数据库事务 + * @method void startTrans() static 启动事务 + * @method void commit() static 用于非自动提交状态下面的查询提交 + * @method void rollback() static 事务回滚 * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 */ class Db diff --git a/thinkphp/library/think/File.php b/thinkphp/library/think/File.php index 43f3857..60a1641 100644 --- a/thinkphp/library/think/File.php +++ b/thinkphp/library/think/File.php @@ -39,7 +39,7 @@ class File extends SplFileObject public function __construct($filename, $mode = 'r') { parent::__construct($filename, $mode); - $this->filename = $this->getRealPath(); + $this->filename = $this->getRealPath() ?: $this->getPathname(); } /** diff --git a/thinkphp/library/think/Model.php b/thinkphp/library/think/Model.php index 561792a..aed24f7 100644 --- a/thinkphp/library/think/Model.php +++ b/thinkphp/library/think/Model.php @@ -101,6 +101,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess protected $failException = false; // 全局查询范围 protected $useGlobalScope = true; + // 是否采用批量验证 + protected $batchValidate = false; /** * 初始化过的模型. @@ -741,7 +743,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 数据批量验证 $validate = $this->validate; foreach ($dataSet as $data) { - if (!$this->validate($validate)->validateData($data)) { + if (!$this->validateData($data, $validate)) { return false; } } @@ -854,9 +856,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access public * @param array|string|bool $rule 验证规则 true表示自动读取验证器类 * @param array $msg 提示信息 + * @param bool $batch 批量验证 * @return $this */ - public function validate($rule = true, $msg = []) + public function validate($rule = true, $msg = [], $batch = false) { if (is_array($rule)) { $this->validate = [ @@ -866,6 +869,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } else { $this->validate = true === $rule ? $this->name : $rule; } + $this->batchValidate = $batch; return $this; } @@ -885,12 +889,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * 自动验证数据 * @access protected * @param array $data 验证数据 + * @param mixed $rule 验证规则 + * @param bool $batch 批量验证 * @return bool */ - protected function validateData($data) + protected function validateData($data, $rule = null, $batch = null) { - if (!empty($this->validate)) { - $info = $this->validate; + $info = is_null($rule) ? $this->validate : $rule; + + if (!empty($info)) { if (is_array($info)) { $validate = Loader::validate(); $validate->rule($info['rule']); @@ -905,7 +912,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $validate->scene($scene); } } - if (!$validate->check($data)) { + $batch = is_null($batch) ? $this->batchValidate : $batch; + + if (!$validate->batch($batch)->check($data)) { $this->error = $validate->getError(); if ($this->failException) { throw new ValidateException($this->error); @@ -1356,19 +1365,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public static function __callStatic($method, $params) { - $query = self::getDb(); + $query = (new static())->db(); return call_user_func_array([$query, $method], $params); } - protected static function getDb() - { - $model = get_called_class(); - if (!isset(self::$links[$model])) { - self::$links[$model] = (new static())->db(); - } - return self::$links[$model]; - } - /** * 修改器 设置数据对象的值 * @access public diff --git a/thinkphp/library/think/Request.php b/thinkphp/library/think/Request.php index c6f1198..45b6542 100644 --- a/thinkphp/library/think/Request.php +++ b/thinkphp/library/think/Request.php @@ -121,6 +121,8 @@ class Request protected $input; // 请求缓存 protected $cache; + // 缓存是否检查 + protected $isCheckCache; /** * 架构函数 @@ -607,7 +609,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function param($name = '', $default = null, $filter = null) + public function param($name = '', $default = null, $filter = '') { if (empty($this->param)) { $method = $this->method(true); @@ -644,7 +646,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function route($name = '', $default = null, $filter = null) + public function route($name = '', $default = null, $filter = '') { if (is_array($name)) { $this->param = []; @@ -661,7 +663,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function get($name = '', $default = null, $filter = null) + public function get($name = '', $default = null, $filter = '') { if (empty($this->get)) { $this->get = $_GET; @@ -681,7 +683,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function post($name = '', $default = null, $filter = null) + public function post($name = '', $default = null, $filter = '') { if (empty($this->post)) { $this->post = $_POST; @@ -701,7 +703,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function put($name = '', $default = null, $filter = null) + public function put($name = '', $default = null, $filter = '') { if (is_null($this->put)) { $content = $this->input; @@ -727,7 +729,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function delete($name = '', $default = null, $filter = null) + public function delete($name = '', $default = null, $filter = '') { return $this->put($name, $default, $filter); } @@ -740,7 +742,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function patch($name = '', $default = null, $filter = null) + public function patch($name = '', $default = null, $filter = '') { return $this->put($name, $default, $filter); } @@ -752,7 +754,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function request($name = '', $default = null, $filter = null) + public function request($name = '', $default = null, $filter = '') { if (empty($this->request)) { $this->request = $_REQUEST; @@ -772,7 +774,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function session($name = '', $default = null, $filter = null) + public function session($name = '', $default = null, $filter = '') { if (empty($this->session)) { $this->session = Session::get(); @@ -791,7 +793,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function cookie($name = '', $default = null, $filter = null) + public function cookie($name = '', $default = null, $filter = '') { if (empty($this->cookie)) { $this->cookie = $_COOKIE; @@ -810,7 +812,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function server($name = '', $default = null, $filter = null) + public function server($name = '', $default = null, $filter = '') { if (empty($this->server)) { $this->server = $_SERVER; @@ -888,7 +890,7 @@ class Request * @param string|array $filter 过滤方法 * @return mixed */ - public function env($name = '', $default = null, $filter = null) + public function env($name = '', $default = null, $filter = '') { if (empty($this->env)) { $this->env = $_ENV; @@ -947,7 +949,7 @@ class Request * @param string|array $filter 过滤函数 * @return mixed */ - public function input($data = [], $name = '', $default = null, $filter = null) + public function input($data = [], $name = '', $default = null, $filter = '') { if (false === $name) { // 获取原始数据 @@ -976,13 +978,17 @@ class Request } // 解析过滤器 - $filter = $filter ?: $this->filter; - - if (is_string($filter)) { - $filter = explode(',', $filter); + if (is_null($filter)) { + $filter = []; } else { - $filter = (array) $filter; + $filter = $filter ?: $this->filter; + if (is_string($filter)) { + $filter = explode(',', $filter); + } else { + $filter = (array) $filter; + } } + $filter[] = $default; if (is_array($data)) { array_walk_recursive($data, [$this, 'filterValue'], $filter); @@ -1471,15 +1477,34 @@ class Request } /** - * 读取或者设置缓存 + * 设置当前地址的请求缓存 * @access public * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id * @param mixed $expire 缓存有效期 - * @return mixed + * @return void */ public function cache($key, $expire = null) { - if ($this->isGet()) { + if (false !== $key && $this->isGet() && !$this->isCheckCache) { + // 标记请求缓存检查 + $this->isCheckCache = true; + if (false === $expire) { + // 关闭当前缓存 + return; + } + if ($key instanceof \Closure) { + $key = call_user_func_array($key, [$this]); + } elseif (true === $key) { + // 自动缓存功能 + $key = '__URL__'; + } elseif (strpos($key, '|')) { + list($key, $fun) = explode('|', $key); + } + // 特殊规则替换 + if (false !== strpos($key, '__')) { + $key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__'], [$this->module, $this->controller, $this->action, md5($this->url())], $key); + } + if (false !== strpos($key, ':')) { $param = $this->param(); foreach ($param as $item => $val) { @@ -1487,9 +1512,6 @@ class Request $key = str_replace(':' . $item, $val, $key); } } - } elseif ('__URL__' == $key) { - // 当前URL地址作为缓存标识 - $key = md5($this->url()); } elseif (strpos($key, ']')) { if ('[' . $this->ext() . ']' == $key) { // 缓存某个后缀的请求 @@ -1498,6 +1520,9 @@ class Request return; } } + if (isset($fun)) { + $key = $fun($key); + } if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) { // 读取缓存 @@ -1514,7 +1539,7 @@ class Request } /** - * 读取缓存设置 + * 读取请求缓存设置 * @access public * @return array */ diff --git a/thinkphp/library/think/Route.php b/thinkphp/library/think/Route.php index 2db507e..c8eb24e 100644 --- a/thinkphp/library/think/Route.php +++ b/thinkphp/library/think/Route.php @@ -69,6 +69,8 @@ class Route private static $domainRule; // 当前域名 private static $domain; + // 当前路由执行过程中的参数 + private static $option = []; /** * 注册变量规则 @@ -127,7 +129,7 @@ class Route * 设置路由绑定 * @access public * @param mixed $bind 绑定信息 - * @param string $type 绑定类型 默认为module 支持 namespace class + * @param string $type 绑定类型 默认为module 支持 namespace class controller * @return mixed */ public static function bind($bind, $type = 'module') @@ -302,12 +304,12 @@ class Route $rule = substr($rule, 0, -1); } - if ('/' != $rule) { + if ('/' != $rule || $group) { $rule = trim($rule, '/'); } $vars = self::parseVar($rule); if (isset($name)) { - $key = $group ? $group . '/' . $rule : $rule; + $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; self::name(strtolower($name), [$key, $vars, self::$domain]); } if ($group) { @@ -341,6 +343,27 @@ class Route } } + /** + * 设置当前执行的参数信息 + * @access public + * @param array $options 参数信息 + * @return mixed + */ + protected static function setOption($options = []) + { + self::$option[] = $options; + } + + /** + * 获取当前执行的所有参数信息 + * @access public + * @return array + */ + public static function getOption() + { + return self::$option; + } + /** * 获取当前的分组信息 * @access public @@ -425,10 +448,11 @@ class Route $options['complete_match'] = true; $key = substr($key, 0, -1); } + $key = trim($key, '/'); $vars = self::parseVar($key); $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns]; // 设置路由标识 - self::name($route, [$name . '/' . $key, $vars, self::$domain]); + self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain]); } self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern]; } @@ -578,7 +602,8 @@ class Route } elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) { $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]); } - $item = ltrim($rule . $val[1], '/'); + $item = ltrim($rule . $val[1], '/'); + $option['rest'] = $key; self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern); } } @@ -637,7 +662,7 @@ class Route * rest方法定义和修改 * @access public * @param string $name 方法名称 - * @param array $resourece 资源 + * @param array $resource 资源 * @return void */ public static function rest($name, $resource = []) @@ -833,6 +858,7 @@ class Route $rule = self::getRouteExpress($item); } if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) { + self::setOption($rule['option']); return self::parseRule($item, $rule['route'], $url, $rule['option']); } } @@ -896,7 +922,7 @@ class Route if (is_string($str) && $str && 0 !== strpos(str_replace('|', '/', $url), $str)) { continue; } - + self::setOption($option); $result = self::checkRoute($request, $rule, $url, $depr, $key, $option); if (false !== $result) { return $result; @@ -911,6 +937,8 @@ class Route if ($group) { $rule = $group . ($rule ? '/' . ltrim($rule, '/') : ''); } + + self::setOption($option); if (isset($options['bind_model']) && isset($option['bind_model'])) { $option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']); } @@ -997,6 +1025,9 @@ class Route case 'class': // 绑定到类 return self::bindToClass($url, $bind, $depr); + case 'controller': + // 绑定到控制器类 + return self::bindToController($url, $bind, $depr); case 'namespace': // 绑定到命名空间 return self::bindToNamespace($url, $bind, $depr); @@ -1041,7 +1072,7 @@ class Route if (!empty($array[2])) { self::parseUrlParams($array[2]); } - return ['type' => 'method', 'method' => [$namespace . '\\' . $class, $method]]; + return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method]]; } /** @@ -1091,14 +1122,16 @@ class Route */ private static function checkOption($option, $request) { - // 请求类型检测 if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method())) - || (!empty($option['ajax']) && !$request->isAjax()) // Ajax检测 - || (!empty($option['pjax']) && !$request->isPjax()) // Pjax检测 + || (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测 + || (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测 + || (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测 + || (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测 || (isset($option['ext']) && false === stripos($option['ext'], $request->ext())) // 伪静态后缀检测 || (isset($option['deny_ext']) && false !== stripos($option['deny_ext'], $request->ext())) || (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测 - || (!empty($option['https']) && !$request->isSsl()) // https检测 + || (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测 + || (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测 || (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测 || (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测 ) { @@ -1184,7 +1217,9 @@ class Route $find = false; foreach ($path as $val) { $item[] = $val; - if (is_file($dir . DS . str_replace('.', DS, $val) . $suffix . EXT)) { + $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT; + $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT; + if (is_file($file)) { $find = true; break; } else { @@ -1370,7 +1405,6 @@ class Route foreach ($matches as $key => $val) { if (false !== strpos($route, ':' . $key)) { $route = str_replace(':' . $key, $val, $route); - unset($matches[$key]); } } } @@ -1457,7 +1491,7 @@ class Route $result = self::parseModule($route); } // 开启请求缓存 - if ($request->isGet() && !empty($option['cache'])) { + if ($request->isGet() && isset($option['cache'])) { $cache = $option['cache']; if (is_array($cache)) { list($key, $expire) = $cache; diff --git a/thinkphp/library/think/Url.php b/thinkphp/library/think/Url.php index 18b42c4..d873074 100644 --- a/thinkphp/library/think/Url.php +++ b/thinkphp/library/think/Url.php @@ -20,6 +20,7 @@ class Url { // 生成URL地址的root protected static $root; + protected static $bindCheck; /** * URL生成 支持路由反射 @@ -31,7 +32,7 @@ class Url */ public static function build($url = '', $vars = '', $suffix = true, $domain = false) { - if (false === $domain && Config::get('url_domain_deploy')) { + if (false === $domain && Route::rules('domain')) { $domain = true; } // 解析URL @@ -80,7 +81,7 @@ class Url // 匹配路由命名标识 $url = $match[0]; // 替换可选分隔符 - $url = preg_replace(['/\((\W)\?\)$/', '/\((\W)\?\)/'], ['', '\1'], $url); + $url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url); if (!empty($match[1])) { $domain = $match[1]; } @@ -112,11 +113,13 @@ class Url } // 检测URL绑定 - $type = Route::getBind('type'); - if ($type) { - $bind = Route::getBind($type); - if (0 === strpos($url, $bind)) { - $url = substr($url, strlen($bind) + 1); + if (!self::$bindCheck) { + $type = Route::getBind('type'); + if ($type) { + $bind = Route::getBind($type); + if (0 === strpos($url, $bind)) { + $url = substr($url, strlen($bind) + 1); + } } } // 还原URL分隔符 @@ -152,7 +155,8 @@ class Url // 检测域名 $domain = self::parseDomain($url, $domain); // URL组装 - $url = $domain . (self::$root ?: Request::instance()->root()) . '/' . ltrim($url, '/'); + $url = $domain . (self::$root ?: Request::instance()->root()) . '/' . ltrim($url, '/'); + self::$bindCheck = false; return $url; } @@ -171,16 +175,29 @@ class Url $url = substr($url, 1); } else { // 解析到 模块/控制器/操作 - $module = $request->module(); - if (true === $domain) { - $domains = Route::rules('domain'); + $module = $request->module(); + $domains = Route::rules('domain'); + if (true === $domain && 2 == substr_count($url, '/')) { + $current = $request->host(); + $match = []; + $pos = []; foreach ($domains as $key => $item) { if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) { - $url = substr($url, strlen($item['[bind]'][0]) + 1); - $domain = $key; - $module = ''; + $pos[$key] = strlen($item['[bind]'][0]) + 1; + $match[] = $key; + $module = ''; } } + if ($match) { + $domain = current($match); + foreach ($match as $item) { + if (0 === strpos($current, $item)) { + $domain = $item; + } + } + self::$bindCheck = true; + $url = substr($url, $pos[$domain]); + } } elseif ($domain) { if (isset($domains[$domain]['[bind]'][0])) { $bindModule = $domains[$domain]['[bind]'][0]; @@ -212,15 +229,15 @@ class Url if (!$domain) { return ''; } - $request = Request::instance(); + $request = Request::instance(); + $rootDomain = Config::get('url_domain_root'); if (true === $domain) { // 自动判断域名 $domain = $request->host(); - if (Config::get('url_domain_deploy')) { - // 根域名 - $urlDomainRoot = Config::get('url_domain_root'); - $domains = Route::rules('domain'); - $route_domain = array_keys($domains); + + $domains = Route::rules('domain'); + if ($domains) { + $route_domain = array_keys($domains); foreach ($route_domain as $domain_prefix) { if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) { foreach ($domains as $key => $rule) { @@ -229,13 +246,13 @@ class Url $url = ltrim($url, $rule); $domain = $key; // 生成对应子域名 - if (!empty($urlDomainRoot)) { - $domain .= $urlDomainRoot; + if (!empty($rootDomain)) { + $domain .= $rootDomain; } break; } else if (false !== strpos($key, '*')) { - if (!empty($urlDomainRoot)) { - $domain .= $urlDomainRoot; + if (!empty($rootDomain)) { + $domain .= $rootDomain; } break; } @@ -243,13 +260,15 @@ class Url } } } - } elseif (!strpos($domain, '.')) { - $rootDomain = Config::get('url_domain_root'); + + } else { if (empty($rootDomain)) { $host = $request->host(); $rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host; } - $domain .= '.' . $rootDomain; + if (!strpos($domain, $rootDomain)) { + $domain .= '.' . $rootDomain; + } } return ($request->isSsl() ? 'https://' : 'http://') . $domain; } diff --git a/thinkphp/library/think/db/Connection.php b/thinkphp/library/think/db/Connection.php index f39ca34..9806b82 100644 --- a/thinkphp/library/think/db/Connection.php +++ b/thinkphp/library/think/db/Connection.php @@ -433,7 +433,7 @@ abstract class Connection $sql . ' '); } } - return $sql; + return rtrim($sql); } /** diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php index c78b17c..3f4d5fd 100644 --- a/thinkphp/library/think/db/Query.php +++ b/thinkphp/library/think/db/Query.php @@ -507,22 +507,24 @@ class Query * MIN查询 * @access public * @param string $field 字段名 - * @return float|int + * @return mixed */ public function min($field = '*') { - return $this->value('MIN(' . $field . ') AS tp_min', 0) + 0; + $value = $this->value('MIN(' . $field . ') AS tp_min', 0); + return is_numeric($value) ? $value + 0 : $value; } /** * MAX查询 * @access public * @param string $field 字段名 - * @return float|int + * @return mixed */ public function max($field = '*') { - return $this->value('MAX(' . $field . ') AS tp_max', 0) + 0; + $value = $this->value('MAX(' . $field . ') AS tp_max', 0); + return is_numeric($value) ? $value + 0 : $value; } /** @@ -659,35 +661,53 @@ class Query } } } else { - // 传入的表名为数组 - if (is_array($join)) { - if (0 !== $key = key($join)) { - // 设置了键名则键名为表名,键值作为表的别名 - $table = [$key => array_shift($join)]; - $this->alias($table); - } else { - $table = array_shift($join); - } - } else { - $prefix = $this->prefix; - $join = trim($join); - if ($prefix && false === strpos($join, ' ') && false === strpos($join, '(') && false === strpos($join, '.') && 0 !== strpos($join, $prefix) && 0 !== strpos($join, '__')) { - $table = $this->getTable($join); - $table = [$table => $join]; - $this->alias($table); - } elseif (strpos($join, ' ') && !strpos($join, ')')) { - list($table, $alias) = explode(' ', $join); - $table = [$table => $alias]; - $this->alias($table); - } else { - $table = $join; - } - } + $table = $this->getJoinTable($join); + $this->options['join'][] = [$table, strtoupper($type), $condition]; } return $this; } + /** + * 获取Join表名及别名 支持 + * ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias' + * @access public + * @param array|string $join + * @return array|string + */ + protected function getJoinTable($join, &$alias = null) + { + // 传入的表名为数组 + if (is_array($join)) { + list($table, $alias) = each($join); + } else { + $join = trim($join); + if (false !== strpos($join, '(')) { + // 使用子查询 + $table = $join; + } else { + $prefix = $this->prefix; + if (strpos($join, ' ')) { + // 使用别名 + list($table, $alias) = explode(' ', $join); + } else { + $table = $join; + if (false === strpos($join, '.') && 0 !== strpos($join, '__')) { + $alias = $join; + } + } + if ($prefix && false === strpos($table, '.') && 0 !== strpos($table, $prefix) && 0 !== strpos($table, '__')) { + $table = $this->getTable($table); + } + } + } + if (isset($alias)) { + $table = [$table => $alias]; + $this->alias($table); + } + return $table; + } + /** * 查询SQL组装 union * @access public @@ -770,19 +790,8 @@ class Query } } else { $fields = []; - $prefix = $this->prefix; - if (is_array($join)) { - // 支持数据表别名 - list($table, $alias) = each($join); - } elseif ($prefix && false === strpos($join, ' ') && 0 !== strpos($join, $prefix) && 0 !== strpos($join, '__')) { - $table = $this->getTable($join); - $alias = $join; - } elseif (strpos($join, ' ')) { - list($table, $alias) = explode(' ', $join); - } else { - $alias = $join; - } - $table = isset($table) ? [$table => $alias] : $alias; + $table = $this->getJoinTable($join, $alias); + if (true === $field) { $fields = $alias . '.*'; } else { @@ -906,13 +915,9 @@ class Query if (is_array($field)) { // 数组批量查询 $where = $field; - } elseif ($field) { + } elseif ($field && is_string($field)) { // 字符串查询 - if (is_numeric($field)) { - $where[] = ['exp', $field]; - } else { - $where[$field] = ['null', '']; - } + $where[$field] = ['null', '']; } } elseif (is_array($op)) { $where[$field] = $param; @@ -933,6 +938,22 @@ class Query } } + /** + * 去除某个查询条件 + * @access public + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function removeWhereField($field, $logic = 'AND') + { + $logic = strtoupper($logic); + if (isset($this->options['where'][$logic][$field])) { + unset($this->options['where'][$logic][$field]); + } + return $this; + } + /** * 指定查询数量 * @access public @@ -1002,6 +1023,9 @@ class Query if (!isset($total) && !$simple) { $options = $this->getOptions(); + if (isset($options['order'])) { + unset($this->options['order']); + } $bind = $this->bind; $total = $this->count(); $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); @@ -1183,6 +1207,9 @@ class Query } else { if (isset($this->options['table'])) { $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + if (false !== strpos($table, '__')) { + $table = $this->parseSqlTable($table); + } } else { $table = $this->getTable(); } @@ -1604,6 +1631,8 @@ class Query $field = $this->options['with_field']; unset($this->options['with_field']); } + } elseif (isset($info['option']['field'])) { + $field = $info['option']['field']; } $this->field($field, false, $joinTable, $joinAlias, $relation . '__'); $i++; diff --git a/thinkphp/library/think/model/Merge.php b/thinkphp/library/think/model/Merge.php index c28d0fd..de8d9e1 100644 --- a/thinkphp/library/think/model/Merge.php +++ b/thinkphp/library/think/model/Merge.php @@ -61,14 +61,14 @@ class Merge extends Model { $class = new static(); $master = $class->name; - $fields = self::getModelField($query, $master, '', $class->mapFields); + $fields = self::getModelField($query, $master, '', $class->mapFields, $class->field); $query->alias($master)->field($fields); foreach ($class->relationModel as $key => $model) { $name = is_int($key) ? $model : $key; $table = is_int($key) ? $query->getTable($name) : $model; $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk()); - $fields = self::getModelField($query, $name, $table, $class->mapFields); + $fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field); $query->field($fields); } return $query; @@ -81,12 +81,13 @@ class Merge extends Model * @param string $name 模型名称 * @param string $table 关联表名称 * @param array $map 字段映射 + * @param array $fields 查询字段 * @return array */ - protected static function getModelField($query, $name, $table = '', $map = []) + protected static function getModelField($query, $name, $table = '', $map = [], $fields = []) { // 获取模型的字段信息 - $fields = $query->getTableInfo($table, 'fields'); + $fields = $fields ?: $query->getTableInfo($table, 'fields'); $array = []; foreach ($fields as $field) { if ($key = array_search($name . '.' . $field, $map)) { diff --git a/thinkphp/library/think/model/Relation.php b/thinkphp/library/think/model/Relation.php index 22670bd..f9675c1 100644 --- a/thinkphp/library/think/model/Relation.php +++ b/thinkphp/library/think/model/Relation.php @@ -47,7 +47,8 @@ class Relation protected $query; // 关联查询条件 protected $where; - + // 关联查询参数 + protected $option; /** * 架构函数 * @access public @@ -74,6 +75,7 @@ class Relation 'localKey' => $this->localKey, 'alias' => $this->alias, 'joinType' => $this->joinType, + 'option' => $this->option, ]; return $name ? $info[$name] : $info; } @@ -689,8 +691,10 @@ class Relation } $result = call_user_func_array([$this->query, $method], $args); if ($result instanceof \think\db\Query) { + $this->option = $result->getOptions(); return $this; } else { + $this->option = []; return $result; } } else { diff --git a/thinkphp/library/think/view/driver/Think.php b/thinkphp/library/think/view/driver/Think.php index 33729b4..db1d981 100644 --- a/thinkphp/library/think/view/driver/Think.php +++ b/thinkphp/library/think/view/driver/Think.php @@ -24,6 +24,8 @@ class Think private $template; // 模板引擎参数 protected $config = [ + // 视图基础目录(集中式) + 'view_base' => '', // 模板起始路径 'view_path' => '', // 模板文件后缀 @@ -103,18 +105,21 @@ class Think */ private function parseTemplate($template) { + // 分析模板文件规则 + $request = Request::instance(); // 获取视图根目录 if (strpos($template, '@')) { // 跨模块调用 list($module, $template) = explode('@', $template); - $path = APP_PATH . $module . DS . 'view' . DS; + } + if ($this->config['view_base']) { + // 基础视图目录 + $module = isset($module) ? $module : $request->module(); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); } else { - // 当前视图目录 - $path = $this->config['view_path']; + $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; } - // 分析模板文件规则 - $request = Request::instance(); $controller = Loader::parseName($request->controller()); if ($controller && 0 !== strpos($template, '/')) { $depr = $this->config['view_depr']; diff --git a/thinkphp/library/traits/model/SoftDelete.php b/thinkphp/library/traits/model/SoftDelete.php index 4bb072d..9d855c7 100644 --- a/thinkphp/library/traits/model/SoftDelete.php +++ b/thinkphp/library/traits/model/SoftDelete.php @@ -27,7 +27,8 @@ trait SoftDelete public static function withTrashed() { $model = new static(); - return $model->db(); + $field = $model->getDeleteTimeField(true); + return $model->db(false)->removeWhereField($field); } /** @@ -38,8 +39,8 @@ trait SoftDelete public static function onlyTrashed() { $model = new static(); - $field = $model->getDeleteTimeField(); - return $model->db()->where($field, 'exp', 'is not null'); + $field = $model->getDeleteTimeField(true); + return $model->db(false)->where($field, 'exp', 'is not null'); } /** @@ -138,7 +139,8 @@ trait SoftDelete $field = $this->db(false)->getTable() . '.' . $field; } if (!$read && strpos($field, '.')) { - list($alias, $field) = explode('.', $field); + $array = explode('.', $field); + $field = array_pop($array); } return $field; }