作者 潘浩文
1 个管道 的构建 通过 耗费 0 秒

websocket协议测试

要显示太多修改。

为保证性能只显示 13 of 13+ 个文件。

@@ -4,7 +4,7 @@ use think\Db; @@ -4,7 +4,7 @@ use think\Db;
4 // 数据库配置信息设置(全局有效) 4 // 数据库配置信息设置(全局有效)
5 5
6 require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; 6 require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php';
7 - 7 +require_once __DIR__ . '/simplewind/vendor/autoload.php';
8 //$global_uid = 0; 8 //$global_uid = 0;
9 // 9 //
10 //// 当客户端连上来时分配uid,并保存连接,并通知所有客户端 10 //// 当客户端连上来时分配uid,并保存连接,并通知所有客户端
@@ -124,7 +124,7 @@ require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; @@ -124,7 +124,7 @@ require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php';
124 124
125 // 创建一个Worker监听2347端口,不使用任何应用层协议 125 // 创建一个Worker监听2347端口,不使用任何应用层协议
126 $tcp_worker = new Worker("websocket://114.215.223.17:5001"); 126 $tcp_worker = new Worker("websocket://114.215.223.17:5001");
127 -//Db::setConfig(['数据库配置参数(数组)']); 127 +
128 //$tcp_worker->name='panhaowen'; 128 //$tcp_worker->name='panhaowen';
129 // 启动4个进程对外提供服务 129 // 启动4个进程对外提供服务
130 $tcp_worker->count = 1; 130 $tcp_worker->count = 1;
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
  4 +
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "{}"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright {yyyy} {name of copyright owner}
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
  1 +# think-orm
  2 +
  3 +基于PHP5.6+ 的ORM实现,主要特性:
  4 +
  5 +- 基于ThinkPHP5.1的ORM独立封装
  6 +- 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb
  7 +- 支持Db类和查询构造器
  8 +- 支持事务
  9 +- 支持模型和关联
  10 +
  11 +适用于不使用ThinkPHP框架的开发者。
  12 +
  13 +安装
  14 +~~~
  15 +composer require topthink/think-orm
  16 +~~~
  17 +
  18 +Db类用法:
  19 +~~~php
  20 +use think\Db;
  21 +// 数据库配置信息设置(全局有效)
  22 +Db::setConfig(['数据库配置参数(数组)']);
  23 +// 进行CURD操作
  24 +Db::table('user')
  25 + ->data(['name'=>'thinkphp','email'=>'thinkphp@qq.com'])
  26 + ->insert();
  27 +Db::table('user')->find();
  28 +Db::table('user')
  29 + ->where('id','>',10)
  30 + ->order('id','desc')
  31 + ->limit(10)
  32 + ->select();
  33 +Db::table('user')
  34 + ->where('id',10)
  35 + ->update(['name'=>'test']);
  36 +Db::table('user')
  37 + ->where('id',10)
  38 + ->delete();
  39 +~~~
  40 +
  41 +Db类增加的(静态)方法包括:
  42 +- `setConfig` 设置全局配置信息
  43 +- `getConfig` 获取数据库配置信息
  44 +- `setQuery` 设置数据库Query类名称
  45 +- `setCacheHandler` 设置缓存对象Handler(必须支持get、set及rm方法)
  46 +- `getSqlLog` 用于获取当前请求的SQL日志信息(包含连接信息)
  47 +
  48 +其它操作参考TP5.1的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp5_1/353998)章节
  49 +
  50 +定义模型:
  51 +~~~php
  52 +namespace app\index\model;
  53 +use think\Model;
  54 +class User extends Model
  55 +{
  56 +}
  57 +~~~
  58 +
  59 +代码调用:
  60 +
  61 +~~~php
  62 +use app\index\model\User;
  63 +
  64 +$user = User::get(1);
  65 +$user->name = 'thinkphp';
  66 +$user->save();
  67 +~~~
  68 +
  69 +## Db类和模型对比使用
  70 +#### :white_check_mark: 创建Create
  71 +* Db用法
  72 +
  73 + ```php
  74 + Db::table('user')
  75 + ->insert([
  76 + 'name' => 'thinkphp',
  77 + 'email' => 'thinkphp@qq.com',
  78 + ]);
  79 + ```
  80 +* 模型用法
  81 +
  82 + ```php
  83 + $user = new User;
  84 + $user->name = 'thinkphp';
  85 + $user->email = 'thinkphp@qq.com';
  86 + $user->save();
  87 + ```
  88 +* 或者批量设置
  89 +
  90 + ```php
  91 + $user = new User;
  92 + $user->save([
  93 + 'name' => 'thinkphp',
  94 + 'email' => 'thinkphp@qq.com',
  95 + ]);
  96 + ```
  97 +#### :white_check_mark: 读取Read
  98 +* Db用法
  99 +
  100 + ```php
  101 + $user = Db::table('user')
  102 + ->where('id', 1)
  103 + ->find();
  104 + // 或者
  105 + $user = Db::table('user')
  106 + ->find(1);
  107 + echo $user['id'];
  108 + echo $user['name'];
  109 + ```
  110 +* 模型用法
  111 +
  112 + ```php
  113 + $user = User::get(1);
  114 + echo $user->id;
  115 + echo $user->name;
  116 + ```
  117 +* 模型实现读取多个记录
  118 +
  119 + ```php
  120 + // 查询用户数据集
  121 + $users = User::where('id', '>', 1)
  122 + ->limit(5)
  123 + ->select();
  124 +
  125 + // 遍历读取用户数据
  126 + foreach ($users as $user) {
  127 + echo $user->id;
  128 + echo $user->name;
  129 + }
  130 + ```
  131 +#### :white_check_mark: 更新Update
  132 +* Db用法
  133 +
  134 + ```php
  135 + Db::table('user')
  136 + ->where('id', 1)
  137 + ->update([
  138 + 'name' => 'topthink',
  139 + 'email' => 'topthink@qq.com',
  140 + ]);
  141 + ```
  142 +* 模型用法
  143 +
  144 + ```php
  145 + $user = User::get(1);
  146 + $user->name = 'topthink';
  147 + $user->email = 'topthink@qq.com';
  148 + $user->save();
  149 + ```
  150 +* 或者使用
  151 +
  152 + ```php
  153 + $user = User::get(1);
  154 + $user->save([
  155 + 'name' => 'topthink',
  156 + 'email' => 'topthink@qq.com',
  157 + ]);
  158 + ```
  159 +* 静态调用
  160 +
  161 + ```php
  162 + User::update([
  163 + 'name' => 'topthink',
  164 + 'email' => 'topthink@qq.com',
  165 + ], ['id' => 1]);
  166 + ```
  167 +#### :white_check_mark: 删除Delete
  168 +* Db用法
  169 +
  170 + ```php
  171 + Db::table('user')->delete(1);
  172 + ```
  173 +* 模型用法
  174 +
  175 + ```php
  176 + $user = User::get(1);
  177 + $user->delete();
  178 + ```
  179 +* 或者静态实现
  180 +
  181 + ```php
  182 + User::destroy(1);
  183 + ```
  184 +* 静态调用
  185 +
  186 + ```php
  187 + User::update([
  188 + 'name' => 'topthink',
  189 + 'email' => 'topthink@qq.com',
  190 + ], ['id' => 1]);
  191 + ```
  192 +* destroy方法支持删除指定主键或者查询条件的数据
  193 +
  194 + ```php
  195 + // 根据主键删除多个数据
  196 + User::destroy([1, 2, 3]);
  197 + // 指定条件删除数据
  198 + User::destroy([
  199 + 'status' => 0,
  200 + ]);
  201 + // 使用闭包条件
  202 + User::destroy(function ($query) {
  203 + $query->where('id', '>', 0)
  204 + ->where('status', 0);
  205 + });
  206 + ```
  207 +更多模型用法可以参考5.1完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp5_1/354041)章节
  1 +{
  2 + "name": "topthink/think-orm",
  3 + "description": "think orm",
  4 + "license": "Apache-2.0",
  5 + "authors": [
  6 + {
  7 + "name": "liu21st",
  8 + "email": "liu21st@gmail.com"
  9 + }
  10 + ],
  11 + "require": {
  12 + "php": ">=5.6.0"
  13 + },
  14 + "autoload": {
  15 + "psr-4": {
  16 + "think\\": "src"
  17 + },
  18 + "files": [
  19 + "src/config.php"
  20 + ]
  21 + }
  22 +}
  1 +<?php
  2 +
  3 +namespace think;
  4 +
  5 +/**
  6 + * 数据库缓存接口
  7 + * Interface CacheInterface
  8 + * @author : evalor <master@evalor.cn>
  9 + * @package think
  10 + */
  11 +interface CacheInterface
  12 +{
  13 + function get($name, $default = false);
  14 +
  15 + function set($name, $value, $expire = null);
  16 +
  17 + function rm($name);
  18 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: zhangyajun <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use ArrayAccess;
  15 +use ArrayIterator;
  16 +use Countable;
  17 +use IteratorAggregate;
  18 +use JsonSerializable;
  19 +
  20 +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
  21 +{
  22 + protected $items = [];
  23 +
  24 + public function __construct($items = [])
  25 + {
  26 + $this->items = $this->convertToArray($items);
  27 + }
  28 +
  29 + public static function make($items = [])
  30 + {
  31 + return new static($items);
  32 + }
  33 +
  34 + /**
  35 + * 是否为空
  36 + * @return bool
  37 + */
  38 + public function isEmpty()
  39 + {
  40 + return empty($this->items);
  41 + }
  42 +
  43 + public function toArray()
  44 + {
  45 + return array_map(function ($value) {
  46 + return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
  47 + }, $this->items);
  48 + }
  49 +
  50 + public function all()
  51 + {
  52 + return $this->items;
  53 + }
  54 +
  55 + /**
  56 + * 合并数组
  57 + *
  58 + * @param mixed $items
  59 + * @return static
  60 + */
  61 + public function merge($items)
  62 + {
  63 + return new static(array_merge($this->items, $this->convertToArray($items)));
  64 + }
  65 +
  66 + /**
  67 + * 交换数组中的键和值
  68 + *
  69 + * @return static
  70 + */
  71 + public function flip()
  72 + {
  73 + return new static(array_flip($this->items));
  74 + }
  75 +
  76 + /**
  77 + * 按指定键整理数据
  78 + *
  79 + * @access public
  80 + * @param mixed $items 数据
  81 + * @param string $indexKey 键名
  82 + * @return array
  83 + */
  84 + public function dictionary($items = null, &$indexKey = null)
  85 + {
  86 + if ($items instanceof self || $items instanceof Paginator) {
  87 + $items = $items->all();
  88 + }
  89 +
  90 + $items = is_null($items) ? $this->items : $items;
  91 +
  92 + if ($items && empty($indexKey)) {
  93 + $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
  94 + }
  95 +
  96 + if (isset($indexKey) && is_string($indexKey)) {
  97 + return array_column($items, null, $indexKey);
  98 + }
  99 +
  100 + return $items;
  101 + }
  102 +
  103 + /**
  104 + * 比较数组,返回差集
  105 + *
  106 + * @access public
  107 + * @param mixed $items 数据
  108 + * @param string $indexKey 指定比较的键名
  109 + * @return static
  110 + */
  111 + public function diff($items, $indexKey = null)
  112 + {
  113 + if ($this->isEmpty() || is_scalar($this->items[0])) {
  114 + return new static(array_diff($this->items, $this->convertToArray($items)));
  115 + }
  116 +
  117 + $diff = [];
  118 + $dictionary = $this->dictionary($items, $indexKey);
  119 +
  120 + if (is_string($indexKey)) {
  121 + foreach ($this->items as $item) {
  122 + if (!isset($dictionary[$item[$indexKey]])) {
  123 + $diff[] = $item;
  124 + }
  125 + }
  126 + }
  127 +
  128 + return new static($diff);
  129 + }
  130 +
  131 + /**
  132 + * 比较数组,返回交集
  133 + *
  134 + * @access public
  135 + * @param mixed $items 数据
  136 + * @param string $indexKey 指定比较的键名
  137 + * @return static
  138 + */
  139 + public function intersect($items, $indexKey = null)
  140 + {
  141 + if ($this->isEmpty() || is_scalar($this->items[0])) {
  142 + return new static(array_diff($this->items, $this->convertToArray($items)));
  143 + }
  144 +
  145 + $intersect = [];
  146 + $dictionary = $this->dictionary($items, $indexKey);
  147 +
  148 + if (is_string($indexKey)) {
  149 + foreach ($this->items as $item) {
  150 + if (isset($dictionary[$item[$indexKey]])) {
  151 + $intersect[] = $item;
  152 + }
  153 + }
  154 + }
  155 +
  156 + return new static($intersect);
  157 + }
  158 +
  159 + /**
  160 + * 返回数组中所有的键名
  161 + *
  162 + * @access public
  163 + * @return array
  164 + */
  165 + public function keys()
  166 + {
  167 + $current = current($this->items);
  168 +
  169 + if (is_scalar($current)) {
  170 + $array = $this->items;
  171 + } elseif (is_array($current)) {
  172 + $array = $current;
  173 + } else {
  174 + $array = $current->toArray();
  175 + }
  176 +
  177 + return array_keys($array);
  178 + }
  179 +
  180 + /**
  181 + * 删除数组的最后一个元素(出栈)
  182 + *
  183 + * @return mixed
  184 + */
  185 + public function pop()
  186 + {
  187 + return array_pop($this->items);
  188 + }
  189 +
  190 + /**
  191 + * 通过使用用户自定义函数,以字符串返回数组
  192 + *
  193 + * @param callable $callback
  194 + * @param mixed $initial
  195 + * @return mixed
  196 + */
  197 + public function reduce(callable $callback, $initial = null)
  198 + {
  199 + return array_reduce($this->items, $callback, $initial);
  200 + }
  201 +
  202 + /**
  203 + * 以相反的顺序返回数组。
  204 + *
  205 + * @return static
  206 + */
  207 + public function reverse()
  208 + {
  209 + return new static(array_reverse($this->items));
  210 + }
  211 +
  212 + /**
  213 + * 删除数组中首个元素,并返回被删除元素的值
  214 + *
  215 + * @return mixed
  216 + */
  217 + public function shift()
  218 + {
  219 + return array_shift($this->items);
  220 + }
  221 +
  222 + /**
  223 + * 在数组结尾插入一个元素
  224 + * @param mixed $value
  225 + * @param mixed $key
  226 + * @return void
  227 + */
  228 + public function push($value, $key = null)
  229 + {
  230 + if (is_null($key)) {
  231 + $this->items[] = $value;
  232 + } else {
  233 + $this->items[$key] = $value;
  234 + }
  235 + }
  236 +
  237 + /**
  238 + * 把一个数组分割为新的数组块.
  239 + *
  240 + * @param int $size
  241 + * @param bool $preserveKeys
  242 + * @return static
  243 + */
  244 + public function chunk($size, $preserveKeys = false)
  245 + {
  246 + $chunks = [];
  247 +
  248 + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
  249 + $chunks[] = new static($chunk);
  250 + }
  251 +
  252 + return new static($chunks);
  253 + }
  254 +
  255 + /**
  256 + * 在数组开头插入一个元素
  257 + * @param mixed $value
  258 + * @param mixed $key
  259 + * @return void
  260 + */
  261 + public function unshift($value, $key = null)
  262 + {
  263 + if (is_null($key)) {
  264 + array_unshift($this->items, $value);
  265 + } else {
  266 + $this->items = [$key => $value] + $this->items;
  267 + }
  268 + }
  269 +
  270 + /**
  271 + * 给每个元素执行个回调
  272 + *
  273 + * @param callable $callback
  274 + * @return $this
  275 + */
  276 + public function each(callable $callback)
  277 + {
  278 + foreach ($this->items as $key => $item) {
  279 + $result = $callback($item, $key);
  280 +
  281 + if (false === $result) {
  282 + break;
  283 + } elseif (!is_object($item)) {
  284 + $this->items[$key] = $result;
  285 + }
  286 + }
  287 +
  288 + return $this;
  289 + }
  290 +
  291 + /**
  292 + * 用回调函数过滤数组中的元素
  293 + * @param callable|null $callback
  294 + * @return static
  295 + */
  296 + public function filter(callable $callback = null)
  297 + {
  298 + if ($callback) {
  299 + return new static(array_filter($this->items, $callback));
  300 + }
  301 +
  302 + return new static(array_filter($this->items));
  303 + }
  304 +
  305 + /**
  306 + * 根据字段条件过滤数组中的元素
  307 + * @access public
  308 + * @param string $field 字段名
  309 + * @param mixed $operator 操作符
  310 + * @param mixed $value 数据
  311 + * @return static
  312 + */
  313 + public function where($field, $operator, $value = null)
  314 + {
  315 + if (is_null($value)) {
  316 + $value = $operator;
  317 + $operator = '=';
  318 + }
  319 +
  320 + return $this->filter(function ($data) use ($field, $operator, $value) {
  321 + if (strpos($field, '.')) {
  322 + list($field, $relation) = explode('.', $field);
  323 +
  324 + $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
  325 + } else {
  326 + $result = isset($data[$field]) ? $data[$field] : null;
  327 + }
  328 +
  329 + switch ($operator) {
  330 + case '===':
  331 + return $result === $value;
  332 + case '!==':
  333 + return $result !== $value;
  334 + case '!=':
  335 + case '<>':
  336 + return $result != $value;
  337 + case '>':
  338 + return $result > $value;
  339 + case '>=':
  340 + return $result >= $value;
  341 + case '<':
  342 + return $result < $value;
  343 + case '<=':
  344 + return $result <= $value;
  345 + case 'like':
  346 + return is_string($result) && false !== strpos($result, $value);
  347 + case 'not like':
  348 + return is_string($result) && false === strpos($result, $value);
  349 + case 'in':
  350 + return is_scalar($result) && in_array($result, $value, true);
  351 + case 'not in':
  352 + return is_scalar($result) && !in_array($result, $value, true);
  353 + case 'between':
  354 + list($min, $max) = is_string($value) ? explode(',', $value) : $value;
  355 + return is_scalar($result) && $result >= $min && $result <= $max;
  356 + case 'not between':
  357 + list($min, $max) = is_string($value) ? explode(',', $value) : $value;
  358 + return is_scalar($result) && $result > $max || $result < $min;
  359 + case '==':
  360 + case '=':
  361 + default:
  362 + return $result == $value;
  363 + }
  364 + });
  365 + }
  366 +
  367 + /**
  368 + * 返回数组中指定的一列
  369 + * @param mixed $column_key
  370 + * @param mixed $index_key
  371 + * @return array
  372 + */
  373 + public function column($column_key, $index_key = null)
  374 + {
  375 + return array_column($this->items, $column_key, $index_key);
  376 + }
  377 +
  378 + /**
  379 + * 对数组排序
  380 + *
  381 + * @access public
  382 + * @param callable|null $callback
  383 + * @return static
  384 + */
  385 + public function sort(callable $callback = null)
  386 + {
  387 + $items = $this->items;
  388 +
  389 + $callback = $callback ?: function ($a, $b) {
  390 + return $a == $b ? 0 : (($a < $b) ? -1 : 1);
  391 +
  392 + };
  393 +
  394 + uasort($items, $callback);
  395 +
  396 + return new static($items);
  397 + }
  398 +
  399 + /**
  400 + * 指定字段排序
  401 + * @access public
  402 + * @param string $field 排序字段
  403 + * @param string $order 排序
  404 + * @param bool $intSort 是否为数字排序
  405 + * @return $this
  406 + */
  407 + public function order($field, $order = null, $intSort = true)
  408 + {
  409 + return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
  410 + $fieldA = isset($a[$field]) ? $a[$field] : null;
  411 + $fieldB = isset($b[$field]) ? $b[$field] : null;
  412 +
  413 + if ($intSort) {
  414 + return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
  415 + } else {
  416 + return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
  417 + }
  418 + });
  419 + }
  420 +
  421 + /**
  422 + * 将数组打乱
  423 + *
  424 + * @return static
  425 + */
  426 + public function shuffle()
  427 + {
  428 + $items = $this->items;
  429 +
  430 + shuffle($items);
  431 +
  432 + return new static($items);
  433 + }
  434 +
  435 + /**
  436 + * 截取数组
  437 + *
  438 + * @param int $offset
  439 + * @param int $length
  440 + * @param bool $preserveKeys
  441 + * @return static
  442 + */
  443 + public function slice($offset, $length = null, $preserveKeys = false)
  444 + {
  445 + return new static(array_slice($this->items, $offset, $length, $preserveKeys));
  446 + }
  447 +
  448 + // ArrayAccess
  449 + public function offsetExists($offset)
  450 + {
  451 + return array_key_exists($offset, $this->items);
  452 + }
  453 +
  454 + public function offsetGet($offset)
  455 + {
  456 + return $this->items[$offset];
  457 + }
  458 +
  459 + public function offsetSet($offset, $value)
  460 + {
  461 + if (is_null($offset)) {
  462 + $this->items[] = $value;
  463 + } else {
  464 + $this->items[$offset] = $value;
  465 + }
  466 + }
  467 +
  468 + public function offsetUnset($offset)
  469 + {
  470 + unset($this->items[$offset]);
  471 + }
  472 +
  473 + //Countable
  474 + public function count()
  475 + {
  476 + return count($this->items);
  477 + }
  478 +
  479 + //IteratorAggregate
  480 + public function getIterator()
  481 + {
  482 + return new ArrayIterator($this->items);
  483 + }
  484 +
  485 + //JsonSerializable
  486 + public function jsonSerialize()
  487 + {
  488 + return $this->toArray();
  489 + }
  490 +
  491 + /**
  492 + * 转换当前数据集为JSON字符串
  493 + * @access public
  494 + * @param integer $options json参数
  495 + * @return string
  496 + */
  497 + public function toJson($options = JSON_UNESCAPED_UNICODE)
  498 + {
  499 + return json_encode($this->toArray(), $options);
  500 + }
  501 +
  502 + public function __toString()
  503 + {
  504 + return $this->toJson();
  505 + }
  506 +
  507 + /**
  508 + * 转换成数组
  509 + *
  510 + * @param mixed $items
  511 + * @return array
  512 + */
  513 + protected function convertToArray($items)
  514 + {
  515 + if ($items instanceof self) {
  516 + return $items->all();
  517 + }
  518 + return (array) $items;
  519 + }
  520 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use think\db\Query;
  15 +
  16 +/**
  17 + * Class Db
  18 + * @package think
  19 + * @method Query table(string $table) static 指定数据表(含前缀)
  20 + * @method Query name(string $name) static 指定数据表(不含前缀)
  21 + * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
  22 + * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
  23 + * @method Query union(mixed $union, boolean $all = false) static UNION查询
  24 + * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
  25 + * @method Query order(mixed $field, string $order = null) static 查询ORDER
  26 + * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
  27 + * @method mixed value(string $field) static 获取某个字段的值
  28 + * @method array column(string $field, string $key = '') static 获取某个列的值
  29 + * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
  30 + * @method mixed find(mixed $data = null) static 查询单个记录
  31 + * @method mixed select(mixed $data = null) static 查询多个记录
  32 + * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
  33 + * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
  34 + * @method integer insertAll(array $dataSet) static 插入多条记录
  35 + * @method integer update(array $data) static 更新记录
  36 + * @method integer delete(mixed $data = null) static 删除记录
  37 + * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
  38 + * @method \Generator cursor(mixed $data = null) static 使用游标查找记录
  39 + * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
  40 + * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
  41 + * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
  42 + * @method mixed transaction(callable $callback) static 执行数据库事务
  43 + * @method void startTrans() static 启动事务
  44 + * @method void commit() static 用于非自动提交状态下面的查询提交
  45 + * @method void rollback() static 事务回滚
  46 + * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
  47 + * @method string getLastInsID($sequence = null) static 获取最近插入的ID
  48 + */
  49 +class Db
  50 +{
  51 + /**
  52 + * 数据库配置
  53 + * @var array
  54 + */
  55 + protected static $config = [];
  56 +
  57 + /**
  58 + * 查询类名
  59 + * @var string
  60 + */
  61 + protected static $query;
  62 +
  63 + /**
  64 + * 查询类自动映射
  65 + * @var array
  66 + */
  67 + protected static $queryMap = [
  68 + 'mongo' => '\\think\\db\Mongo',
  69 + ];
  70 +
  71 + /**
  72 + * 查询次数
  73 + * @var integer
  74 + */
  75 + public static $queryTimes = 0;
  76 +
  77 + /**
  78 + * 执行次数
  79 + * @var integer
  80 + */
  81 + public static $executeTimes = 0;
  82 +
  83 + /**
  84 + * 缓存对象
  85 + * @var object
  86 + */
  87 + protected static $cacheHandler;
  88 +
  89 + public static function setConfig($config = [])
  90 + {
  91 + self::$config = array_merge(self::$config, $config);
  92 + }
  93 +
  94 + public static function getConfig($name = null)
  95 + {
  96 + if ($name) {
  97 + return isset(self::$config[$name]) ? self::$config[$name] : null;
  98 + } else {
  99 + return self::$config;
  100 + }
  101 + }
  102 +
  103 + public static function setQuery($query)
  104 + {
  105 + self::$query = $query;
  106 + }
  107 +
  108 + /**
  109 + * 字符串命名风格转换
  110 + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  111 + * @param string $name 字符串
  112 + * @param integer $type 转换类型
  113 + * @param bool $ucfirst 首字母是否大写(驼峰规则)
  114 + * @return string
  115 + */
  116 + public static function parseName($name, $type = 0, $ucfirst = true)
  117 + {
  118 + if ($type) {
  119 + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  120 + return strtoupper($match[1]);
  121 + }, $name);
  122 + return $ucfirst ? ucfirst($name) : lcfirst($name);
  123 + } else {
  124 + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  125 + }
  126 + }
  127 +
  128 + public static function setCacheHandler($cacheHandler)
  129 + {
  130 + self::$cacheHandler = $cacheHandler;
  131 + }
  132 +
  133 + public static function getCacheHandler()
  134 + {
  135 + return self::$cacheHandler;
  136 + }
  137 +
  138 + public static function __callStatic($method, $args)
  139 + {
  140 + if (!self::$query) {
  141 + $type = strtolower(self::getConfig('type'));
  142 +
  143 + $class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query';
  144 +
  145 + self::$query = $class;
  146 + }
  147 +
  148 + $class = self::$query;
  149 +
  150 + return call_user_func_array([new $class, $method], $args);
  151 + }
  152 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +class Exception extends \Exception
  15 +{
  16 +
  17 + /**
  18 + * 保存异常页面显示的额外Debug数据
  19 + * @var array
  20 + */
  21 + protected $data = [];
  22 +
  23 + /**
  24 + * 设置异常额外的Debug数据
  25 + * 数据将会显示为下面的格式
  26 + *
  27 + * Exception Data
  28 + * --------------------------------------------------
  29 + * Label 1
  30 + * key1 value1
  31 + * key2 value2
  32 + * Label 2
  33 + * key1 value1
  34 + * key2 value2
  35 + *
  36 + * @param string $label 数据分类,用于异常页面显示
  37 + * @param array $data 需要显示的数据,必须为关联数组
  38 + */
  39 + final protected function setData($label, array $data)
  40 + {
  41 + $this->data[$label] = $data;
  42 + }
  43 +
  44 + /**
  45 + * 获取异常额外Debug数据
  46 + * 主要用于输出到异常页面便于调试
  47 + * @return array 由setData设置的Debug数据
  48 + */
  49 + final public function getData()
  50 + {
  51 + return $this->data;
  52 + }
  53 +
  54 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use think\db\Query;
  15 +
  16 +/**
  17 + * Class Model
  18 + * @package think
  19 + * @mixin Query
  20 + * @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器
  21 + */
  22 +abstract class Model implements \JsonSerializable, \ArrayAccess
  23 +{
  24 + use model\concern\Attribute;
  25 + use model\concern\RelationShip;
  26 + use model\concern\ModelEvent;
  27 + use model\concern\TimeStamp;
  28 + use model\concern\Conversion;
  29 +
  30 + /**
  31 + * 数据是否存在
  32 + * @var bool
  33 + */
  34 + private $exists = false;
  35 +
  36 + /**
  37 + * 是否强制更新所有数据
  38 + * @var bool
  39 + */
  40 + private $force = false;
  41 +
  42 + /**
  43 + * 更新条件
  44 + * @var array
  45 + */
  46 + private $updateWhere;
  47 +
  48 + /**
  49 + * 数据库配置信息
  50 + * @var array|string
  51 + */
  52 + protected $connection = [];
  53 +
  54 + /**
  55 + * 数据库查询对象类名
  56 + * @var string
  57 + */
  58 + protected $query;
  59 +
  60 + /**
  61 + * 模型名称
  62 + * @var string
  63 + */
  64 + protected $name;
  65 +
  66 + /**
  67 + * 数据表名称
  68 + * @var string
  69 + */
  70 + protected $table;
  71 +
  72 + /**
  73 + * 写入自动完成定义
  74 + * @var array
  75 + */
  76 + protected $auto = [];
  77 +
  78 + /**
  79 + * 新增自动完成定义
  80 + * @var array
  81 + */
  82 + protected $insert = [];
  83 +
  84 + /**
  85 + * 更新自动完成定义
  86 + * @var array
  87 + */
  88 + protected $update = [];
  89 +
  90 + /**
  91 + * 初始化过的模型.
  92 + * @var array
  93 + */
  94 + protected static $initialized = [];
  95 +
  96 + /**
  97 + * 是否从主库读取(主从分布式有效)
  98 + * @var array
  99 + */
  100 + protected static $readMaster;
  101 +
  102 + /**
  103 + * 查询对象实例
  104 + * @var Query
  105 + */
  106 + protected $queryInstance;
  107 +
  108 + /**
  109 + * 错误信息
  110 + * @var mixed
  111 + */
  112 + protected $error;
  113 +
  114 + /**
  115 + * 软删除字段默认值
  116 + * @var mixed
  117 + */
  118 + protected $defaultSoftDelete;
  119 +
  120 + /**
  121 + * 全局查询范围
  122 + * @var array
  123 + */
  124 + protected $globalScope = [];
  125 +
  126 + /**
  127 + * 架构函数
  128 + * @access public
  129 + * @param array|object $data 数据
  130 + */
  131 + public function __construct($data = [])
  132 + {
  133 + if (is_object($data)) {
  134 + $this->data = get_object_vars($data);
  135 + } else {
  136 + $this->data = $data;
  137 + }
  138 +
  139 + if ($this->disuse) {
  140 + // 废弃字段
  141 + foreach ((array) $this->disuse as $key) {
  142 + if (array_key_exists($key, $this->data)) {
  143 + unset($this->data[$key]);
  144 + }
  145 + }
  146 + }
  147 +
  148 + // 记录原始数据
  149 + $this->origin = $this->data;
  150 +
  151 + $config = Db::getConfig();
  152 +
  153 + if (empty($this->name)) {
  154 + // 当前模型名
  155 + $name = str_replace('\\', '/', static::class);
  156 + $this->name = basename($name);
  157 + if (!empty($config['class_suffix'])) {
  158 + $suffix = basename(dirname($name));
  159 + $this->name = substr($this->name, 0, -strlen($suffix));
  160 + }
  161 + }
  162 +
  163 + if (is_null($this->autoWriteTimestamp)) {
  164 + // 自动写入时间戳
  165 + $this->autoWriteTimestamp = $config['auto_timestamp'];
  166 + }
  167 +
  168 + if (is_null($this->dateFormat)) {
  169 + // 设置时间戳格式
  170 + $this->dateFormat = $config['datetime_format'];
  171 + }
  172 +
  173 + if (is_null($this->resultSetType)) {
  174 + $this->resultSetType = $config['resultset_type'];
  175 + }
  176 +
  177 + if (is_null($this->query)) {
  178 + // 设置查询对象
  179 + $this->query = $config['query'];
  180 + }
  181 +
  182 + if (!empty($this->connection) && is_array($this->connection)) {
  183 + // 设置模型的数据库连接
  184 + $this->connection = array_merge($config, $this->connection);
  185 + }
  186 +
  187 + if ($this->observerClass) {
  188 + // 注册模型观察者
  189 + static::observe($this->observerClass);
  190 + }
  191 +
  192 + // 执行初始化操作
  193 + $this->initialize();
  194 + }
  195 +
  196 + /**
  197 + * 是否从主库读取数据(主从分布有效)
  198 + * @access public
  199 + * @param bool $all 是否所有模型有效
  200 + * @return $this
  201 + */
  202 + public function readMaster($all = false)
  203 + {
  204 + $model = $all ? '*' : static::class;
  205 +
  206 + static::$readMaster[$model] = true;
  207 +
  208 + return $this;
  209 + }
  210 +
  211 + /**
  212 + * 创建新的模型实例
  213 + * @access public
  214 + * @param array|object $data 数据
  215 + * @param bool $isUpdate 是否为更新
  216 + * @param mixed $where 更新条件
  217 + * @return Model
  218 + */
  219 + public function newInstance($data = [], $isUpdate = false, $where = null)
  220 + {
  221 + return (new static($data))->isUpdate($isUpdate, $where);
  222 + }
  223 +
  224 + /**
  225 + * 创建模型的查询对象
  226 + * @access protected
  227 + * @return Query
  228 + */
  229 + protected function buildQuery()
  230 + {
  231 + // 设置当前模型 确保查询返回模型对象
  232 + $class = $this->query;
  233 + $query = (new $class())->connect($this->connection)
  234 + ->model($this)
  235 + ->json($this->json, $this->jsonAssoc)
  236 + ->setJsonFieldType($this->jsonType);
  237 +
  238 + if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) {
  239 + $query->master(true);
  240 + }
  241 +
  242 + // 设置当前数据表和模型名
  243 + if (!empty($this->table)) {
  244 + $query->table($this->table);
  245 + } else {
  246 + $query->name($this->name);
  247 + }
  248 +
  249 + if (!empty($this->pk)) {
  250 + $query->pk($this->pk);
  251 + }
  252 +
  253 + return $query;
  254 + }
  255 +
  256 + /**
  257 + * 获取当前模型的数据库查询对象
  258 + * @access public
  259 + * @param Query $query 查询对象实例
  260 + * @return $this
  261 + */
  262 + public function setQuery($query)
  263 + {
  264 + $this->queryInstance = $query;
  265 + return $this;
  266 + }
  267 +
  268 + /**
  269 + * 获取当前模型的数据库查询对象
  270 + * @access public
  271 + * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称)
  272 + * @return Query
  273 + */
  274 + public function db($useBaseQuery = true)
  275 + {
  276 + if ($this->queryInstance) {
  277 + return $this->queryInstance;
  278 + }
  279 +
  280 + $query = $this->buildQuery();
  281 +
  282 + // 软删除
  283 + if (property_exists($this, 'withTrashed') && !$this->withTrashed) {
  284 + $this->withNoTrashed($query);
  285 + }
  286 +
  287 + // 全局作用域
  288 + if (true === $useBaseQuery && method_exists($this, 'base')) {
  289 + call_user_func_array([$this, 'base'], [ & $query]);
  290 + }
  291 +
  292 + $globalScope = is_array($useBaseQuery) && $useBaseQuery ?: $this->globalScope;
  293 +
  294 + if ($globalScope) {
  295 + $query->scope($globalScope);
  296 + }
  297 +
  298 + // 返回当前模型的数据库查询对象
  299 + return $query;
  300 + }
  301 +
  302 + /**
  303 + * 初始化模型
  304 + * @access protected
  305 + * @return void
  306 + */
  307 + protected function initialize()
  308 + {
  309 + if (!isset(static::$initialized[static::class])) {
  310 + static::$initialized[static::class] = true;
  311 + static::init();
  312 + }
  313 + }
  314 +
  315 + /**
  316 + * 初始化处理
  317 + * @access protected
  318 + * @return void
  319 + */
  320 + protected static function init()
  321 + {}
  322 +
  323 + /**
  324 + * 更新是否强制写入数据 而不做比较
  325 + * @access public
  326 + * @param bool $force
  327 + * @return $this
  328 + */
  329 + public function force($force = true)
  330 + {
  331 + $this->force = $force;
  332 + return $this;
  333 + }
  334 +
  335 + /**
  336 + * 判断force
  337 + * @access public
  338 + * @return bool
  339 + */
  340 + public function isForce()
  341 + {
  342 + return $this->force;
  343 + }
  344 +
  345 + /**
  346 + * 设置数据是否存在
  347 + * @access public
  348 + * @param bool $exists
  349 + * @return $this
  350 + */
  351 + public function exists($exists)
  352 + {
  353 + $this->exists = $exists;
  354 + return $this;
  355 + }
  356 +
  357 + /**
  358 + * 判断数据是否存在数据库
  359 + * @access public
  360 + * @return bool
  361 + */
  362 + public function isExists()
  363 + {
  364 + return $this->exists;
  365 + }
  366 +
  367 + /**
  368 + * 数据自动完成
  369 + * @access protected
  370 + * @param array $auto 要自动更新的字段列表
  371 + * @return void
  372 + */
  373 + protected function autoCompleteData($auto = [])
  374 + {
  375 + foreach ($auto as $field => $value) {
  376 + if (is_integer($field)) {
  377 + $field = $value;
  378 + $value = null;
  379 + }
  380 +
  381 + if (!isset($this->data[$field])) {
  382 + $default = null;
  383 + } else {
  384 + $default = $this->data[$field];
  385 + }
  386 +
  387 + $this->setAttr($field, !is_null($value) ? $value : $default);
  388 + }
  389 + }
  390 +
  391 + /**
  392 + * 保存当前数据对象
  393 + * @access public
  394 + * @param array $data 数据
  395 + * @param array $where 更新条件
  396 + * @param string $sequence 自增序列名
  397 + * @return false
  398 + */
  399 + public function save($data = [], $where = [], $sequence = null)
  400 + {
  401 + if (is_string($data)) {
  402 + $sequence = $data;
  403 + $data = [];
  404 + }
  405 +
  406 + if (!$this->checkBeforeSave($data, $where)) {
  407 + return false;
  408 + }
  409 +
  410 + $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence);
  411 +
  412 + if (false === $result) {
  413 + return false;
  414 + }
  415 +
  416 + // 写入回调
  417 + $this->trigger('after_write');
  418 +
  419 + // 重新记录原始数据
  420 + $this->origin = $this->data;
  421 + $this->set = [];
  422 +
  423 + return true;
  424 + }
  425 +
  426 + /**
  427 + * 解析查询条件
  428 + * @access protected
  429 + * @param array|null $where 保存条件
  430 + * @return array|null
  431 + */
  432 + protected static function parseWhere($where)
  433 + {
  434 + if (is_array($where) && key($where) !== 0) {
  435 + $item = [];
  436 + foreach ($where as $key => $val) {
  437 + $item[] = [$key, '=', $val];
  438 + }
  439 + return $item;
  440 + }
  441 + return $where;
  442 + }
  443 +
  444 + /**
  445 + * 写入之前检查数据
  446 + * @access protected
  447 + * @param array $data 数据
  448 + * @param array $where 保存条件
  449 + * @return bool
  450 + */
  451 + protected function checkBeforeSave($data, $where)
  452 + {
  453 + if (!empty($data)) {
  454 +
  455 + // 数据对象赋值
  456 + foreach ($data as $key => $value) {
  457 + $this->setAttr($key, $value, $data);
  458 + }
  459 +
  460 + if (!empty($where)) {
  461 + $this->exists = true;
  462 + $this->updateWhere = self::parseWhere($where);
  463 + }
  464 + }
  465 +
  466 + // 数据自动完成
  467 + $this->autoCompleteData($this->auto);
  468 +
  469 + // 事件回调
  470 + if (false === $this->trigger('before_write')) {
  471 + return false;
  472 + }
  473 +
  474 + return true;
  475 + }
  476 +
  477 + /**
  478 + * 检查数据是否允许写入
  479 + * @access protected
  480 + * @param array $autoFields 自动完成的字段列表
  481 + * @return array
  482 + */
  483 + protected function checkAllowFields($append = [])
  484 + {
  485 + // 检测字段
  486 + if (empty($this->field) || true === $this->field) {
  487 + $query = $this->db(false);
  488 + $table = $this->table ?: $query->getTable();
  489 +
  490 + $this->field = $query->getConnection()->getTableFields($table);
  491 +
  492 + $field = $this->field;
  493 + } else {
  494 + $field = array_merge($this->field, $append);
  495 +
  496 + if ($this->autoWriteTimestamp) {
  497 + array_push($field, $this->createTime, $this->updateTime);
  498 + }
  499 + }
  500 +
  501 + if ($this->disuse) {
  502 + // 废弃字段
  503 + $field = array_diff($field, (array) $this->disuse);
  504 + }
  505 + return $field;
  506 + }
  507 +
  508 + /**
  509 + * 保存写入数据
  510 + * @access protected
  511 + * @param array $where 保存条件
  512 + * @return int|false
  513 + */
  514 + protected function updateData($where)
  515 + {
  516 + // 自动更新
  517 + $this->autoCompleteData($this->update);
  518 +
  519 + // 事件回调
  520 + if (false === $this->trigger('before_update')) {
  521 + return false;
  522 + }
  523 +
  524 + // 获取有更新的数据
  525 + $data = $this->getChangedData();
  526 +
  527 + if (empty($data)) {
  528 + // 关联更新
  529 + if (isset($this->relationWrite)) {
  530 + $this->autoRelationUpdate();
  531 + }
  532 +
  533 + return 0;
  534 + } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
  535 + // 自动写入更新时间
  536 + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
  537 +
  538 + $this->data[$this->updateTime] = $data[$this->updateTime];
  539 + }
  540 +
  541 + if (empty($where) && !empty($this->updateWhere)) {
  542 + $where = $this->updateWhere;
  543 + }
  544 +
  545 + // 检查允许字段
  546 + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update));
  547 +
  548 + // 保留主键数据
  549 + foreach ($this->data as $key => $val) {
  550 + if ($this->isPk($key)) {
  551 + $data[$key] = $val;
  552 + }
  553 + }
  554 +
  555 + $pk = $this->getPk();
  556 +
  557 + foreach ((array) $pk as $key) {
  558 + if (isset($data[$key])) {
  559 + $array[] = [$key, '=', $data[$key]];
  560 + unset($data[$key]);
  561 + }
  562 + }
  563 +
  564 + if (!empty($array)) {
  565 + $where = $array;
  566 + }
  567 +
  568 + if ($this->relationWrite) {
  569 + foreach ($this->relationWrite as $name => $val) {
  570 + if (is_array($val)) {
  571 + foreach ($val as $key) {
  572 + if (isset($data[$key])) {
  573 + unset($data[$key]);
  574 + }
  575 + }
  576 + }
  577 + }
  578 + }
  579 +
  580 + $db = $this->db(false);
  581 + $db->startTrans();
  582 +
  583 + try {
  584 + // 模型更新
  585 + $result = $db->where($where)
  586 + ->strict(false)
  587 + ->field($allowFields)
  588 + ->update($data);
  589 +
  590 + // 关联更新
  591 + if (isset($this->relationWrite)) {
  592 + $this->autoRelationUpdate();
  593 + }
  594 +
  595 + $db->commit();
  596 +
  597 + // 更新回调
  598 + $this->trigger('after_update');
  599 +
  600 + return $result;
  601 + } catch (\Exception $e) {
  602 + $db->rollback();
  603 + throw $e;
  604 + }
  605 + }
  606 +
  607 + /**
  608 + * 新增写入数据
  609 + * @access protected
  610 + * @param string $sequence 自增名
  611 + * @return int|false
  612 + */
  613 + protected function insertData($sequence)
  614 + {
  615 + // 自动写入
  616 + $this->autoCompleteData($this->insert);
  617 +
  618 + // 时间戳自动写入
  619 + $this->checkTimeStampWrite();
  620 +
  621 + if (false === $this->trigger('before_insert')) {
  622 + return false;
  623 + }
  624 +
  625 + // 检查允许字段
  626 + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert));
  627 +
  628 + $db = $this->db(false);
  629 + $db->startTrans();
  630 +
  631 + try {
  632 + $result = $db->strict(false)
  633 + ->field($allowFields)
  634 + ->insert($this->data);
  635 +
  636 + // 获取自动增长主键
  637 + if ($result && $insertId = $db->getLastInsID($sequence)) {
  638 + $pk = $this->getPk();
  639 +
  640 + foreach ((array) $pk as $key) {
  641 + if (!isset($this->data[$key]) || '' == $this->data[$key]) {
  642 + $this->data[$key] = $insertId;
  643 + }
  644 + }
  645 + }
  646 +
  647 + // 关联写入
  648 + if (isset($this->relationWrite)) {
  649 + $this->autoRelationInsert();
  650 + }
  651 +
  652 + $db->commit();
  653 +
  654 + // 标记为更新
  655 + $this->exists = true;
  656 +
  657 + // 新增回调
  658 + $this->trigger('after_insert');
  659 +
  660 + return $result;
  661 + } catch (\Exception $e) {
  662 + $db->rollback();
  663 + throw $e;
  664 + }
  665 + }
  666 +
  667 + /**
  668 + * 字段值(延迟)增长
  669 + * @access public
  670 + * @param string $field 字段名
  671 + * @param integer $step 增长值
  672 + * @param integer $lazyTime 延时时间(s)
  673 + * @return integer|true
  674 + * @throws Exception
  675 + */
  676 + public function setInc($field, $step = 1, $lazyTime = 0)
  677 + {
  678 + // 读取更新条件
  679 + $where = $this->getWhere();
  680 +
  681 + $result = $this->db(false)->where($where)->setInc($field, $step, $lazyTime);
  682 +
  683 + if (true !== $result) {
  684 + $this->data[$field] += $step;
  685 + }
  686 +
  687 + return $result;
  688 + }
  689 +
  690 + /**
  691 + * 字段值(延迟)增长
  692 + * @access public
  693 + * @param string $field 字段名
  694 + * @param integer $step 增长值
  695 + * @param integer $lazyTime 延时时间(s)
  696 + * @return integer|true
  697 + * @throws Exception
  698 + */
  699 + public function setDec($field, $step = 1, $lazyTime = 0)
  700 + {
  701 + // 读取更新条件
  702 + $where = $this->getWhere();
  703 +
  704 + $result = $this->db(false)->where($where)->setDec($field, $step, $lazyTime);
  705 +
  706 + if (true !== $result) {
  707 + $this->data[$field] -= $step;
  708 + }
  709 +
  710 + return $result;
  711 + }
  712 +
  713 + /**
  714 + * 获取当前的更新条件
  715 + * @access protected
  716 + * @return mixed
  717 + */
  718 + protected function getWhere()
  719 + {
  720 + // 删除条件
  721 + $pk = $this->getPk();
  722 +
  723 + if (is_string($pk) && isset($this->data[$pk])) {
  724 + $where[] = [$pk, '=', $this->data[$pk]];
  725 + } elseif (!empty($this->updateWhere)) {
  726 + $where = $this->updateWhere;
  727 + } else {
  728 + $where = null;
  729 + }
  730 +
  731 + return $where;
  732 + }
  733 +
  734 + /**
  735 + * 保存多个数据到当前数据对象
  736 + * @access public
  737 + * @param array $dataSet 数据
  738 + * @param boolean $replace 是否自动识别更新和写入
  739 + * @return Collection|false
  740 + * @throws \Exception
  741 + */
  742 + public function saveAll($dataSet, $replace = true)
  743 + {
  744 + $result = [];
  745 +
  746 + $db = $this->db(false);
  747 + $db->startTrans();
  748 +
  749 + try {
  750 + $pk = $this->getPk();
  751 +
  752 + if (is_string($pk) && $replace) {
  753 + $auto = true;
  754 + }
  755 +
  756 + foreach ($dataSet as $key => $data) {
  757 + if (!empty($auto) && isset($data[$pk])) {
  758 + $result[$key] = self::update($data, [], $this->field);
  759 + } else {
  760 + $result[$key] = self::create($data, $this->field);
  761 + }
  762 + }
  763 +
  764 + $db->commit();
  765 +
  766 + return $this->toCollection($result);
  767 + } catch (\Exception $e) {
  768 + $db->rollback();
  769 + throw $e;
  770 + }
  771 + }
  772 +
  773 + /**
  774 + * 是否为更新数据
  775 + * @access public
  776 + * @param mixed $update
  777 + * @param mixed $where
  778 + * @return $this
  779 + */
  780 + public function isUpdate($update = true, $where = null)
  781 + {
  782 + if (is_bool($update)) {
  783 + $this->exists = $update;
  784 +
  785 + if (!empty($where)) {
  786 + $this->updateWhere = $where;
  787 + }
  788 + } else {
  789 + $this->exists = true;
  790 + $this->updateWhere = $update;
  791 + }
  792 +
  793 + return $this;
  794 + }
  795 +
  796 + /**
  797 + * 删除当前的记录
  798 + * @access public
  799 + * @return bool
  800 + */
  801 + public function delete()
  802 + {
  803 + if (!$this->exists || false === $this->trigger('before_delete')) {
  804 + return false;
  805 + }
  806 +
  807 + // 读取更新条件
  808 + $where = $this->getWhere();
  809 +
  810 + $db = $this->db(false);
  811 + $db->startTrans();
  812 +
  813 + try {
  814 + // 删除当前模型数据
  815 + $db->where($where)->delete();
  816 +
  817 + // 关联删除
  818 + if (!empty($this->relationWrite)) {
  819 + $this->autoRelationDelete();
  820 + }
  821 +
  822 + $db->commit();
  823 +
  824 + $this->trigger('after_delete');
  825 +
  826 + $this->exists = false;
  827 +
  828 + return true;
  829 + } catch (\Exception $e) {
  830 + $db->rollback();
  831 + throw $e;
  832 + }
  833 + }
  834 +
  835 + /**
  836 + * 设置自动完成的字段( 规则通过修改器定义)
  837 + * @access public
  838 + * @param array $fields 需要自动完成的字段
  839 + * @return $this
  840 + */
  841 + public function auto($fields)
  842 + {
  843 + $this->auto = $fields;
  844 +
  845 + return $this;
  846 + }
  847 +
  848 + /**
  849 + * 写入数据
  850 + * @access public
  851 + * @param array $data 数据数组
  852 + * @param array|true $field 允许字段
  853 + * @return $this
  854 + */
  855 + public static function create($data = [], $field = null)
  856 + {
  857 + $model = new static();
  858 +
  859 + if (!empty($field)) {
  860 + $model->allowField($field);
  861 + }
  862 +
  863 + $model->isUpdate(false)->save($data, []);
  864 +
  865 + return $model;
  866 + }
  867 +
  868 + /**
  869 + * 更新数据
  870 + * @access public
  871 + * @param array $data 数据数组
  872 + * @param array $where 更新条件
  873 + * @param array|true $field 允许字段
  874 + * @return $this
  875 + */
  876 + public static function update($data = [], $where = [], $field = null)
  877 + {
  878 + $model = new static();
  879 +
  880 + if (!empty($field)) {
  881 + $model->allowField($field);
  882 + }
  883 +
  884 + $result = $model->isUpdate(true)->save($data, $where);
  885 +
  886 + return $model;
  887 + }
  888 +
  889 + /**
  890 + * 删除记录
  891 + * @access public
  892 + * @param mixed $data 主键列表 支持闭包查询条件
  893 + * @return bool
  894 + */
  895 + public static function destroy($data)
  896 + {
  897 + $model = new static();
  898 +
  899 + $query = $model->db();
  900 +
  901 + if (empty($data) && 0 !== $data) {
  902 + return false;
  903 + } elseif (is_array($data) && key($data) !== 0) {
  904 + $query->where(self::parseWhere($data));
  905 + $data = null;
  906 + } elseif ($data instanceof \Closure) {
  907 + $data($query);
  908 + $data = null;
  909 + }
  910 +
  911 + $resultSet = $query->select($data);
  912 +
  913 + if ($resultSet) {
  914 + foreach ($resultSet as $data) {
  915 + $data->delete();
  916 + }
  917 + }
  918 +
  919 + return true;
  920 + }
  921 +
  922 + /**
  923 + * 获取错误信息
  924 + * @access public
  925 + * @return mixed
  926 + */
  927 + public function getError()
  928 + {
  929 + return $this->error;
  930 + }
  931 +
  932 + /**
  933 + * 解序列化后处理
  934 + */
  935 + public function __wakeup()
  936 + {
  937 + $this->initialize();
  938 + }
  939 +
  940 + public function __debugInfo()
  941 + {
  942 + return [
  943 + 'data' => $this->data,
  944 + 'relation' => $this->relation,
  945 + ];
  946 + }
  947 +
  948 + /**
  949 + * 修改器 设置数据对象的值
  950 + * @access public
  951 + * @param string $name 名称
  952 + * @param mixed $value 值
  953 + * @return void
  954 + */
  955 + public function __set($name, $value)
  956 + {
  957 + $this->setAttr($name, $value);
  958 + }
  959 +
  960 + /**
  961 + * 获取器 获取数据对象的值
  962 + * @access public
  963 + * @param string $name 名称
  964 + * @return mixed
  965 + */
  966 + public function __get($name)
  967 + {
  968 + return $this->getAttr($name);
  969 + }
  970 +
  971 + /**
  972 + * 检测数据对象的值
  973 + * @access public
  974 + * @param string $name 名称
  975 + * @return boolean
  976 + */
  977 + public function __isset($name)
  978 + {
  979 + try {
  980 + return !is_null($this->getAttr($name));
  981 + } catch (InvalidArgumentException $e) {
  982 + return false;
  983 + }
  984 + }
  985 +
  986 + /**
  987 + * 销毁数据对象的值
  988 + * @access public
  989 + * @param string $name 名称
  990 + * @return void
  991 + */
  992 + public function __unset($name)
  993 + {
  994 + unset($this->data[$name], $this->relation[$name]);
  995 + }
  996 +
  997 + // ArrayAccess
  998 + public function offsetSet($name, $value)
  999 + {
  1000 + $this->setAttr($name, $value);
  1001 + }
  1002 +
  1003 + public function offsetExists($name)
  1004 + {
  1005 + return $this->__isset($name);
  1006 + }
  1007 +
  1008 + public function offsetUnset($name)
  1009 + {
  1010 + $this->__unset($name);
  1011 + }
  1012 +
  1013 + public function offsetGet($name)
  1014 + {
  1015 + return $this->getAttr($name);
  1016 + }
  1017 +
  1018 + /**
  1019 + * 设置是否使用全局查询范围
  1020 + * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称)
  1021 + * @access public
  1022 + * @return Query
  1023 + */
  1024 + public static function useGlobalScope($use)
  1025 + {
  1026 + $model = new static();
  1027 +
  1028 + return $model->db($use);
  1029 + }
  1030 +
  1031 + public function __call($method, $args)
  1032 + {
  1033 + if ('withattr' == strtolower($method)) {
  1034 + return call_user_func_array([$this, 'withAttribute'], $args);
  1035 + }
  1036 +
  1037 + return call_user_func_array([$this->db(), $method], $args);
  1038 + }
  1039 +
  1040 + public static function __callStatic($method, $args)
  1041 + {
  1042 + $model = new static();
  1043 +
  1044 + return call_user_func_array([$model->db(), $method], $args);
  1045 + }
  1046 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: zhangyajun <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use ArrayAccess;
  15 +use ArrayIterator;
  16 +use Countable;
  17 +use IteratorAggregate;
  18 +use JsonSerializable;
  19 +use Traversable;
  20 +
  21 +abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
  22 +{
  23 + /** @var bool 是否为简洁模式 */
  24 + protected $simple = false;
  25 +
  26 + /** @var Collection 数据集 */
  27 + protected $items;
  28 +
  29 + /** @var integer 当前页 */
  30 + protected $currentPage;
  31 +
  32 + /** @var integer 最后一页 */
  33 + protected $lastPage;
  34 +
  35 + /** @var integer|null 数据总数 */
  36 + protected $total;
  37 +
  38 + /** @var integer 每页的数量 */
  39 + protected $listRows;
  40 +
  41 + /** @var bool 是否有下一页 */
  42 + protected $hasMore;
  43 +
  44 + /** @var array 一些配置 */
  45 + protected $options = [
  46 + 'var_page' => 'page',
  47 + 'path' => '/',
  48 + 'query' => [],
  49 + 'fragment' => '',
  50 + ];
  51 +
  52 + public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
  53 + {
  54 + $this->options = array_merge($this->options, $options);
  55 +
  56 + $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
  57 +
  58 + $this->simple = $simple;
  59 + $this->listRows = $listRows;
  60 +
  61 + if (!$items instanceof Collection) {
  62 + $items = Collection::make($items);
  63 + }
  64 +
  65 + if ($simple) {
  66 + $this->currentPage = $this->setCurrentPage($currentPage);
  67 + $this->hasMore = count($items) > ($this->listRows);
  68 + $items = $items->slice(0, $this->listRows);
  69 + } else {
  70 + $this->total = $total;
  71 + $this->lastPage = (int) ceil($total / $listRows);
  72 + $this->currentPage = $this->setCurrentPage($currentPage);
  73 + $this->hasMore = $this->currentPage < $this->lastPage;
  74 + }
  75 + $this->items = $items;
  76 + }
  77 +
  78 + /**
  79 + * @param $items
  80 + * @param $listRows
  81 + * @param null $currentPage
  82 + * @param bool $simple
  83 + * @param null $total
  84 + * @param array $options
  85 + * @return Paginator
  86 + */
  87 + public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
  88 + {
  89 + return new static($items, $listRows, $currentPage, $total, $simple, $options);
  90 + }
  91 +
  92 + protected function setCurrentPage($currentPage)
  93 + {
  94 + if (!$this->simple && $currentPage > $this->lastPage) {
  95 + return $this->lastPage > 0 ? $this->lastPage : 1;
  96 + }
  97 +
  98 + return $currentPage;
  99 + }
  100 +
  101 + /**
  102 + * 获取页码对应的链接
  103 + *
  104 + * @param $page
  105 + * @return string
  106 + */
  107 + protected function url($page)
  108 + {
  109 + if ($page <= 0) {
  110 + $page = 1;
  111 + }
  112 +
  113 + if (strpos($this->options['path'], '[PAGE]') === false) {
  114 + $parameters = [$this->options['var_page'] => $page];
  115 + $path = $this->options['path'];
  116 + } else {
  117 + $parameters = [];
  118 + $path = str_replace('[PAGE]', $page, $this->options['path']);
  119 + }
  120 +
  121 + if (count($this->options['query']) > 0) {
  122 + $parameters = array_merge($this->options['query'], $parameters);
  123 + }
  124 +
  125 + $url = $path;
  126 + if (!empty($parameters)) {
  127 + $url .= '?' . urldecode(http_build_query($parameters, null, '&'));
  128 + }
  129 +
  130 + return $url . $this->buildFragment();
  131 + }
  132 +
  133 + /**
  134 + * 自动获取当前页码
  135 + * @param string $varPage
  136 + * @param int $default
  137 + * @return int
  138 + */
  139 + public static function getCurrentPage($varPage = 'page', $default = 1)
  140 + {
  141 + $page = isset($_REQUEST[$varPage]) ? $_REQUEST[$varPage] : 1;
  142 +
  143 + if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
  144 + return $page;
  145 + }
  146 +
  147 + return $default;
  148 + }
  149 +
  150 + /**
  151 + * 自动获取当前的path
  152 + * @return string
  153 + */
  154 + public static function getCurrentPath()
  155 + {
  156 + if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
  157 + $url = $_SERVER['HTTP_X_REWRITE_URL'];
  158 + } elseif (isset($_SERVER['REQUEST_URI'])) {
  159 + $url = $_SERVER['REQUEST_URI'];
  160 + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
  161 + $url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
  162 + } else {
  163 + $url = '';
  164 + }
  165 +
  166 + return strpos($url, '?') ? strstr($url, '?', true) : $url;
  167 + }
  168 +
  169 + public function total()
  170 + {
  171 + if ($this->simple) {
  172 + throw new \DomainException('not support total');
  173 + }
  174 +
  175 + return $this->total;
  176 + }
  177 +
  178 + public function listRows()
  179 + {
  180 + return $this->listRows;
  181 + }
  182 +
  183 + public function currentPage()
  184 + {
  185 + return $this->currentPage;
  186 + }
  187 +
  188 + public function lastPage()
  189 + {
  190 + if ($this->simple) {
  191 + throw new \DomainException('not support last');
  192 + }
  193 +
  194 + return $this->lastPage;
  195 + }
  196 +
  197 + /**
  198 + * 数据是否足够分页
  199 + * @return boolean
  200 + */
  201 + public function hasPages()
  202 + {
  203 + return !(1 == $this->currentPage && !$this->hasMore);
  204 + }
  205 +
  206 + /**
  207 + * 创建一组分页链接
  208 + *
  209 + * @param int $start
  210 + * @param int $end
  211 + * @return array
  212 + */
  213 + public function getUrlRange($start, $end)
  214 + {
  215 + $urls = [];
  216 +
  217 + for ($page = $start; $page <= $end; $page++) {
  218 + $urls[$page] = $this->url($page);
  219 + }
  220 +
  221 + return $urls;
  222 + }
  223 +
  224 + /**
  225 + * 设置URL锚点
  226 + *
  227 + * @param string|null $fragment
  228 + * @return $this
  229 + */
  230 + public function fragment($fragment)
  231 + {
  232 + $this->options['fragment'] = $fragment;
  233 +
  234 + return $this;
  235 + }
  236 +
  237 + /**
  238 + * 添加URL参数
  239 + *
  240 + * @param array|string $key
  241 + * @param string|null $value
  242 + * @return $this
  243 + */
  244 + public function appends($key, $value = null)
  245 + {
  246 + if (!is_array($key)) {
  247 + $queries = [$key => $value];
  248 + } else {
  249 + $queries = $key;
  250 + }
  251 +
  252 + foreach ($queries as $k => $v) {
  253 + if ($k !== $this->options['var_page']) {
  254 + $this->options['query'][$k] = $v;
  255 + }
  256 + }
  257 +
  258 + return $this;
  259 + }
  260 +
  261 + /**
  262 + * 构造锚点字符串
  263 + *
  264 + * @return string
  265 + */
  266 + protected function buildFragment()
  267 + {
  268 + return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
  269 + }
  270 +
  271 + /**
  272 + * 渲染分页html
  273 + * @return mixed
  274 + */
  275 + abstract public function render();
  276 +
  277 + public function items()
  278 + {
  279 + return $this->items->all();
  280 + }
  281 +
  282 + public function getCollection()
  283 + {
  284 + return $this->items;
  285 + }
  286 +
  287 + public function isEmpty()
  288 + {
  289 + return $this->items->isEmpty();
  290 + }
  291 +
  292 + /**
  293 + * 给每个元素执行个回调
  294 + *
  295 + * @param callable $callback
  296 + * @return $this
  297 + */
  298 + public function each(callable $callback)
  299 + {
  300 + foreach ($this->items as $key => $item) {
  301 + $result = $callback($item, $key);
  302 +
  303 + if (false === $result) {
  304 + break;
  305 + } elseif (!is_object($item)) {
  306 + $this->items[$key] = $result;
  307 + }
  308 + }
  309 +
  310 + return $this;
  311 + }
  312 +
  313 + /**
  314 + * Retrieve an external iterator
  315 + * @return Traversable An instance of an object implementing <b>Iterator</b> or
  316 + * <b>Traversable</b>
  317 + */
  318 + public function getIterator()
  319 + {
  320 + return new ArrayIterator($this->items->all());
  321 + }
  322 +
  323 + /**
  324 + * Whether a offset exists
  325 + * @param mixed $offset
  326 + * @return bool
  327 + */
  328 + public function offsetExists($offset)
  329 + {
  330 + return $this->items->offsetExists($offset);
  331 + }
  332 +
  333 + /**
  334 + * Offset to retrieve
  335 + * @param mixed $offset
  336 + * @return mixed
  337 + */
  338 + public function offsetGet($offset)
  339 + {
  340 + return $this->items->offsetGet($offset);
  341 + }
  342 +
  343 + /**
  344 + * Offset to set
  345 + * @param mixed $offset
  346 + * @param mixed $value
  347 + */
  348 + public function offsetSet($offset, $value)
  349 + {
  350 + $this->items->offsetSet($offset, $value);
  351 + }
  352 +
  353 + /**
  354 + * Offset to unset
  355 + * @param mixed $offset
  356 + * @return void
  357 + * @since 5.0.0
  358 + */
  359 + public function offsetUnset($offset)
  360 + {
  361 + $this->items->offsetUnset($offset);
  362 + }
  363 +
  364 + /**
  365 + * Count elements of an object
  366 + */
  367 + public function count()
  368 + {
  369 + return $this->items->count();
  370 + }
  371 +
  372 + public function __toString()
  373 + {
  374 + return (string) $this->render();
  375 + }
  376 +
  377 + public function toArray()
  378 + {
  379 + try {
  380 + $total = $this->total();
  381 + } catch (\DomainException $e) {
  382 + $total = null;
  383 + }
  384 +
  385 + return [
  386 + 'total' => $total,
  387 + 'per_page' => $this->listRows(),
  388 + 'current_page' => $this->currentPage(),
  389 + 'last_page' => $this->lastPage,
  390 + 'data' => $this->items->toArray(),
  391 + ];
  392 + }
  393 +
  394 + /**
  395 + * Specify data which should be serialized to JSON
  396 + */
  397 + public function jsonSerialize()
  398 + {
  399 + return $this->toArray();
  400 + }
  401 +
  402 + public function __call($name, $arguments)
  403 + {
  404 + return call_user_func_array([$this->getCollection(), $name], $arguments);
  405 + }
  406 +
  407 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace think;
  12 +
  13 +Db::setConfig([
  14 + // 数据库类型
  15 + 'type' => '',
  16 + // 服务器地址
  17 + 'hostname' => '',
  18 + // 数据库名
  19 + 'database' => '',
  20 + // 用户名
  21 + 'username' => '',
  22 + // 密码
  23 + 'password' => '',
  24 + // 端口
  25 + 'hostport' => '',
  26 + // 连接dsn
  27 + 'dsn' => '',
  28 + // 数据库连接参数
  29 + 'params' => [],
  30 + // 数据库编码默认采用utf8
  31 + 'charset' => 'utf8',
  32 + // 数据库表前缀
  33 + 'prefix' => '',
  34 + // 数据库调试模式
  35 + 'debug' => false,
  36 + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  37 + 'deploy' => 0,
  38 + // 数据库读写是否分离 主从式有效
  39 + 'rw_separate' => false,
  40 + // 读写分离后 主服务器数量
  41 + 'master_num' => 1,
  42 + // 指定从服务器序号
  43 + 'slave_no' => '',
  44 + // 是否严格检查字段是否存在
  45 + 'fields_strict' => true,
  46 + // 数据集返回类型
  47 + 'resultset_type' => '',
  48 + // 自动写入时间戳字段
  49 + 'auto_timestamp' => false,
  50 + // 时间字段取出后的默认时间格式
  51 + 'datetime_format' => 'Y-m-d H:i:s',
  52 + // 是否需要进行SQL性能分析
  53 + 'sql_explain' => false,
  54 + // Builder类
  55 + 'builder' => '',
  56 + // Query类
  57 + 'query' => '\\think\\db\\Query',
  58 + // 是否需要断线重连
  59 + 'break_reconnect' => false,
  60 + // 默认分页设置
  61 + 'paginate' => [
  62 + 'type' => 'bootstrap',
  63 + 'var_page' => 'page',
  64 + 'list_rows' => 15,
  65 + ]
  66 +]);
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think\db;
  13 +
  14 +use PDO;
  15 +use think\Exception;
  16 +
  17 +abstract class Builder
  18 +{
  19 + // connection对象实例
  20 + protected $connection;
  21 +
  22 + // 查询表达式映射
  23 + protected $exp = ['EQ' => '=', 'NEQ' => '<>', 'GT' => '>', 'EGT' => '>=', 'LT' => '<', 'ELT' => '<=', 'NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME'];
  24 +
  25 + // 查询表达式解析
  26 + protected $parser = [
  27 + 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='],
  28 + 'parseLike' => ['LIKE', 'NOT LIKE'],
  29 + 'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],
  30 + 'parseIn' => ['NOT IN', 'IN'],
  31 + 'parseExp' => ['EXP'],
  32 + 'parseNull' => ['NOT NULL', 'NULL'],
  33 + 'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
  34 + 'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
  35 + 'parseExists' => ['NOT EXISTS', 'EXISTS'],
  36 + 'parseColumn' => ['COLUMN'],
  37 + ];
  38 +
  39 + // SQL表达式
  40 + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%';
  41 +
  42 + protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
  43 +
  44 + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
  45 +
  46 + protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
  47 +
  48 + protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
  49 +
  50 + /**
  51 + * 架构函数
  52 + * @access public
  53 + * @param Connection $connection 数据库连接对象实例
  54 + */
  55 + public function __construct(Connection $connection)
  56 + {
  57 + $this->connection = $connection;
  58 + }
  59 +
  60 + /**
  61 + * 获取当前的连接对象实例
  62 + * @access public
  63 + * @return Connection
  64 + */
  65 + public function getConnection()
  66 + {
  67 + return $this->connection;
  68 + }
  69 +
  70 + /**
  71 + * 注册查询表达式解析
  72 + * @access public
  73 + * @param string $name 解析方法
  74 + * @param array $parser 匹配表达式数据
  75 + * @return $this
  76 + */
  77 + public function bindParser($name, $parser)
  78 + {
  79 + $this->parser[$name] = $parser;
  80 + return $this;
  81 + }
  82 +
  83 + /**
  84 + * 数据分析
  85 + * @access protected
  86 + * @param Query $query 查询对象
  87 + * @param array $data 数据
  88 + * @param array $fields 字段信息
  89 + * @param array $bind 参数绑定
  90 + * @return array
  91 + */
  92 + protected function parseData(Query $query, $data = [], $fields = [], $bind = [])
  93 + {
  94 + if (empty($data)) {
  95 + return [];
  96 + }
  97 +
  98 + $options = $query->getOptions();
  99 +
  100 + // 获取绑定信息
  101 + if (empty($bind)) {
  102 + $bind = $this->connection->getFieldsBind($options['table']);
  103 + }
  104 +
  105 + if (empty($fields)) {
  106 + if ('*' == $options['field']) {
  107 + $fields = array_keys($bind);
  108 + } else {
  109 + $fields = $options['field'];
  110 + }
  111 + }
  112 +
  113 + $result = [];
  114 +
  115 + foreach ($data as $key => $val) {
  116 + if ('*' != $options['field'] && !in_array($key, $fields, true)) {
  117 + continue;
  118 + }
  119 +
  120 + $item = $this->parseKey($query, $key, true);
  121 +
  122 + if ($val instanceof Expression) {
  123 + $result[$item] = $val->getValue();
  124 + continue;
  125 + } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) {
  126 + $val = json_encode($val, JSON_UNESCAPED_UNICODE);
  127 + } elseif (is_object($val) && method_exists($val, '__toString')) {
  128 + // 对象数据写入
  129 + $val = $val->__toString();
  130 + }
  131 +
  132 + if (false !== strpos($key, '->')) {
  133 + list($key, $name) = explode('->', $key);
  134 + $item = $this->parseKey($query, $key);
  135 + $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')';
  136 + } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) {
  137 + if ($options['strict']) {
  138 + throw new Exception('fields not exists:[' . $key . ']');
  139 + }
  140 + } elseif (is_null($val)) {
  141 + $result[$item] = 'NULL';
  142 + } elseif (is_array($val) && !empty($val)) {
  143 + switch (strtoupper($val[0])) {
  144 + case 'INC':
  145 + $result[$item] = $item . ' + ' . floatval($val[1]);
  146 + break;
  147 + case 'DEC':
  148 + $result[$item] = $item . ' - ' . floatval($val[1]);
  149 + break;
  150 + case 'EXP':
  151 + throw new Exception('not support data:[' . $val[0] . ']');
  152 + }
  153 + } elseif (is_scalar($val)) {
  154 + // 过滤非标量数据
  155 + $result[$item] = $this->parseDataBind($query, $key, $val, $bind);
  156 + }
  157 + }
  158 +
  159 + return $result;
  160 + }
  161 +
  162 + /**
  163 + * 数据绑定处理
  164 + * @access protected
  165 + * @param Query $query 查询对象
  166 + * @param string $key 字段名
  167 + * @param mixed $data 数据
  168 + * @param array $bind 绑定数据
  169 + * @return string
  170 + */
  171 + protected function parseDataBind(Query $query, $key, $data, $bind = [])
  172 + {
  173 + if ($data instanceof Expression) {
  174 + return $data->getValue();
  175 + }
  176 +
  177 + $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
  178 +
  179 + return '?';
  180 + }
  181 +
  182 + /**
  183 + * 字段名分析
  184 + * @access public
  185 + * @param Query $query 查询对象
  186 + * @param mixed $key 字段名
  187 + * @param bool $strict 严格检测
  188 + * @return string
  189 + */
  190 + public function parseKey(Query $query, $key, $strict = false)
  191 + {
  192 + return $key instanceof Expression ? $key->getValue() : $key;
  193 + }
  194 +
  195 + /**
  196 + * field分析
  197 + * @access protected
  198 + * @param Query $query 查询对象
  199 + * @param mixed $fields
  200 + * @return string
  201 + */
  202 + protected function parseField(Query $query, $fields)
  203 + {
  204 + if ('*' == $fields || empty($fields)) {
  205 + $fieldsStr = '*';
  206 + } elseif (is_array($fields)) {
  207 + // 支持 'field1'=>'field2' 这样的字段别名定义
  208 + $array = [];
  209 +
  210 + foreach ($fields as $key => $field) {
  211 + if (!is_numeric($key)) {
  212 + $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true);
  213 + } else {
  214 + $array[] = $this->parseKey($query, $field);
  215 + }
  216 + }
  217 +
  218 + $fieldsStr = implode(',', $array);
  219 + }
  220 +
  221 + return $fieldsStr;
  222 + }
  223 +
  224 + /**
  225 + * table分析
  226 + * @access protected
  227 + * @param Query $query 查询对象
  228 + * @param mixed $tables
  229 + * @return string
  230 + */
  231 + protected function parseTable(Query $query, $tables)
  232 + {
  233 + $item = [];
  234 + $options = $query->getOptions();
  235 +
  236 + foreach ((array) $tables as $key => $table) {
  237 + if (!is_numeric($key)) {
  238 + $key = $this->connection->parseSqlTable($key);
  239 + $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);
  240 + } else {
  241 + $table = $this->connection->parseSqlTable($table);
  242 +
  243 + if (isset($options['alias'][$table])) {
  244 + $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]);
  245 + } else {
  246 + $item[] = $this->parseKey($query, $table);
  247 + }
  248 + }
  249 + }
  250 +
  251 + return implode(',', $item);
  252 + }
  253 +
  254 + /**
  255 + * where分析
  256 + * @access protected
  257 + * @param Query $query 查询对象
  258 + * @param mixed $where 查询条件
  259 + * @return string
  260 + */
  261 + protected function parseWhere(Query $query, $where)
  262 + {
  263 + $options = $query->getOptions();
  264 + $whereStr = $this->buildWhere($query, $where);
  265 +
  266 + if (!empty($options['soft_delete'])) {
  267 + // 附加软删除条件
  268 + list($field, $condition) = $options['soft_delete'];
  269 +
  270 + $binds = $this->connection->getFieldsBind($options['table']);
  271 + $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
  272 + $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds);
  273 + }
  274 +
  275 + return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
  276 + }
  277 +
  278 + /**
  279 + * 生成查询条件SQL
  280 + * @access public
  281 + * @param Query $query 查询对象
  282 + * @param mixed $where
  283 + * @param array $options
  284 + * @return string
  285 + */
  286 + public function buildWhere(Query $query, $where)
  287 + {
  288 + if (empty($where)) {
  289 + $where = [];
  290 + }
  291 +
  292 + $whereStr = '';
  293 + $binds = $this->connection->getFieldsBind($query->getOptions('table'));
  294 +
  295 + foreach ($where as $logic => $val) {
  296 + $str = [];
  297 +
  298 + foreach ($val as $value) {
  299 + if ($value instanceof Expression) {
  300 + $str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )';
  301 + continue;
  302 + }
  303 +
  304 + if (is_array($value)) {
  305 + if (key($value) !== 0) {
  306 + throw new Exception('where express error:' . var_export($value, true));
  307 + }
  308 + $field = array_shift($value);
  309 + } elseif (!($value instanceof \Closure)) {
  310 + throw new Exception('where express error:' . var_export($value, true));
  311 + }
  312 +
  313 + if ($value instanceof \Closure) {
  314 + // 使用闭包查询
  315 + $newQuery = $query->newQuery()->setConnection($this->connection);
  316 + $value($newQuery);
  317 + $whereClause = $this->buildWhere($query, $newQuery->getOptions('where'));
  318 +
  319 + if (!empty($whereClause)) {
  320 + $str[] = ' ' . $logic . ' ( ' . $whereClause . ' )';
  321 + }
  322 + } elseif (is_array($field)) {
  323 + array_unshift($value, $field);
  324 + $str2 = [];
  325 + foreach ($value as $item) {
  326 + $str2[] = $this->parseWhereItem($query, array_shift($item), $item, $logic, $binds);
  327 + }
  328 +
  329 + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $str2) . ' )';
  330 + } elseif (strpos($field, '|')) {
  331 + // 不同字段使用相同查询条件(OR)
  332 + $array = explode('|', $field);
  333 + $item = [];
  334 +
  335 + foreach ($array as $k) {
  336 + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds);
  337 + }
  338 +
  339 + $str[] = ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )';
  340 + } elseif (strpos($field, '&')) {
  341 + // 不同字段使用相同查询条件(AND)
  342 + $array = explode('&', $field);
  343 + $item = [];
  344 +
  345 + foreach ($array as $k) {
  346 + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds);
  347 + }
  348 +
  349 + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )';
  350 + } else {
  351 + // 对字段使用表达式查询
  352 + $field = is_string($field) ? $field : '';
  353 + $str[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $logic, $binds);
  354 + }
  355 + }
  356 +
  357 + $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str);
  358 + }
  359 +
  360 + return $whereStr;
  361 + }
  362 +
  363 + // where子单元分析
  364 + protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [])
  365 + {
  366 + // 字段分析
  367 + $key = $field ? $this->parseKey($query, $field, true) : '';
  368 +
  369 + // 查询规则和条件
  370 + if (!is_array($val)) {
  371 + $val = is_null($val) ? ['NULL', ''] : ['=', $val];
  372 + }
  373 +
  374 + list($exp, $value) = $val;
  375 +
  376 + // 对一个字段使用多个查询条件
  377 + if (is_array($exp)) {
  378 + $item = array_pop($val);
  379 +
  380 + // 传入 or 或者 and
  381 + if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) {
  382 + $rule = $item;
  383 + } else {
  384 + array_push($val, $item);
  385 + }
  386 +
  387 + foreach ($val as $k => $item) {
  388 + $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds);
  389 + }
  390 +
  391 + return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
  392 + }
  393 +
  394 + // 检测操作符
  395 + $exp = strtoupper($exp);
  396 + if (isset($this->exp[$exp])) {
  397 + $exp = $this->exp[$exp];
  398 + }
  399 +
  400 + if ($value instanceof Expression) {
  401 +
  402 + } elseif (is_object($value) && method_exists($value, '__toString')) {
  403 + // 对象数据写入
  404 + $value = $value->__toString();
  405 + }
  406 +
  407 + if (strpos($field, '->')) {
  408 + $jsonType = $query->getJsonFieldType($field);
  409 + $bindType = $this->connection->getFieldBindType($jsonType);
  410 + } else {
  411 + $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
  412 + }
  413 +
  414 + if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
  415 + $query->bind($value, $bindType);
  416 + $value = '?';
  417 + }
  418 +
  419 + // 解析查询表达式
  420 + foreach ($this->parser as $fun => $parse) {
  421 + if (in_array($exp, $parse)) {
  422 + $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND');
  423 + break;
  424 + }
  425 + }
  426 +
  427 + if (!isset($whereStr)) {
  428 + throw new Exception('where express error:' . $exp);
  429 + }
  430 +
  431 + return $whereStr;
  432 + }
  433 +
  434 + /**
  435 + * 模糊查询
  436 + * @access protected
  437 + * @param Query $query 查询对象
  438 + * @param string $key
  439 + * @param string $exp
  440 + * @param mixed $value
  441 + * @param string $field
  442 + * @param integer $bindType
  443 + * @param string $logic
  444 + * @return string
  445 + */
  446 + protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic)
  447 + {
  448 + // 模糊匹配
  449 + if (is_array($value)) {
  450 + foreach ($value as $item) {
  451 + $bind[] = [$item, $bindType];
  452 + $array[] = $key . ' ' . $exp . ' ?';
  453 + }
  454 +
  455 + $query->bind($bind);
  456 +
  457 + $whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
  458 + } else {
  459 + $whereStr = $key . ' ' . $exp . ' ' . $value;
  460 + }
  461 +
  462 + return $whereStr;
  463 + }
  464 +
  465 + /**
  466 + * 表达式查询
  467 + * @access protected
  468 + * @param Query $query 查询对象
  469 + * @param string $key
  470 + * @param string $exp
  471 + * @param Expression $value
  472 + * @param string $field
  473 + * @param integer $bindType
  474 + * @return string
  475 + */
  476 + protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType)
  477 + {
  478 + // 表达式查询
  479 + return '( ' . $key . ' ' . $value->getValue() . ' )';
  480 + }
  481 +
  482 + /**
  483 + * 表达式查询
  484 + * @access protected
  485 + * @param Query $query 查询对象
  486 + * @param string $key
  487 + * @param string $exp
  488 + * @param array $value
  489 + * @param string $field
  490 + * @param integer $bindType
  491 + * @return string
  492 + */
  493 + protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType)
  494 + {
  495 + // 字段比较查询
  496 + list($op, $field2) = $value;
  497 +
  498 + if (!in_array($op, ['=', '<>', '>', '>=', '<', '<='])) {
  499 + throw new Exception('where express error:' . var_export($value, true));
  500 + }
  501 +
  502 + return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field2, true) . ' )';
  503 + }
  504 +
  505 + /**
  506 + * Null查询
  507 + * @access protected
  508 + * @param Query $query 查询对象
  509 + * @param string $key
  510 + * @param string $exp
  511 + * @param mixed $value
  512 + * @param string $field
  513 + * @param integer $bindType
  514 + * @return string
  515 + */
  516 + protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType)
  517 + {
  518 + // NULL 查询
  519 + return $key . ' IS ' . $exp;
  520 + }
  521 +
  522 + /**
  523 + * 范围查询
  524 + * @access protected
  525 + * @param Query $query 查询对象
  526 + * @param string $key
  527 + * @param string $exp
  528 + * @param mixed $value
  529 + * @param string $field
  530 + * @param integer $bindType
  531 + * @return string
  532 + */
  533 + protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType)
  534 + {
  535 + // BETWEEN 查询
  536 + $data = is_array($value) ? $value : explode(',', $value);
  537 +
  538 + $bind[] = [$data[0], $bindType];
  539 + $bind[] = [$data[1], $bindType];
  540 +
  541 + $query->bind($bind);
  542 +
  543 + return $key . ' ' . $exp . ' ? AND ? ';
  544 + }
  545 +
  546 + /**
  547 + * Exists查询
  548 + * @access protected
  549 + * @param Query $query 查询对象
  550 + * @param string $key
  551 + * @param string $exp
  552 + * @param mixed $value
  553 + * @param string $field
  554 + * @param integer $bindType
  555 + * @return string
  556 + */
  557 + protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType)
  558 + {
  559 + // EXISTS 查询
  560 + if ($value instanceof \Closure) {
  561 + $value = $this->parseClosure($query, $value, false);
  562 + } elseif ($value instanceof Expression) {
  563 + $value = $value->getValue();
  564 + } else {
  565 + throw new Exception('where express error:' . $value);
  566 + }
  567 +
  568 + return $exp . ' (' . $value . ')';
  569 + }
  570 +
  571 + /**
  572 + * 时间比较查询
  573 + * @access protected
  574 + * @param Query $query 查询对象
  575 + * @param string $key
  576 + * @param string $exp
  577 + * @param mixed $value
  578 + * @param string $field
  579 + * @param integer $bindType
  580 + * @return string
  581 + */
  582 + protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType)
  583 + {
  584 + return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType);
  585 + }
  586 +
  587 + /**
  588 + * 大小比较查询
  589 + * @access protected
  590 + * @param Query $query 查询对象
  591 + * @param string $key
  592 + * @param string $exp
  593 + * @param mixed $value
  594 + * @param string $field
  595 + * @param integer $bindType
  596 + * @return string
  597 + */
  598 + protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType)
  599 + {
  600 + if (is_array($value)) {
  601 + throw new Exception('where express error:' . $exp . var_export($value, true));
  602 + }
  603 +
  604 + // 比较运算
  605 + if ($value instanceof \Closure) {
  606 + $value = $this->parseClosure($query, $value);
  607 + }
  608 +
  609 + return $key . ' ' . $exp . ' ' . $value;
  610 + }
  611 +
  612 + /**
  613 + * 时间范围查询
  614 + * @access protected
  615 + * @param Query $query 查询对象
  616 + * @param string $key
  617 + * @param string $exp
  618 + * @param mixed $value
  619 + * @param string $field
  620 + * @param integer $bindType
  621 + * @return string
  622 + */
  623 + protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType)
  624 + {
  625 + if (is_string($value)) {
  626 + $value = explode(',', $value);
  627 + }
  628 +
  629 + return $key . ' ' . substr($exp, 0, -4)
  630 + . $this->parseDateTime($query, $value[0], $field, $bindType)
  631 + . ' AND '
  632 + . $this->parseDateTime($query, $value[1], $field, $bindType);
  633 +
  634 + }
  635 +
  636 + /**
  637 + * IN查询
  638 + * @access protected
  639 + * @param Query $query 查询对象
  640 + * @param string $key
  641 + * @param string $exp
  642 + * @param mixed $value
  643 + * @param string $field
  644 + * @param integer $bindType
  645 + * @return string
  646 + */
  647 + protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType)
  648 + {
  649 + // IN 查询
  650 + if ($value instanceof \Closure) {
  651 + $value = $this->parseClosure($query, $value, false);
  652 + } else {
  653 + $value = array_unique(is_array($value) ? $value : explode(',', $value));
  654 +
  655 + $bind = [];
  656 + $array = [];
  657 +
  658 + foreach ($value as $k => $v) {
  659 + $bind[] = [$v, $bindType];
  660 + $array[] = '?';
  661 + }
  662 +
  663 + $zone = implode(',', $array);
  664 + $query->bind($bind);
  665 +
  666 + $value = empty($zone) ? "''" : $zone;
  667 + }
  668 +
  669 + return $key . ' ' . $exp . ' (' . $value . ')';
  670 + }
  671 +
  672 + /**
  673 + * 闭包子查询
  674 + * @access protected
  675 + * @param Query $query 查询对象
  676 + * @param \Closure $call
  677 + * @param bool $show
  678 + * @return string
  679 + */
  680 + protected function parseClosure(Query $query, $call, $show = true)
  681 + {
  682 + $newQuery = $query->newQuery()->setConnection($this->connection);
  683 + $call($newQuery);
  684 +
  685 + return $newQuery->buildSql($show);
  686 + }
  687 +
  688 + /**
  689 + * 日期时间条件解析
  690 + * @access protected
  691 + * @param Query $query 查询对象
  692 + * @param string $value
  693 + * @param string $key
  694 + * @param integer $bindType
  695 + * @return string
  696 + */
  697 + protected function parseDateTime(Query $query, $value, $key, $bindType = null)
  698 + {
  699 + $options = $query->getOptions();
  700 +
  701 + // 获取时间字段类型
  702 + if (strpos($key, '.')) {
  703 + list($table, $key) = explode('.', $key);
  704 +
  705 + if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
  706 + $table = $pos;
  707 + }
  708 + } else {
  709 + $table = $options['table'];
  710 + }
  711 +
  712 + $type = $this->connection->getTableInfo($table, 'type');
  713 +
  714 + if (isset($type[$key])) {
  715 + $info = $type[$key];
  716 + }
  717 +
  718 + if (isset($info)) {
  719 + if (is_string($value)) {
  720 + $value = strtotime($value) ?: $value;
  721 + }
  722 +
  723 + if (preg_match('/(datetime|timestamp)/is', $info)) {
  724 + // 日期及时间戳类型
  725 + $value = date('Y-m-d H:i:s', $value);
  726 + } elseif (preg_match('/(date)/is', $info)) {
  727 + // 日期及时间戳类型
  728 + $value = date('Y-m-d', $value);
  729 + }
  730 + }
  731 +
  732 + $query->bind($value, $bindType);
  733 +
  734 + return '?';
  735 + }
  736 +
  737 + /**
  738 + * limit分析
  739 + * @access protected
  740 + * @param Query $query 查询对象
  741 + * @param mixed $limit
  742 + * @return string
  743 + */
  744 + protected function parseLimit(Query $query, $limit)
  745 + {
  746 + return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
  747 + }
  748 +
  749 + /**
  750 + * join分析
  751 + * @access protected
  752 + * @param Query $query 查询对象
  753 + * @param array $join
  754 + * @return string
  755 + */
  756 + protected function parseJoin(Query $query, $join)
  757 + {
  758 + $joinStr = '';
  759 +
  760 + if (!empty($join)) {
  761 + foreach ($join as $item) {
  762 + list($table, $type, $on) = $item;
  763 +
  764 + $condition = [];
  765 +
  766 + foreach ((array) $on as $val) {
  767 + if ($val instanceof Expression) {
  768 + $condition[] = $val->getValue();
  769 + } elseif (strpos($val, '=')) {
  770 + list($val1, $val2) = explode('=', $val, 2);
  771 + $condition[] = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2);
  772 + } else {
  773 + $condition[] = $val;
  774 + }
  775 + }
  776 +
  777 + $table = $this->parseTable($query, $table);
  778 +
  779 + $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition);
  780 + }
  781 + }
  782 +
  783 + return $joinStr;
  784 + }
  785 +
  786 + /**
  787 + * order分析
  788 + * @access protected
  789 + * @param Query $query 查询对象
  790 + * @param mixed $order
  791 + * @return string
  792 + */
  793 + protected function parseOrder(Query $query, $order)
  794 + {
  795 + foreach ($order as $key => $val) {
  796 + if ($val instanceof Expression) {
  797 + $array[] = $val->getValue();
  798 + } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) {
  799 + $array[] = $this->parseOrderField($query, $key, $val);
  800 + } elseif ('[rand]' == $val) {
  801 + $array[] = $this->parseRand($query);
  802 + } elseif (is_string($val)) {
  803 + if (is_numeric($key)) {
  804 + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
  805 + } else {
  806 + $sort = $val;
  807 + }
  808 +
  809 + if (preg_match('/^[\w\.]+$/', $key)) {
  810 + $sort = strtoupper($sort);
  811 + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
  812 + $array[] = $this->parseKey($query, $key, true) . $sort;
  813 + } else {
  814 + throw new Exception('order express error:' . $key);
  815 + }
  816 + }
  817 + }
  818 +
  819 + return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
  820 + }
  821 +
  822 + /**
  823 + * orderField分析
  824 + * @access protected
  825 + * @param Query $query 查询对象
  826 + * @param mixed $key
  827 + * @param array $val
  828 + * @return string
  829 + */
  830 + protected function parseOrderField($query, $key, $val)
  831 + {
  832 + if (isset($val['sort'])) {
  833 + $sort = $val['sort'];
  834 + unset($val['sort']);
  835 + } else {
  836 + $sort = '';
  837 + }
  838 +
  839 + $sort = strtoupper($sort);
  840 + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
  841 +
  842 + $options = $query->getOptions();
  843 + $bind = $this->connection->getFieldsBind($options['table']);
  844 +
  845 + foreach ($val as $k => $item) {
  846 + $val[$k] = $this->parseDataBind($query, $key, $item, $bind);
  847 + }
  848 +
  849 + return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort;
  850 + }
  851 +
  852 + /**
  853 + * group分析
  854 + * @access protected
  855 + * @param Query $query 查询对象
  856 + * @param mixed $group
  857 + * @return string
  858 + */
  859 + protected function parseGroup(Query $query, $group)
  860 + {
  861 + return !empty($group) ? ' GROUP BY ' . $this->parseKey($query, $group) : '';
  862 + }
  863 +
  864 + /**
  865 + * having分析
  866 + * @access protected
  867 + * @param Query $query 查询对象
  868 + * @param string $having
  869 + * @return string
  870 + */
  871 + protected function parseHaving(Query $query, $having)
  872 + {
  873 + return !empty($having) ? ' HAVING ' . $having : '';
  874 + }
  875 +
  876 + /**
  877 + * comment分析
  878 + * @access protected
  879 + * @param Query $query 查询对象
  880 + * @param string $comment
  881 + * @return string
  882 + */
  883 + protected function parseComment(Query $query, $comment)
  884 + {
  885 + if (false !== strpos($comment, '*/')) {
  886 + $comment = strstr($comment, '*/', true);
  887 + }
  888 +
  889 + return !empty($comment) ? ' /* ' . $comment . ' */' : '';
  890 + }
  891 +
  892 + /**
  893 + * distinct分析
  894 + * @access protected
  895 + * @param Query $query 查询对象
  896 + * @param mixed $distinct
  897 + * @return string
  898 + */
  899 + protected function parseDistinct(Query $query, $distinct)
  900 + {
  901 + return !empty($distinct) ? ' DISTINCT ' : '';
  902 + }
  903 +
  904 + /**
  905 + * union分析
  906 + * @access protected
  907 + * @param Query $query 查询对象
  908 + * @param mixed $union
  909 + * @return string
  910 + */
  911 + protected function parseUnion(Query $query, $union)
  912 + {
  913 + if (empty($union)) {
  914 + return '';
  915 + }
  916 +
  917 + $type = $union['type'];
  918 + unset($union['type']);
  919 +
  920 + foreach ($union as $u) {
  921 + if ($u instanceof \Closure) {
  922 + $sql[] = $type . ' ' . $this->parseClosure($query, $u);
  923 + } elseif (is_string($u)) {
  924 + $sql[] = $type . ' ( ' . $this->connection->parseSqlTable($u) . ' )';
  925 + }
  926 + }
  927 +
  928 + return ' ' . implode(' ', $sql);
  929 + }
  930 +
  931 + /**
  932 + * index分析,可在操作链中指定需要强制使用的索引
  933 + * @access protected
  934 + * @param Query $query 查询对象
  935 + * @param mixed $index
  936 + * @return string
  937 + */
  938 + protected function parseForce(Query $query, $index)
  939 + {
  940 + if (empty($index)) {
  941 + return '';
  942 + }
  943 +
  944 + if (is_array($index)) {
  945 + $index = join(",", $index);
  946 + }
  947 +
  948 + return sprintf(" FORCE INDEX ( %s ) ", $index);
  949 + }
  950 +
  951 + /**
  952 + * 设置锁机制
  953 + * @access protected
  954 + * @param Query $query 查询对象
  955 + * @param bool|string $lock
  956 + * @return string
  957 + */
  958 + protected function parseLock(Query $query, $lock = false)
  959 + {
  960 + if (is_bool($lock)) {
  961 + return $lock ? ' FOR UPDATE ' : '';
  962 + } elseif (is_string($lock) && !empty($lock)) {
  963 + return ' ' . trim($lock) . ' ';
  964 + }
  965 + }
  966 +
  967 + /**
  968 + * 生成查询SQL
  969 + * @access public
  970 + * @param Query $query 查询对象
  971 + * @return string
  972 + */
  973 + public function select(Query $query)
  974 + {
  975 + $options = $query->getOptions();
  976 +
  977 + return str_replace(
  978 + ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
  979 + [
  980 + $this->parseTable($query, $options['table']),
  981 + $this->parseDistinct($query, $options['distinct']),
  982 + $this->parseField($query, $options['field']),
  983 + $this->parseJoin($query, $options['join']),
  984 + $this->parseWhere($query, $options['where']),
  985 + $this->parseGroup($query, $options['group']),
  986 + $this->parseHaving($query, $options['having']),
  987 + $this->parseOrder($query, $options['order']),
  988 + $this->parseLimit($query, $options['limit']),
  989 + $this->parseUnion($query, $options['union']),
  990 + $this->parseLock($query, $options['lock']),
  991 + $this->parseComment($query, $options['comment']),
  992 + $this->parseForce($query, $options['force']),
  993 + ],
  994 + $this->selectSql);
  995 + }
  996 +
  997 + /**
  998 + * 生成Insert SQL
  999 + * @access public
  1000 + * @param Query $query 查询对象
  1001 + * @param bool $replace 是否replace
  1002 + * @return string
  1003 + */
  1004 + public function insert(Query $query, $replace = false)
  1005 + {
  1006 + $options = $query->getOptions();
  1007 +
  1008 + // 分析并处理数据
  1009 + $data = $this->parseData($query, $options['data']);
  1010 + if (empty($data)) {
  1011 + return '';
  1012 + }
  1013 +
  1014 + $fields = array_keys($data);
  1015 + $values = array_values($data);
  1016 +
  1017 + return str_replace(
  1018 + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
  1019 + [
  1020 + $replace ? 'REPLACE' : 'INSERT',
  1021 + $this->parseTable($query, $options['table']),
  1022 + implode(' , ', $fields),
  1023 + implode(' , ', $values),
  1024 + $this->parseComment($query, $options['comment']),
  1025 + ],
  1026 + $this->insertSql);
  1027 + }
  1028 +
  1029 + /**
  1030 + * 生成insertall SQL
  1031 + * @access public
  1032 + * @param Query $query 查询对象
  1033 + * @param array $dataSet 数据集
  1034 + * @param bool $replace 是否replace
  1035 + * @return string
  1036 + */
  1037 + public function insertAll(Query $query, $dataSet, $replace = false)
  1038 + {
  1039 + $options = $query->getOptions();
  1040 +
  1041 + // 获取合法的字段
  1042 + if ('*' == $options['field']) {
  1043 + $allowFields = $this->connection->getTableFields($options['table']);
  1044 + } else {
  1045 + $allowFields = $options['field'];
  1046 + }
  1047 + // 获取绑定信息
  1048 + $bind = $this->connection->getFieldsBind($options['table']);
  1049 +
  1050 + foreach ($dataSet as $data) {
  1051 + $data = $this->parseData($query, $data, $allowFields, $bind);
  1052 +
  1053 + $values[] = 'SELECT ' . implode(',', array_values($data));
  1054 +
  1055 + if (!isset($insertFields)) {
  1056 + $insertFields = array_keys($data);
  1057 + }
  1058 + }
  1059 +
  1060 + $fields = [];
  1061 +
  1062 + foreach ($insertFields as $field) {
  1063 + $fields[] = $this->parseKey($query, $field);
  1064 + }
  1065 +
  1066 + return str_replace(
  1067 + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
  1068 + [
  1069 + $replace ? 'REPLACE' : 'INSERT',
  1070 + $this->parseTable($query, $options['table']),
  1071 + implode(' , ', $fields),
  1072 + implode(' UNION ALL ', $values),
  1073 + $this->parseComment($query, $options['comment']),
  1074 + ],
  1075 + $this->insertAllSql);
  1076 + }
  1077 +
  1078 + /**
  1079 + * 生成slect insert SQL
  1080 + * @access public
  1081 + * @param Query $query 查询对象
  1082 + * @param array $fields 数据
  1083 + * @param string $table 数据表
  1084 + * @return string
  1085 + */
  1086 + public function selectInsert(Query $query, $fields, $table)
  1087 + {
  1088 + if (is_string($fields)) {
  1089 + $fields = explode(',', $fields);
  1090 + }
  1091 +
  1092 + foreach ($fields as &$field) {
  1093 + $field = $this->parseKey($query, $field, true);
  1094 + }
  1095 +
  1096 + return 'INSERT INTO ' . $this->parseTable($query, $table) . ' (' . implode(',', $fields) . ') ' . $this->select($query);
  1097 + }
  1098 +
  1099 + /**
  1100 + * 生成update SQL
  1101 + * @access public
  1102 + * @param Query $query 查询对象
  1103 + * @return string
  1104 + */
  1105 + public function update(Query $query)
  1106 + {
  1107 + $options = $query->getOptions();
  1108 +
  1109 + $table = $this->parseTable($query, $options['table']);
  1110 + $data = $this->parseData($query, $options['data']);
  1111 +
  1112 + if (empty($data)) {
  1113 + return '';
  1114 + }
  1115 +
  1116 + foreach ($data as $key => $val) {
  1117 + $set[] = $key . ' = ' . $val;
  1118 + }
  1119 +
  1120 + return str_replace(
  1121 + ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
  1122 + [
  1123 + $this->parseTable($query, $options['table']),
  1124 + implode(' , ', $set),
  1125 + $this->parseJoin($query, $options['join']),
  1126 + $this->parseWhere($query, $options['where']),
  1127 + $this->parseOrder($query, $options['order']),
  1128 + $this->parseLimit($query, $options['limit']),
  1129 + $this->parseLock($query, $options['lock']),
  1130 + $this->parseComment($query, $options['comment']),
  1131 + ],
  1132 + $this->updateSql);
  1133 + }
  1134 +
  1135 + /**
  1136 + * 生成delete SQL
  1137 + * @access public
  1138 + * @param Query $query 查询对象
  1139 + * @return string
  1140 + */
  1141 + public function delete(Query $query)
  1142 + {
  1143 + $options = $query->getOptions();
  1144 +
  1145 + return str_replace(
  1146 + ['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
  1147 + [
  1148 + $this->parseTable($query, $options['table']),
  1149 + !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '',
  1150 + $this->parseJoin($query, $options['join']),
  1151 + $this->parseWhere($query, $options['where']),
  1152 + $this->parseOrder($query, $options['order']),
  1153 + $this->parseLimit($query, $options['limit']),
  1154 + $this->parseLock($query, $options['lock']),
  1155 + $this->parseComment($query, $options['comment']),
  1156 + ],
  1157 + $this->deleteSql);
  1158 + }
  1159 +}