Przeglądaj źródła

!20 tp升级到5.0.23
Merge pull request !20 from 聆听/develop

聆听 7 lat temu
rodzic
commit
ed0dc89623

+ 1 - 1
thinkphp/base.php

@@ -9,7 +9,7 @@
 // | Author: liu21st <liu21st@gmail.com>
 // +----------------------------------------------------------------------
 
-define('THINK_VERSION', '5.0.21');
+define('THINK_VERSION', '5.0.23');
 define('THINK_START_TIME', microtime(true));
 define('THINK_START_MEM', memory_get_usage());
 define('EXT', '.php');

+ 2 - 1
thinkphp/library/think/App.php

@@ -551,12 +551,13 @@ class App
 
         // 获取控制器名
         $controller = strip_tags($result[1] ?: $config['default_controller']);
-        $controller = $convert ? strtolower($controller) : $controller;
 
         if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
             throw new HttpException(404, 'controller not exists:' . $controller);
         }
 
+        $controller = $convert ? strtolower($controller) : $controller;
+
         // 获取操作名
         $actionName = strip_tags($result[2] ?: $config['default_action']);
         if (!empty($config['action_convert'])) {

+ 2 - 2
thinkphp/library/think/Log.php

@@ -176,7 +176,7 @@ class Log
             }
         }
 
-        if ($result = self::$driver->save($log)) {
+        if ($result = self::$driver->save($log, true)) {
             self::$log = [];
         }
 
@@ -211,7 +211,7 @@ class Log
         is_null(self::$driver) && self::init(Config::get('log'));
 
         // 写入日志
-        if ($result = self::$driver->save($log)) {
+        if ($result = self::$driver->save($log, false)) {
             self::$log = [];
         }
 

+ 29 - 15
thinkphp/library/think/Model.php

@@ -94,6 +94,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     protected $type = [];
     // 是否为更新数据
     protected $isUpdate = false;
+    // 是否使用Replace
+    protected $replace = false;
     // 是否强制更新所有数据
     protected $force = false;
     // 更新条件
@@ -1013,6 +1015,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
         return false;
     }
 
+    /**
+     * 新增数据是否使用Replace
+     * @access public
+     * @param  bool $replace
+     * @return $this
+     */
+    public function replace($replace = true)
+    {
+        $this->replace = $replace;
+        return $this;
+    }
+
     /**
      * 保存当前数据对象
      * @access public
@@ -1028,19 +1042,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             $data     = [];
         }
 
-        if (!empty($data)) {
-            // 数据自动验证
-            if (!$this->validateData($data)) {
-                return false;
-            }
-            // 数据对象赋值
-            foreach ($data as $key => $value) {
-                $this->setAttr($key, $value, $data);
-            }
-            if (!empty($where)) {
-                $this->isUpdate    = true;
-                $this->updateWhere = $where;
-            }
+        // 数据自动验证
+        if (!$this->validateData($data)) {
+            return false;
+        }
+
+        // 数据对象赋值
+        foreach ($data as $key => $value) {
+            $this->setAttr($key, $value, $data);
+        }
+        
+        if (!empty($where)) {
+            $this->isUpdate    = true;
+            $this->updateWhere = $where;
         }
 
         // 自动关联写入
@@ -1163,9 +1177,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             // 检测字段
             $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert));
             if (!empty($allowFields)) {
-                $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, false, false, $sequence);
+                $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence);
             } else {
-                $result = $this->getQuery()->insert($this->data, false, false, $sequence);
+                $result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence);
             }
 
             // 获取自动增长主键

+ 85 - 80
thinkphp/library/think/Request.php

@@ -160,8 +160,8 @@ class Request
     /**
      * Hook 方法注入
      * @access public
-     * @param string|array  $method 方法名
-     * @param mixed         $callback callable
+     * @param string|array $method   方法名
+     * @param mixed        $callback callable
      * @return void
      */
     public static function hook($method, $callback = null)
@@ -202,13 +202,13 @@ class Request
     /**
      * 创建一个URL请求
      * @access public
-     * @param string    $uri URL地址
-     * @param string    $method 请求类型
-     * @param array     $params 请求参数
-     * @param array     $cookie
-     * @param array     $files
-     * @param array     $server
-     * @param string    $content
+     * @param string $uri    URL地址
+     * @param string $method 请求类型
+     * @param array  $params 请求参数
+     * @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 = [], $content = null)
@@ -415,7 +415,7 @@ class Request
                 foreach (Config::get('pathinfo_fetch') as $type) {
                     if (!empty($_SERVER[$type])) {
                         $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ?
-                        substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
+                            substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
                         break;
                     }
                 }
@@ -496,8 +496,8 @@ class Request
     /**
      * 设置资源类型
      * @access public
-     * @param string|array  $type 资源类型名
-     * @param string        $val 资源类型
+     * @param string|array $type 资源类型名
+     * @param string       $val  资源类型
      * @return void
      */
     public function mimeType($type, $val = '')
@@ -512,7 +512,7 @@ class Request
     /**
      * 当前的请求类型
      * @access public
-     * @param bool $method  true 获取原始请求类型
+     * @param bool $method true 获取原始请求类型
      * @return string
      */
     public function method($method = false)
@@ -626,9 +626,9 @@ class Request
     /**
      * 获取当前请求的参数
      * @access public
-     * @param string|array  $name 变量名
-     * @param mixed         $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function param($name = '', $default = null, $filter = '')
@@ -664,15 +664,16 @@ class Request
     /**
      * 设置获取路由参数
      * @access public
-     * @param string|array  $name 变量名
-     * @param mixed         $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function route($name = '', $default = null, $filter = '')
     {
         if (is_array($name)) {
-            $this->param        = [];
+            $this->param      = [];
+            $this->mergeParam = false;
             return $this->route = array_merge($this->route, $name);
         }
         return $this->input($this->route, $name, $default, $filter);
@@ -681,9 +682,9 @@ class Request
     /**
      * 设置获取GET参数
      * @access public
-     * @param string|array  $name 变量名
-     * @param mixed         $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function get($name = '', $default = null, $filter = '')
@@ -693,6 +694,7 @@ class Request
         }
         if (is_array($name)) {
             $this->param      = [];
+            $this->mergeParam = false;
             return $this->get = array_merge($this->get, $name);
         }
         return $this->input($this->get, $name, $default, $filter);
@@ -701,9 +703,9 @@ class Request
     /**
      * 设置获取POST参数
      * @access public
-     * @param string        $name 变量名
-     * @param mixed         $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string       $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function post($name = '', $default = null, $filter = '')
@@ -717,7 +719,8 @@ class Request
             }
         }
         if (is_array($name)) {
-            $this->param       = [];
+            $this->param      = [];
+            $this->mergeParam = false;
             return $this->post = array_merge($this->post, $name);
         }
         return $this->input($this->post, $name, $default, $filter);
@@ -726,9 +729,9 @@ class Request
     /**
      * 设置获取PUT参数
      * @access public
-     * @param string|array      $name 变量名
-     * @param mixed             $default 默认值
-     * @param string|array      $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function put($name = '', $default = null, $filter = '')
@@ -743,6 +746,7 @@ class Request
         }
         if (is_array($name)) {
             $this->param      = [];
+            $this->mergeParam = false;
             return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name);
         }
 
@@ -752,9 +756,9 @@ class Request
     /**
      * 设置获取DELETE参数
      * @access public
-     * @param string|array      $name 变量名
-     * @param mixed             $default 默认值
-     * @param string|array      $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function delete($name = '', $default = null, $filter = '')
@@ -765,9 +769,9 @@ class Request
     /**
      * 设置获取PATCH参数
      * @access public
-     * @param string|array      $name 变量名
-     * @param mixed             $default 默认值
-     * @param string|array      $filter 过滤方法
+     * @param string|array $name    变量名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function patch($name = '', $default = null, $filter = '')
@@ -777,9 +781,9 @@ class Request
 
     /**
      * 获取request变量
-     * @param string        $name 数据名称
-     * @param string        $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string       $name    数据名称
+     * @param string       $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function request($name = '', $default = null, $filter = '')
@@ -788,7 +792,8 @@ class Request
             $this->request = $_REQUEST;
         }
         if (is_array($name)) {
-            $this->param          = [];
+            $this->param      = [];
+            $this->mergeParam = false;
             return $this->request = array_merge($this->request, $name);
         }
         return $this->input($this->request, $name, $default, $filter);
@@ -797,9 +802,9 @@ class Request
     /**
      * 获取session数据
      * @access public
-     * @param string|array  $name 数据名称
-     * @param string        $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    数据名称
+     * @param string       $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function session($name = '', $default = null, $filter = '')
@@ -816,9 +821,9 @@ class Request
     /**
      * 获取cookie参数
      * @access public
-     * @param string|array  $name 数据名称
-     * @param string        $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    数据名称
+     * @param string       $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function cookie($name = '', $default = null, $filter = '')
@@ -849,9 +854,9 @@ class Request
     /**
      * 获取server参数
      * @access public
-     * @param string|array  $name 数据名称
-     * @param string        $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    数据名称
+     * @param string       $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function server($name = '', $default = null, $filter = '')
@@ -927,9 +932,9 @@ class Request
 
     /**
      * 获取环境变量
-     * @param string|array  $name 数据名称
-     * @param string        $default 默认值
-     * @param string|array  $filter 过滤方法
+     * @param string|array $name    数据名称
+     * @param string       $default 默认值
+     * @param string|array $filter  过滤方法
      * @return mixed
      */
     public function env($name = '', $default = null, $filter = '')
@@ -946,8 +951,8 @@ class Request
     /**
      * 设置或者获取当前的Header
      * @access public
-     * @param string|array  $name header名称
-     * @param string        $default 默认值
+     * @param string|array $name    header名称
+     * @param string       $default 默认值
      * @return string
      */
     public function header($name = '', $default = null)
@@ -985,10 +990,10 @@ class Request
 
     /**
      * 获取变量 支持过滤和默认值
-     * @param array         $data 数据源
-     * @param string|false  $name 字段名
-     * @param mixed         $default 默认值
-     * @param string|array  $filter 过滤函数
+     * @param array        $data    数据源
+     * @param string|false $name    字段名
+     * @param mixed        $default 默认值
+     * @param string|array $filter  过滤函数
      * @return mixed
      */
     public function input($data = [], $name = '', $default = null, $filter = '')
@@ -1069,9 +1074,9 @@ class Request
 
     /**
      * 递归过滤给定的值
-     * @param mixed     $value 键值
-     * @param mixed     $key 键名
-     * @param array     $filters 过滤方法+默认值
+     * @param mixed $value   键值
+     * @param mixed $key     键名
+     * @param array $filters 过滤方法+默认值
      * @return mixed
      */
     private function filterValue(&$value, $key, $filters)
@@ -1156,9 +1161,9 @@ class Request
     /**
      * 是否存在某个请求参数
      * @access public
-     * @param string    $name 变量名
-     * @param string    $type 变量类型
-     * @param bool      $checkEmpty 是否检测空值
+     * @param string $name       变量名
+     * @param string $type       变量类型
+     * @param bool   $checkEmpty 是否检测空值
      * @return mixed
      */
     public function has($name, $type = 'param', $checkEmpty = false)
@@ -1182,8 +1187,8 @@ class Request
     /**
      * 获取指定的参数
      * @access public
-     * @param string|array  $name 变量名
-     * @param string        $type 变量类型
+     * @param string|array $name 变量名
+     * @param string       $type 变量类型
      * @return mixed
      */
     public function only($name, $type = 'param')
@@ -1204,8 +1209,8 @@ class Request
     /**
      * 排除指定参数获取
      * @access public
-     * @param string|array  $name 变量名
-     * @param string        $type 变量类型
+     * @param string|array $name 变量名
+     * @param string       $type 变量类型
      * @return mixed
      */
     public function except($name, $type = 'param')
@@ -1247,7 +1252,7 @@ class Request
     /**
      * 当前是否Ajax请求
      * @access public
-     * @param bool $ajax  true 获取原始ajax请求
+     * @param bool $ajax true 获取原始ajax请求
      * @return bool
      */
     public function isAjax($ajax = false)
@@ -1257,7 +1262,7 @@ class Request
         if (true === $ajax) {
             return $result;
         } else {
-            $result =  $this->param(Config::get('var_ajax')) ? true : $result;
+            $result           = $this->param(Config::get('var_ajax')) ? true : $result;
             $this->mergeParam = false;
             return $result;
         }
@@ -1266,7 +1271,7 @@ class Request
     /**
      * 当前是否Pjax请求
      * @access public
-     * @param bool $pjax  true 获取原始pjax请求
+     * @param bool $pjax true 获取原始pjax请求
      * @return bool
      */
     public function isPjax($pjax = false)
@@ -1275,7 +1280,7 @@ class Request
         if (true === $pjax) {
             return $result;
         } else {
-            $result = $this->param(Config::get('var_pjax')) ? true : $result;
+            $result           = $this->param(Config::get('var_pjax')) ? true : $result;
             $this->mergeParam = false;
             return $result;
         }
@@ -1283,13 +1288,13 @@ class Request
 
     /**
      * 获取客户端IP地址
-     * @param integer   $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
-     * @param boolean   $adv 是否进行高级模式获取(有可能被伪装)
+     * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
+     * @param boolean $adv  是否进行高级模式获取(有可能被伪装)
      * @return mixed
      */
     public function ip($type = 0, $adv = true)
     {
-        $type      = $type ? 1 : 0;
+        $type = $type ? 1 : 0;
         static $ip = null;
         if (null !== $ip) {
             return $ip[$type];
@@ -1364,7 +1369,7 @@ class Request
     /**
      * 当前请求的host
      * @access public
-     * @param bool $strict  true 仅仅获取HOST
+     * @param bool $strict true 仅仅获取HOST
      * @return string
      */
     public function host($strict = false)
@@ -1445,7 +1450,7 @@ class Request
     /**
      * 设置或者获取当前请求的调度信息
      * @access public
-     * @param array  $dispatch 调度信息
+     * @param array $dispatch 调度信息
      * @return array
      */
     public function dispatch($dispatch = null)
@@ -1565,7 +1570,7 @@ class Request
     /**
      * 设置当前地址的请求缓存
      * @access public
-     * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
+     * @param string $key    缓存标识,支持变量规则 ,例如 item/:name/:id
      * @param mixed  $expire 缓存有效期
      * @param array  $except 缓存排除
      * @param string $tag    缓存标签
@@ -1628,7 +1633,7 @@ class Request
                 throw new \think\exception\HttpResponseException($response);
             } elseif (Cache::has($key)) {
                 list($content, $header) = Cache::get($key);
-                $response               = Response::create($content)->header($header);
+                $response = Response::create($content)->header($header);
                 throw new \think\exception\HttpResponseException($response);
             } else {
                 $this->cache = [$key, $expire, $tag];
@@ -1650,7 +1655,7 @@ class Request
      * 设置当前请求绑定的对象实例
      * @access public
      * @param string|array $name 绑定的对象标识
-     * @param mixed  $obj 绑定的对象实例
+     * @param mixed        $obj  绑定的对象实例
      * @return mixed
      */
     public function bind($name, $obj = null)

+ 34 - 2
thinkphp/library/think/Validate.php

@@ -67,6 +67,8 @@ class Validate
         'min'         => 'min size of :attribute must be :rule',
         'after'       => ':attribute cannot be less than :rule',
         'before'      => ':attribute cannot exceed :rule',
+        'afterWith'   => ':attribute cannot be less than :rule',
+        'beforeWith'  => ':attribute cannot exceed :rule',
         'expire'      => ':attribute not within :rule',
         'allowIp'     => 'access IP is not allowed',
         'denyIp'      => 'access IP denied',
@@ -1113,9 +1115,10 @@ class Validate
      * @access protected
      * @param mixed     $value  字段值
      * @param mixed     $rule  验证规则
+     * @param array     $data  数据
      * @return bool
      */
-    protected function after($value, $rule)
+    protected function after($value, $rule, $data)
     {
         return strtotime($value) >= strtotime($rule);
     }
@@ -1125,13 +1128,42 @@ class Validate
      * @access protected
      * @param mixed     $value  字段值
      * @param mixed     $rule  验证规则
+     * @param array     $data  数据
      * @return bool
      */
-    protected function before($value, $rule)
+    protected function before($value, $rule, $data)
     {
         return strtotime($value) <= strtotime($rule);
     }
 
+    /**
+     * 验证日期字段
+     * @access protected
+     * @param mixed     $value  字段值
+     * @param mixed     $rule  验证规则
+     * @param array     $data  数据
+     * @return bool
+     */
+    protected function afterWith($value, $rule, $data)
+    {
+        $rule = $this->getDataValue($data, $rule);
+        return !is_null($rule) && strtotime($value) >= strtotime($rule);
+    }
+
+    /**
+     * 验证日期字段
+     * @access protected
+     * @param mixed     $value  字段值
+     * @param mixed     $rule  验证规则
+     * @param array     $data  数据
+     * @return bool
+     */
+    protected function beforeWith($value, $rule, $data)
+    {
+        $rule = $this->getDataValue($data, $rule);
+        return !is_null($rule) && strtotime($value) <= strtotime($rule);
+    }
+
     /**
      * 验证有效期
      * @access protected

+ 6 - 10
thinkphp/library/think/db/Builder.php

@@ -98,6 +98,10 @@ abstract class Builder
 
         $result = [];
         foreach ($data as $key => $val) {
+            if ('*' != $options['field'] && !in_array($key, $fields, true)) {
+                continue;
+            }
+
             $item = $this->parseKey($key, $options, true);
             if ($val instanceof Expression) {
                 $result[$item] = $val->getValue();
@@ -115,18 +119,10 @@ abstract class Builder
             } elseif (is_array($val) && !empty($val)) {
                 switch (strtolower($val[0])) {
                     case 'inc':
-//                        $result[$item] = $item . '+' . floatval($val[1]);
-                        if ($key == $val[1]) {
-                            $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
-                        }
-
+                        $result[$item] = $item . '+' . floatval($val[1]);
                         break;
                     case 'dec':
-//                        $result[$item] = $item . '-' . floatval($val[1]);
-                        if ($key == $val[1]) {
-                            $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
-                        }
-
+                        $result[$item] = $item . '-' . floatval($val[1]);
                         break;
                     case 'exp':
                         throw new Exception('not support data:[' . $val[0] . ']');

+ 8 - 19
thinkphp/library/think/db/Connection.php

@@ -361,14 +361,9 @@ abstract class Connection
             // 调试开始
             $this->debug(true);
 
-            // 释放前次的查询结果
-            if (!empty($this->PDOStatement)) {
-                $this->free();
-            }
             // 预处理
-            if (empty($this->PDOStatement)) {
-                $this->PDOStatement = $this->linkID->prepare($sql);
-            }
+            $this->PDOStatement = $this->linkID->prepare($sql);
+
             // 是否为存储过程调用
             $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
             // 参数绑定
@@ -429,14 +424,9 @@ abstract class Connection
             // 调试开始
             $this->debug(true);
 
-            //释放前次的查询结果
-            if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
-                $this->free();
-            }
             // 预处理
-            if (empty($this->PDOStatement)) {
-                $this->PDOStatement = $this->linkID->prepare($sql);
-            }
+            $this->PDOStatement = $this->linkID->prepare($sql);
+
             // 是否为存储过程调用
             $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
             // 参数绑定
@@ -659,18 +649,15 @@ abstract class Connection
                 );
             }
 
-        } catch (\PDOException $e) {
-            if ($this->isBreak($e)) {
-                return $this->close()->startTrans();
-            }
-            throw $e;
         } catch (\Exception $e) {
             if ($this->isBreak($e)) {
+                --$this->transTimes;
                 return $this->close()->startTrans();
             }
             throw $e;
         } catch (\Error $e) {
             if ($this->isBreak($e)) {
+                --$this->transTimes;
                 return $this->close()->startTrans();
             }
             throw $e;
@@ -804,6 +791,8 @@ abstract class Connection
         $this->linkWrite = null;
         $this->linkRead  = null;
         $this->links     = [];
+        // 释放查询
+        $this->free();
         return $this;
     }
 

+ 57 - 10
thinkphp/library/think/db/Query.php

@@ -92,6 +92,13 @@ class Query
             $name         = Loader::parseName(substr($method, 10));
             $where[$name] = $args[0];
             return $this->where($where)->value($args[1]);
+        } elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
+            // 动态调用命名范围
+            $method = 'scope' . $method;
+            array_unshift($args, $this);
+
+            call_user_func_array([$this->model, $method], $args);
+            return $this;
         } else {
             throw new Exception('method not exist:' . __CLASS__ . '->' . $method);
         }
@@ -436,9 +443,10 @@ class Query
                 // 返回SQL语句
                 return $pdo;
             }
+
             $result = $pdo->fetchColumn();
             if ($force) {
-                $result += 0;
+                $result = (float) $result;
             }
 
             if (isset($cache) && false !== $result) {
@@ -531,13 +539,43 @@ class Query
     public function count($field = '*')
     {
         if (isset($this->options['group'])) {
+            if (!preg_match('/^[\w\.\*]+$/', $field)) {
+                throw new Exception('not support data:' . $field);
+            }
             // 支持GROUP
             $options = $this->getOptions();
             $subSql  = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql();
-            return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true);
+
+            $count = $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true);
+        } else {
+            $count = $this->aggregate('COUNT', $field, true);
         }
 
-        return $this->value('COUNT(' . $field . ') AS tp_count', 0, true);
+        return is_string($count) ? $count : (int) $count;
+
+    }
+
+    /**
+     * 聚合查询
+     * @access public
+     * @param  string $aggregate    聚合方法
+     * @param  string $field        字段名
+     * @param  bool   $force        强制转为数字类型
+     * @return mixed
+     */
+    public function aggregate($aggregate, $field, $force = false)
+    {
+        if (0 === stripos($field, 'DISTINCT ')) {
+            list($distinct, $field) = explode(' ', $field);
+        }
+
+        if (!preg_match('/^[\w\.\+\-\*]+$/', $field)) {
+            throw new Exception('not support data:' . $field);
+        }
+
+        $result = $this->value($aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $field . ') AS tp_' . strtolower($aggregate), 0, $force);
+
+        return $result;
     }
 
     /**
@@ -548,7 +586,7 @@ class Query
      */
     public function sum($field)
     {
-        return $this->value('SUM(' . $field . ') AS tp_sum', 0, true);
+        return $this->aggregate('SUM', $field, true);
     }
 
     /**
@@ -560,7 +598,7 @@ class Query
      */
     public function min($field, $force = true)
     {
-        return $this->value('MIN(' . $field . ') AS tp_min', 0, $force);
+        return $this->aggregate('MIN', $field, $force);
     }
 
     /**
@@ -572,7 +610,7 @@ class Query
      */
     public function max($field, $force = true)
     {
-        return $this->value('MAX(' . $field . ') AS tp_max', 0, $force);
+        return $this->aggregate('MAX', $field, $force);
     }
 
     /**
@@ -583,7 +621,7 @@ class Query
      */
     public function avg($field)
     {
-        return $this->value('AVG(' . $field . ') AS tp_avg', 0, true);
+        return $this->aggregate('AVG', $field, true);
     }
 
     /**
@@ -2092,14 +2130,23 @@ class Query
                 $this->field('*');
             }
             foreach ($relations as $key => $relation) {
-                $closure = false;
+                $closure = $name = null;
                 if ($relation instanceof \Closure) {
                     $closure  = $relation;
                     $relation = $key;
+                } elseif (!is_int($key)) {
+                    $name     = $relation;
+                    $relation = $key;
                 }
                 $relation = Loader::parseName($relation, 1, false);
-                $count    = '(' . $this->model->$relation()->getRelationCountQuery($closure) . ')';
-                $this->field([$count => Loader::parseName($relation) . '_count']);
+
+                $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $name) . ')';
+
+                if (empty($name)) {
+                    $name = Loader::parseName($relation) . '_count';
+                }
+
+                $this->field([$count => $name]);
             }
         }
         return $this;

+ 3 - 0
thinkphp/library/think/db/builder/Mysql.php

@@ -109,6 +109,9 @@ class Mysql extends Builder
             }
         }
 
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+            throw new Exception('not support data:' . $key);
+        }
         if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
             $key = '`' . $key . '`';
         }

+ 4 - 0
thinkphp/library/think/db/builder/Sqlsrv.php

@@ -94,6 +94,10 @@ class Sqlsrv extends Builder
                 $table = $options['alias'][$table];
             }
         }
+
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+            throw new Exception('not support data:' . $key);
+        }
         if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
             $key = '[' . $key . ']';
         }

+ 4 - 1
thinkphp/library/think/db/connector/Sqlsrv.php

@@ -50,7 +50,10 @@ class Sqlsrv extends Connection
     public function getFields($tableName)
     {
         list($tableName) = explode(' ', $tableName);
-        $sql             = "SELECT   column_name,   data_type,   column_default,   is_nullable
+        $tableNames      = explode('.', $tableName);
+        $tableName       = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0];
+
+        $sql = "SELECT   column_name,   data_type,   column_default,   is_nullable
         FROM    information_schema.tables AS t
         JOIN    information_schema.columns AS c
         ON  t.table_catalog = c.table_catalog

+ 190 - 66
thinkphp/library/think/log/driver/File.php

@@ -26,10 +26,9 @@ class File
         'path'        => LOG_PATH,
         'apart_level' => [],
         'max_files'   => 0,
+        'json'        => false,
     ];
 
-    protected $writed = [];
-
     // 实例化并传入参数
     public function __construct($config = [])
     {
@@ -41,106 +40,231 @@ class File
     /**
      * 日志写入接口
      * @access public
-     * @param array $log 日志信息
+     * @param  array    $log 日志信息
+     * @param  bool     $append 是否追加请求信息
      * @return bool
      */
-    public function save(array $log = [])
+    public function save(array $log = [], $append = false)
+    {
+        $destination = $this->getMasterLogFile();
+
+        $path = dirname($destination);
+        !is_dir($path) && mkdir($path, 0755, true);
+
+        $info = [];
+        foreach ($log as $type => $val) {
+
+            foreach ($val as $msg) {
+                if (!is_string($msg)) {
+                    $msg = var_export($msg, true);
+                }
+
+                $info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg;
+            }
+
+            if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) {
+                // 独立记录的日志级别
+                $filename = $this->getApartLevelFile($path, $type);
+
+                $this->write($info[$type], $filename, true, $append);
+                unset($info[$type]);
+            }
+        }
+
+        if ($info) {
+            return $this->write($info, $destination, false, $append);
+        }
+
+        return true;
+    }
+
+    /**
+     * 获取主日志文件名
+     * @access public
+     * @return string
+     */
+    protected function getMasterLogFile()
     {
         if ($this->config['single']) {
-            $destination = $this->config['path'] . 'single.log';
+            $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
+
+            $destination = $this->config['path'] . $name . '.log';
         } else {
-            $cli = IS_CLI ? '_cli' : '';
+            $cli = PHP_SAPI == 'cli' ? '_cli' : '';
 
             if ($this->config['max_files']) {
                 $filename = date('Ymd') . $cli . '.log';
                 $files    = glob($this->config['path'] . '*.log');
 
-                if (count($files) > $this->config['max_files']) {
-                    unlink($files[0]);
+                try {
+                    if (count($files) > $this->config['max_files']) {
+                        unlink($files[0]);
+                    }
+                } catch (\Exception $e) {
                 }
             } else {
-                $filename = date('Ym') . '/' . date('d') . $cli . '.log';
+                $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
             }
 
             $destination = $this->config['path'] . $filename;
         }
 
-        $path = dirname($destination);
-        !is_dir($path) && mkdir($path, 0755, true);
+        return $destination;
+    }
 
-        $info = '';
-        foreach ($log as $type => $val) {
-            $level = '';
-            foreach ($val as $msg) {
-                if (!is_string($msg)) {
-                    $msg = var_export($msg, true);
-                }
-                $level .= '[ ' . $type . ' ] ' . $msg . "\r\n";
-            }
-            if (in_array($type, $this->config['apart_level'])) {
-                // 独立记录的日志级别
-                if ($this->config['single']) {
-                    $filename = $path . DS . $type . '.log';
-                } elseif ($this->config['max_files']) {
-                    $filename = $path . DS . date('Ymd') . '_' . $type . $cli . '.log';
-                } else {
-                    $filename = $path . DS . date('d') . '_' . $type . $cli . '.log';
-                }
-                $this->write($level, $filename, true);
-            } else {
-                $info .= $level;
-            }
+    /**
+     * 获取独立日志文件名
+     * @access public
+     * @param  string $path 日志目录
+     * @param  string $type 日志类型
+     * @return string
+     */
+    protected function getApartLevelFile($path, $type)
+    {
+        $cli = PHP_SAPI == 'cli' ? '_cli' : '';
+
+        if ($this->config['single']) {
+            $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
+
+            $name .= '_' . $type;
+        } elseif ($this->config['max_files']) {
+            $name = date('Ymd') . '_' . $type . $cli;
+        } else {
+            $name = date('d') . '_' . $type . $cli;
         }
-        if ($info) {
-            return $this->write($info, $destination);
+
+        return $path . DIRECTORY_SEPARATOR . $name . '.log';
+    }
+
+    /**
+     * 日志写入
+     * @access protected
+     * @param  array     $message 日志信息
+     * @param  string    $destination 日志文件
+     * @param  bool      $apart 是否独立文件写入
+     * @param  bool      $append 是否追加请求信息
+     * @return bool
+     */
+    protected function write($message, $destination, $apart = false, $append = false)
+    {
+        // 检测日志文件大小,超过配置大小则备份日志文件重新生成
+        $this->checkLogSize($destination);
+
+        // 日志信息封装
+        $info['timestamp'] = date($this->config['time_format']);
+
+        foreach ($message as $type => $msg) {
+            $info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;
         }
-        return true;
+
+        if (PHP_SAPI == 'cli') {
+            $message = $this->parseCliLog($info);
+        } else {
+            // 添加调试日志
+            $this->getDebugLog($info, $append, $apart);
+
+            $message = $this->parseLog($info);
+        }
+
+        return error_log($message, 3, $destination);
     }
 
-    protected function write($message, $destination, $apart = false)
+    /**
+     * 检查日志文件大小并自动生成备份文件
+     * @access protected
+     * @param  string    $destination 日志文件
+     * @return void
+     */
+    protected function checkLogSize($destination)
     {
-        //检测日志文件大小,超过配置大小则备份日志文件重新生成
         if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
             try {
-                rename($destination, dirname($destination) . DS . time() . '-' . basename($destination));
+                rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
             } catch (\Exception $e) {
             }
-            $this->writed[$destination] = false;
         }
+    }
+
+    /**
+     * CLI日志解析
+     * @access protected
+     * @param  array     $info 日志信息
+     * @return string
+     */
+    protected function parseCliLog($info)
+    {
+        if ($this->config['json']) {
+            $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
+        } else {
+            $now = $info['timestamp'];
+            unset($info['timestamp']);
+
+            $message = implode("\r\n", $info);
+
+            $message = "[{$now}]" . $message . "\r\n";
+        }
+
+        return $message;
+    }
+
+    /**
+     * 解析日志
+     * @access protected
+     * @param  array     $info 日志信息
+     * @return string
+     */
+    protected function parseLog($info)
+    {
+        $request     = Request::instance();
+        $requestInfo = [
+            'ip'     => $request->ip(),
+            'method' => $request->method(),
+            'host'   => $request->host(),
+            'uri'    => $request->url(),
+        ];
+
+        if ($this->config['json']) {
+            $info = $requestInfo + $info;
+            return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
+        }
+
+        array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
+        unset($info['timestamp']);
+
+        return implode("\r\n", $info) . "\r\n";
+    }
+
+    protected function getDebugLog(&$info, $append, $apart)
+    {
+        if (App::$debug && $append) {
 
-        if (empty($this->writed[$destination]) && !IS_CLI) {
-            if (App::$debug && !$apart) {
+            if ($this->config['json']) {
                 // 获取基本信息
-                if (isset($_SERVER['HTTP_HOST'])) {
-                    $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-                } else {
-                    $current_uri = "cmd:" . implode(' ', $_SERVER['argv']);
-                }
+                $runtime = round(microtime(true) - THINK_START_TIME, 10);
+                $reqs    = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
 
-                $runtime    = round(microtime(true) - THINK_START_TIME, 10);
-                $reqs       = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
-                $time_str   = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
                 $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
+
+                $info = [
+                    'runtime' => number_format($runtime, 6) . 's',
+                    'reqs'    => $reqs . 'req/s',
+                    'memory'  => $memory_use . 'kb',
+                    'file'    => count(get_included_files()),
+                ] + $info;
+
+            } elseif (!$apart) {
+                // 增加额外的调试信息
+                $runtime = round(microtime(true) - THINK_START_TIME, 10);
+                $reqs    = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
+
+                $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
+
+                $time_str   = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]';
                 $memory_str = ' [内存消耗:' . $memory_use . 'kb]';
                 $file_load  = ' [文件加载:' . count(get_included_files()) . ']';
 
-                $message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
+                array_unshift($info, $time_str . $memory_str . $file_load);
             }
-            $now     = date($this->config['time_format']);
-            $ip      = Request::instance()->ip();
-            $method  = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
-            $uri     = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
-            $message = "---------------------------------------------------------------\r\n[{$now}] {$ip} {$method} {$uri}\r\n" . $message;
-
-            $this->writed[$destination] = true;
-        }
-
-        if (IS_CLI) {
-            $now     = date($this->config['time_format']);
-            $message = "[{$now}]" . $message;
         }
-
-        return error_log($message, 3, $destination);
     }
-
 }

+ 1 - 1
thinkphp/library/think/log/driver/Socket.php

@@ -60,7 +60,7 @@ class Socket
      * @param array     $log 日志信息
      * @return bool
      */
-    public function save(array $log = [])
+    public function save(array $log = [], $append = false)
     {
         if (!$this->check()) {
             return false;

+ 48 - 3
thinkphp/library/think/model/relation/BelongsToMany.php

@@ -29,6 +29,8 @@ class BelongsToMany extends Relation
     protected $pivotName;
     // 中间表模型对象
     protected $pivot;
+    // 中间表数据名称
+    protected $pivotDataName = 'pivot';
 
     /**
      * 构造函数
@@ -70,6 +72,18 @@ class BelongsToMany extends Relation
         return $this;
     }
 
+    /**
+     * 设置中间表数据名称
+     * @access public
+     * @param  string $name
+     * @return $this
+     */
+    public function pivotDataName($name)
+    {
+        $this->pivotDataName = $name;
+        return $this;
+    }
+
     /**
      * 获取中间表更新条件
      * @param $data
@@ -118,7 +132,7 @@ class BelongsToMany extends Relation
                     }
                 }
             }
-            $model->setRelation('pivot', $this->newPivot($pivot, true));
+            $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
         }
     }
 
@@ -346,10 +360,18 @@ class BelongsToMany extends Relation
      * 获取关联统计子查询
      * @access public
      * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
      * @return string
      */
-    public function getRelationCountQuery($closure)
+    public function getRelationCountQuery($closure, &$name = null)
     {
+        if ($closure) {
+            $return = call_user_func_array($closure, [ & $this->query]);
+            if ($return && is_string($return)) {
+                $name = $return;
+            }
+        }
+
         return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
             'pivot.' . $this->localKey => [
                 'exp',
@@ -384,7 +406,7 @@ class BelongsToMany extends Relation
                     }
                 }
             }
-            $set->setRelation('pivot', $this->newPivot($pivot, true));
+            $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
             $data[$pivot[$this->localKey]][] = $set;
         }
         return $data;
@@ -499,6 +521,29 @@ class BelongsToMany extends Relation
         }
     }
 
+    /**
+     * 判断是否存在关联数据
+     * @access public
+     * @param  mixed $data  数据 可以使用关联模型对象 或者 关联对象的主键
+     * @return Pivot
+     * @throws Exception
+     */
+    public function attached($data)
+    {
+        if ($data instanceof Model) {
+            $relationFk = $data->getPk();
+            $id         = $data->$relationFk;
+        } else {
+            $id = $data;
+        }
+
+        $pk = $this->parent->getPk();
+
+        $pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find();
+
+        return $pivot ?: false;
+    }
+
     /**
      * 解除关联的一个中间表数据
      * @access public

+ 20 - 4
thinkphp/library/think/model/relation/HasMany.php

@@ -152,12 +152,16 @@ class HasMany extends Relation
      * 创建关联统计子查询
      * @access public
      * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
      * @return string
      */
-    public function getRelationCountQuery($closure)
+    public function getRelationCountQuery($closure, &$name = null)
     {
         if ($closure) {
-            call_user_func_array($closure, [ & $this->query]);
+            $return = call_user_func_array($closure, [ & $this->query]);
+            if ($return && is_string($return)) {
+                $name = $return;
+            }
         }
         $localKey = $this->localKey ?: $this->parent->getPk();
         return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
@@ -197,14 +201,26 @@ class HasMany extends Relation
      * @return Model|false
      */
     public function save($data)
+    {
+        $model = $this->make($data);
+        return $model->save($data) ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array $data
+     * @return Model
+     */
+    public function make($data = [])
     {
         if ($data instanceof Model) {
             $data = $data->getData();
         }
+
         // 保存关联表数据
-        $model                   = new $this->model;
         $data[$this->foreignKey] = $this->parent->{$this->localKey};
-        return $model->save($data) ? $model : false;
+
+        return new $this->model($data);
     }
 
     /**

+ 12 - 0
thinkphp/library/think/model/relation/HasManyThrough.php

@@ -120,6 +120,18 @@ class HasManyThrough extends Relation
     public function relationCount($result, $closure)
     {}
 
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
+     * @return string
+     */
+    public function getRelationCountQuery($closure, &$name = null)
+    {
+        throw new Exception('relation not support: withCount');
+    }
+
     /**
      * 执行基础查询(进执行一次)
      * @access protected

+ 22 - 5
thinkphp/library/think/model/relation/MorphMany.php

@@ -188,15 +188,19 @@ class MorphMany extends Relation
     }
 
     /**
-     * 获取关联统计子查询
+     * 创建关联统计子查询
      * @access public
      * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
      * @return string
      */
-    public function getRelationCountQuery($closure)
+    public function getRelationCountQuery($closure, &$name = null)
     {
         if ($closure) {
-            call_user_func_array($closure, [ & $this->query]);
+            $return = call_user_func_array($closure, [ & $this->query]);
+            if ($return && is_string($return)) {
+                $name = $return;
+            }
         }
 
         return $this->query->where([
@@ -240,17 +244,30 @@ class MorphMany extends Relation
      * @return Model|false
      */
     public function save($data)
+    {
+        $model = $this->make($data);
+
+        return $model->save($data) ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array $data
+     * @return Model
+     */
+    public function make($data = [])
     {
         if ($data instanceof Model) {
             $data = $data->getData();
         }
+
         // 保存关联表数据
         $pk = $this->parent->getPk();
 
-        $model                  = new $this->model;
         $data[$this->morphKey]  = $this->parent->$pk;
         $data[$this->morphType] = $this->type;
-        return $model->save($data) ? $model : false;
+
+        return new $this->model($data);
     }
 
     /**

+ 26 - 4
thinkphp/library/think/model/relation/MorphOne.php

@@ -81,8 +81,8 @@ class MorphOne extends Relation
     /**
      * 根据关联条件查询当前模型
      * @access public
-     * @param  mixed  $where 查询条件(数组或者闭包)
-     * @param  mixed  $fields   字段
+     * @param  mixed $where  查询条件(数组或者闭包)
+     * @param  mixed $fields 字段
      * @return Query
      */
     public function hasWhere($where = [], $fields = null)
@@ -198,6 +198,17 @@ class MorphOne extends Relation
      * @return Model|false
      */
     public function save($data)
+    {
+        $model = $this->make($data);
+        return $model->save() ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array $data
+     * @return Model
+     */
+    public function make($data = [])
     {
         if ($data instanceof Model) {
             $data = $data->getData();
@@ -205,10 +216,10 @@ class MorphOne extends Relation
         // 保存关联表数据
         $pk = $this->parent->getPk();
 
-        $model                  = new $this->model;
         $data[$this->morphKey]  = $this->parent->$pk;
         $data[$this->morphType] = $this->type;
-        return $model->save($data) ? $model : false;
+
+        return new $this->model($data);
     }
 
     /**
@@ -227,4 +238,15 @@ class MorphOne extends Relation
         }
     }
 
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
+     * @return string
+     */
+    public function getRelationCountQuery($closure, &$name = null)
+    {
+        throw new Exception('relation not support: withCount');
+    }
 }

+ 11 - 0
thinkphp/library/think/model/relation/MorphTo.php

@@ -285,4 +285,15 @@ class MorphTo extends Relation
         return $this->parent->setRelation($this->relation, null);
     }
 
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
+     * @return string
+     */
+    public function getRelationCountQuery($closure, &$name = null)
+    {
+        throw new Exception('relation not support: withCount');
+    }
 }

+ 11 - 0
thinkphp/library/think/model/relation/OneToOne.php

@@ -323,4 +323,15 @@ abstract class OneToOne extends Relation
         return $data;
     }
 
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param \Closure $closure 闭包
+     * @param string   $name    统计数据别名
+     * @return string
+     */
+    public function getRelationCountQuery($closure, &$name = null)
+    {
+        throw new Exception('relation not support: withCount');
+    }
 }

+ 2 - 1
thinkphp/library/traits/model/SoftDelete.php

@@ -2,6 +2,7 @@
 
 namespace traits\model;
 
+use think\Collection;
 use think\db\Query;
 use think\Model;
 
@@ -111,7 +112,7 @@ trait SoftDelete
         }
 
         // 包含软删除数据
-        $query = self::withTrashed();
+        $query = (new static())->db(false);
         if (is_array($data) && key($data) !== 0) {
             $query->where($data);
             $data = null;

+ 6 - 6
vendor/composer/installed.json

@@ -894,17 +894,17 @@
     },
     {
         "name": "topthink/framework",
-        "version": "v5.0.21",
-        "version_normalized": "5.0.21.0",
+        "version": "v5.0.23",
+        "version_normalized": "5.0.23.0",
         "source": {
             "type": "git",
             "url": "https://github.com/top-think/framework.git",
-            "reference": "ab826da071a7a47116a7f1d01f72228d6bcf212a"
+            "reference": "4cbc0b5e93314446243ebc7d5f005f9c32864737"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/top-think/framework/zipball/ab826da071a7a47116a7f1d01f72228d6bcf212a",
-            "reference": "ab826da071a7a47116a7f1d01f72228d6bcf212a",
+            "url": "https://api.github.com/repos/top-think/framework/zipball/4cbc0b5e93314446243ebc7d5f005f9c32864737",
+            "reference": "4cbc0b5e93314446243ebc7d5f005f9c32864737",
             "shasum": ""
         },
         "require": {
@@ -919,7 +919,7 @@
             "phpunit/phpunit": "4.8.*",
             "sebastian/phpcpd": "2.*"
         },
-        "time": "2018-09-04T09:18:48+00:00",
+        "time": "2018-12-09T12:40:40+00:00",
         "type": "think-framework",
         "installation-source": "dist",
         "autoload": {