diff --git a/core/base.php b/core/base.php index b1e1b373..052febe7 100644 --- a/core/base.php +++ b/core/base.php @@ -31,9 +31,32 @@ defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录 defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀 defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀 defined('IS_API') or define('IS_API', false); // 是否API接口 -defined('APP_AUTO_RUN') or define('APP_AUTO_RUN', true); // 是否自动运行 defined('AUTO_SCAN_PACKAGE') or define('AUTO_SCAN_PACKAGE', false); // 是否自动扫描非Composer安装类库 // 环境常量 define('IS_CLI', PHP_SAPI == 'cli' ? true : false); define('IS_WIN', strstr(PHP_OS, 'WIN') ? true : false); + +// 载入Loader类 +require CORE_PATH . 'Loader.php'; + +// 加载环境变量配置文件 +if (is_file(ROOT_PATH . 'env' . EXT)) { + $env = include ROOT_PATH . 'env' . EXT; + foreach ($env as $key => $val) { + $name = ENV_PREFIX . $key; + if (is_bool($val)) { + $val = $val ? 1 : 0; + } + putenv("$name=$val"); + } +} + +// 注册自动加载 +\think\Loader::register(); + +// 注册错误和异常处理机制 +\think\Error::register(); + +// 加载模式配置文件 +\think\Config::set(include THINK_PATH . 'convention' . EXT); diff --git a/core/alias.php b/core/classmap.php similarity index 100% rename from core/alias.php rename to core/classmap.php diff --git a/core/helper.php b/core/helper.php index 8e7908c8..adaa31c1 100644 --- a/core/helper.php +++ b/core/helper.php @@ -29,8 +29,8 @@ use think\View; /** * 快速导入Traits PHP5.5以上无需调用 - * @param string $class trait库 - * @param string $ext 类库后缀 + * @param string $class trait库 + * @param string $ext 类库后缀 * @return boolean */ function load_trait($class, $ext = EXT) @@ -41,9 +41,9 @@ function load_trait($class, $ext = EXT) /** * 抛出异常处理 * - * @param string $msg 异常消息 - * @param integer $code 异常代码 默认为0 - * @param string $exception 异常类 + * @param string $msg 异常消息 + * @param integer $code 异常代码 默认为0 + * @param string $exception 异常类 * * @throws Exception */ @@ -55,9 +55,9 @@ function exception($msg, $code = 0, $exception = '') /** * 记录时间(微秒)和内存使用情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 如果是m 表示统计内存占用 + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 如果是m 表示统计内存占用 * @return mixed */ function debug($start, $end = '', $dec = 6) @@ -71,9 +71,9 @@ function debug($start, $end = '', $dec = 6) /** * 获取语言变量值 - * @param string $name 语言变量名 - * @param array $vars 动态变量值 - * @param string $lang 语言 + * @param string $name 语言变量名 + * @param array $vars 动态变量值 + * @param string $lang 语言 * @return mixed */ function lang($name, $vars = [], $lang = '') @@ -83,9 +83,9 @@ function lang($name, $vars = [], $lang = '') /** * 获取和设置配置参数 - * @param string|array $name 参数名 - * @param mixed $value 参数值 - * @param string $range 作用域 + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @param string $range 作用域 * @return mixed */ function config($name = '', $value = null, $range = '') @@ -99,10 +99,9 @@ function config($name = '', $value = null, $range = '') /** * 获取输入数据 支持默认值和过滤 - * @param string $key 获取的变量名 - * @param mixed $default 默认值 - * @param string $filter 过滤方法 - * @param bool $merge 是否合并系统默认过滤方法 + * @param string $key 获取的变量名 + * @param mixed $default 默认值 + * @param string $filter 过滤方法 * @return mixed */ function input($key, $default = null, $filter = null) @@ -123,17 +122,17 @@ function input($key, $default = null, $filter = null) // 默认为自动判断 $method = 'param'; } - if(isset($has)){ + if (isset($has)) { return request()->has($key, $method, $default); - }else{ + } else { return request()->$method($key, $default, $filter); } } /** * 渲染输出Widget - * @param string $name Widget名称 - * @param array $data 传人的参数 + * @param string $name Widget名称 + * @param array $data 传人的参数 * @return mixed */ function widget($name, $data = []) @@ -143,9 +142,9 @@ function widget($name, $data = []) /** * 实例化Model - * @param string $name Model名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return \think\Model */ function model($name = '', $layer = 'model', $appendSuffix = false) @@ -155,9 +154,9 @@ function model($name = '', $layer = 'model', $appendSuffix = false) /** * 实例化验证器 - * @param string $name 验证器名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @param string $name 验证器名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return \think\Validate */ function validate($name = '', $layer = 'validate', $appendSuffix = false) @@ -167,8 +166,8 @@ function validate($name = '', $layer = 'validate', $appendSuffix = false) /** * 实例化数据库类 - * @param string $name 操作的数据表名称(不含前缀) - * @param array|string $config 数据库配置参数 + * @param string $name 操作的数据表名称(不含前缀) + * @param array|string $config 数据库配置参数 * @return \think\db\Query */ function db($name = '', $config = []) @@ -178,9 +177,9 @@ function db($name = '', $config = []) /** * 实例化控制器 格式:[模块/]控制器 - * @param string $name 资源地址 - * @param string $layer 控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return \think\Controller */ function controller($name, $layer = 'controller', $appendSuffix = false) @@ -190,10 +189,10 @@ function controller($name, $layer = 'controller', $appendSuffix = false) /** * 调用模块的操作方法 参数格式 [模块/控制器/]操作 - * @param string $url 调用地址 - * @param string|array $vars 调用参数 支持字符串和数组 - * @param string $layer 要调用的控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return mixed */ function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) @@ -203,9 +202,9 @@ function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) /** * 导入所需的类库 同java的Import 本函数有缓存功能 - * @param string $class 类库命名空间字符串 - * @param string $baseUrl 起始路径 - * @param string $ext 导入的文件扩展名 + * @param string $class 类库命名空间字符串 + * @param string $baseUrl 起始路径 + * @param string $ext 导入的文件扩展名 * @return boolean */ function import($class, $baseUrl = '', $ext = EXT) @@ -215,8 +214,8 @@ function import($class, $baseUrl = '', $ext = EXT) /** * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面 - * @param string $class 类库 - * @param string $ext 类库后缀 + * @param string $class 类库 + * @param string $ext 类库后缀 * @return boolean */ function vendor($class, $ext = EXT) @@ -226,9 +225,9 @@ function vendor($class, $ext = EXT) /** * 浏览器友好的变量输出 - * @param mixed $var 变量 - * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 - * @param string $label 标签 默认为空 + * @param mixed $var 变量 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 + * @param string $label 标签 默认为空 * @return void|string */ function dump($var, $echo = true, $label = null) @@ -238,10 +237,10 @@ function dump($var, $echo = true, $label = null) /** * Url生成 - * @param string $url 路由地址 - * @param string|array $value 变量 - * @param bool|string $suffix 前缀 - * @param bool|string $domain 域名 + * @param string $url 路由地址 + * @param string|array $value 变量 + * @param bool|string $suffix 前缀 + * @param bool|string $domain 域名 * @return string */ function url($url = '', $vars = '', $suffix = true, $domain = false) @@ -251,9 +250,9 @@ function url($url = '', $vars = '', $suffix = true, $domain = false) /** * Session管理 - * @param string|array $name session名称,如果为数组表示进行session设置 - * @param mixed $value session值 - * @param string $prefix 前缀 + * @param string|array $name session名称,如果为数组表示进行session设置 + * @param mixed $value session值 + * @param string $prefix 前缀 * @return mixed */ function session($name, $value = '', $prefix = null) @@ -278,9 +277,9 @@ function session($name, $value = '', $prefix = null) /** * Cookie管理 - * @param string|array $name cookie名称,如果为数组表示进行cookie设置 - * @param mixed $value cookie值 - * @param mixed $option 参数 + * @param string|array $name cookie名称,如果为数组表示进行cookie设置 + * @param mixed $value cookie值 + * @param mixed $option 参数 * @return mixed */ function cookie($name, $value = '', $option = null) @@ -305,9 +304,9 @@ function cookie($name, $value = '', $option = null) /** * 缓存管理 - * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 - * @param mixed $value 缓存值 - * @param mixed $options 缓存参数 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 + * @param mixed $value 缓存值 + * @param mixed $options 缓存参数 * @return mixed */ function cache($name, $value = '', $options = null) @@ -338,8 +337,8 @@ function cache($name, $value = '', $options = null) /** * 记录日志信息 - * @param mixed $log log信息 支持字符串和数组 - * @param string $level 日志级别 + * @param mixed $log log信息 支持字符串和数组 + * @param string $level 日志级别 * @return void|array */ function trace($log = '[think]', $level = 'log') @@ -375,9 +374,9 @@ function response($data = [], $code = 200, $header = [], $type = 'html') /** * 渲染模板输出 - * @param string $template 模板文件 - * @param array $vars 模板变量 - * @param integer $code 状态码 + * @param string $template 模板文件 + * @param array $vars 模板变量 + * @param integer $code 状态码 * @return \think\response\View */ function view($template = '', $vars = [], $code = 200) @@ -426,9 +425,9 @@ function xml($data = [], $code = 200, $header = [], $options = []) /** * 获取\think\response\Redirect对象实例 - * @param mixed $url 重定向地址 支持Url::build方法的地址 + * @param mixed $url 重定向地址 支持Url::build方法的地址 * @param array|integer $params 额外参数 - * @param integer $code 状态码 + * @param integer $code 状态码 * @return \think\response\Redirect */ function redirect($url = [], $params = [], $code = 302) @@ -442,9 +441,9 @@ function redirect($url = [], $params = [], $code = 302) /** * 抛出HTTP异常 - * @param integer $code 状态码 - * @param string $message 错误信息 - * @param array $header 参数 + * @param integer $code 状态码 + * @param string $message 错误信息 + * @param array $header 参数 */ function abort($code, $message = null, $header = []) { diff --git a/core/library/think/App.php b/core/library/think/App.php index e3724e98..266475f2 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -41,29 +41,31 @@ class App /** * @var bool 应用调试模式 - */ + */ public static $debug = true; /** * @var string 应用类库命名空间 - */ + */ public static $namespace = 'app'; /** * @var bool 应用类库后缀 - */ + */ public static $suffix = false; /** * @var bool 应用路由检测 - */ + */ protected static $routeCheck; /** * @var bool 严格路由检测 - */ + */ protected static $routeMust; + protected static $dispatch; + /** * 执行应用程序 * @access public @@ -90,12 +92,14 @@ class App } } - // 获取当前请求的调度信息 - $dispatch = $request->dispatch(); + // 获取应用调度信息 + $dispatch = self::$dispatch; if (empty($dispatch)) { - // 未指定调度类型 则进行URL路由检测 + // 进行URL路由检测 $dispatch = self::routeCheck($request, $config); } + // 记录当前调度信息 + $request->dispatch($dispatch); // 记录路由信息 self::$debug && Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); // 监听app_begin @@ -108,7 +112,7 @@ class App break; case 'module': // 模块/控制器/操作 - $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null ); + $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null); break; case 'controller': // 执行控制器操作 @@ -136,11 +140,11 @@ class App Hook::listen('app_end', $data); // 清空类的实例化 Loader::clearInstance(); - + // 输出数据到客户端 if ($data instanceof Response) { return $data; - } elseif(!is_null($data)) { + } elseif (!is_null($data)) { // 默认自动识别响应输出类型 $isAjax = $request->isAjax(); $type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); @@ -150,6 +154,19 @@ class App } } + /** + * 设置当前请求的调度信息 + * @access public + * @param array|string $dispatch 调度信息 + * @param string $type 调度类型 + * @param array $params 参数 + * @return void + */ + public static function dispatch($dispath, $type = 'module', $params = []) + { + self::$dispatch = ['type' => $type, $type => $dispatch, 'params' => $params]; + } + /** * 执行函数或者闭包方法 支持参数调用 * @access public @@ -222,7 +239,7 @@ class App } } // 全局过滤 - array_walk_recursive($args, [Request::instance(),'filterExp']); + array_walk_recursive($args, [Request::instance(), 'filterExp']); } return $args; } @@ -240,10 +257,11 @@ class App if (is_string($result)) { $result = explode('/', $result); } + $request = Request::instance(); if ($config['app_multi_module']) { // 多模块部署 $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); - $bind = Route::bind('module'); + $bind = Route::getBind('module'); $available = false; if ($bind) { // 绑定模块 @@ -258,6 +276,7 @@ class App // 模块初始化 if ($module && $available) { // 初始化模块 + $request->module($module); $config = self::init($module); } else { throw new HttpException(404, 'module not exists:' . $module); @@ -265,12 +284,13 @@ class App } else { // 单一模块部署 $module = ''; + $request->module($module); } // 当前模块路径 App::$modulePath = APP_PATH . ($module ? $module . DS : ''); // 是否自动转换控制器和操作名 - $convert = is_bool($convert) ? $convert : $config['url_convert']; + $convert = is_bool($convert) ? $convert : $config['url_convert']; // 获取控制器名 $controller = strip_tags($result[1] ?: $config['default_controller']); $controller = $convert ? strtolower($controller) : $controller; @@ -279,15 +299,8 @@ class App $actionName = strip_tags($result[2] ?: $config['default_action']); $actionName = $convert ? strtolower($actionName) : $actionName; - // 执行操作 - if (!preg_match('/^[A-Za-z](\/|\.|\w)*$/', $controller)) { - // 安全检测 - throw new \InvalidArgumentException('illegal controller name:' . $controller); - } - - // 设置当前请求的模块、控制器、操作 - $request = Request::instance(); - $request->module($module)->controller($controller)->action($actionName); + // 设置当前请求的控制器、操作 + $request->controller($controller)->action($actionName); // 监听module_init Hook::listen('module_init', $request); @@ -327,17 +340,17 @@ class App { if (empty(self::$init)) { // 初始化应用 - $config = self::init(); - self::$suffix = $config['class_suffix']; - + $config = self::init(); + self::$suffix = $config['class_suffix']; + // 应用调试模式 - self::$debug = Config::get('app_debug'); + self::$debug = Config::get('app_debug'); if (!self::$debug) { ini_set('display_errors', 'Off'); } - + // 应用命名空间 - self::$namespace = $config['app_namespace']; + self::$namespace = $config['app_namespace']; Loader::addNamespace($config['app_namespace'], APP_PATH); if (!empty($config['root_namespace'])) { Loader::addNamespace($config['root_namespace']); @@ -364,7 +377,6 @@ class App return self::$init; } - /** * 初始化应用或模块 * @access public @@ -399,7 +411,7 @@ class App // 加载别名文件 if (is_file(CONF_PATH . $module . 'alias' . EXT)) { - Loader::addMap(include CONF_PATH . $module . 'alias' . EXT); + Loader::addClassMap(include CONF_PATH . $module . 'alias' . EXT); } // 加载行为扩展文件 @@ -432,14 +444,14 @@ class App { // 检测URL禁用后缀 if ($config['url_deny_suffix'] && preg_match('/\.(' . $config['url_deny_suffix'] . ')$/i', $request->pathinfo())) { - throw new Exception('url suffix deny:'.$request->ext()); + throw new Exception('url suffix deny:' . $request->ext()); } $path = $request->path(); $depr = $config['pathinfo_depr']; $result = false; // 路由检测 - $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; + $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; if ($check) { // 开启路由 if (!empty($config['route'])) { @@ -456,11 +468,9 @@ class App } if (false === $result) { // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 - $result = Route::parseUrl($path, $depr, $config['controller_auto_search'], $config['url_param_type']); + $result = Route::parseUrl($path, $depr, $config['controller_auto_search']); } - - // 注册调度机制 - return $request->dispatch($result); + return $result; } /** diff --git a/core/library/think/Lang.php b/core/library/think/Lang.php index b049dc01..571921be 100644 --- a/core/library/think/Lang.php +++ b/core/library/think/Lang.php @@ -102,7 +102,7 @@ class Lang public static function has($name, $range = '') { $range = $range ?: self::$range; - return isset(self::$lang[$range][strtolower($name)]); + return isset(self::$lang[$range][strtolower($name)]); } /** @@ -169,7 +169,7 @@ class Lang } if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { // 合法的语言 - self::$range = $langSet; + self::$range = $langSet ?: self::$range; } return self::$range; } diff --git a/core/library/think/Loader.php b/core/library/think/Loader.php index 546ae937..00f758dd 100644 --- a/core/library/think/Loader.php +++ b/core/library/think/Loader.php @@ -59,34 +59,38 @@ class Loader if (!strpos($class, '\\')) { return false; } - - // 解析命名空间 - list($name, $class) = explode('\\', $class, 2); - if (isset(self::$namespace[$name])) { - // 根命名空间 - $path = self::$namespace[$name]; - } elseif (is_dir(EXTEND_PATH . $name)) { + $item = explode('\\', $class); + // 解析命名空间所在的路径 + if (count($item) > 2 && isset(self::$namespace[$item[0] . '\\' . $item[1]])) { + // 子命名空间定义(仅支持二级) + list($ns1, $ns2, $class) = explode('\\', $class, 3); + $path = self::$namespace[$ns1 . '\\' . $ns2]; + } elseif (isset(self::$namespace[$item[0]])) { + // 根命名空间定义 + list($name, $class) = explode('\\', $class, 2); + $path = self::$namespace[$name]; + } elseif (is_dir(EXTEND_PATH . $item[0])) { // 扩展类库命名空间 - $path = EXTEND_PATH . $name . DS; + list($name, $class) = explode('\\', $class, 2); + $path = EXTEND_PATH . $name . DS; } else { - // 非根命名空间检测 - foreach (self::$namespace as $ns => $val) { - if (strpos($ns, '\\') && 0 === strpos($name . '\\' . $class, $ns)) { - $path = $val; - $class = substr($name . $class, strlen($ns)); - break; + return false; + } + // 定位文件 + $match = false; + foreach ((array) $path as $p) { + $filename = $p . str_replace('\\', DS, $class) . EXT; + if (is_file($filename)) { + // Win环境严格区分大小写 + if (IS_WIN && false === strpos(realpath($filename), $class . EXT)) { + continue; } - } - if (!isset($path)) { - return false; + $match = true; + break; } } - $filename = $path . str_replace('\\', DS, $class) . EXT; - if (is_file($filename)) { - // 开启调试模式Win环境严格区分大小写 - if (IS_WIN && false === strpos(realpath($filename), $class . EXT)) { - return false; - } + + if ($match) { include $filename; } else { return false; @@ -96,7 +100,7 @@ class Loader } // 注册classmap - public static function addMap($class, $map = '') + public static function addClassMap($class, $map = '') { if (is_array($class)) { self::$map = array_merge(self::$map, $class); @@ -119,7 +123,7 @@ class Loader public static function addNamespaceAlias($namespace, $original = '') { if (is_array($namespace)) { - self::$namespaceAlias = array_merge(self::$namespace, $namespace); + self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace); } else { self::$namespaceAlias[$namespace] = $original; } @@ -130,83 +134,33 @@ class Loader { // 注册系统自动加载 spl_autoload_register($autoload ?: 'think\\Loader::autoload'); + // 注册命名空间定义 + self::addNamespace([ + 'think' => LIB_PATH . 'think' . DS, + 'behavior' => LIB_PATH . 'behavior' . DS, + 'traits' => LIB_PATH . 'traits' . DS, + ]); + // 加载类库映射文件 + self::addClassMap(include THINK_PATH . 'classmap' . EXT); + // Composer自动加载支持 if (is_dir(VENDOR_PATH . 'composer')) { // 注册Composer自动加载 - self::registerComposerLoader(); self::$composerLoader = true; + self::registerComposerLoader(); } elseif (is_file(VENDOR_PATH . 'think_autoload.php')) { // 读取Composer自动加载文件 $autoload = include VENDOR_PATH . 'think_autoload.php'; if (is_array($autoload)) { - self::addMap($autoload); - } - } elseif (AUTO_SCAN_PACKAGE) { - if (is_file(RUNTIME_PATH . 'class_namespace.php')) { - self::addNamespace(include RUNTIME_PATH . 'class_namespace.php'); - if (is_file(RUNTIME_PATH . 'load_files.php')) { - $files = include RUNTIME_PATH . 'load_files.php'; - foreach ($files as $file) { - include $file; - } - } - } elseif (is_dir(VENDOR_PATH)) { - self::scanComposerPackage(VENDOR_PATH); - } - } - } - - // 扫描composer package - private static function scanComposerPackage($path) - { - // 自动扫描下载Composer安装类库 - $dirs = scandir($path, 1); - $namespace = []; - foreach ($dirs as $dir) { - if ('.' != $dir && '..' != $dir && is_file($path . $dir . DS . 'composer.json')) { - // 解析 package的composer.json 文件 - $namespace = array_merge($namespace, self::parseComposerPackage($path . $dir . DS)); - } - } - if (!empty($namespace)) { - self::addNamespace($namespace); - // 生成缓存 - file_put_contents(RUNTIME_PATH . 'class_namespace.php', " $path) { - $namespace[rtrim($ns, '\\')] = realpath($package . $path . DS . str_replace('\\', DS, $ns)) . DS; - } - } - - if (isset($autoload['psr-4'])) { - foreach ($autoload['psr-4'] as $ns => $path) { - $namespace[rtrim($ns, '\\')] = realpath($package . $path) . DS; - } - } - - if (isset($autoload['files'])) { + // 命名空间和类库映射注册 + self::addNamespace($autoload['namespace']); + self::addClassMap($autoload['classmap']); + // 载入composer包的文件列表 foreach ($autoload['files'] as $file) { - self::$load[] = realpath($package . $file); - require $package . $file; + include $file; } } } - return $namespace; } // 注册composer自动加载 @@ -234,7 +188,7 @@ class Loader if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) { $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php'; if ($classMap) { - self::addMap($classMap); + self::addClassMap($classMap); } } diff --git a/core/library/think/Model.php b/core/library/think/Model.php index a9be8662..cbcb1994 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -164,10 +164,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 获取关联模型实例 * @access protected - * @return Relation + * @param string|array $relation 关联查询 + * @return Relation|Query */ - protected function relation() + protected function relation($relation = null) { + if (!is_null($relation)) { + // 执行关联查询 + return $this->db->relation($relation); + } + + // 获取关联对象实例 if (is_null($this->relation)) { $this->relation = new Relation($this); } @@ -250,11 +257,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess list($type, $param) = explode(':', $type, 2); } switch ($type) { - case 'timestamp': + case 'datetime': $format = !empty($param) ? $param : $this->dateFormat; $value = date($format, $_SERVER['REQUEST_TIME']); break; - case 'datetime': + case 'timestamp': $value = $_SERVER['REQUEST_TIME']; break; } @@ -549,6 +556,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 检测字段 if (!empty($this->field)) { + if (true === $this->field) { + $this->field = $this->db->getTableInfo('', 'fields'); + } foreach ($this->data as $key => $val) { if (!in_array($key, $this->field)) { unset($this->data[$key]); @@ -666,7 +676,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置允许写入的字段 * @access public - * @param bool $update + * @param bool|array $field 允许写入的字段 如果为true只允许写入数据表字段 * @return $this */ public function allowField($field) @@ -971,25 +981,27 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * 命名范围 * @access public * @param string|array|Closure $name 命名范围名称 逗号分隔 - * @param mixed $params 参数调用 + * @param mixed ...$params 参数调用 * @return Model */ - public static function scope($name, $params = []) + public static function scope($name) { - $model = new static(); - $query = $model->db(); - if ($name instanceof \Closure) { - call_user_func_array($name, [ & $query, $params]); - } elseif ($name instanceof Query) { + if ($name instanceof Query) { return $name; - } else { - if (is_string($name)) { - $names = explode(',', $name); - } - foreach ($names as $scope) { + } + $model = new static(); + $params = func_get_args(); + $params[0] = $model->db(); + if ($name instanceof \Closure) { + call_user_func_array($name, $params); + } elseif (is_string($name)) { + $name = explode(',', $name); + } + if (is_array($name)) { + foreach ($name as $scope) { $method = 'scope' . trim($scope); if (method_exists($model, $method)) { - $model->$method($query, $params); + call_user_func_array([$model, $method], $params); } } } diff --git a/core/library/think/Request.php b/core/library/think/Request.php index 6013448e..8d5e9ad4 100644 --- a/core/library/think/Request.php +++ b/core/library/think/Request.php @@ -109,6 +109,8 @@ class Request 'csv' => 'text/csv', ]; + protected $content; + // 全局过滤规则 protected $filter; // Hook扩展方法 @@ -126,6 +128,7 @@ class Request $this->$name = $item; } } + $this->filter = Config::get('default_filter'); } public function __call($method, $args) @@ -177,9 +180,10 @@ class Request * @param array $cookie * @param array $files * @param array $server + * @param string $content * @return \think\Request */ - public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = []) + public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) { $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); @@ -235,6 +239,7 @@ class Request $options['pathinfo'] = ltrim($info['path'], '/'); $options['method'] = $server['REQUEST_METHOD']; $options['domain'] = $server['HTTP_HOST']; + $options['content'] = $content; self::$instance = new self($options); return self::$instance; } @@ -983,7 +988,7 @@ class Request $value = $default; break; } - } else { + } elseif (!empty($filter)) { // filter函数不存在时, 则使用filter_var进行过滤 // filter为非整形值时, 调用filter_id取得过滤id $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter)); @@ -1289,14 +1294,14 @@ class Request } /** - * 获取当前请求的调度信息 + * 设置或者获取当前请求的调度信息 * @access public - * @param array $dispatch 调度信息 + * @param array $dispatch 调度信息 * @return array */ - public function dispatch($dispatch = []) + public function dispatch($dispatch = null) { - if (!empty($dispatch)) { + if (!is_null($dispatch)) { $this->dispatch = $dispatch; } return $this->dispatch; @@ -1366,4 +1371,16 @@ class Request } } + /** + * 设置或者获取当前请求的content + * @access public + * @return string + */ + public function getContent() + { + if (is_null($this->content)) { + $this->content = file_get_contents('php://input'); + } + return $this->content; + } } diff --git a/core/library/think/Route.php b/core/library/think/Route.php index dbb9cd64..088dcdb7 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -123,19 +123,26 @@ class Route } /** - * 设置和读取路由绑定 + * 设置路由绑定 * @access public - * @param string $type 请求类型 * @param mixed $bind 绑定信息 + * @param string $type 绑定类型 默认为module * @return mixed */ - public static function bind($type, $bind = '') + public static function bind($bind, $type = 'module') { - if ('' == $bind) { - return isset(self::$bind[$type]) ? self::$bind[$type] : null; - } else { - self::$bind = ['type' => $type, $type => $bind]; - } + self::$bind = ['type' => $type, $type => $bind]; + } + + /** + * 读取路由绑定 + * @access public + * @param string $type 绑定类型 + * @return mixed + */ + public static function getBind($type) + { + return isset(self::$bind[$type]) ? self::$bind[$type] : null; } /** @@ -210,8 +217,8 @@ class Route */ public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = [], $group = '') { - $group = $group ?: self::$group; - $type = strtoupper($type); + $group = $group ?: self::$group; + $type = strtoupper($type); if (strpos($type, '|')) { foreach (explode('|', $type) as $val) { self::rule($rule, $route, $val, $option, $pattern, $group); @@ -497,11 +504,12 @@ class Route * @param string $route 路由地址 * @param string $method 请求类型 * @param array $option 路由参数 + * @param string $group 路由分组 * @return void */ - public static function miss($route, $method = '*', $option = []) + public static function miss($route, $method = '*', $option = [], $group = '') { - self::rule('__miss__', $route, $method, $option, []); + self::rule('__miss__', $route, $method, $option, [], $group); } /** @@ -522,7 +530,7 @@ class Route /** * 检测子域名部署 * @access public - * @param Request $request Request请求对象 + * @param Request $request Request请求对象 * @return void */ public static function checkDomain($request) @@ -648,13 +656,13 @@ class Route // 路由不匹配 } elseif (0 === strpos($rule, '\\')) { // 路由到类 - return self::bindToClass($array[1], substr($rule, 1)); + return self::bindToClass($array[1], substr($rule, 1), $depr); } elseif (0 === strpos($url, '@')) { // 路由到控制器类 - return self::bindToController($array[1], substr($rule, 1)); + return self::bindToController($array[1], substr($rule, 1), $depr); } else { // 路由到模块/控制器 - return self::bindToModule($array[1], $rule); + return self::bindToModule($array[1], $rule, $depr); } } @@ -671,18 +679,13 @@ class Route self::checkDomain($request); } // 检测URL绑定 - $return = self::checkUrlBind($url, $rules); + $return = self::checkUrlBind($url, $rules, $depr); if ($return) { return $return; } // 路由规则检测 if (!empty($rules)) { - if (isset($rules['__miss__'])) { - // 指定未匹配路由的处理 - $miss = $rules['__miss__']; - unset($rules['__miss__']); - } foreach ($rules as $rule => $val) { $option = isset($val['option']) ? $val['option'] : []; $pattern = isset($val['pattern']) ? $val['pattern'] : []; @@ -691,7 +694,11 @@ class Route if (!self::checkOption($option, $url, $request)) { continue; } - + if ('__miss__' == $rule) { + // 指定分组MISS路由 + $miss = $val['route']; + continue; + } if (!empty($val['routes'])) { // 分组路由 if (($pos = strpos($rule, ':')) || ($pos = strpos($rule, '<'))) { @@ -702,13 +709,12 @@ class Route if (0 !== strpos($url, $str)) { continue; } + $missGroup = false; // 匹配到路由分组 foreach ($val['routes'] as $key => $route) { if (is_numeric($key)) { $key = array_shift($route); } - - $key = $rule . ($key ? '/' . ltrim($key, '/') : ''); // 检查规则路由 if (is_array($route)) { $option1 = $route[1]; @@ -720,12 +726,27 @@ class Route $route = $route[0]; $option = array_merge($option, $option1); } + if ('__miss__' == $key) { + // 指定分组MISS路由 + $missGroup = $route; + continue; + } + $key = $rule . ($key ? '/' . ltrim($key, '/') : ''); $result = self::checkRule($key, $route, $url, $pattern, $option); if (false !== $result) { $request->route(['rule' => $key, 'route' => $route, 'pattern' => $pattern, 'option' => $option]); return $result; } } + if ($missGroup) { + // 未匹配所有路由的路由规则处理 + if ($missGroup instanceof \Closure) { + // 执行闭包 + return ['type' => 'function', 'function' => $missGroup, 'params' => []]; + } else { + return self::parseRule('', $missGroup, $url, []); + } + } } else { if (is_numeric($rule)) { $rule = array_shift($val); @@ -742,11 +763,11 @@ class Route } if (isset($miss)) { // 未匹配所有路由的路由规则处理 - if ($miss['route'] instanceof \Closure) { + if ($miss instanceof \Closure) { // 执行闭包 - return ['type' => 'function', 'function' => $miss['route'], 'params' => []]; - } elseif (self::checkOption($miss['option'], $url, $request)) { - return self::parseRule('', $miss['route'], $url, []); + return ['type' => 'function', 'function' => $miss, 'params' => []]; + } else { + return self::parseRule('', $miss, $url, []); } } } @@ -758,9 +779,10 @@ class Route * @access private * @param string $url URL地址 * @param array $rules 路由规则 + * @param string $depr URL分隔符 * @return false */ - private static function checkUrlBind(&$url, &$rules) + private static function checkUrlBind(&$url, &$rules, $depr = '/') { if (!empty(self::$bind['type'])) { // 记录绑定信息 @@ -769,10 +791,10 @@ class Route switch (self::$bind['type']) { case 'class': // 绑定到类 - return self::bindToClass($url, self::$bind['class']); + return self::bindToClass($url, self::$bind['class'], $depr); case 'namespace': // 绑定到命名空间 - return self::bindToNamespace($url, self::$bind['namespace']); + return self::bindToNamespace($url, self::$bind['namespace'], $depr); case 'module': // 如果有模块/控制器绑定 针对路由到 模块/控制器 有效 $url = self::$bind['module'] . '/' . $url; @@ -793,15 +815,17 @@ class Route * @access public * @param string $url URL地址 * @param string $class 类名(带命名空间) + * @param string $depr URL分隔符 * @return array */ - public static function bindToClass($url, $class) + public static function bindToClass($url, $class, $depr = '/') { - $array = explode('/', $url, 2); + $array = explode($depr, $url, 2); + $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); } - return ['type' => 'method', 'method' => [$class, $array[0] ?: Config::get('default_action')], 'params' => []]; + return ['type' => 'method', 'method' => [$class, $action], 'params' => []]; } /** @@ -809,11 +833,12 @@ class Route * @access public * @param string $url URL地址 * @param string $namespace 命名空间 + * @param string $depr URL分隔符 * @return array */ - public static function bindToNamespace($url, $namespace) + public static function bindToNamespace($url, $namespace, $depr = '/') { - $array = explode('/', $url, 3); + $array = explode($depr, $url, 3); $class = !empty($array[0]) ? $array[0] : Config::get('default_controller'); $method = !empty($array[1]) ? $array[1] : Config::get('default_action'); if (!empty($array[2])) { @@ -822,21 +847,43 @@ class Route return ['type' => 'method', 'method' => [$namespace . '\\' . $class, $method], 'params' => []]; } + /** + * 绑定到应用 直接进行控制器类库访问 + * @access public + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @return array + */ + public static function bindToApp($url, $depr = '/') + { + $array = explode($depr, $url, 4); + $module = !empty($array[0]) ? $array[0] : Config::get('default_module'); + $controller = !empty($array[1]) ? $array[1] : Config::get('default_controller'); + $method = !empty($array[2]) ? $array[2] : Config::get('default_action'); + $layer = Config::get('url_controller_layer'); + $class = App::$namespace . '\\' . $module . '\\' . $layer . '\\' . $controller; + if (!empty($array[3])) { + self::parseUrlParams($array[3]); + } + return ['type' => 'method', 'method' => [$class, $method], 'params' => []]; + } + /** * 绑定到控制器类 * @access public * @param string $url URL地址 - * @param string $module 模块名 + * @param string $controller 控制器名 (支持带模块名 index/user ) + * @param string $depr URL分隔符 * @return array */ - public static function bindToController($url, $controller) + public static function bindToController($url, $controller, $depr = '/') { - $array = explode('/', $url, 2); + $array = explode($depr, $url, 2); $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); } - return ['type' => 'method', 'method' => [$controller, $action], 'params' => []]; + return ['type' => 'controller', 'controller' => $controller . '/' . $action, 'params' => []]; } /** @@ -844,11 +891,12 @@ class Route * @access public * @param string $url URL地址 * @param string $class 控制器类名(带命名空间) + * @param string $depr URL分隔符 * @return array */ - public static function bindToModule($url, $controller) + public static function bindToModule($url, $controller, $depr = '/') { - $array = explode('/', $url, 2); + $array = explode($depr, $url, 2); $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); @@ -883,8 +931,8 @@ class Route * 检测路由规则 * @access private * @param string $rule 路由规则 - * @param string $url URL地址 * @param string $route 路由地址 + * @param string $url URL地址 * @param array $pattern 变量规则 * @param array $option 路由参数 * @return array|false @@ -910,7 +958,7 @@ class Route if ($len1 >= $len2 || strpos($rule, '[')) { if ('$' == substr($rule, -1, 1)) { // 完整匹配 - if (!$merge && $len1 != $len2 && false === strpos($rule, '[')) { + if (!$merge && $len1 != $len2 && (false === strpos($rule, '[') || $len1 > $len2 || $len1 < $len2 - substr_count($rule, '['))) { return false; } else { $rule = substr($rule, 0, -1); @@ -924,12 +972,12 @@ class Route if ($option['after_behavior'] instanceof \Closure) { $result = call_user_func_array($option['after_behavior'], [$route]); } else { - foreach((array)$option['after_behavior'] as $behavior){ + foreach ((array) $option['after_behavior'] as $behavior) { $result = Hook::exec($behavior, '', $route); if (!is_null($result)) { break; } - } + } } // 路由规则重定向 if ($result instanceof Response) { @@ -954,10 +1002,9 @@ class Route * @param string $url URL地址 * @param string $depr URL分隔符 * @param bool $autoSearch 是否自动深度搜索控制器 - * @param integer $paramType URL参数解析方式 0 名称解析 1 顺序解析 * @return array */ - public static function parseUrl($url, $depr = '/', $autoSearch = false, $paramType = 0) + public static function parseUrl($url, $depr = '/', $autoSearch = false) { if (isset(self::$bind['module'])) { // 如果有模块/控制器绑定 @@ -968,24 +1015,46 @@ class Route $url = str_replace($depr, '/', $url); } - $result = self::parseRoute($url, $autoSearch, true, $paramType); - - if (!empty($result['var'])) { - $_GET = array_merge($result['var'], $_GET); + list($path, $var) = self::parseUrlPath($url); + $route = [null, null, null]; + if (isset($path)) { + // 解析模块 + $module = Config::get('app_multi_module') ? array_shift($path) : null; + if ($autoSearch) { + // 自动搜索控制器 + $dir = APP_PATH . ($module ? $module . DS : '') . 'controller'; + $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; + $item = []; + foreach ($path as $val) { + $item[] = array_shift($path); + if (is_file($dir . DS . $val . $suffix . EXT)) { + break; + } else { + $dir .= DS . $val; + } + } + $controller = implode('.', $item); + } else { + // 解析控制器 + $controller = !empty($path) ? array_shift($path) : null; + } + // 解析操作 + $action = !empty($path) ? array_shift($path) : null; + // 解析额外参数 + self::parseUrlParams(empty($path) ? '' : implode('/', $path)); + // 封装路由 + $route = [$module, $controller, $action]; } - return ['type' => 'module', 'module' => $result['route']]; + return ['type' => 'module', 'module' => $route]; } /** - * 解析规范的路由地址 地址格式 [模块/控制器/操作?]参数1=值1&参数2=值2... + * 解析URL的pathinfo参数和变量 * @access private * @param string $url URL地址 - * @param bool $autoSearch 是否自动深度搜索控制器 - * @param bool $reverse 是否反转解析URL - * @param integer $paramType URL参数解析方式 0 名称解析 1 顺序解析 * @return array */ - private static function parseRoute($url, $autoSearch = false, $reverse = false, $paramType = 0) + private static function parseUrlPath($url) { $url = trim($url, '/'); $var = []; @@ -1003,58 +1072,7 @@ class Route } else { $path = [$url]; } - $route = [null, null, null]; - if (isset($path)) { - if ($reverse) { - // 解析模块 - $module = Config::get('app_multi_module') ? array_shift($path) : null; - if ($autoSearch) { - // 自动搜索控制器 - $dir = APP_PATH . ($module ? $module . DS : '') . 'controller'; - $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; - $item = []; - foreach ($path as $val) { - $item[] = array_shift($path); - if (is_file($dir . DS . $val . $suffix . EXT)) { - break; - } else { - $dir .= DS . $val; - } - } - $controller = implode('.', $item); - } else { - // 解析控制器 - $controller = !empty($path) ? array_shift($path) : null; - } - // 解析操作 - $action = !empty($path) ? array_shift($path) : null; - // 解析额外参数 - if (!empty($path)) { - if ($paramType) { - $var += $path; - } else { - preg_replace_callback('/([^\/]+)\/([^\/]+)/', function ($match) use (&$var) { - $var[strtolower($match[1])] = strip_tags($match[2]); - }, implode('/', $path)); - } - } - } else { - $action = array_pop($path); - $controller = !empty($path) ? array_pop($path) : null; - $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; - $method = Request::instance()->method(); - // REST 操作方法支持 - if ('[rest]' == $action) { - $action = $method; - } elseif (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) { - // 操作方法前缀支持 - $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action; - } - } - // 封装路由 - $route = [$module, $controller, $action]; - } - return ['route' => $route, 'var' => $var]; + return [$path, $var]; } /** @@ -1166,14 +1184,22 @@ class Route // 路由到控制器 $result = ['type' => 'controller', 'controller' => substr($url, 1), 'params' => $matches]; } else { - // 解析路由地址 - $result = self::parseRoute($url); - $var = array_merge($matches, $result['var']); - // 解析剩余的URL参数 - self::parseUrlParams(implode('/', $paths), $var); // 路由到模块/控制器/操作 - $result = ['type' => 'module', 'module' => $result['route'], 'convert' => false]; + list($path, $var) = self::parseUrlPath($url); + $action = array_pop($path); + $controller = !empty($path) ? array_pop($path) : null; + $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; + $method = Request::instance()->method(); + if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) { + // 操作方法前缀支持 + $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action; + } + $_GET = array_merge($_GET, $var); + // 路由到模块/控制器/操作 + $result = ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false]; } + // 解析额外参数 + self::parseUrlParams(empty($paths) ? '' : implode('/', $paths), $matches); return $result; } @@ -1191,11 +1217,10 @@ class Route $var += explode('/', $url); } else { preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { - $var[strtolower($match[1])] = strip_tags($match[2]); + $var[$match[1]] = strip_tags($match[2]); }, $url); } } - // 设置当前请求的参数 Request::instance()->param(array_merge($var, $_GET)); } diff --git a/core/library/think/Template.php b/core/library/think/Template.php index 18a37d86..a0f27706 100644 --- a/core/library/think/Template.php +++ b/core/library/think/Template.php @@ -672,7 +672,7 @@ class Template */ public function parseTagLib($tagLib, &$content, $hide = false) { - if (strpos($tagLib, '\\')) { + if (false !== strpos($tagLib, '\\')) { // 支持指定标签库的命名空间 $className = $tagLib; $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); diff --git a/core/library/think/Url.php b/core/library/think/Url.php index fb9f5a79..774fc6e5 100644 --- a/core/library/think/Url.php +++ b/core/library/think/Url.php @@ -78,9 +78,9 @@ class Url } // 检测URL绑定 - $type = Route::bind('type'); + $type = Route::getBind('type'); if ($type) { - $bind = Route::bind($type); + $bind = Route::getBind($type); if (0 === strpos($url, $bind)) { $url = substr($url, strlen($bind) + 1); } @@ -153,9 +153,10 @@ class Url protected static function parseDomain(&$url, $domain) { if ($domain) { + $request = Request::instance(); if (true === $domain) { // 自动判断域名 - $domain = $_SERVER['HTTP_HOST']; + $domain = $request->host(); if (Config::get('url_domain_deploy')) { // 根域名 $urlDomainRoot = Config::get('url_domain_root'); @@ -184,9 +185,9 @@ class Url } } } else { - $domain .= strpos($domain, '.') ? '' : strstr($_SERVER['HTTP_HOST'], '.'); + $domain .= strpos($domain, '.') ? '' : strstr($request->host(), '.'); } - $domain = (self::isSsl() ? 'https://' : 'http://') . $domain; + $domain = ($request->isSsl() ? 'https://' : 'http://') . $domain; } else { $domain = ''; } @@ -217,20 +218,6 @@ class Url return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix; } - /** - * 判断是否SSL协议 - * @return boolean - */ - public static function isSsl() - { - if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) { - return true; - } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) { - return true; - } - return false; - } - // 匹配路由地址 public static function getRouteUrl($alias, &$vars = []) { @@ -253,11 +240,13 @@ class Url } } $match = true; + } elseif (empty($pattern) && array_intersect_assoc($param, $array) == $param) { + $match = true; } - if (empty($pattern) && empty($param)) { - // 没有任何变量 - return $url; - } elseif ($match && (empty($param) || array_intersect_assoc($param, $array) == $param)) { + if (!empty($param) && array_intersect_assoc($param, $array) != $param) { + $match = false; + } + if ($match) { // 存在变量定义 $vars = array_diff_key($array, $param); return $url; diff --git a/core/library/think/db/Connection.php b/core/library/think/db/Connection.php index 410c661f..a45d8d3e 100644 --- a/core/library/think/db/Connection.php +++ b/core/library/think/db/Connection.php @@ -16,11 +16,11 @@ use PDOStatement; use think\App; use think\Collection; use think\Db; +use think\db\exception\BindParamException; use think\db\Query; use think\Debug; use think\Exception; use think\exception\PDOException; -use think\db\exception\BindParamException; use think\Log; abstract class Connection @@ -102,7 +102,7 @@ abstract class Connection // PDO连接参数 protected $params = [ - PDO::ATTR_CASE => PDO::CASE_LOWER, + PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false, @@ -188,7 +188,7 @@ abstract class Connection * @param array $info 字段信息 * @return array */ - protected function fieldCase($info) + public function fieldCase($info) { // 字段大小写转换 switch ($this->attrCase) { @@ -330,7 +330,7 @@ abstract class Connection } // 根据参数绑定组装最终的SQL语句 $this->queryStr = $this->getRealSql($sql, $bind); - + //释放前次的查询结果 if (!empty($this->PDOStatement)) { $this->free(); @@ -474,6 +474,7 @@ abstract class Connection return $this->PDOStatement; } if ($procedure) { + // 存储过程返回结果 return $this->procedure($class); } $result = $this->PDOStatement->fetchAll($this->fetchType); @@ -482,11 +483,10 @@ abstract class Connection if (!empty($class)) { // 返回指定数据集对象类 $result = new $class($result); - } elseif ('collection' == $this->resultSetType){ + } elseif ('collection' == $this->resultSetType) { // 返回数据集Collection对象 $result = new Collection($result); } - return $result; } @@ -551,7 +551,7 @@ abstract class Connection ++$this->transTimes; - if ($this->transTimes == 1) { + if (1 == $this->transTimes) { $this->linkID->beginTransaction(); } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { $this->linkID->exec( @@ -570,7 +570,7 @@ abstract class Connection { $this->initConnect(true); - if ($this->transTimes == 1) { + if (1 == $this->transTimes) { $this->linkID->commit(); } @@ -587,7 +587,7 @@ abstract class Connection { $this->initConnect(true); - if ($this->transTimes == 1) { + if (1 == $this->transTimes) { $this->linkID->rollBack(); } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { $this->linkID->exec( @@ -647,9 +647,9 @@ abstract class Connection } // 提交事务 $this->commit(); - } catch (\PDOException $e) { + } catch (\Exception $e) { $this->rollback(); - return false; + throw $e; } return true; } diff --git a/core/library/think/db/builder/Sqlsrv.php b/core/library/think/db/builder/Sqlsrv.php index 4010fe3c..8cc0f324 100644 --- a/core/library/think/db/builder/Sqlsrv.php +++ b/core/library/think/db/builder/Sqlsrv.php @@ -18,10 +18,10 @@ use think\db\Builder; */ class Sqlsrv extends Builder { - protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; + protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; /** * order分析 @@ -79,9 +79,10 @@ class Sqlsrv extends Builder } return 'WHERE ' . $limitStr; } - public function selectInsert($fields, $table, $options) + + public function selectInsert($fields, $table, $options) { - $this->selectSql=$this->selectInsertSql; + $this->selectSql = $this->selectInsertSql; return parent::selectInsert($fields, $table, $options); } diff --git a/core/library/think/model/Merge.php b/core/library/think/model/Merge.php index 8ade20ce..2239af73 100644 --- a/core/library/think/model/Merge.php +++ b/core/library/think/model/Merge.php @@ -250,7 +250,7 @@ class Merge extends Model return $result; } catch (\Exception $e) { $db->rollback(); - return false; + throw $e; } } @@ -283,9 +283,9 @@ class Merge extends Model $this->trigger('after_delete', $this); $db->commit(); return $result; - } catch (\PDOException $e) { + } catch (\Exception $e) { $db->rollback(); - return false; + throw $e; } } diff --git a/core/library/think/session/driver/Redis.php b/core/library/think/session/driver/Redis.php index 65db47e3..367df10c 100644 --- a/core/library/think/session/driver/Redis.php +++ b/core/library/think/session/driver/Redis.php @@ -16,6 +16,7 @@ use think\Exception; class Redis extends SessionHandler { + /** @var \Redis */ protected $handler = null; protected $config = [ 'host' => '127.0.0.1', // redis主机 @@ -35,8 +36,10 @@ class Redis extends SessionHandler /** * 打开Session * @access public - * @param string $savePath - * @param mixed $sessName + * @param string $savePath + * @param mixed $sessName + * @return bool + * @throws Exception */ public function open($savePath, $sessName) { @@ -72,6 +75,7 @@ class Redis extends SessionHandler * 读取Session * @access public * @param string $sessID + * @return bool|string */ public function read($sessID) { @@ -83,6 +87,7 @@ class Redis extends SessionHandler * @access public * @param string $sessID * @param String $sessData + * @return bool */ public function write($sessID, $sessData) { @@ -97,16 +102,18 @@ class Redis extends SessionHandler * 删除Session * @access public * @param string $sessID + * @return bool|void */ public function destroy($sessID) { - return $this->handler->delete($this->config['session_name'] . $sessID) ? true : false; + $this->handler->delete($this->config['session_name'] . $sessID); } /** * Session 垃圾回收 * @access public * @param string $sessMaxLifeTime + * @return bool */ public function gc($sessMaxLifeTime) { diff --git a/core/library/think/template/TagLib.php b/core/library/think/template/TagLib.php index 456036e0..b0e40f57 100644 --- a/core/library/think/template/TagLib.php +++ b/core/library/think/template/TagLib.php @@ -87,15 +87,15 @@ class TagLib public function parseTag(&$content, $lib = '') { $tags = []; - $_lib = $lib ? $lib . ':' : ''; + $lib = $lib ? strtolower($lib) . ':' : ''; foreach ($this->tags as $name => $val) { - $close = !isset($val['close']) || $val['close'] ? 1 : 0; - $tags[$close][$_lib . $name] = $name; + $close = !isset($val['close']) || $val['close'] ? 1 : 0; + $tags[$close][$lib . $name] = $name; if (isset($val['alias'])) { // 别名设置 $array = (array) $val['alias']; foreach (explode(',', $array[0]) as $v) { - $tags[$close][$_lib . $v] = $name; + $tags[$close][$lib . $v] = $name; } } } @@ -108,7 +108,7 @@ class TagLib $right = []; foreach ($matches as $match) { if ('' == $match[1][0]) { - $name = $match[2][0]; + $name = strtolower($match[2][0]); // 如果有没闭合的标签头则取出最后一个 if (!empty($right[$name])) { // $match[0][1]为标签结束符在模板中的位置 @@ -120,7 +120,7 @@ class TagLib } } else { // 标签头压入栈 - $right[$match[1][0]][] = $match[0]; + $right[strtolower($match[1][0])][] = $match[0]; } } unset($right, $matches); @@ -135,10 +135,10 @@ class TagLib foreach ($nodes as $pos => $node) { // 对应的标签名 $name = $tags[1][$node['name']]; - $alias = $_lib . $name != $node['name'] ? ($_lib ? strstr($node['name'], $_lib) : $node['name']) : ''; + $alias = $lib . $name != $node['name'] ? ($lib ? strstr($node['name'], $lib) : $node['name']) : ''; // 解析标签属性 $attrs = $this->parseAttr($node['begin'][0], $name, $alias); - $method = '_' . $name; + $method = 'tag' . $name; // 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾 $replace = explode($break, $this->$method($attrs, $break)); if (count($replace) > 1) { @@ -170,13 +170,13 @@ class TagLib // 自闭合标签 if (!empty($tags[0])) { $regex = $this->getRegex(array_keys($tags[0]), 0); - $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$_lib) { + $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) { // 对应的标签名 - $name = $tags[0][$matches[1]]; - $alias = $_lib . $name != $matches[1] ? ($_lib ? strstr($matches[1], $_lib) : $matches[1]) : ''; + $name = $tags[0][strtolower($matches[1])]; + $alias = $lib . $name != $matches[1] ? ($lib ? strstr($matches[1], $lib) : $matches[1]) : ''; // 解析标签属性 $attrs = $this->parseAttr($matches[0], $name, $alias); - $method = '_' . $name; + $method = 'tag' . $name; return $this->$method($attrs, ''); }, $content); } @@ -286,6 +286,9 @@ class TagLib */ public function parseCondition($condition) { + if (strpos($condition, ':')) { + $condition = ' ' . substr(strstr($condition, ':'), 1); + } $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition); $this->tpl->parseVar($condition); // $this->tpl->parseVarFunction($condition); // XXX: 此句能解析表达式中用|分隔的函数,但表达式中如果有|、||这样的逻辑运算就产生了歧异 diff --git a/core/library/think/template/taglib/Cx.php b/core/library/think/template/taglib/Cx.php index b085b83a..7336db86 100644 --- a/core/library/think/template/taglib/Cx.php +++ b/core/library/think/template/taglib/Cx.php @@ -43,8 +43,7 @@ class Cx extends Taglib 'notpresent' => ['attr' => 'name'], 'defined' => ['attr' => 'name'], 'notdefined' => ['attr' => 'name'], - 'import' => ['attr' => 'file,href,type,value,basepath', 'close' => 0], - 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['css,js', 'type']], + 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']], 'assign' => ['attr' => 'name,value', 'close' => 0], 'define' => ['attr' => 'name,value', 'close' => 0], 'for' => ['attr' => 'start,end,name,comparison,step'], @@ -61,7 +60,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _php($tag, $content) + public function tagPhp($tag, $content) { $parseStr = ''; return $parseStr; @@ -79,7 +78,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string|void */ - public function _volist($tag, $content) + public function tagVolist($tag, $content) { $name = $tag['name']; $id = $tag['id']; @@ -131,7 +130,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string|void */ - public function _foreach($tag, $content) + public function tagForeach($tag, $content) { // 直接使用表达式 if (!empty($tag['expression'])) { @@ -212,7 +211,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _if($tag, $content) + public function tagIf($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); @@ -228,7 +227,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _elseif($tag, $content) + public function tagElseif($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); @@ -243,7 +242,7 @@ class Cx extends Taglib * @param array $tag 标签属性 * @return string */ - public function _else($tag) + public function tagElse($tag) { $parseStr = ''; return $parseStr; @@ -262,7 +261,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _switch($tag, $content) + public function tagSwitch($tag, $content) { $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; $name = $this->autoBuildVar($name); @@ -277,7 +276,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _case($tag, $content) + public function tagCase($tag, $content) { $value = !empty($tag['expression']) ? $tag['expression'] : $tag['value']; $flag = substr($value, 0, 1); @@ -309,7 +308,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _default($tag) + public function tagDefault($tag) { $parseStr = ''; return $parseStr; @@ -324,7 +323,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _compare($tag, $content) + public function tagCompare($tag, $content) { $name = $tag['name']; $value = $tag['value']; @@ -359,7 +358,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _range($tag, $content) + public function tagRange($tag, $content) { $name = $tag['name']; $value = $tag['value']; @@ -394,7 +393,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _present($tag, $content) + public function tagPresent($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); @@ -411,7 +410,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _notpresent($tag, $content) + public function tagNotpresent($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); @@ -428,7 +427,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _empty($tag, $content) + public function tagEmpty($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); @@ -445,7 +444,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _notempty($tag, $content) + public function tagNotempty($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); @@ -460,7 +459,7 @@ class Cx extends Taglib * @param string $content * @return string */ - public function _defined($tag, $content) + public function tagDefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; @@ -474,7 +473,7 @@ class Cx extends Taglib * @param string $content * @return string */ - public function _notdefined($tag, $content) + public function tagNotdefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; @@ -482,19 +481,17 @@ class Cx extends Taglib } /** - * import 标签解析 {import file="Js.Base" /} - * 格式:{import file="Css.Base" type="css" /} + * load 标签解析 {load file="/static/js/base.js" /} + * 格式:{load file="/static/css/base.css" /} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 - * @param boolean $isFile 是否文件方式 - * @param string $type 类型 * @return string */ - public function _import($tag, $content, $isFile = false) + public function tagLoad($tag, $content) { $file = isset($tag['file']) ? $tag['file'] : $tag['href']; - $type = isset($tag['type']) ? strtolower($tag['type']) : ($isFile ? null : 'js'); + $type = isset($tag['type']) ? strtolower($tag['type']) : ''; $parseStr = ''; $endStr = ''; // 判断是否存在加载条件 允许使用函数判断(默认为isset) @@ -505,58 +502,26 @@ class Cx extends Taglib $parseStr .= ''; $endStr = ''; } - if ($isFile) { - // 文件方式导入 - $array = explode(',', $file); - foreach ($array as $val) { - if (!$type || isset($reset)) { - $type = $reset = strtolower(substr(strrchr($val, '.'), 1)); - } - switch ($type) { - case 'js': - $parseStr .= ''; - break; - case 'css': - $parseStr .= ''; - break; - case 'php': - $parseStr .= ''; - break; - } - } - } else { - // 命名空间导入模式 - $basepath = !empty($tag['basepath']) ? $tag['basepath'] : '/public'; - // 命名空间方式导入外部文件 - $array = explode(',', $file); - foreach ($array as $val) { - if (strpos($val, '?')) { - list($val, $version) = explode('?', $val); - } else { - $version = ''; - } - switch ($type) { - case 'js': - $parseStr .= ''; - break; - case 'css': - $parseStr .= ''; - break; - case 'php': - $parseStr .= ''; - break; - } + + // 文件方式导入 + $array = explode(',', $file); + foreach ($array as $val) { + $type = strtolower(substr(strrchr($val, '.'), 1)); + switch ($type) { + case 'js': + $parseStr .= ''; + break; + case 'css': + $parseStr .= ''; + break; + case 'php': + $parseStr .= ''; + break; } } return $parseStr . $endStr; } - // import别名 采用文件方式加载(要使用命名空间必须用import) 例如 - public function _load($tag, $content) - { - return $this->_import($tag, $content, true); - } - /** * assign标签解析 * 在模板中给某个变量赋值 支持变量赋值 @@ -566,7 +531,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _assign($tag, $content) + public function tagAssign($tag, $content) { $name = $this->autoBuildVar($tag['name']); $flag = substr($tag['value'], 0, 1); @@ -588,7 +553,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _define($tag, $content) + public function tagDefine($tag, $content) { $name = '\'' . $tag['name'] . '\''; $flag = substr($tag['value'], 0, 1); @@ -612,7 +577,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _for($tag, $content) + public function tagFor($tag, $content) { //设置默认值 $start = 0; @@ -663,7 +628,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _url($tag, $content) + public function tagUrl($tag, $content) { $url = isset($tag['link']) ? $tag['link'] : ''; $vars = isset($tag['vars']) ? $tag['vars'] : ''; @@ -689,7 +654,7 @@ class Cx extends Taglib * @param string $content 标签内容 * @return string */ - public function _function($tag, $content) + public function tagFunction($tag, $content) { $name = !empty($tag['name']) ? $tag['name'] : 'func'; $vars = !empty($tag['vars']) ? $tag['vars'] : ''; diff --git a/core/start.php b/core/start.php index 23907dcc..2d517914 100644 --- a/core/start.php +++ b/core/start.php @@ -11,43 +11,8 @@ namespace think; -// ThinkPHP 实际引导文件 +// ThinkPHP 引导文件 // 加载基础文件 require __DIR__ . '/base.php'; -require CORE_PATH . 'Loader.php'; - -// 加载环境变量配置文件 -if (is_file(ROOT_PATH . 'env' . EXT)) { - $env = include ROOT_PATH . 'env' . EXT; - foreach ($env as $key => $val) { - $name = ENV_PREFIX . $key; - if (is_bool($val)) { - $val = $val ? 1 : 0; - } - putenv("$name=$val"); - } -} - -// 注册命名空间定义 -Loader::addNamespace([ - 'think' => LIB_PATH . 'think' . DS, - 'behavior' => LIB_PATH . 'behavior' . DS, - 'traits' => LIB_PATH . 'traits' . DS, -]); - -// 注册自动加载 -Loader::register(); - -// 加载别名定义 -Loader::addMap(include THINK_PATH . 'alias' . EXT); - -// 注册错误和异常处理机制 -Error::register(); - -// 加载模式配置文件 -Config::set(include THINK_PATH . 'convention' . EXT); - -// 是否自动运行 -if (APP_AUTO_RUN) { - App::run()->send(); -} +// 执行应用 +App::run()->send(); diff --git a/core/tpl/think_exception.tpl b/core/tpl/think_exception.tpl index 725fa9ba..5fadd7cc 100644 --- a/core/tpl/think_exception.tpl +++ b/core/tpl/think_exception.tpl @@ -1,3 +1,80 @@ +'.end($names).''; + } + } + + if(!function_exists('parse_file')){ + function parse_file($file, $line) + { + return ''.basename($file)." line {$line}".''; + } + } + + if(!function_exists('parse_args')){ + function parse_args($args) + { + $result = []; + + foreach ($args as $key => $item) { + switch (true) { + case is_object($item): + $value = sprintf('object(%s)', parse_class(get_class($item))); + break; + case is_array($item): + if(count($item) > 3){ + $value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3))); + } else { + $value = sprintf('[%s]', parse_args($item)); + } + break; + case is_string($item): + if(strlen($item) > 20){ + $value = sprintf( + '\'%s...\'', + htmlentities($item), + htmlentities(substr($item, 0, 20)) + ); + } else { + $value = sprintf("'%s'", htmlentities($item)); + } + break; + case is_int($item): + case is_float($item): + $value = $item; + break; + case is_null($item): + $value = 'null'; + break; + case is_bool($item): + $value = '' . ($item ? 'true' : 'false') . ''; + break; + case is_resource($item): + $value = 'resource'; + break; + default: + $value = htmlentities(str_replace("\n", '', var_export(strval($item), true))); + break; + } + + $result[] = is_int($key) ? $value : "'{$key}' => {$value}"; + } + + return implode(', ', $result); + } + } +?> @@ -399,72 +476,4 @@ - -'.end($names).''; - } - - function parse_file($file, $line) - { - return ''.basename($file)." line {$line}".''; - } - - function parse_args($args) - { - $result = []; - - foreach ($args as $key => $item) { - switch (true) { - case is_object($item): - $value = sprintf('object(%s)', parse_class(get_class($item))); - break; - case is_array($item): - if(count($item) > 3){ - $value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3))); - } else { - $value = sprintf('[%s]', parse_args($item)); - } - break; - case is_string($item): - if(strlen($item) > 20){ - $value = sprintf( - '\'%s...\'', - htmlentities($item), - htmlentities(substr($item, 0, 20)) - ); - } else { - $value = sprintf("'%s'", htmlentities($item)); - } - break; - case is_int($item): - case is_float($item): - $value = $item; - break; - case is_null($item): - $value = 'null'; - break; - case is_bool($item): - $value = '' . ($item ? 'true' : 'false') . ''; - break; - case is_resource($item): - $value = 'resource'; - break; - default: - $value = htmlentities(str_replace("\n", '', var_export(strval($item), true))); - break; - } - - $result[] = is_int($key) ? $value : "'{$key}' => {$value}"; - } - - return implode(', ', $result); - } + \ No newline at end of file