diff --git a/core/base.php b/core/base.php index dc43b26b..85ac946b 100644 --- a/core/base.php +++ b/core/base.php @@ -9,7 +9,7 @@ // | Author: liu21st // +---------------------------------------------------------------------- -define('THINK_VERSION', '5.0.4beta'); +define('THINK_VERSION', '5.0.5beta'); define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); diff --git a/core/convention.php b/core/convention.php index 487c2481..a35ac0b9 100644 --- a/core/convention.php +++ b/core/convention.php @@ -231,39 +231,49 @@ return [ 'database' => [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 数据库连接DSN配置 - 'dsn' => '', + 'dsn' => '', // 服务器地址 - 'hostname' => 'localhost', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => '', + 'database' => '', // 数据库用户名 - 'username' => 'root', + 'username' => 'root', // 数据库密码 - 'password' => '', + 'password' => '', // 数据库连接端口 - 'hostport' => '', + 'hostport' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, + // 数据集返回类型 + 'resultset_type' => 'array', // 自动写入时间戳字段 - 'auto_timestamp' => false, + 'auto_timestamp' => false, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + // Builder类 + 'builder' => '', + // Query类 + 'query' => '\\think\\db\\Query', ], //分页配置 diff --git a/core/lang/zh-cn.php b/core/lang/zh-cn.php index db43a1c4..b3fef357 100644 --- a/core/lang/zh-cn.php +++ b/core/lang/zh-cn.php @@ -62,4 +62,5 @@ return [ 'sae mc write error' => 'SAE mc 写入错误', 'route name not exists' => '路由标识不存在(或参数不够)', 'invalid request' => '非法请求', + 'bind attr has exists' => '模型的属性已经存在', ]; diff --git a/core/library/think/App.php b/core/library/think/App.php index 01af92ce..a7e1ddfa 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -11,19 +11,9 @@ namespace think; -use think\Config; -use think\Env; -use think\Exception; use think\exception\HttpException; use think\exception\HttpResponseException; use think\exception\RouteNotFoundException; -use think\Hook; -use think\Lang; -use think\Loader; -use think\Log; -use think\Request; -use think\Response; -use think\Route; /** * App 应用管理 @@ -141,13 +131,13 @@ class App break; case 'controller': // 执行控制器操作 - $vars = Request::instance()->param(); - $data = Loader::action($dispatch['controller'], array_merge($vars, $dispatch['var'])); + $vars = array_merge(Request::instance()->param(), $dispatch['var']); + $data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']); break; case 'method': // 执行回调方法 - $vars = Request::instance()->param(); - $data = self::invokeMethod($dispatch['method'], array_merge($vars, $dispatch['var'])); + $vars = array_merge(Request::instance()->param(), $dispatch['var']); + $data = self::invokeMethod($dispatch['method'], $vars); break; case 'function': // 执行闭包 @@ -257,7 +247,7 @@ class App * 绑定参数 * @access public * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类 - * @param array $vars 变量 + * @param array $vars 变量 * @return array */ private static function bindParams($reflect, $vars = []) @@ -304,8 +294,6 @@ class App throw new \InvalidArgumentException('method param miss:' . $name); } } - // 全局过滤 - array_walk_recursive($args, [Request::instance(), 'filterExp']); } return $args; } diff --git a/core/library/think/Cache.php b/core/library/think/Cache.php index 208190be..7e1b98a9 100644 --- a/core/library/think/Cache.php +++ b/core/library/think/Cache.php @@ -11,7 +11,7 @@ namespace think; -use think\App; +use think\cache\Driver; class Cache { @@ -31,7 +31,7 @@ class Cache * @access public * @param array $options 配置数组 * @param bool|string $name 缓存连接标识 true 强制重新连接 - * @return \think\cache\Driver + * @return Driver */ public static function connect(array $options = [], $name = false) { @@ -79,7 +79,7 @@ class Cache * 切换缓存类型 需要配置 cache.type 为 complex * @access public * @param string $name 缓存标识 - * @return \think\cache\Driver + * @return Driver */ public static function store($name) { @@ -220,7 +220,7 @@ class Cache * @param string $name 标签名 * @param string|array $keys 缓存标识 * @param bool $overlay 是否覆盖 - * @return \think\cache\Driver + * @return Driver */ public static function tag($name, $keys = null, $overlay = false) { diff --git a/core/library/think/Collection.php b/core/library/think/Collection.php index 8315268a..cf2888d5 100644 --- a/core/library/think/Collection.php +++ b/core/library/think/Collection.php @@ -225,11 +225,11 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria $result = []; foreach ($this->items as $row) { - $key = $value = null; + $key = $value = null; $keySet = $valueSet = false; if (null !== $index_key && array_key_exists($index_key, $row)) { $keySet = true; - $key = (string)$row[$index_key]; + $key = (string) $row[$index_key]; } if (null === $column_key) { $valueSet = true; @@ -368,6 +368,6 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria if ($items instanceof self) { return $items->all(); } - return (array)$items; + return (array) $items; } } diff --git a/core/library/think/Controller.php b/core/library/think/Controller.php index fe4efae2..e9d6829b 100644 --- a/core/library/think/Controller.php +++ b/core/library/think/Controller.php @@ -13,7 +13,6 @@ namespace think; \think\Loader::import('controller/Jump', TRAIT_PATH, EXT); -use think\Exception; use think\exception\ValidateException; class Controller diff --git a/core/library/think/Cookie.php b/core/library/think/Cookie.php index 648b2c60..f1c2426a 100644 --- a/core/library/think/Cookie.php +++ b/core/library/think/Cookie.php @@ -99,6 +99,22 @@ class Cookie $_COOKIE[$name] = $value; } + /** + * 永久保存Cookie数据 + * @param string $name cookie名称 + * @param mixed $value cookie值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void + */ + public static function forever($name, $value = '', $option = null) + { + if (is_null($option) || is_numeric($option)) { + $option = []; + } + $option['expire'] = 315360000; + self::set($name, $value, $option); + } + /** * 判断Cookie数据 * @param string $name cookie名称 @@ -133,7 +149,7 @@ class Cookie } return $value; } else { - return null; + return; } } diff --git a/core/library/think/Db.php b/core/library/think/Db.php index c67cba6f..99df4366 100644 --- a/core/library/think/Db.php +++ b/core/library/think/Db.php @@ -11,8 +11,7 @@ namespace think; -use think\App; -use think\Collection; +use think\db\Connection; use think\db\Query; use think\paginator\Collection as PaginatorCollection; @@ -62,7 +61,7 @@ class Db * @access public * @param mixed $config 连接配置 * @param bool|string $name 连接标识 true 强制重新连接 - * @return \think\db\Connection + * @return Connection * @throws Exception */ public static function connect($config = [], $name = false) diff --git a/core/library/think/Debug.php b/core/library/think/Debug.php index e36a764e..0b5d022d 100644 --- a/core/library/think/Debug.php +++ b/core/library/think/Debug.php @@ -11,11 +11,7 @@ namespace think; -use think\Config; use think\exception\ClassNotFoundException; -use think\Log; -use think\Request; -use think\Response; use think\response\Redirect; class Debug @@ -178,8 +174,8 @@ class Debug $output = '
' . $label . $output . '
'; } if ($echo) { - echo ($output); - return null; + echo($output); + return; } else { return $output; } diff --git a/core/library/think/Hook.php b/core/library/think/Hook.php index 062ac962..4559cd30 100644 --- a/core/library/think/Hook.php +++ b/core/library/think/Hook.php @@ -1,5 +1,4 @@ $behavior) { - self::add($tag, $behavior); - } - } else { - self::$tags = $tags + self::$tags; - } - } + /** + * 批量导入插件 + * @param array $tags 插件信息 + * @param boolean $recursive 是否递归合并 + */ + public static function import(array $tags, $recursive = true) + { + if ($recursive) { + foreach ($tags as $tag => $behavior) { + self::add($tag, $behavior); + } + } else { + self::$tags = $tags + self::$tags; + } + } - /** - * 获取插件信息 - * @param string $tag 插件位置 留空获取全部 - * @return array - */ - public static function get($tag = '') - { - if (empty($tag)) {//获取全部的插件信息 - return self::$tags; - } else { - return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; - } - } + /** + * 获取插件信息 + * @param string $tag 插件位置 留空获取全部 + * @return array + */ + public static function get($tag = '') + { + if (empty($tag)) { + //获取全部的插件信息 + return self::$tags; + } else { + return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; + } + } - /** - * 监听标签的行为 - * @param string $tag 标签名称 - * @param mixed $params 传入参数 - * @param mixed $extra 额外参数 - * @param bool $once 只获取一个有效返回值 - * @return mixed - */ - public static function listen($tag, &$params = null, $extra = null, $once = false) - { - $results = []; - $tags = static::get($tag); - foreach ($tags as $key => $name) { - $results[$key] = self::exec($name, $tag, $params, $extra); - if (false === $results[$key]) {// 如果返回false 则中断行为执行 - break; - } elseif (!is_null($results[$key]) && $once) { - break; - } - } - return $once ? end($results) : $results; - } + /** + * 监听标签的行为 + * @param string $tag 标签名称 + * @param mixed $params 传入参数 + * @param mixed $extra 额外参数 + * @param bool $once 只获取一个有效返回值 + * @return mixed + */ + public static function listen($tag, &$params = null, $extra = null, $once = false) + { + $results = []; + $tags = static::get($tag); + foreach ($tags as $key => $name) { + $results[$key] = self::exec($name, $tag, $params, $extra); + if (false === $results[$key]) { + // 如果返回false 则中断行为执行 + break; + } elseif (!is_null($results[$key]) && $once) { + break; + } + } + return $once ? end($results) : $results; + } - /** - * 执行某个行为 - * @param mixed $class 要执行的行为 - * @param string $tag 方法名(标签名) - * @param Mixed $params 传人的参数 - * @param mixed $extra 额外参数 - * @return mixed - */ - public static function exec($class, $tag = '', &$params = null, $extra = null) - { - App::$debug && Debug::remark('behavior_start', 'time'); - if (is_callable($class)) { - $result = call_user_func_array($class, [ & $params, $extra]); - $class = 'Closure'; - } elseif (is_object($class)) { - $result = call_user_func_array([$class, $tag], [ & $params, $extra]); - $class = get_class($class); - } else { - $obj = new $class(); - $result = ($tag && is_callable([$obj, $tag])) ? $obj->$tag($params, $extra) : $obj->run($params, $extra); - } - if (App::$debug) { - Debug::remark('behavior_end', 'time'); - Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); - } - return $result; - } + /** + * 执行某个行为 + * @param mixed $class 要执行的行为 + * @param string $tag 方法名(标签名) + * @param Mixed $params 传人的参数 + * @param mixed $extra 额外参数 + * @return mixed + */ + public static function exec($class, $tag = '', &$params = null, $extra = null) + { + App::$debug && Debug::remark('behavior_start', 'time'); + $method = Loader::parseName($tag, 1, false); + if ($class instanceof \Closure) { + $result = call_user_func_array($class, [ & $params, $extra]); + $class = 'Closure'; + } elseif (is_array($class)) { + list($class, $method) = $class; + + $result = (new $class())->$method($params, $extra); + $class = $class . '->' . $method; + } elseif (is_object($class)) { + $result = $class->$method($params, $extra); + $class = get_class($class); + } elseif (strpos($class, '::')) { + $result = call_user_func_array($class, [ & $params, $extra]); + } else { + $obj = new $class(); + $method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; + $result = $obj->$method($params, $extra); + } + if (App::$debug) { + Debug::remark('behavior_end', 'time'); + Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); + } + return $result; + } } diff --git a/core/library/think/Lang.php b/core/library/think/Lang.php index e064ae45..551481f1 100644 --- a/core/library/think/Lang.php +++ b/core/library/think/Lang.php @@ -11,10 +11,6 @@ namespace think; -use think\App; -use think\Cookie; -use think\Log; - class Lang { // 语言数据 diff --git a/core/library/think/Loader.php b/core/library/think/Loader.php index 9f54ad3a..effd3f4a 100644 --- a/core/library/think/Loader.php +++ b/core/library/think/Loader.php @@ -369,12 +369,16 @@ class Loader if (isset(self::$instance[$guid])) { return self::$instance[$guid]; } - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name, 2); + if (strpos($name, '\\')) { + $class = $name; } else { - $module = Request::instance()->module(); + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name, 2); + } else { + $module = Request::instance()->module(); + } + $class = self::parseClass($module, $layer, $name, $appendSuffix); } - $class = self::parseClass($module, $layer, $name, $appendSuffix); if (class_exists($class)) { $model = new $class(); } else { @@ -400,12 +404,16 @@ class Loader */ public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); + if (strpos($name, '\\')) { + $class = $name; } else { - $module = Request::instance()->module(); + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name); + } else { + $module = Request::instance()->module(); + } + $class = self::parseClass($module, $layer, $name, $appendSuffix); } - $class = self::parseClass($module, $layer, $name, $appendSuffix); if (class_exists($class)) { return App::invokeClass($class); } elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) { @@ -432,12 +440,16 @@ class Loader if (isset(self::$instance[$guid])) { return self::$instance[$guid]; } - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); + if (strpos($name, '\\')) { + $class = $name; } else { - $module = Request::instance()->module(); + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name); + } else { + $module = Request::instance()->module(); + } + $class = self::parseClass($module, $layer, $name, $appendSuffix); } - $class = self::parseClass($module, $layer, $name, $appendSuffix); if (class_exists($class)) { $validate = new $class; } else { @@ -494,14 +506,16 @@ class Loader * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 * @param string $name 字符串 * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) * @return string */ - public static function parseName($name, $type = 0) + public static function parseName($name, $type = 0, $ucfirst = true) { if ($type) { - return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) { + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]); - }, $name)); + }, $name); + return $ucfirst ? ucfirst($name) : lcfirst($name); } else { return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } diff --git a/core/library/think/Model.php b/core/library/think/Model.php index 2b7f3ae3..605d48d3 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -12,14 +12,8 @@ namespace think; use InvalidArgumentException; -use think\Cache; -use think\Collection; -use think\Config; -use think\Db; use think\db\Query; -use think\Exception; use think\Exception\ValidateException; -use think\Loader; use think\model\Relation; use think\model\relation\BelongsTo; use think\model\relation\BelongsToMany; @@ -96,7 +90,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 更新时间字段 protected $updateTime = 'update_time'; // 时间字段取出后的默认时间格式 - protected $dateFormat = 'Y-m-d H:i:s'; + protected $dateFormat; // 字段类型或者格式转换 protected $type = []; // 是否为更新数据 @@ -151,7 +145,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (is_null($this->autoWriteTimestamp)) { // 自动写入时间戳 - $this->autoWriteTimestamp = $this->db()->getConfig('auto_timestamp'); + $this->autoWriteTimestamp = $this->db(false)->getConfig('auto_timestamp'); + } + + if (is_null($this->dateFormat)) { + // 设置时间戳格式 + $this->dateFormat = $this->db(false)->getConfig('datetime_format'); } // 执行初始化操作 @@ -169,13 +168,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $model = $this->class; if (!isset(self::$links[$model])) { // 合并数据库配置 - if (is_array($this->connection)) { - $connection = array_merge(Config::get('database'), $this->connection); + if (!empty($this->connection)) { + if (is_array($this->connection)) { + $connection = array_merge(Config::get('database'), $this->connection); + } else { + $connection = $this->connection; + } } else { - $connection = $this->connection; + $connection = []; } // 设置当前模型 确保查询返回模型对象 - $query = Db::connect($connection)->model($model, $this->query); + $query = Db::connect($connection)->getQuery($model, $this->query); // 设置当前数据表和模型名 if (!empty($this->table)) { @@ -319,21 +322,40 @@ abstract class Model implements \JsonSerializable, \ArrayAccess case 'datetime': case 'date': $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, $_SERVER['REQUEST_TIME']); + $value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $format); break; case 'timestamp': - case 'int': + case 'integer': + default: $value = $_SERVER['REQUEST_TIME']; break; } } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ['datetime', 'date', 'timestamp'])) { - $value = date($this->dateFormat, $_SERVER['REQUEST_TIME']); + $value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat); } else { - $value = $_SERVER['REQUEST_TIME']; + $value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat, true); } return $value; } + /** + * 时间日期字段格式化处理 + * @access public + * @param mixed $time 时间日期表达式 + * @param mixed $format 日期格式 + * @param bool $timestamp 是否进行时间戳转换 + * @return mixed + */ + protected function formatDateTime($time, $format, $timestamp = false) + { + if (false !== strpos($format, '\\')) { + $time = new $format($time); + } elseif (!$timestamp) { + $time = date($format, $time); + } + return $time; + } + /** * 数据写入 类型转换 * @access public @@ -369,7 +391,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess break; case 'datetime': $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, is_numeric($value) ? $value : strtotime($value)); + $value = is_numeric($value) ? $value : strtotime($value); + $value = $this->formatDateTime($value, $format); break; case 'object': if (is_object($value)) { @@ -385,6 +408,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess case 'serialize': $value = serialize($value); break; + } return $value; } @@ -414,7 +438,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 类型转换 $value = $this->readTransform($value, $this->type[$name]); } elseif ($notFound) { - $method = Loader::parseName($name, 1); + $method = Loader::parseName($name, 1, false); if (method_exists($this, $method) && $this->$method() instanceof Relation) { // 不存在该字段 获取关联数据 $value = $this->$method()->getRelation(); @@ -458,13 +482,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess case 'timestamp': if (!is_null($value)) { $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, $value); + $value = $this->formatDateTime($value, $format); } break; case 'datetime': if (!is_null($value)) { $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, strtotime($value)); + $value = $this->formatDateTime(strtotime($value), $format); } break; case 'json': @@ -479,6 +503,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess case 'serialize': $value = unserialize($value); break; + default: + if (false !== strpos($type, '\\')) { + // 对象类型 + $value = new $type($value); + } } return $value; } @@ -496,6 +525,32 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return $this; } + /** + * 设置附加关联对象的属性 + * @access public + * @param string $relation 关联方法 + * @param string|array $append 追加属性名 + * @return $this + */ + public function appendRelationAttr($relation, $append) + { + if (is_string($append)) { + $append = explode(',', $append); + } + $model = $this->getAttr($relation); + if ($model instanceof Model) { + foreach ($append as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if ($this->__isset($key)) { + throw new Exception('bind attr has exists:' . $key); + } else { + $this->setAttr($key, $model->$attr); + } + } + } + return $this; + } + /** * 设置需要隐藏的输出属性 * @access public @@ -603,10 +658,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public function getPk($name = '') { if (!empty($name)) { - $table = $this->db()->getTable($name); - return $this->db()->getPk($table); + $table = $this->db(false)->getTable($name); + return $this->db(false)->getPk($table); } elseif (empty($this->pk)) { - $this->pk = $this->db()->getPk(); + $this->pk = $this->db(false)->getPk(); } return $this->pk; } @@ -665,7 +720,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->autoCompleteData($this->auto); // 自动写入更新时间 - if ($this->autoWriteTimestamp && $this->updateTime) { + if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) { $this->setAttr($this->updateTime, null); } @@ -722,7 +777,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->autoCompleteData($this->insert); // 自动写入创建时间 - if ($this->autoWriteTimestamp && $this->createTime) { + if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) { $this->setAttr($this->createTime, null); } @@ -803,7 +858,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public function allowField($field) { if (true === $field) { - $field = $this->db()->getTableInfo('', 'fields'); + $field = $this->db(false)->getTableInfo('', 'fields'); } $this->field = $field; return $this; @@ -1156,8 +1211,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public static function useGlobalScope($use) { - $model = new static(); - self::$db = $model->db($use); + $model = new static(); + static::$db = $model->db($use); return $model; } @@ -1245,6 +1300,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation); } + $relation = Loader::parseName($relation, 1, false); $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $class); } } @@ -1271,10 +1327,34 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation); } + $relation = Loader::parseName($relation, 1, false); $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure, $class); } } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param string|array $relation 关联名 + * @return void + */ + public function relationCount(&$result, $relation) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + foreach ($relations as $key => $relation) { + $closure = false; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + $relation = Loader::parseName($relation, 1, false); + $count = $this->$relation()->relationCount($result, $closure); + $result->setAttr(Loader::parseName($relation) . '_count', $count); + } + } + /** * HAS ONE 关联定义 * @access public @@ -1369,7 +1449,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 记录当前关联信息 $model = $this->parseModel($model); $name = Loader::parseName(basename(str_replace('\\', '/', $model))); - $table = $table ?: $this->db()->getTable(Loader::parseName($this->name) . '_' . $name); + $table = $table ?: $this->db(false)->getTable(Loader::parseName($this->name) . '_' . $name); $foreignKey = $foreignKey ?: $name . '_id'; $localKey = $localKey ?: Loader::parseName($this->name) . '_id'; return new BelongsToMany($this, $model, $table, $foreignKey, $localKey, $alias); @@ -1426,7 +1506,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public function __call($method, $args) { - $query = $this->db(); + if (isset(static::$db)) { + $query = static::$db; + static::$db = null; + } else { + $query = $this->db(); + } + if (method_exists($this, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; @@ -1441,7 +1527,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public static function __callStatic($method, $params) { if (isset(static::$db)) { - $query = static::$db; + $query = static::$db; + static::$db = null; } else { $query = (new static())->db(); } diff --git a/core/library/think/Paginator.php b/core/library/think/Paginator.php index 7385ebb0..dc4cdeeb 100644 --- a/core/library/think/Paginator.php +++ b/core/library/think/Paginator.php @@ -12,7 +12,6 @@ namespace think; use think\paginator\Collection as PaginatorCollection; -use think\Request; abstract class Paginator { @@ -42,14 +41,14 @@ abstract class Paginator 'var_page' => 'page', 'path' => '/', 'query' => [], - 'fragment' => '' + 'fragment' => '', ]; protected function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) { $this->options = array_merge($this->options, $options); - $this->options['path'] = $this->options['path'] != '/' ? rtrim($this->options['path'], '/') : $this->options['path']; + $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path']; $this->simple = $simple; $this->listRows = $listRows; @@ -63,7 +62,7 @@ abstract class Paginator $items = $items->slice(0, $this->listRows); } else { $this->total = $total; - $this->lastPage = (int)ceil($total / $listRows); + $this->lastPage = (int) ceil($total / $listRows); $this->currentPage = $this->setCurrentPage($currentPage); $this->hasMore = $this->currentPage < $this->lastPage; } @@ -134,7 +133,7 @@ abstract class Paginator { $page = Request::instance()->request($varPage); - if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int)$page >= 1) { + if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return $page; } @@ -182,7 +181,7 @@ abstract class Paginator */ public function hasPages() { - return !($this->currentPage == 1 && !$this->hasMore); + return !(1 == $this->currentPage && !$this->hasMore); } /** @@ -239,7 +238,6 @@ abstract class Paginator return $this; } - /** * 构造锚点字符串 * @@ -255,4 +253,4 @@ abstract class Paginator * @return mixed */ abstract public function render(); -} \ No newline at end of file +} diff --git a/core/library/think/Process.php b/core/library/think/Process.php index 1982de24..6f3faa31 100644 --- a/core/library/think/Process.php +++ b/core/library/think/Process.php @@ -14,9 +14,9 @@ namespace think; use think\process\exception\Failed as ProcessFailedException; use think\process\exception\Timeout as ProcessTimeoutException; use think\process\pipes\Pipes; -use think\process\Utils; use think\process\pipes\Unix as UnixPipes; use think\process\pipes\Windows as WindowsPipes; +use think\process\Utils; class Process { @@ -47,10 +47,10 @@ class Process private $exitcode; private $fallbackExitcode; private $processInformation; - private $outputDisabled = false; + private $outputDisabled = false; private $stdout; private $stderr; - private $enhanceWindowsCompatibility = true; + private $enhanceWindowsCompatibility = true; private $enhanceSigchildCompatibility; private $process; private $status = self::STATUS_READY; @@ -147,7 +147,7 @@ class Process $this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled(); $this->options = array_replace([ 'suppress_errors' => true, - 'binary_pipes' => true + 'binary_pipes' => true, ], $options); } @@ -490,7 +490,7 @@ class Process public function getExitCodeText() { if (null === $exitcode = $this->getExitCode()) { - return null; + return; } return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error'; @@ -586,7 +586,7 @@ class Process */ public function isStarted() { - return $this->status != self::STATUS_READY; + return self::STATUS_READY != $this->status; } /** @@ -597,7 +597,7 @@ class Process { $this->updateStatus(false); - return $this->status == self::STATUS_TERMINATED; + return self::STATUS_TERMINATED == $this->status; } /** @@ -645,7 +645,7 @@ class Process * @param string $line */ public function addOutput($line) - { +{ $this->lastOutputTime = microtime(true); $this->stdout .= $line; } @@ -655,7 +655,7 @@ class Process * @param string $line */ public function addErrorOutput($line) - { +{ $this->lastOutputTime = microtime(true); $this->stderr .= $line; } @@ -665,7 +665,7 @@ class Process * @return string */ public function getCommandLine() - { +{ return $this->commandline; } @@ -675,7 +675,7 @@ class Process * @return self */ public function setCommandLine($commandline) - { +{ $this->commandline = $commandline; return $this; @@ -686,7 +686,7 @@ class Process * @return float|null */ public function getTimeout() - { +{ return $this->timeout; } @@ -695,7 +695,7 @@ class Process * @return float|null */ public function getIdleTimeout() - { +{ return $this->idleTimeout; } @@ -705,7 +705,7 @@ class Process * @return self */ public function setTimeout($timeout) - { +{ $this->timeout = $this->validateTimeout($timeout); return $this; @@ -717,7 +717,7 @@ class Process * @return self */ public function setIdleTimeout($timeout) - { +{ if (null !== $timeout && $this->outputDisabled) { throw new \LogicException('Idle timeout can not be set while the output is disabled.'); } @@ -733,7 +733,7 @@ class Process * @return self */ public function setTty($tty) - { +{ if ('\\' === DS && $tty) { throw new \RuntimeException('TTY mode is not supported on Windows platform.'); } @@ -741,7 +741,7 @@ class Process throw new \RuntimeException('TTY mode requires /dev/tty to be readable.'); } - $this->tty = (bool)$tty; + $this->tty = (bool) $tty; return $this; } @@ -751,7 +751,7 @@ class Process * @return bool */ public function isTty() - { +{ return $this->tty; } @@ -761,8 +761,8 @@ class Process * @return self */ public function setPty($bool) - { - $this->pty = (bool)$bool; +{ + $this->pty = (bool) $bool; return $this; } @@ -772,7 +772,7 @@ class Process * @return bool */ public function isPty() - { +{ return $this->pty; } @@ -781,7 +781,7 @@ class Process * @return string|null */ public function getWorkingDirectory() - { +{ if (null === $this->cwd) { return getcwd() ?: null; } @@ -795,7 +795,7 @@ class Process * @return self */ public function setWorkingDirectory($cwd) - { +{ $this->cwd = $cwd; return $this; @@ -806,7 +806,7 @@ class Process * @return array */ public function getEnv() - { +{ return $this->env; } @@ -816,14 +816,14 @@ class Process * @return self */ public function setEnv(array $env) - { +{ $env = array_filter($env, function ($value) { return !is_array($value); }); $this->env = []; foreach ($env as $key => $value) { - $this->env[(binary)$key] = (binary)$value; + $this->env[(binary) $key] = (binary) $value; } return $this; @@ -834,7 +834,7 @@ class Process * @return null|string */ public function getInput() - { +{ return $this->input; } @@ -844,7 +844,7 @@ class Process * @return self */ public function setInput($input) - { +{ if ($this->isRunning()) { throw new \LogicException('Input can not be set while the process is running.'); } @@ -859,7 +859,7 @@ class Process * @return array */ public function getOptions() - { +{ return $this->options; } @@ -869,7 +869,7 @@ class Process * @return self */ public function setOptions(array $options) - { +{ $this->options = $options; return $this; @@ -880,7 +880,7 @@ class Process * @return bool */ public function getEnhanceWindowsCompatibility() - { +{ return $this->enhanceWindowsCompatibility; } @@ -890,8 +890,8 @@ class Process * @return self */ public function setEnhanceWindowsCompatibility($enhance) - { - $this->enhanceWindowsCompatibility = (bool)$enhance; +{ + $this->enhanceWindowsCompatibility = (bool) $enhance; return $this; } @@ -901,7 +901,7 @@ class Process * @return bool */ public function getEnhanceSigchildCompatibility() - { +{ return $this->enhanceSigchildCompatibility; } @@ -911,8 +911,8 @@ class Process * @return self */ public function setEnhanceSigchildCompatibility($enhance) - { - $this->enhanceSigchildCompatibility = (bool)$enhance; +{ + $this->enhanceSigchildCompatibility = (bool) $enhance; return $this; } @@ -921,8 +921,8 @@ class Process * 是否超时 */ public function checkTimeout() - { - if ($this->status !== self::STATUS_STARTED) { +{ + if (self::STATUS_STARTED !== $this->status) { return; } @@ -944,7 +944,7 @@ class Process * @return bool */ public static function isPtySupported() - { +{ static $result; if (null !== $result) { @@ -970,7 +970,7 @@ class Process * @return array */ private function getDescriptors() - { +{ if ('\\' === DS) { $this->processPipes = WindowsPipes::create($this, $this->input); } else { @@ -994,7 +994,7 @@ class Process * @return callable */ protected function buildCallback($callback) - { +{ $out = self::OUT; $callback = function ($type, $data) use ($callback, $out) { if ($out == $type) { @@ -1016,7 +1016,7 @@ class Process * @param bool $blocking */ protected function updateStatus($blocking) - { +{ if (self::STATUS_STARTED !== $this->status) { return; } @@ -1036,7 +1036,7 @@ class Process * @return bool */ protected function isSigchildEnabled() - { +{ if (null !== self::$sigchild) { return self::$sigchild; } @@ -1057,8 +1057,8 @@ class Process * @return float|null */ private function validateTimeout($timeout) - { - $timeout = (float)$timeout; +{ + $timeout = (float) $timeout; if (0.0 === $timeout) { $timeout = null; @@ -1075,15 +1075,15 @@ class Process * @param bool $close */ private function readPipes($blocking, $close) - { +{ $result = $this->processPipes->readAndWrite($blocking, $close); $callback = $this->callback; foreach ($result as $type => $data) { if (3 == $type) { - $this->fallbackExitcode = (int)$data; + $this->fallbackExitcode = (int) $data; } else { - $callback($type === self::STDOUT ? self::OUT : self::ERR, $data); + $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data); } } } @@ -1092,7 +1092,7 @@ class Process * 捕获退出码 */ private function captureExitCode() - { +{ if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) { $this->exitcode = $this->processInformation['exitcode']; } @@ -1103,7 +1103,7 @@ class Process * @return int 退出码 */ private function close() - { +{ $this->processPipes->close(); if (is_resource($this->process)) { $exitcode = proc_close($this->process); @@ -1117,7 +1117,7 @@ class Process if (-1 === $this->exitcode && null !== $this->fallbackExitcode) { $this->exitcode = $this->fallbackExitcode; } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] - && 0 < $this->processInformation['termsig'] + && 0 < $this->processInformation['termsig'] ) { $this->exitcode = 128 + $this->processInformation['termsig']; } @@ -1129,7 +1129,7 @@ class Process * 重置数据 */ private function resetProcessData() - { +{ $this->starttime = null; $this->callback = null; $this->exitcode = null; @@ -1151,7 +1151,7 @@ class Process * @return bool */ private function doSignal($signal, $throwException) - { +{ if (!$this->isRunning()) { if ($throwException) { throw new \LogicException('Can not send signal on a non running process.'); @@ -1186,7 +1186,7 @@ class Process * @param string $functionName */ private function requireProcessIsStarted($functionName) - { +{ if (!$this->isStarted()) { throw new \LogicException(sprintf('Process must be started before calling %s.', $functionName)); } @@ -1197,9 +1197,9 @@ class Process * @param string $functionName */ private function requireProcessIsTerminated($functionName) - { +{ if (!$this->isTerminated()) { throw new \LogicException(sprintf('Process must be terminated before calling %s.', $functionName)); } } -} \ No newline at end of file +} diff --git a/core/library/think/Request.php b/core/library/think/Request.php index a30180a8..a806dd87 100644 --- a/core/library/think/Request.php +++ b/core/library/think/Request.php @@ -11,11 +11,6 @@ namespace think; -use think\Config; -use think\Exception; -use think\File; -use think\Session; - class Request { /** @@ -25,7 +20,7 @@ class Request protected $method; /** - * @var string 域名 + * @var string 域名(含协议和端口) */ protected $domain; @@ -226,8 +221,9 @@ class Request if (!isset($info['path'])) { $info['path'] = '/'; } - $options = []; - $queryString = ''; + $options = []; + $options[strtolower($method)] = $params; + $queryString = ''; if (isset($info['query'])) { parse_str(html_entity_decode($info['query']), $query); if (!empty($params)) { @@ -240,6 +236,11 @@ class Request } elseif (!empty($params)) { $queryString = http_build_query($params, '', '&'); } + if ($queryString) { + parse_str($queryString, $get); + $options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get; + } + $server['REQUEST_URI'] = $info['path'] . ('' !== $queryString ? '?' . $queryString : ''); $server['QUERY_STRING'] = $queryString; $options['cookie'] = $cookie; @@ -257,7 +258,7 @@ class Request } /** - * 获取当前包含协议的域名 + * 设置或获取当前包含协议的域名 * @access public * @param string $domain 域名 * @return string @@ -274,7 +275,7 @@ class Request } /** - * 获取当前完整URL 包括QUERY_STRING + * 设置或获取当前完整URL 包括QUERY_STRING * @access public * @param string|true $url URL地址 true 带域名获取 * @return string @@ -301,7 +302,7 @@ class Request } /** - * 获取当前URL 不含QUERY_STRING + * 设置或获取当前URL 不含QUERY_STRING * @access public * @param string $url URL地址 * @return string @@ -319,7 +320,7 @@ class Request } /** - * 获取当前执行的文件 SCRIPT_NAME + * 设置或获取当前执行的文件 SCRIPT_NAME * @access public * @param string $file 当前执行的文件 * @return string @@ -351,7 +352,7 @@ class Request } /** - * 获取URL访问根地址 + * 设置或获取URL访问根地址 * @access public * @param string $url URL地址 * @return string @@ -455,7 +456,7 @@ class Request */ public function type() { - $accept = isset($this->server['HTTP_ACCEPT']) ? $this->server['HTTP_ACCEPT'] : $_SERVER['HTTP_ACCEPT']; + $accept = $this->server('HTTP_ACCEPT'); if (empty($accept)) { return false; } @@ -602,7 +603,7 @@ class Request } /** - * 设置获取获取当前请求的参数 + * 获取获取当前请求的参数 * @access public * @param string|array $name 变量名 * @param mixed $default 默认值 @@ -688,7 +689,7 @@ class Request if (empty($this->post)) { $content = $this->input; if (empty($_POST) && strpos($content, '":')) { - $this->post = json_decode($content, true); + $this->post = (array) json_decode($content, true); } else { $this->post = $_POST; } @@ -713,7 +714,7 @@ class Request if (is_null($this->put)) { $content = $this->input; if (strpos($content, '":')) { - $this->put = json_decode($content, true); + $this->put = (array) json_decode($content, true); } else { parse_str($content, $this->put); } @@ -885,7 +886,7 @@ class Request return $array[$name]; } } - return null; + return; } /** diff --git a/core/library/think/Response.php b/core/library/think/Response.php index a7ebecf7..8b107589 100644 --- a/core/library/think/Response.php +++ b/core/library/think/Response.php @@ -11,11 +11,6 @@ namespace think; -use think\Cache; -use think\Config; -use think\Debug; -use think\Env; -use think\Request; use think\response\Json as JsonResponse; use think\response\Jsonp as JsonpResponse; use think\response\Redirect as RedirectResponse; diff --git a/core/library/think/Route.php b/core/library/think/Route.php index d3272071..7a31659a 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -11,14 +11,7 @@ namespace think; -use think\App; -use think\Config; use think\exception\HttpException; -use think\Hook; -use think\Loader; -use think\Log; -use think\Request; -use think\Response; class Route { @@ -296,12 +289,14 @@ class Route } elseif ('$' == substr($rule, -1, 1)) { // 是否完整匹配 $option['complete_match'] = true; - $rule = substr($rule, 0, -1); } } elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) { // 是否完整匹配 $option['complete_match'] = true; - $rule = substr($rule, 0, -1); + } + + if ('$' == substr($rule, -1, 1)) { + $rule = substr($rule, 0, -1); } if ('/' != $rule || $group) { @@ -661,14 +656,14 @@ class Route /** * rest方法定义和修改 * @access public - * @param string $name 方法名称 - * @param array $resource 资源 + * @param string $name 方法名称 + * @param array|bool $resource 资源 * @return void */ public static function rest($name, $resource = []) { if (is_array($name)) { - self::$rest = array_merge(self::$rest, $name); + self::$rest = $resource ? $name : array_merge(self::$rest, $name); } else { self::$rest[$name] = $resource; } @@ -1493,6 +1488,10 @@ class Route $route = substr($route, 1); list($route, $var) = self::parseUrlPath($route); $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; + $request->action(array_pop($route)); + $request->controller($route ? array_pop($route) : Config::get('default_controller')); + $request->module($route ? array_pop($route) : Config::get('default_module')); + App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); } else { // 路由到模块/控制器/操作 $result = self::parseModule($route); diff --git a/core/library/think/Session.php b/core/library/think/Session.php index b9527a5c..06adcff6 100644 --- a/core/library/think/Session.php +++ b/core/library/think/Session.php @@ -11,7 +11,6 @@ namespace think; -use think\App; use think\exception\ClassNotFoundException; class Session @@ -193,7 +192,7 @@ class Session self::delete($name, $prefix); return $result; } else { - return null; + return; } } diff --git a/core/library/think/Template.php b/core/library/think/Template.php index e73bbcf8..072cc80c 100644 --- a/core/library/think/Template.php +++ b/core/library/think/Template.php @@ -12,7 +12,6 @@ namespace think; use think\exception\TemplateNotFoundException; -use think\Request; /** * ThinkPHP分离出来的模板引擎 @@ -130,7 +129,7 @@ class Template } elseif (isset($this->config[$config])) { return $this->config[$config]; } else { - return null; + return; } } @@ -669,7 +668,7 @@ class Template $content = str_replace($matches[0], '', $content); return explode(',', $matches['name']); } - return null; + return; } /** @@ -1067,6 +1066,8 @@ class Template } if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $this->config['view_depr'], $template); + } else { + $template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1)); } if ($this->config['view_base']) { $module = isset($module) ? $module : Request::instance()->module(); diff --git a/core/library/think/Url.php b/core/library/think/Url.php index d8730743..a1bc8d2a 100644 --- a/core/library/think/Url.php +++ b/core/library/think/Url.php @@ -11,11 +11,6 @@ namespace think; -use think\Config; -use think\Loader; -use think\Request; -use think\Route; - class Url { // 生成URL地址的root @@ -89,7 +84,8 @@ class Url throw new \InvalidArgumentException('route name not exists:' . $name); } else { // 检查别名路由 - $alias = Route::rules('alias'); + $alias = Route::rules('alias'); + $matchAlias = false; if ($alias) { // 别名路由解析 foreach ($alias as $key => $val) { @@ -97,11 +93,13 @@ class Url $val = $val[0]; } if (0 === strpos($url, $val)) { - $url = $key . substr($url, strlen($val)); + $url = $key . substr($url, strlen($val)); + $matchAlias = true; break; } } - } else { + } + if (!$matchAlias) { // 路由标识不存在 直接解析 $url = self::parseUrl($url, $domain); } @@ -250,7 +248,7 @@ class Url $domain .= $rootDomain; } break; - } else if (false !== strpos($key, '*')) { + } elseif (false !== strpos($key, '*')) { if (!empty($rootDomain)) { $domain .= $rootDomain; } diff --git a/core/library/think/Validate.php b/core/library/think/Validate.php index 81fc58a3..528861b4 100644 --- a/core/library/think/Validate.php +++ b/core/library/think/Validate.php @@ -11,10 +11,7 @@ namespace think; -use think\File; -use think\Lang; -use think\Request; -use think\Session; +use think\exception\ClassNotFoundException; class Validate { @@ -72,8 +69,8 @@ class Validate 'expire' => '不在有效期内 :rule', 'allowIp' => '不允许的IP访问', 'denyIp' => '禁止的IP访问', - 'confirm' => ':attribute和字段 :rule 不一致', - 'different' => ':attribute和字段 :rule 不能相同', + 'confirm' => ':attribute和确认字段:2不一致', + 'different' => ':attribute和比较字段:2不能相同', 'egt' => ':attribute必须大于等于 :rule', 'gt' => ':attribute必须大于 :rule', 'elt' => ':attribute必须小于等于 :rule', @@ -317,7 +314,12 @@ class Validate $value = $this->getDataValue($data, $key); // 字段验证 - $result = $this->checkItem($key, $value, $rule, $data, $title, $msg); + if ($rule instanceof \Closure) { + // 匿名函数验证 支持传入当前字段和所有字段两个数据 + $result = call_user_func_array($rule, [$value, $data]); + } else { + $result = $this->checkItem($key, $value, $rule, $data, $title, $msg); + } if (true !== $result) { // 没有返回true 则表示验证失败 @@ -350,73 +352,69 @@ class Validate */ protected function checkItem($field, $value, $rules, $data, $title = '', $msg = []) { - if ($rules instanceof \Closure) { - // 匿名函数验证 支持传入当前字段和所有字段两个数据 - $result = call_user_func_array($rules, [$value, $data]); - } else { - // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...] - if (is_string($rules)) { - $rules = explode('|', $rules); - } - $i = 0; - foreach ($rules as $key => $rule) { - if ($rule instanceof \Closure) { - $result = call_user_func_array($rule, [$value, $data]); - } else { - // 判断验证类型 - if (is_numeric($key)) { - if (strpos($rule, ':')) { - list($type, $rule) = explode(':', $rule, 2); - if (isset($this->alias[$type])) { - // 判断别名 - $type = $this->alias[$type]; - } - $info = $type; - } elseif (method_exists($this, $rule)) { - $type = $rule; - $info = $rule; - $rule = ''; - } else { - $type = 'is'; - $info = $rule; - } - } else { - $info = $type = $key; - } - - // 如果不是require 有数据才会行验证 - if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { - // 验证类型 - $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; - // 验证数据 - $result = call_user_func_array($callback, [$value, $rule, $data, $field]); - } else { - $result = true; - } - } - - if (false === $result) { - // 验证失败 返回错误信息 - if (isset($msg[$i])) { - $message = $msg[$i]; - if (is_string($message) && strpos($message, '{%') === 0) { - $message = Lang::get(substr($message, 2, -1)); - } - } else { - $message = $this->getRuleMsg($field, $title, $info, $rule); - } - return $message; - } elseif (true !== $result) { - // 返回自定义错误信息 - if (is_string($result) && false !== strpos($result, ':')) { - $result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result); - } - return $result; - } - $i++; - } + // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...] + if (is_string($rules)) { + $rules = explode('|', $rules); } - return true !== $result ? $result : true; + $i = 0; + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value, $data]); + $info = is_numeric($key) ? '' : $key; + } else { + // 判断验证类型 + if (is_numeric($key)) { + if (strpos($rule, ':')) { + list($type, $rule) = explode(':', $rule, 2); + if (isset($this->alias[$type])) { + // 判断别名 + $type = $this->alias[$type]; + } + $info = $type; + } elseif (method_exists($this, $rule)) { + $type = $rule; + $info = $rule; + $rule = ''; + } else { + $type = 'is'; + $info = $rule; + } + } else { + $info = $type = $key; + } + + // 如果不是require 有数据才会行验证 + if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { + // 验证类型 + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + // 验证数据 + $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]); + } else { + $result = true; + } + } + + if (false === $result) { + // 验证失败 返回错误信息 + if (isset($msg[$i])) { + $message = $msg[$i]; + if (is_string($message) && strpos($message, '{%') === 0) { + $message = Lang::get(substr($message, 2, -1)); + } + } else { + $message = $this->getRuleMsg($field, $title, $info, $rule); + } + return $message; + } elseif (true !== $result) { + // 返回自定义错误信息 + if (is_string($result) && false !== strpos($result, ':')) { + $result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result); + } + return $result; + } + $i++; + } + return $result; } /** @@ -811,7 +809,16 @@ class Validate if (is_string($rule)) { $rule = explode(',', $rule); } - $db = Db::name($rule[0]); + if (false !== strpos($rule[0], '\\')) { + // 指定模型类 + $db = new $rule[0]; + } else { + try { + $db = Loader::model($rule[0]); + } catch (ClassNotFoundException $e) { + $db = Db::name($rule[0]); + } + } $key = isset($rule[1]) ? $rule[1] : $field; if (strpos($key, '^')) { @@ -1201,6 +1208,8 @@ class Validate { if (isset($this->message[$attribute . '.' . $type])) { $msg = $this->message[$attribute . '.' . $type]; + } elseif (isset($this->message[$attribute][$type])) { + $msg = $this->message[$attribute][$type]; } elseif (isset($this->message[$attribute])) { $msg = $this->message[$attribute]; } elseif (isset(self::$typeMsg[$type])) { @@ -1213,7 +1222,7 @@ class Validate $msg = Lang::get(substr($msg, 2, -1)); } - if (is_string($msg) && false !== strpos($msg, ':')) { + if (is_string($msg) && is_string($rule) && false !== strpos($msg, ':')) { // 变量替换 if (strpos($rule, ',')) { $array = array_pad(explode(',', $rule), 3, ''); diff --git a/core/library/think/View.php b/core/library/think/View.php index b724ba91..1be47f4e 100644 --- a/core/library/think/View.php +++ b/core/library/think/View.php @@ -11,9 +11,6 @@ namespace think; -use think\Loader; -use think\Request; - class View { // 视图实例 diff --git a/core/library/think/cache/Driver.php b/core/library/think/cache/Driver.php index a2a57795..f21da7bf 100644 --- a/core/library/think/cache/Driver.php +++ b/core/library/think/cache/Driver.php @@ -105,7 +105,7 @@ abstract class Driver $this->rm($name); return $result; } else { - return null; + return; } } diff --git a/core/library/think/cache/driver/Memcache.php b/core/library/think/cache/driver/Memcache.php index bddf7157..ca2f8aa8 100644 --- a/core/library/think/cache/driver/Memcache.php +++ b/core/library/think/cache/driver/Memcache.php @@ -12,7 +12,6 @@ namespace think\cache\driver; use think\cache\Driver; -use think\Exception; class Memcache extends Driver { diff --git a/core/library/think/cache/driver/Sqlite.php b/core/library/think/cache/driver/Sqlite.php index 6dbd41fe..907f93df 100644 --- a/core/library/think/cache/driver/Sqlite.php +++ b/core/library/think/cache/driver/Sqlite.php @@ -12,7 +12,6 @@ namespace think\cache\driver; use think\cache\Driver; -use think\Exception; /** * Sqlite缓存驱动 diff --git a/core/library/think/cache/driver/Wincache.php b/core/library/think/cache/driver/Wincache.php index a9cc7d22..11d059e4 100644 --- a/core/library/think/cache/driver/Wincache.php +++ b/core/library/think/cache/driver/Wincache.php @@ -12,7 +12,6 @@ namespace think\cache\driver; use think\cache\Driver; -use think\Exception; /** * Wincache缓存驱动 @@ -28,7 +27,7 @@ class Wincache extends Driver /** * 架构函数 * @param array $options 缓存参数 - * @throws Exception + * @throws \BadFunctionCallException * @access public */ public function __construct($options = []) diff --git a/core/library/think/cache/driver/Xcache.php b/core/library/think/cache/driver/Xcache.php index 6b18b3fc..e790ef7c 100644 --- a/core/library/think/cache/driver/Xcache.php +++ b/core/library/think/cache/driver/Xcache.php @@ -12,7 +12,6 @@ namespace think\cache\driver; use think\cache\Driver; -use think\Exception; /** * Xcache缓存驱动 diff --git a/core/library/think/db/Builder.php b/core/library/think/db/Builder.php index 49b3aebe..51b7a871 100644 --- a/core/library/think/db/Builder.php +++ b/core/library/think/db/Builder.php @@ -12,9 +12,6 @@ namespace think\db; use PDO; -use think\Db; -use think\db\Connection; -use think\db\Query; use think\Exception; abstract class Builder @@ -118,6 +115,9 @@ abstract class Builder $this->query->bind($key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); $result[$item] = ':' . $key; } + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $result[$item] = $val->__toString(); } } return $result; @@ -317,6 +317,11 @@ abstract class Builder } } $bindName = $bindName ?: 'where_' . str_replace('.', '_', $field); + if (preg_match('/\W/', $bindName)) { + // 处理带非单词字符的字段名 + $bindName = md5($bindName); + } + $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) { @@ -702,8 +707,13 @@ abstract class Builder throw new Exception('fields not exists:[' . $key . ']'); } unset($data[$key]); + } elseif (is_null($val)) { + $data[$key] = 'NULL'; } elseif (is_scalar($val)) { $data[$key] = $this->parseValue($val, $key); + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $data[$key] = $val->__toString(); } else { // 过滤掉非标量数据 unset($data[$key]); diff --git a/core/library/think/db/Connection.php b/core/library/think/db/Connection.php index f7d33161..e5c60aae 100644 --- a/core/library/think/db/Connection.php +++ b/core/library/think/db/Connection.php @@ -16,7 +16,6 @@ use PDOStatement; 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; @@ -65,47 +64,49 @@ abstract class Connection // 数据库连接参数配置 protected $config = [ // 数据库类型 - 'type' => '', + 'type' => '', // 服务器地址 - 'hostname' => '', + 'hostname' => '', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据集返回类型 - 'resultset_type' => 'array', + 'resultset_type' => 'array', // 自动写入时间戳字段 - 'auto_timestamp' => false, + 'auto_timestamp' => false, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, // Builder类 - 'builder' => '', + 'builder' => '', // Query类 - 'query' => '\\think\\db\\Query', + 'query' => '\\think\\db\\Query', ]; // PDO连接参数 @@ -136,11 +137,11 @@ abstract class Connection * @param string $queryClass 查询对象类名 * @return Query */ - public function model($model, $queryClass = '') + public function getQuery($model = 'db', $queryClass = '') { if (!isset($this->query[$model])) { $class = $queryClass ?: $this->config['query']; - $this->query[$model] = new $class($this, $model); + $this->query[$model] = new $class($this, 'db' == $model ? '' : $model); } return $this->query[$model]; } @@ -154,11 +155,7 @@ abstract class Connection */ public function __call($method, $args) { - if (!isset($this->query['database'])) { - $class = $this->config['query']; - $this->query['database'] = new $class($this); - } - return call_user_func_array([$this->query['database'], $method], $args); + return call_user_func_array([$this->getQuery(), $method], $args); } /** diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index dc854a6c..7bc75c3d 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -16,8 +16,6 @@ use think\Cache; use think\Collection; use think\Config; use think\Db; -use think\db\Builder; -use think\db\Connection; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; @@ -27,8 +25,7 @@ use think\exception\PDOException; use think\Loader; use think\Model; use think\model\Relation; -use think\model\relation\BelongsTo; -use think\model\relation\HasOne; +use think\model\relation\OneToOne; use think\Paginator; class Query @@ -1182,7 +1179,14 @@ class Query } } } - $this->options['order'] = $field; + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } } return $this; } @@ -1552,7 +1556,7 @@ class Query */ protected function getFieldBindType($type) { - if (preg_match('/(int|double|float|decimal|real|numeric|serial)/is', $type)) { + if (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { $bind = PDO::PARAM_INT; } elseif (preg_match('/bool/is', $type)) { $bind = PDO::PARAM_BOOL; @@ -1653,8 +1657,9 @@ class Query } /** @var Relation $model */ - $model = $class->$relation(); - if ($model instanceof HasOne || $model instanceof BelongsTo) { + $relation = Loader::parseName($relation, 1, false); + $model = $class->$relation(); + if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { $model->eagerly($this, $relation, $subRelation, $closure, $first); $first = false; } elseif ($closure) { @@ -1666,6 +1671,36 @@ class Query return $this; } + /** + * 关联统计 + * @access public + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withCount($relation, $subQuery = true) + { + if (!$subQuery) { + $this->options['with_count'] = $relation; + } else { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + if (!isset($this->options['field'])) { + $this->field('*'); + } + foreach ($relations as $key => $relation) { + $closure = false; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + $relation = Loader::parseName($relation, 1, false); + $count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; + $this->field([$count => Loader::parseName($relation) . '_count']); + } + } + return $this; + } + /** * 关联预加载中 获取关联指定字段值 * example: @@ -1997,6 +2032,10 @@ class Query if (!empty($options['relation'])) { $result->relationQuery($options['relation']); } + // 关联统计 + if (!empty($options['with_count'])) { + $result->relationCount($result, $options['with_count']); + } $resultSet[$key] = $result; } if (!empty($options['with'])) { @@ -2095,10 +2134,14 @@ class Query if (!empty($options['relation'])) { $data->relationQuery($options['relation']); } + // 预载入查询 if (!empty($options['with'])) { - // 预载入 $data->eagerlyResult($data, $options['with'], is_object($result) ? get_class($result) : ''); } + // 关联统计 + if (!empty($options['with_count'])) { + $data->relationCount($data, $options['with_count']); + } } } elseif (!empty($options['fail'])) { $this->throwNotFound($options); diff --git a/core/library/think/db/exception/BindParamException.php b/core/library/think/db/exception/BindParamException.php index 585a0d73..3872e7c0 100644 --- a/core/library/think/db/exception/BindParamException.php +++ b/core/library/think/db/exception/BindParamException.php @@ -16,7 +16,7 @@ use think\exception\DbException; /** * PDO参数绑定异常 */ -class BindParamException extends DbException +class BindParamException extends DbException { /** diff --git a/core/library/think/db/exception/DataNotFoundException.php b/core/library/think/db/exception/DataNotFoundException.php index efd66d3b..cd4253ab 100644 --- a/core/library/think/db/exception/DataNotFoundException.php +++ b/core/library/think/db/exception/DataNotFoundException.php @@ -13,7 +13,7 @@ namespace think\db\exception; use think\exception\DbException; -class DataNotFoundException extends DbException +class DataNotFoundException extends DbException { protected $table; @@ -23,10 +23,10 @@ class DataNotFoundException extends DbException * @param string $table * @param array $config */ - public function __construct($message, $table = '', Array $config = []) + public function __construct($message, $table = '', array $config = []) { - $this->message = $message; - $this->table = $table; + $this->message = $message; + $this->table = $table; $this->setData('Database Config', $config); } diff --git a/core/library/think/db/exception/ModelNotFoundException.php b/core/library/think/db/exception/ModelNotFoundException.php index 69b70965..1d4724a6 100644 --- a/core/library/think/db/exception/ModelNotFoundException.php +++ b/core/library/think/db/exception/ModelNotFoundException.php @@ -13,7 +13,7 @@ namespace think\db\exception; use think\exception\DbException; -class ModelNotFoundException extends DbException +class ModelNotFoundException extends DbException { protected $model; @@ -22,10 +22,10 @@ class ModelNotFoundException extends DbException * @param string $message * @param string $model */ - public function __construct($message, $model = '', Array $config = []) + public function __construct($message, $model = '', array $config = []) { - $this->message = $message; - $this->model = $model; + $this->message = $message; + $this->model = $model; $this->setData('Database Config', $config); } diff --git a/core/library/think/exception/PDOException.php b/core/library/think/exception/PDOException.php index 677092b9..1d88e9f6 100644 --- a/core/library/think/exception/PDOException.php +++ b/core/library/think/exception/PDOException.php @@ -11,8 +11,6 @@ namespace think\exception; -use think\exception\DbException; - /** * PDO异常处理类 * 重新封装了系统的\PDOException类 diff --git a/core/library/think/exception/RouteNotFoundException.php b/core/library/think/exception/RouteNotFoundException.php index 6ee2f7b6..f85a3c4b 100644 --- a/core/library/think/exception/RouteNotFoundException.php +++ b/core/library/think/exception/RouteNotFoundException.php @@ -11,8 +11,6 @@ namespace think\exception; -use think\exception\HttpException; - class RouteNotFoundException extends HttpException { diff --git a/core/library/think/log/driver/File.php b/core/library/think/log/driver/File.php index f9358a9d..b639fd04 100644 --- a/core/library/think/log/driver/File.php +++ b/core/library/think/log/driver/File.php @@ -63,7 +63,7 @@ class File $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); } - $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $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); diff --git a/core/library/think/log/driver/Socket.php b/core/library/think/log/driver/Socket.php index cc4a8e12..08fd8ace 100644 --- a/core/library/think/log/driver/Socket.php +++ b/core/library/think/log/driver/Socket.php @@ -11,6 +11,8 @@ namespace think\log\driver; +use think\App; + /** * github: https://github.com/luofei614/SocketLog * @author luofei614 @@ -65,7 +67,7 @@ class Socket } $trace = []; if (App::$debug) { - $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $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); @@ -207,19 +209,19 @@ class Socket } if (!isset($_SERVER[$key])) { - return null; + return; } if (empty($args)) { if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) { $args = ['tabid' => null]; - return null; + return; } parse_str($match[1], $args); } if (isset($args[$name])) { return $args[$name]; } - return null; + return; } /** diff --git a/core/library/think/model/Merge.php b/core/library/think/model/Merge.php index de8d9e1a..5edb2a45 100644 --- a/core/library/think/model/Merge.php +++ b/core/library/think/model/Merge.php @@ -12,6 +12,7 @@ namespace think\model; use think\Db; +use think\db\Query; use think\Model; class Merge extends Model @@ -168,12 +169,13 @@ class Merge extends Model $this->autoCompleteData($this->auto); // 自动写入更新时间 - if ($this->autoWriteTimestamp && $this->updateTime) { + if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) { $this->setAttr($this->updateTime, null); } $db = $this->db(); $db->startTrans(); + $pk = $this->getPk(); try { if ($this->isUpdate) { // 自动写入 @@ -187,19 +189,15 @@ class Merge extends Model $where = $this->updateWhere; } - if (!empty($where)) { - $pk = $this->getPk(); - - if (isset($this->mapFields[$pk])) { - $pk = $this->mapFields[$pk]; - } - if (isset($where[$pk])) { - unset($where[$pk]); - } - } - // 处理模型数据 $data = $this->parseData($this->name, $this->data); + if (is_string($pk) && isset($data[$pk])) { + if (!isset($where[$pk])) { + unset($where); + $where[$pk] = $data[$pk]; + } + unset($data[$pk]); + } // 写入主表数据 $result = $db->strict(false)->where($where)->update($data); @@ -209,7 +207,7 @@ class Merge extends Model $table = is_int($key) ? $db->getTable($model) : $model; // 处理关联模型数据 $data = $this->parseData($name, $this->data); - $query = clone $db; + $query = new Query; if ($query->table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) { $result = 1; } @@ -223,7 +221,7 @@ class Merge extends Model $this->autoCompleteData($this->insert); // 自动写入创建时间 - if ($this->autoWriteTimestamp && $this->createTime) { + if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) { $this->setAttr($this->createTime, null); } @@ -238,7 +236,6 @@ class Merge extends Model if ($result) { $insertId = $db->getLastInsID($sequence); // 写入外键数据 - $pk = $this->getPk(); if ($insertId) { if (is_string($pk)) { $this->data[$pk] = $insertId; @@ -259,7 +256,7 @@ class Merge extends Model $table = is_int($key) ? $db->getTable($model) : $model; // 处理关联模型数据 $data = $this->parseData($name, $source, true); - $query = clone $db; + $query = new Query; $query->table($table)->strict(false)->insert($data); } } @@ -300,7 +297,7 @@ class Merge extends Model // 删除关联数据 foreach ($this->relationModel as $key => $model) { $table = is_int($key) ? $db->getTable($model) : $model; - $query = clone $db; + $query = new Query; $query->table($table)->where($this->fk, $pk)->delete(); } } diff --git a/core/library/think/model/relation/BelongsTo.php b/core/library/think/model/relation/BelongsTo.php index ffab752d..0f1f0c73 100644 --- a/core/library/think/model/relation/BelongsTo.php +++ b/core/library/think/model/relation/BelongsTo.php @@ -12,7 +12,6 @@ namespace think\model\relation; use think\Model; -use think\model\relation\OneToOne; class BelongsTo extends OneToOne { @@ -22,7 +21,7 @@ class BelongsTo extends OneToOne * @param Model $parent 上级模型对象 * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $otherKey 关联主键 + * @param string $localKey 关联主键 * @param array $alias 别名定义 * @param string $joinType JOIN类型 */ @@ -48,4 +47,80 @@ class BelongsTo extends OneToOne return $this->query->where($localKey, $this->parent->$foreignKey)->find(); } + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$foreignKey)) { + $range[] = $result->$foreignKey; + } + } + + if (!empty($range)) { + $this->where[$localKey] = ['in', $range]; + $data = $this->eagerlyWhere($this, [ + $localKey => [ + 'in', + $range, + ], + ], $localKey, $relation, $subRelation, $closure); + + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$foreignKey])) { + $data[$result->$foreignKey] = []; + } + $relationModel = $this->resultSetBuild($data[$result->$foreignKey], $class); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + // 设置关联属性 + $result->setAttr($relation, $relationModel); + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); + // 关联数据封装 + if (!isset($data[$result->$foreignKey])) { + $data[$result->$foreignKey] = []; + } + $relationModel = $this->resultSetBuild($data[$result->$foreignKey], $class); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + // 设置关联属性 + $result->setAttr($relation, $relationModel); + } + } diff --git a/core/library/think/model/relation/BelongsToMany.php b/core/library/think/model/relation/BelongsToMany.php index b5d21680..e1849d89 100644 --- a/core/library/think/model/relation/BelongsToMany.php +++ b/core/library/think/model/relation/BelongsToMany.php @@ -13,6 +13,7 @@ namespace think\model\relation; use think\Db; use think\db\Query; +use think\Exception; use think\Model; use think\model\Pivot; use think\model\Relation; @@ -128,14 +129,11 @@ class BelongsToMany extends Relation */ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) { - $localKey = $this->localKey; - $foreignKey = $this->foreignKey; - $pk = $result->getPk(); if (isset($result->$pk)) { $pk = $result->$pk; // 查询管理数据 - $data = $this->eagerlyManyToMany(['pivot.' . $localKey => $pk], $relation, $subRelation); + $data = $this->eagerlyManyToMany(['pivot.' . $this->localKey => $pk], $relation, $subRelation); // 关联数据封装 if (!isset($data[$pk])) { @@ -145,6 +143,35 @@ class BelongsToMany extends Relation } } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $pk = $result->getPk(); + $count = 0; + if (isset($result->$pk)) { + $pk = $result->$pk; + $count = $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count(); + } + return $count; + } + + /** + * 获取关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @return string + */ + public function getRelationCountQuery($closure) + { + return $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()]])->fetchSql()->count(); + } + /** * 多对多 关联模型预查询 * @access public @@ -155,10 +182,8 @@ class BelongsToMany extends Relation */ protected function eagerlyManyToMany($where, $relation, $subRelation = '') { - $foreignKey = $this->foreignKey; - $localKey = $this->localKey; // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->middle, $foreignKey, $localKey, $where)->with($subRelation)->select(); + $list = $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, $where)->with($subRelation)->select(); // 组装模型数据 $data = []; @@ -173,8 +198,8 @@ class BelongsToMany extends Relation } } } - $set->pivot = new Pivot($pivot, $this->middle); - $data[$pivot[$localKey]][] = $set; + $set->pivot = new Pivot($pivot, $this->middle); + $data[$pivot[$this->localKey]][] = $set; } return $data; } @@ -217,13 +242,19 @@ class BelongsToMany extends Relation * @access public * @param array $dataSet 数据集 * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 * @return integer */ - public function saveAll(array $dataSet, array $pivot = []) + public function saveAll(array $dataSet, array $pivot = [], $samePivot = false) { $result = false; foreach ($dataSet as $key => $data) { - $result = $this->attach($data, !empty($pivot) ? $pivot[$key] : []); + if (!$samePivot) { + $pivotData = isset($pivot[$key]) ? $pivot[$key] : []; + } else { + $pivotData = $pivot; + } + $result = $this->attach($data, $pivotData); } return $result; } @@ -238,10 +269,14 @@ class BelongsToMany extends Relation public function attach($data, $pivot = []) { if (is_array($data)) { - // 保存关联表数据 - $model = new $this->model; - $model->save($data); - $id = $model->getLastInsID(); + if (key($data) === 0) { + $id = $data; + } else { + // 保存关联表数据 + $model = new $this->model; + $model->save($data); + $id = $model->getLastInsID(); + } } elseif (is_numeric($data) || is_string($data)) { // 根据关联表主键直接写入中间表 $id = $data; @@ -253,10 +288,14 @@ class BelongsToMany extends Relation if ($id) { // 保存中间表数据 - $pk = $this->parent->getPk(); - $pivot[$this->localKey] = $this->parent->$pk; - $pivot[$this->foreignKey] = $id; - return $this->query->table($this->middle)->insert($pivot); + $pk = $this->parent->getPk(); + $pivot[$this->localKey] = $this->parent->$pk; + $ids = (array) $id; + foreach ($ids as $id) { + $pivot[$this->foreignKey] = $id; + $result = $this->query->table($this->middle)->insert($pivot, true); + } + return $result; } else { throw new Exception('miss relation data'); } diff --git a/core/library/think/model/relation/HasMany.php b/core/library/think/model/relation/HasMany.php index 416c5a5b..e5690137 100644 --- a/core/library/think/model/relation/HasMany.php +++ b/core/library/think/model/relation/HasMany.php @@ -58,10 +58,8 @@ class HasMany extends Relation */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) { - $localKey = $this->localKey; - $foreignKey = $this->foreignKey; - - $range = []; + $localKey = $this->localKey; + $range = []; foreach ($resultSet as $result) { // 获取关联外键列表 if (isset($result->$localKey)) { @@ -70,9 +68,9 @@ class HasMany extends Relation } if (!empty($range)) { - $this->where[$foreignKey] = ['in', $range]; - $data = $this->eagerlyOneToMany($this, [ - $foreignKey => [ + $this->where[$this->foreignKey] = ['in', $range]; + $data = $this->eagerlyOneToMany($this, [ + $this->foreignKey => [ 'in', $range, ], @@ -100,11 +98,10 @@ class HasMany extends Relation */ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) { - $localKey = $this->localKey; - $foreignKey = $this->foreignKey; + $localKey = $this->localKey; if (isset($result->$localKey)) { - $data = $this->eagerlyOneToMany($this, [$foreignKey => $result->$localKey], $relation, $subRelation, $closure); + $data = $this->eagerlyOneToMany($this, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$result->$localKey])) { $data[$result->$localKey] = []; @@ -113,6 +110,41 @@ class HasMany extends Relation } } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $localKey = $this->localKey; + $count = 0; + if (isset($result->$localKey)) { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $count = $this->query->where([$this->foreignKey => $result->$localKey])->count(); + } + return $count; + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @return string + */ + public function getRelationCountQuery($closure) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + + return $this->query->where([$this->foreignKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()]])->fetchSql()->count(); + } + /** * 一对多 关联模型预查询 * @access public diff --git a/core/library/think/model/relation/HasManyThrough.php b/core/library/think/model/relation/HasManyThrough.php index 9339d505..79a822f3 100644 --- a/core/library/think/model/relation/HasManyThrough.php +++ b/core/library/think/model/relation/HasManyThrough.php @@ -21,6 +21,8 @@ class HasManyThrough extends Relation { // 中间关联表外键 protected $throughKey; + // 中间表模型 + protected $through; /** * 架构函数 @@ -37,7 +39,7 @@ class HasManyThrough extends Relation { $this->parent = $parent; $this->model = $model; - $this->middle = $through; + $this->through = $through; $this->foreignKey = $foreignKey; $this->throughKey = $throughKey; $this->localKey = $localKey; @@ -65,8 +67,7 @@ class HasManyThrough extends Relation * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) - { - } + {} /** * 预载入关联查询 返回模型对象 @@ -79,8 +80,17 @@ class HasManyThrough extends Relation * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) - { - } + {} + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + {} /** * 执行基础查询(进执行一次) @@ -90,7 +100,7 @@ class HasManyThrough extends Relation protected function baseQuery() { if (empty($this->baseQuery)) { - $through = $this->middle; + $through = $this->through; $model = $this->model; $alias = Loader::parseName(basename(str_replace('\\', '/', $model))); $throughTable = $through::getTable(); diff --git a/core/library/think/model/relation/HasOne.php b/core/library/think/model/relation/HasOne.php index 5f75afa4..4ac1a589 100644 --- a/core/library/think/model/relation/HasOne.php +++ b/core/library/think/model/relation/HasOne.php @@ -12,7 +12,6 @@ namespace think\model\relation; use think\Model; -use think\model\relation\OneToOne; class HasOne extends OneToOne { @@ -73,4 +72,80 @@ class HasOne extends OneToOne ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType) ->where($where); } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $this->where[$foreignKey] = ['in', $range]; + $data = $this->eagerlyWhere($this, [ + $foreignKey => [ + 'in', + $range, + ], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + $relationModel = $this->resultSetBuild($data[$result->$localKey], $class); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + // 设置关联属性 + $result->setAttr($relation, $relationModel); + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); + // 关联数据封装 + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + $relationModel = $this->resultSetBuild($data[$result->$localKey], $class); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + $result->setAttr($relation, $relationModel); + } + } diff --git a/core/library/think/model/relation/MorphMany.php b/core/library/think/model/relation/MorphMany.php index 5a2e2e9b..d76052ed 100644 --- a/core/library/think/model/relation/MorphMany.php +++ b/core/library/think/model/relation/MorphMany.php @@ -106,16 +106,48 @@ class MorphMany extends Relation */ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) { - $morphType = $this->morphType; - $morphKey = $this->morphKey; - $type = $this->type; - $pk = $result->getPk(); + $pk = $result->getPk(); if (isset($result->$pk)) { - $data = $this->eagerlyMorphToMany([$morphKey => $result->$pk, $morphType => $type], $relation, $subRelation, $closure); + $data = $this->eagerlyMorphToMany([$this->morphKey => $result->$pk, $this->morphType => $this->type], $relation, $subRelation, $closure); $result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class)); } } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $pk = $result->getPk(); + $count = 0; + if (isset($result->$pk)) { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count(); + } + return $count; + } + + /** + * 获取关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @return string + */ + public function getRelationCountQuery($closure) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + + return $this->query->where([$this->morphKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()], $this->morphType => $this->type])->fetchSql()->count(); + } + /** * 多态一对多 关联模型预查询 * @access public diff --git a/core/library/think/model/relation/MorphTo.php b/core/library/think/model/relation/MorphTo.php index 6de1d4f4..fa691489 100644 --- a/core/library/think/model/relation/MorphTo.php +++ b/core/library/think/model/relation/MorphTo.php @@ -136,6 +136,16 @@ class MorphTo extends Relation $this->eagerlyMorphToOne($model, $relation, $result, $subRelation); } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + {} + /** * 多态MorphTo 关联模型预查询 * @access public diff --git a/core/library/think/model/relation/OneToOne.php b/core/library/think/model/relation/OneToOne.php index 650000e9..0f22898b 100644 --- a/core/library/think/model/relation/OneToOne.php +++ b/core/library/think/model/relation/OneToOne.php @@ -12,15 +12,20 @@ namespace think\model\relation; use think\db\Query; +use think\Exception; use think\Loader; use think\Model; use think\model\Relation; -use think\model\relation\BelongsTo; abstract class OneToOne extends Relation { + // 预载入方式 + protected $eagerlyType = 0; + // 要绑定的属性 + protected $bindAttr = []; + /** - * 预载入关联查询 + * 预载入关联查询(JOIN方式) * @access public * @param Query $query 查询对象 * @param string $relation 关联名 @@ -76,7 +81,7 @@ abstract class OneToOne extends Relation } /** - * 预载入关联查询 + * 预载入关联查询(数据集) * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 @@ -85,16 +90,21 @@ abstract class OneToOne extends Relation * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) { - foreach ($resultSet as $result) { + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlySet($resultSet, $relation, $subRelation, $closure, $class); + } else { // 模型关联组装 - $this->match($this->model, $relation, $result); + foreach ($resultSet as $result) { + $this->match($this->model, $relation, $result); + } } } /** - * 预载入关联查询 返回模型对象 + * 预载入关联查询(数据) * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 @@ -103,12 +113,81 @@ abstract class OneToOne extends Relation * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResult(&$result, $relation) + public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) { - // 模型关联组装 - $this->match($this->model, $relation, $result); + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlyOne($result, $relation, $subRelation, $closure, $class); + } else { + // 模型关联组装 + $this->match($this->model, $relation, $result); + } } + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return integer + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + $model = new $this->model; + return $model->save($data); + } + + /** + * 设置预载入方式 + * @access public + * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 + * @return this + */ + public function setEagerlyType($type) + { + $this->eagerlyType = $type; + return $this; + } + + /** + * 获取预载入方式 + * @access public + * @return integer + */ + public function getEagerlyType() + { + return $this->eagerlyType; + } + + /** + * 绑定关联表的属性到父模型属性 + * @access public + * @param mixed $attr 要绑定的属性列表 + * @return this + */ + public function bind($attr) + { + if (is_string($attr)) { + $attr = explode(',', $attr); + } + $this->bindAttr = $attr; + return $this; + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + {} + /** * 一对一 关联模型预查询拼装 * @access public @@ -129,25 +208,60 @@ abstract class OneToOne extends Relation } } } - - $result->setAttr($relation, !isset($list[$relation]) ? null : (new $model($list[$relation]))->isUpdate(true)); + if (isset($list[$relation])) { + $relationModel = new $model($list[$relation]); + if (!empty($this->bindAttr)) { + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + } + $result->setAttr($relation, !isset($relationModel) ? null : $relationModel->isUpdate(true)); } /** - * 保存(新增)当前关联数据对象 - * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @return integer + * 绑定关联属性到父模型 + * @access protected + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 + * @param array $bindAttr 绑定属性 + * @return void */ - public function save($data) + protected function bindAttr($model, &$result, $bindAttr) { - if ($data instanceof Model) { - $data = $data->getData(); + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if (isset($result->$key)) { + throw new Exception('bind attr has exists:' . $key); + } else { + $result->setAttr($key, $model->$attr); + } } - // 保存关联表数据 - $data[$this->foreignKey] = $this->parent->{$this->localKey}; - $model = new $this->model; - return $model->save($data); + } + + /** + * 一对一 关联模型预查询(IN方式) + * @access public + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool $closure + * @return array + */ + protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) + { + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $model]); + } + $list = $model->where($where)->with($subRelation)->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$key][] = $set; + } + return $data; } /** diff --git a/core/library/think/paginator/Collection.php b/core/library/think/paginator/Collection.php index 82e88272..50803ba8 100644 --- a/core/library/think/paginator/Collection.php +++ b/core/library/think/paginator/Collection.php @@ -71,4 +71,4 @@ class Collection extends \think\Collection throw new Exception('method not exists:' . __CLASS__ . '->' . $method); } } -} \ No newline at end of file +} diff --git a/core/library/think/paginator/driver/Bootstrap.php b/core/library/think/paginator/driver/Bootstrap.php index 87e1fe48..13b7bccd 100644 --- a/core/library/think/paginator/driver/Bootstrap.php +++ b/core/library/think/paginator/driver/Bootstrap.php @@ -102,7 +102,6 @@ class Bootstrap extends Paginator return $html; } - /** * 渲染分页html * @return mixed @@ -127,7 +126,6 @@ class Bootstrap extends Paginator } } - /** * 生成一个可点击的按钮 * @@ -204,4 +202,4 @@ class Bootstrap extends Paginator return $this->getAvailablePageWrapper($url, $page); } -} \ No newline at end of file +} diff --git a/core/library/think/process/Builder.php b/core/library/think/process/Builder.php index 826e6745..da561639 100644 --- a/core/library/think/process/Builder.php +++ b/core/library/think/process/Builder.php @@ -15,10 +15,9 @@ use think\Process; class Builder { - private $arguments; private $cwd; - private $env = null; + private $env = null; private $input; private $timeout = 60; private $options = []; @@ -155,7 +154,7 @@ class Builder return $this; } - $timeout = (float)$timeout; + $timeout = (float) $timeout; if ($timeout < 0) { throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); @@ -231,4 +230,4 @@ class Builder return $process; } -} \ No newline at end of file +} diff --git a/core/library/think/process/Utils.php b/core/library/think/process/Utils.php index 3ed136a5..f94c6488 100644 --- a/core/library/think/process/Utils.php +++ b/core/library/think/process/Utils.php @@ -60,7 +60,7 @@ class Utils return $input; } if (is_scalar($input)) { - return (string)$input; + return (string) $input; } throw new \InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller)); } @@ -72,4 +72,4 @@ class Utils return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; } -} \ No newline at end of file +} diff --git a/core/library/think/process/exception/Failed.php b/core/library/think/process/exception/Failed.php new file mode 100644 index 00000000..52950823 --- /dev/null +++ b/core/library/think/process/exception/Failed.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\exception; + +use think\Process; + +class Failed extends \RuntimeException +{ + + private $process; + + public function __construct(Process $process) + { + if ($process->isSuccessful()) { + throw new \InvalidArgumentException('Expected a failed process, but the given process was successful.'); + } + + $error = sprintf('The command "%s" failed.' . "\nExit Code: %s(%s)", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText()); + + if (!$process->isOutputDisabled()) { + $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput()); + } + + parent::__construct($error); + + $this->process = $process; + } + + public function getProcess() + { + return $this->process; + } +} diff --git a/core/library/think/process/exception/Timeout.php b/core/library/think/process/exception/Timeout.php index c085ee8e..d5f1162f 100644 --- a/core/library/think/process/exception/Timeout.php +++ b/core/library/think/process/exception/Timeout.php @@ -58,4 +58,4 @@ class Timeout extends \RuntimeException throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); } } -} \ No newline at end of file +} diff --git a/core/library/think/process/pipes/Pipes.php b/core/library/think/process/pipes/Pipes.php index 51da44f3..82396b8f 100644 --- a/core/library/think/process/pipes/Pipes.php +++ b/core/library/think/process/pipes/Pipes.php @@ -53,7 +53,6 @@ abstract class Pipes */ abstract public function areOpen(); - /** * {@inheritdoc} */ @@ -91,4 +90,4 @@ abstract class Pipes $this->blocked = false; } -} \ No newline at end of file +} diff --git a/core/library/think/process/pipes/Unix.php b/core/library/think/process/pipes/Unix.php index abfe61b2..fd99a5d6 100644 --- a/core/library/think/process/pipes/Unix.php +++ b/core/library/think/process/pipes/Unix.php @@ -25,14 +25,14 @@ class Unix extends Pipes public function __construct($ttyMode, $ptyMode, $input, $disableOutput) { - $this->ttyMode = (bool)$ttyMode; - $this->ptyMode = (bool)$ptyMode; - $this->disableOutput = (bool)$disableOutput; + $this->ttyMode = (bool) $ttyMode; + $this->ptyMode = (bool) $ptyMode; + $this->disableOutput = (bool) $disableOutput; if (is_resource($input)) { $this->input = $input; } else { - $this->inputBuffer = (string)$input; + $this->inputBuffer = (string) $input; } } @@ -134,12 +134,12 @@ class Unix extends Pipes $type = (false !== $found = array_search($pipe, $this->pipes)) ? $found : 'input'; $data = ''; - while ('' !== $dataread = (string)fread($pipe, self::CHUNK_SIZE)) { + while ('' !== $dataread = (string) fread($pipe, self::CHUNK_SIZE)) { $data .= $dataread; } if ('' !== $data) { - if ($type === 'input') { + if ('input' === $type) { $this->inputBuffer .= $data; } else { $read[$type] = $data; @@ -147,7 +147,7 @@ class Unix extends Pipes } if (false === $data || (true === $close && feof($pipe) && '' === $data)) { - if ($type === 'input') { + if ('input' === $type) { $this->input = null; } else { fclose($this->pipes[$type]); @@ -160,7 +160,7 @@ class Unix extends Pipes while (strlen($this->inputBuffer)) { $written = fwrite($w[0], $this->inputBuffer, 2 << 18); // write 512k if ($written > 0) { - $this->inputBuffer = (string)substr($this->inputBuffer, $written); + $this->inputBuffer = (string) substr($this->inputBuffer, $written); } else { break; } @@ -180,7 +180,7 @@ class Unix extends Pipes */ public function areOpen() { - return (bool)$this->pipes; + return (bool) $this->pipes; } /** @@ -193,4 +193,4 @@ class Unix extends Pipes { return new static($process->isTty(), $process->isPty(), $input, $process->isOutputDisabled()); } -} \ No newline at end of file +} diff --git a/core/library/think/process/pipes/Windows.php b/core/library/think/process/pipes/Windows.php index 2cc44ca7..bba7e9b8 100644 --- a/core/library/think/process/pipes/Windows.php +++ b/core/library/think/process/pipes/Windows.php @@ -30,7 +30,7 @@ class Windows extends Pipes public function __construct($disableOutput, $input) { - $this->disableOutput = (bool)$disableOutput; + $this->disableOutput = (bool) $disableOutput; if (!$this->disableOutput) { @@ -128,7 +128,7 @@ class Windows extends Pipes */ public function areOpen() { - return (bool)$this->pipes && (bool)$this->fileHandles; + return (bool) $this->pipes && (bool) $this->fileHandles; } /** @@ -213,7 +213,7 @@ class Windows extends Pipes while (strlen($this->inputBuffer)) { $written = fwrite($w[0], $this->inputBuffer, 2 << 18); if ($written > 0) { - $this->inputBuffer = (string)substr($this->inputBuffer, $written); + $this->inputBuffer = (string) substr($this->inputBuffer, $written); } else { break; } @@ -225,4 +225,4 @@ class Windows extends Pipes unset($this->pipes[0]); } } -} \ No newline at end of file +} diff --git a/core/library/think/session/driver/Memcache.php b/core/library/think/session/driver/Memcache.php index 4ef044a4..78320864 100644 --- a/core/library/think/session/driver/Memcache.php +++ b/core/library/think/session/driver/Memcache.php @@ -79,7 +79,7 @@ class Memcache extends SessionHandler */ public function read($sessID) { - return $this->handler->get($this->config['session_name'] . $sessID); + return (string) $this->handler->get($this->config['session_name'] . $sessID); } /** diff --git a/core/library/think/session/driver/Memcached.php b/core/library/think/session/driver/Memcached.php index 01d0f1e0..c5df23dc 100644 --- a/core/library/think/session/driver/Memcached.php +++ b/core/library/think/session/driver/Memcached.php @@ -87,7 +87,7 @@ class Memcached extends SessionHandler */ public function read($sessID) { - return $this->handler->get($this->config['session_name'] . $sessID); + return (string) $this->handler->get($this->config['session_name'] . $sessID); } /** diff --git a/core/library/think/session/driver/Redis.php b/core/library/think/session/driver/Redis.php index 52f14ab7..b1632f9e 100644 --- a/core/library/think/session/driver/Redis.php +++ b/core/library/think/session/driver/Redis.php @@ -81,11 +81,11 @@ class Redis extends SessionHandler * 读取Session * @access public * @param string $sessID - * @return bool|string + * @return string */ public function read($sessID) { - return $this->handler->get($this->config['session_name'] . $sessID); + return (string) $this->handler->get($this->config['session_name'] . $sessID); } /** diff --git a/core/library/think/template/taglib/Cx.php b/core/library/think/template/taglib/Cx.php index b15c7bba..1a23c764 100644 --- a/core/library/think/template/taglib/Cx.php +++ b/core/library/think/template/taglib/Cx.php @@ -620,8 +620,8 @@ class Cx extends Taglib return $parseStr; } - /** - * U函数的tag标签 + /** + * url函数的tag标签 * 格式:{url link="模块/控制器/方法" vars="参数" suffix="true或者false 是否带有后缀" domain="true或者false 是否携带域名" /} * @access public * @param array $tag 标签属性 diff --git a/core/library/think/view/driver/Php.php b/core/library/think/view/driver/Php.php index 863a7c17..b7028c2d 100644 --- a/core/library/think/view/driver/Php.php +++ b/core/library/think/view/driver/Php.php @@ -127,17 +127,18 @@ class Php $depr = $this->config['view_depr']; if (0 !== strpos($template, '/')) { - $template = str_replace(['/', ':'], $depr, $template); - } - - $controller = Loader::parseName($request->controller()); - if ($controller) { - if ('' == $template) { - // 如果模板文件名为空 按照默认规则定位 - $template = str_replace('.', DS, $controller) . $depr . $request->action(); - } elseif (false === strpos($template, $depr)) { - $template = str_replace('.', DS, $controller) . $depr . $template; + $template = str_replace(['/', ':'], $depr, $template); + $controller = Loader::parseName($request->controller()); + if ($controller) { + if ('' == $template) { + // 如果模板文件名为空 按照默认规则定位 + $template = str_replace('.', DS, $controller) . $depr . $request->action(); + } elseif (false === strpos($template, $depr)) { + $template = str_replace('.', DS, $controller) . $depr . $template; + } } + } else { + $template = str_replace(['/', ':'], $depr, substr($template, 1)); } return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); } diff --git a/core/library/think/view/driver/Think.php b/core/library/think/view/driver/Think.php index 8e673fde..e5336b41 100644 --- a/core/library/think/view/driver/Think.php +++ b/core/library/think/view/driver/Think.php @@ -120,18 +120,20 @@ class Think $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; } - $controller = Loader::parseName($request->controller()); - $depr = $this->config['view_depr']; + $depr = $this->config['view_depr']; if (0 !== strpos($template, '/')) { - $template = str_replace(['/', ':'], $depr, $template); - } - if ($controller) { - if ('' == $template) { - // 如果模板文件名为空 按照默认规则定位 - $template = str_replace('.', DS, $controller) . $depr . $request->action(); - } elseif (false === strpos($template, $depr)) { - $template = str_replace('.', DS, $controller) . $depr . $template; + $template = str_replace(['/', ':'], $depr, $template); + $controller = Loader::parseName($request->controller()); + if ($controller) { + if ('' == $template) { + // 如果模板文件名为空 按照默认规则定位 + $template = str_replace('.', DS, $controller) . $depr . $request->action(); + } elseif (false === strpos($template, $depr)) { + $template = str_replace('.', DS, $controller) . $depr . $template; + } } + } else { + $template = str_replace(['/', ':'], $depr, substr($template, 1)); } return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); } diff --git a/core/library/traits/model/SoftDelete.php b/core/library/traits/model/SoftDelete.php index 9d855c76..1a69c2c5 100644 --- a/core/library/traits/model/SoftDelete.php +++ b/core/library/traits/model/SoftDelete.php @@ -2,6 +2,8 @@ namespace traits\model; +use think\db\Query; + trait SoftDelete { @@ -22,7 +24,7 @@ trait SoftDelete /** * 查询软删除数据 * @access public - * @return \think\db\Query + * @return Query */ public static function withTrashed() { @@ -34,7 +36,7 @@ trait SoftDelete /** * 只查询软删除数据 * @access public - * @return \think\db\Query + * @return Query */ public static function onlyTrashed() { @@ -77,8 +79,8 @@ trait SoftDelete */ public static function destroy($data, $force = false) { - $model = new static(); - $query = $model->db(); + // 包含软删除数据 + $query = self::withTrashed(); if (is_array($data) && key($data) !== 0) { $query->where($data); $data = null; @@ -109,15 +111,19 @@ trait SoftDelete public function restore($where = []) { $name = $this->getDeleteTimeField(); + if (empty($where)) { + $pk = $this->getPk(); + $where[$pk] = $this->getData($pk); + $where[$name] = ['not null', '']; + } // 恢复删除 - return $this->isUpdate()->save([$name => null], $where); - + return $this->db(false)->removeWhereField($this->getDeleteTimeField(true))->where($where)->update([$name => null]); } /** * 查询默认不包含软删除数据 * @access protected - * @param \think\db\Query $query 查询对象 + * @param Query $query 查询对象 * @return void */ protected function base($query) diff --git a/core/library/traits/think/Instance.php b/core/library/traits/think/Instance.php index 206a9412..4dd005e4 100644 --- a/core/library/traits/think/Instance.php +++ b/core/library/traits/think/Instance.php @@ -11,7 +11,7 @@ namespace traits\think; -use \think\Exception; +use think\Exception; trait Instance {