diff --git a/base.php b/base.php index 1184dc51..4d0b703c 100644 --- a/base.php +++ b/base.php @@ -9,7 +9,7 @@ // | Author: liu21st // +---------------------------------------------------------------------- -define('THINK_VERSION', '5.0.2dev'); +define('THINK_VERSION', '5.0.3beta'); define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); diff --git a/convention.php b/convention.php index 206603b1..2ef29dcd 100644 --- a/convention.php +++ b/convention.php @@ -85,6 +85,8 @@ return [ 'url_route_on' => true, // 路由配置文件(支持配置多个) 'route_config_file' => ['route'], + // 路由使用完整匹配 + 'route_complete_match' => false, // 是否强制使用路由 'url_route_must' => false, // 域名部署 diff --git a/helper.php b/helper.php index 6ab2cb34..ba9f46ad 100644 --- a/helper.php +++ b/helper.php @@ -298,7 +298,7 @@ if (!function_exists('session')) { Session::init($name); } elseif (is_null($name)) { // 清除 - Session::clear($value); + Session::clear('' === $value ? null : $value); } elseif ('' === $value) { // 判断或获取 return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix); @@ -359,12 +359,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/library/think/App.php b/library/think/App.php index 98a58d71..7feff2fe 100644 --- a/library/think/App.php +++ b/library/think/App.php @@ -16,6 +16,7 @@ use think\Env; use think\Exception; use think\exception\HttpException; use think\exception\HttpResponseException; +use think\exception\RouteNotFoundException; use think\Hook; use think\Lang; use think\Loader; @@ -369,34 +370,29 @@ class App // 监听module_init Hook::listen('module_init', $request); - try { - $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); - if (is_null($instance)) { - throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1)); - } - // 获取当前操作名 - $action = $actionName . $config['action_suffix']; - if (!preg_match('/^[A-Za-z](\w)*$/', $action)) { - // 非法操作 - throw new \ReflectionException('illegal action name:' . $actionName); - } + $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); + if (is_null($instance)) { + throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1)); + } + // 获取当前操作名 + $action = $actionName . $config['action_suffix']; + $vars = []; + if (is_callable([$instance, $action])) { // 执行操作方法 $call = [$instance, $action]; - Hook::listen('action_begin', $call); - - $data = self::invokeMethod($call); - } catch (\ReflectionException $e) { + } elseif (is_callable([$instance, '_empty'])) { + // 空操作 + $call = [$instance, '_empty']; + $vars = [$action]; + } else { // 操作不存在 - if (method_exists($instance, '_empty')) { - $reflect = new \ReflectionMethod($instance, '_empty'); - $data = $reflect->invokeArgs($instance, [$action]); - self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); - } else { - throw new HttpException(404, 'method not exists:' . (new \ReflectionClass($instance))->getName() . '->' . $action); - } + throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); } - return $data; + + Hook::listen('action_begin', $call); + + return self::invokeMethod($call, $vars); } /** @@ -552,7 +548,7 @@ class App $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; if ($must && false === $result) { // 路由无效 - throw new HttpException(404, 'Route Not Found'); + throw new RouteNotFoundException(); } } if (false === $result) { diff --git a/library/think/Config.php b/library/think/Config.php index d7c3dc56..fb5340d8 100644 --- a/library/think/Config.php +++ b/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/library/think/File.php b/library/think/File.php index 43f38575..60a16417 100644 --- a/library/think/File.php +++ b/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/library/think/Request.php b/library/think/Request.php index bc43eb2f..c6f1198a 100644 --- a/library/think/Request.php +++ b/library/think/Request.php @@ -1241,8 +1241,7 @@ class Request if (false !== $pos) { unset($arr[$pos]); } - - $ip = trim($arr[0]); + $ip = trim(current($arr)); } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (isset($_SERVER['REMOTE_ADDR'])) { diff --git a/library/think/Route.php b/library/think/Route.php index 660b8d32..395b07cc 100644 --- a/library/think/Route.php +++ b/library/think/Route.php @@ -24,13 +24,13 @@ class Route { // 路由规则 private static $rules = [ - 'GET' => [], - 'POST' => [], - 'PUT' => [], - 'DELETE' => [], - 'PATCH' => [], - 'HEAD' => [], - 'OPTIONS' => [], + 'get' => [], + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], + 'head' => [], + 'options' => [], '*' => [], 'alias' => [], 'domain' => [], @@ -40,21 +40,22 @@ class Route // REST路由操作方法定义 private static $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'], + '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'], ]; // 不同请求类型的方法前缀 private static $methodPrefix = [ - 'GET' => 'get', - 'POST' => 'post', - 'PUT' => 'put', - 'DELETE' => 'delete', + 'get' => 'get', + 'post' => 'post', + 'put' => 'put', + 'delete' => 'delete', + 'patch' => 'patch', ]; // 子域名 @@ -68,6 +69,8 @@ class Route private static $domainRule; // 当前域名 private static $domain; + // 当前路由执行过程中的参数 + private static $option = []; /** * 注册变量规则 @@ -150,6 +153,7 @@ class Route } elseif (!is_null($value)) { self::$rules['name'][$name][] = $value; } else { + $name = strtolower($name); return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null; } } @@ -198,7 +202,7 @@ class Route unset($rule['__rest__']); } - self::registerRules($rule, strtoupper($type)); + self::registerRules($rule, strtolower($type)); } // 批量注册路由 @@ -241,7 +245,7 @@ class Route $pattern = array_merge(self::getGroup('pattern'), $pattern); } - $type = strtoupper($type); + $type = strtolower($type); if (strpos($type, '|')) { $option['method'] = $type; @@ -300,13 +304,13 @@ 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; - self::name($name, [$key, $vars, self::$domain]); + $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; + self::name(strtolower($name), [$key, $vars, self::$domain]); } if ($group) { if ('*' != $type) { @@ -328,7 +332,7 @@ class Route } if ('*' == $type) { // 注册路由快捷方式 - foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) { + foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { if (self::$domain) { self::$rules['domain'][self::$domain][$method][$rule] = true; } else { @@ -339,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 @@ -423,15 +448,16 @@ 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]; } - foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) { + foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { if (!isset(self::$rules[$method][$name])) { self::$rules[$method][$name] = true; } elseif (is_array(self::$rules[$method][$name])) { @@ -576,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); } } @@ -625,9 +652,9 @@ class Route public static function setMethodPrefix($method, $prefix = '') { if (is_array($method)) { - self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method, CASE_UPPER)); + self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method)); } else { - self::$methodPrefix[strtoupper($method)] = $prefix; + self::$methodPrefix[strtolower($method)] = $prefix; } } @@ -635,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 = []) @@ -682,7 +709,7 @@ class Route if (is_array($rules)) { self::$rules = $rules; } elseif ($rules) { - return true === $rules ? self::$rules : self::$rules[$rules]; + return true === $rules ? self::$rules : self::$rules[strtolower($rules)]; } else { $rules = self::$rules; unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']); @@ -698,7 +725,7 @@ class Route * @param string $method 请求类型 * @return void */ - public static function checkDomain($request, &$currentRules, $method = 'GET') + public static function checkDomain($request, &$currentRules, $method = 'get') { // 域名规则 $rules = self::$rules['domain']; @@ -808,7 +835,7 @@ class Route return $result; } } - $method = $request->method(); + $method = strtolower($request->method()); // 获取当前请求类型的路由规则 $rules = self::$rules[$method]; // 检测域名部署 @@ -831,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']); } } @@ -894,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; @@ -909,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']); } @@ -1091,7 +1121,9 @@ class Route { // 请求类型检测 if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method())) - || (isset($option['ext']) && false === stripos($option['ext'], $request->ext())) // 伪静态后缀检测 + || (!empty($option['ajax']) && !$request->isAjax()) // Ajax检测 + || (!empty($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检测 @@ -1162,8 +1194,9 @@ class Route { if (isset(self::$bind['module'])) { + $bind = str_replace('/', $depr, self::$bind['module']); // 如果有模块/控制器绑定 - $url = self::$bind['module'] . '/' . ltrim($url, '/'); + $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); } $url = str_replace($depr, '|', $url); list($path, $var) = self::parseUrlPath($url); @@ -1176,15 +1209,22 @@ class Route $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer'); $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; $item = []; + $find = false; foreach ($path as $val) { - $item[] = array_shift($path); - if (is_file($dir . DS . $val . $suffix . EXT)) { + $item[] = $val; + if (is_file($dir . DS . str_replace('.', DS, $val) . $suffix . EXT)) { + $find = true; break; } else { $dir .= DS . $val; } } - $controller = implode('.', $item); + if ($find) { + $controller = implode('.', $item); + $path = array_slice($path, count($item)); + } else { + $controller = array_shift($path); + } } else { // 解析控制器 $controller = !empty($path) ? array_shift($path) : null; @@ -1195,8 +1235,15 @@ class Route self::parseUrlParams(empty($path) ? '' : implode('|', $path)); // 封装路由 $route = [$module, $controller, $action]; - if (isset(self::$rules['name'][implode($depr, $route)])) { - throw new HttpException(404, 'invalid request:' . $url); + // 检查地址是否被定义过路由 + $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); + $name2 = ''; + if (empty($module) || isset($bind) && $module == $bind) { + $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action); + } + + if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) { + throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url)); } } return ['type' => 'module', 'module' => $route]; diff --git a/library/think/Url.php b/library/think/Url.php index 24087fc4..642e48f1 100644 --- a/library/think/Url.php +++ b/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 @@ -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,12 +155,13 @@ 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; } // 直接解析URL地址 - protected static function parseUrl($url, $domain) + protected static function parseUrl($url, &$domain) { $request = Request::instance(); if (0 === strpos($url, '/')) { @@ -173,14 +177,36 @@ class Url // 解析到 模块/控制器/操作 $module = $request->module(); $domains = Route::rules('domain'); - if (isset($domains[$domain]['[bind]'][0])) { - $bindModule = $domains[$domain]['[bind]'][0]; - if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) { - $module = ''; + 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])) { + $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]; + if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) { + $module = ''; + } } - } else { - $module = $module ? $module . '/' : ''; } + $module = $module ? $module . '/' : ''; $controller = Loader::parseName($request->controller()); if ('' == $url) { @@ -203,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) { @@ -220,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; } @@ -234,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/library/think/Validate.php b/library/think/Validate.php index ed12083c..fc38d89a 100644 --- a/library/think/Validate.php +++ b/library/think/Validate.php @@ -348,16 +348,22 @@ class Validate $result = call_user_func_array($rule, [$value, $data]); } else { // 判断验证类型 - if (is_numeric($key) && strpos($rule, ':')) { - list($type, $rule) = explode(':', $rule, 2); - if (isset($this->alias[$type])) { - // 判断别名 - $type = $this->alias[$type]; + if (is_numeric($key)) { + if (strpos($rule, ':')) { + list($type, $rule) = explode(':', $rule, 2); + if (isset($this->alias[$type])) { + // 判断别名 + $type = $this->alias[$type]; + } + $info = $type; + } elseif (method_exists($this, $rule)) { + $type = $rule; + $info = $rule; + $rule = ''; + } else { + $type = 'is'; + $info = $rule; } - $info = $type; - } elseif (is_numeric($key)) { - $type = 'is'; - $info = $rule; } else { $info = $type = $key; } @@ -607,6 +613,9 @@ class Validate */ protected function activeUrl($value, $rule) { + if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) { + $rule = 'MX'; + } return checkdnsrr($value, $rule); } @@ -715,19 +724,24 @@ class Validate if (!($file instanceof File)) { return false; } - $rule = explode(',', $rule); - list($width, $height, $type) = getimagesize($file->getRealPath()); - if (isset($rule[2])) { - $imageType = strtolower($rule[2]); - if ('jpeg' == $imageType) { - $imageType = 'jpg'; - } - if (image_type_to_extension($type, false) != $imageType) { - return false; + if ($rule) { + $rule = explode(',', $rule); + list($width, $height, $type) = getimagesize($file->getRealPath()); + if (isset($rule[2])) { + $imageType = strtolower($rule[2]); + if ('jpeg' == $imageType) { + $imageType = 'jpg'; + } + if (image_type_to_extension($type, false) != $imageType) { + return false; + } } + + list($w, $h) = $rule; + return $w == $width && $h == $height; + } else { + return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]); } - list($w, $h) = $rule; - return $w == $width && $h == $height; } /** diff --git a/library/think/db/Connection.php b/library/think/db/Connection.php index f39ca341..9806b827 100644 --- a/library/think/db/Connection.php +++ b/library/think/db/Connection.php @@ -433,7 +433,7 @@ abstract class Connection $sql . ' '); } } - return $sql; + return rtrim($sql); } /** diff --git a/library/think/db/Query.php b/library/think/db/Query.php index 52b0d18c..0c9d8ef7 100644 --- a/library/think/db/Query.php +++ b/library/think/db/Query.php @@ -669,11 +669,18 @@ class Query $table = array_shift($join); } } else { - $table = trim($join); - if (strpos($table, ' ') && !strpos($table, ')')) { - list($table, $alias) = explode(' ', $table); + $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; } } $this->options['join'][] = [$table, strtoupper($type), $condition]; @@ -763,13 +770,19 @@ class Query } } else { $fields = []; + $prefix = $this->prefix; if (is_array($join)) { // 支持数据表别名 - list($join, $alias, $table) = array_pad($join, 3, ''); + 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 = !empty($table) ? $table : $this->getTable($join); + $table = isset($table) ? [$table => $alias] : $alias; if (true === $field) { $fields = $alias . '.*'; } else { @@ -793,9 +806,9 @@ class Query } $this->field($fields); if ($on) { - $this->join($table . ' ' . $alias, $on, $type); + $this->join($table, $on, $type); } else { - $this->table($table . ' ' . $alias); + $this->table($table); } } return $this; @@ -893,13 +906,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; @@ -989,8 +998,11 @@ class Query if (!isset($total) && !$simple) { $options = $this->getOptions(); - $total = $this->count(); + if (isset($options['order'])) { + unset($this->options['order']); + } $bind = $this->bind; + $total = $this->count(); $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); } elseif ($simple) { $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); diff --git a/library/think/exception/RouteNotFoundException.php b/library/think/exception/RouteNotFoundException.php new file mode 100644 index 00000000..6ee2f7b6 --- /dev/null +++ b/library/think/exception/RouteNotFoundException.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +use think\exception\HttpException; + +class RouteNotFoundException extends HttpException +{ + + public function __construct() + { + parent::__construct(404); + } + +} diff --git a/library/think/view/driver/Think.php b/library/think/view/driver/Think.php index 33729b44..db1d9816 100644 --- a/library/think/view/driver/Think.php +++ b/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/library/traits/model/SoftDelete.php b/library/traits/model/SoftDelete.php index 4bb072dc..e110ff25 100644 --- a/library/traits/model/SoftDelete.php +++ b/library/traits/model/SoftDelete.php @@ -27,7 +27,7 @@ trait SoftDelete public static function withTrashed() { $model = new static(); - return $model->db(); + return $model->db(false); } /** @@ -39,7 +39,7 @@ trait SoftDelete { $model = new static(); $field = $model->getDeleteTimeField(); - return $model->db()->where($field, 'exp', 'is not null'); + return $model->db(false)->where($field, 'exp', 'is not null'); } /** diff --git a/tests/thinkphp/library/think/urlTest.php b/tests/thinkphp/library/think/urlTest.php index ced519d7..acb43365 100644 --- a/tests/thinkphp/library/think/urlTest.php +++ b/tests/thinkphp/library/think/urlTest.php @@ -25,13 +25,13 @@ class urlTest extends \PHPUnit_Framework_TestCase public function setUp() { - Route::rules(['GET' => [], - 'POST' => [], - 'PUT' => [], - 'DELETE' => [], - 'PATCH' => [], - 'HEAD' => [], - 'OPTIONS' => [], + Route::rules(['get' => [], + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], + 'head' => [], + 'options' => [], '*' => [], 'alias' => [], 'domain' => [],