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

websocket协议测试

要显示太多修改。

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

不能预览此文件类型
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# think-orm
基于PHP5.6+ 的ORM实现,主要特性:
- 基于ThinkPHP5.1的ORM独立封装
- 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb
- 支持Db类和查询构造器
- 支持事务
- 支持模型和关联
适用于不使用ThinkPHP框架的开发者。
安装
~~~
composer require topthink/think-orm
~~~
Db类用法:
~~~php
use think\Db;
// 数据库配置信息设置(全局有效)
Db::setConfig(['数据库配置参数(数组)']);
// 进行CURD操作
Db::table('user')
->data(['name'=>'thinkphp','email'=>'thinkphp@qq.com'])
->insert();
Db::table('user')->find();
Db::table('user')
->where('id','>',10)
->order('id','desc')
->limit(10)
->select();
Db::table('user')
->where('id',10)
->update(['name'=>'test']);
Db::table('user')
->where('id',10)
->delete();
~~~
Db类增加的(静态)方法包括:
- `setConfig` 设置全局配置信息
- `getConfig` 获取数据库配置信息
- `setQuery` 设置数据库Query类名称
- `setCacheHandler` 设置缓存对象Handler(必须支持get、set及rm方法)
- `getSqlLog` 用于获取当前请求的SQL日志信息(包含连接信息)
其它操作参考TP5.1的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp5_1/353998)章节
定义模型:
~~~php
namespace app\index\model;
use think\Model;
class User extends Model
{
}
~~~
代码调用:
~~~php
use app\index\model\User;
$user = User::get(1);
$user->name = 'thinkphp';
$user->save();
~~~
## Db类和模型对比使用
#### :white_check_mark: 创建Create
* Db用法
```php
Db::table('user')
->insert([
'name' => 'thinkphp',
'email' => 'thinkphp@qq.com',
]);
```
* 模型用法
```php
$user = new User;
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();
```
* 或者批量设置
```php
$user = new User;
$user->save([
'name' => 'thinkphp',
'email' => 'thinkphp@qq.com',
]);
```
#### :white_check_mark: 读取Read
* Db用法
```php
$user = Db::table('user')
->where('id', 1)
->find();
// 或者
$user = Db::table('user')
->find(1);
echo $user['id'];
echo $user['name'];
```
* 模型用法
```php
$user = User::get(1);
echo $user->id;
echo $user->name;
```
* 模型实现读取多个记录
```php
// 查询用户数据集
$users = User::where('id', '>', 1)
->limit(5)
->select();
// 遍历读取用户数据
foreach ($users as $user) {
echo $user->id;
echo $user->name;
}
```
#### :white_check_mark: 更新Update
* Db用法
```php
Db::table('user')
->where('id', 1)
->update([
'name' => 'topthink',
'email' => 'topthink@qq.com',
]);
```
* 模型用法
```php
$user = User::get(1);
$user->name = 'topthink';
$user->email = 'topthink@qq.com';
$user->save();
```
* 或者使用
```php
$user = User::get(1);
$user->save([
'name' => 'topthink',
'email' => 'topthink@qq.com',
]);
```
* 静态调用
```php
User::update([
'name' => 'topthink',
'email' => 'topthink@qq.com',
], ['id' => 1]);
```
#### :white_check_mark: 删除Delete
* Db用法
```php
Db::table('user')->delete(1);
```
* 模型用法
```php
$user = User::get(1);
$user->delete();
```
* 或者静态实现
```php
User::destroy(1);
```
* 静态调用
```php
User::update([
'name' => 'topthink',
'email' => 'topthink@qq.com',
], ['id' => 1]);
```
* destroy方法支持删除指定主键或者查询条件的数据
```php
// 根据主键删除多个数据
User::destroy([1, 2, 3]);
// 指定条件删除数据
User::destroy([
'status' => 0,
]);
// 使用闭包条件
User::destroy(function ($query) {
$query->where('id', '>', 0)
->where('status', 0);
});
```
更多模型用法可以参考5.1完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp5_1/354041)章节
{
"name": "topthink/think-orm",
"description": "think orm",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
}
],
"require": {
"php": ">=5.6.0"
},
"autoload": {
"psr-4": {
"think\\": "src"
},
"files": [
"src/config.php"
]
}
}
\ No newline at end of file
<?php
namespace think;
/**
* 数据库缓存接口
* Interface CacheInterface
* @author : evalor <master@evalor.cn>
* @package think
*/
interface CacheInterface
{
function get($name, $default = false);
function set($name, $value, $expire = null);
function rm($name);
}
\ No newline at end of file
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
protected $items = [];
public function __construct($items = [])
{
$this->items = $this->convertToArray($items);
}
public static function make($items = [])
{
return new static($items);
}
/**
* 是否为空
* @return bool
*/
public function isEmpty()
{
return empty($this->items);
}
public function toArray()
{
return array_map(function ($value) {
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
}, $this->items);
}
public function all()
{
return $this->items;
}
/**
* 合并数组
*
* @param mixed $items
* @return static
*/
public function merge($items)
{
return new static(array_merge($this->items, $this->convertToArray($items)));
}
/**
* 交换数组中的键和值
*
* @return static
*/
public function flip()
{
return new static(array_flip($this->items));
}
/**
* 按指定键整理数据
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 键名
* @return array
*/
public function dictionary($items = null, &$indexKey = null)
{
if ($items instanceof self || $items instanceof Paginator) {
$items = $items->all();
}
$items = is_null($items) ? $this->items : $items;
if ($items && empty($indexKey)) {
$indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
}
if (isset($indexKey) && is_string($indexKey)) {
return array_column($items, null, $indexKey);
}
return $items;
}
/**
* 比较数组,返回差集
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function diff($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$diff = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (!isset($dictionary[$item[$indexKey]])) {
$diff[] = $item;
}
}
}
return new static($diff);
}
/**
* 比较数组,返回交集
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function intersect($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$intersect = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (isset($dictionary[$item[$indexKey]])) {
$intersect[] = $item;
}
}
}
return new static($intersect);
}
/**
* 返回数组中所有的键名
*
* @access public
* @return array
*/
public function keys()
{
$current = current($this->items);
if (is_scalar($current)) {
$array = $this->items;
} elseif (is_array($current)) {
$array = $current;
} else {
$array = $current->toArray();
}
return array_keys($array);
}
/**
* 删除数组的最后一个元素(出栈)
*
* @return mixed
*/
public function pop()
{
return array_pop($this->items);
}
/**
* 通过使用用户自定义函数,以字符串返回数组
*
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->items, $callback, $initial);
}
/**
* 以相反的顺序返回数组。
*
* @return static
*/
public function reverse()
{
return new static(array_reverse($this->items));
}
/**
* 删除数组中首个元素,并返回被删除元素的值
*
* @return mixed
*/
public function shift()
{
return array_shift($this->items);
}
/**
* 在数组结尾插入一个元素
* @param mixed $value
* @param mixed $key
* @return void
*/
public function push($value, $key = null)
{
if (is_null($key)) {
$this->items[] = $value;
} else {
$this->items[$key] = $value;
}
}
/**
* 把一个数组分割为新的数组块.
*
* @param int $size
* @param bool $preserveKeys
* @return static
*/
public function chunk($size, $preserveKeys = false)
{
$chunks = [];
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
$chunks[] = new static($chunk);
}
return new static($chunks);
}
/**
* 在数组开头插入一个元素
* @param mixed $value
* @param mixed $key
* @return void
*/
public function unshift($value, $key = null)
{
if (is_null($key)) {
array_unshift($this->items, $value);
} else {
$this->items = [$key => $value] + $this->items;
}
}
/**
* 给每个元素执行个回调
*
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
$result = $callback($item, $key);
if (false === $result) {
break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
}
}
return $this;
}
/**
* 用回调函数过滤数组中的元素
* @param callable|null $callback
* @return static
*/
public function filter(callable $callback = null)
{
if ($callback) {
return new static(array_filter($this->items, $callback));
}
return new static(array_filter($this->items));
}
/**
* 根据字段条件过滤数组中的元素
* @access public
* @param string $field 字段名
* @param mixed $operator 操作符
* @param mixed $value 数据
* @return static
*/
public function where($field, $operator, $value = null)
{
if (is_null($value)) {
$value = $operator;
$operator = '=';
}
return $this->filter(function ($data) use ($field, $operator, $value) {
if (strpos($field, '.')) {
list($field, $relation) = explode('.', $field);
$result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
} else {
$result = isset($data[$field]) ? $data[$field] : null;
}
switch ($operator) {
case '===':
return $result === $value;
case '!==':
return $result !== $value;
case '!=':
case '<>':
return $result != $value;
case '>':
return $result > $value;
case '>=':
return $result >= $value;
case '<':
return $result < $value;
case '<=':
return $result <= $value;
case 'like':
return is_string($result) && false !== strpos($result, $value);
case 'not like':
return is_string($result) && false === strpos($result, $value);
case 'in':
return is_scalar($result) && in_array($result, $value, true);
case 'not in':
return is_scalar($result) && !in_array($result, $value, true);
case 'between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result >= $min && $result <= $max;
case 'not between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result > $max || $result < $min;
case '==':
case '=':
default:
return $result == $value;
}
});
}
/**
* 返回数组中指定的一列
* @param mixed $column_key
* @param mixed $index_key
* @return array
*/
public function column($column_key, $index_key = null)
{
return array_column($this->items, $column_key, $index_key);
}
/**
* 对数组排序
*
* @access public
* @param callable|null $callback
* @return static
*/
public function sort(callable $callback = null)
{
$items = $this->items;
$callback = $callback ?: function ($a, $b) {
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
};
uasort($items, $callback);
return new static($items);
}
/**
* 指定字段排序
* @access public
* @param string $field 排序字段
* @param string $order 排序
* @param bool $intSort 是否为数字排序
* @return $this
*/
public function order($field, $order = null, $intSort = true)
{
return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
$fieldA = isset($a[$field]) ? $a[$field] : null;
$fieldB = isset($b[$field]) ? $b[$field] : null;
if ($intSort) {
return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
} else {
return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
}
});
}
/**
* 将数组打乱
*
* @return static
*/
public function shuffle()
{
$items = $this->items;
shuffle($items);
return new static($items);
}
/**
* 截取数组
*
* @param int $offset
* @param int $length
* @param bool $preserveKeys
* @return static
*/
public function slice($offset, $length = null, $preserveKeys = false)
{
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
}
// ArrayAccess
public function offsetExists($offset)
{
return array_key_exists($offset, $this->items);
}
public function offsetGet($offset)
{
return $this->items[$offset];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
//Countable
public function count()
{
return count($this->items);
}
//IteratorAggregate
public function getIterator()
{
return new ArrayIterator($this->items);
}
//JsonSerializable
public function jsonSerialize()
{
return $this->toArray();
}
/**
* 转换当前数据集为JSON字符串
* @access public
* @param integer $options json参数
* @return string
*/
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
public function __toString()
{
return $this->toJson();
}
/**
* 转换成数组
*
* @param mixed $items
* @return array
*/
protected function convertToArray($items)
{
if ($items instanceof self) {
return $items->all();
}
return (array) $items;
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\db\Query;
/**
* Class Db
* @package think
* @method Query table(string $table) static 指定数据表(含前缀)
* @method Query name(string $name) static 指定数据表(不含前缀)
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
* @method Query union(mixed $union, boolean $all = false) static UNION查询
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
* @method Query order(mixed $field, string $order = null) static 查询ORDER
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
* @method mixed value(string $field) static 获取某个字段的值
* @method array column(string $field, string $key = '') static 获取某个列的值
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
* @method mixed find(mixed $data = null) static 查询单个记录
* @method mixed select(mixed $data = null) static 查询多个记录
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
* @method integer insertAll(array $dataSet) static 插入多条记录
* @method integer update(array $data) static 更新记录
* @method integer delete(mixed $data = null) static 删除记录
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
* @method \Generator cursor(mixed $data = null) static 使用游标查找记录
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
* @method mixed transaction(callable $callback) static 执行数据库事务
* @method void startTrans() static 启动事务
* @method void commit() static 用于非自动提交状态下面的查询提交
* @method void rollback() static 事务回滚
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
* @method string getLastInsID($sequence = null) static 获取最近插入的ID
*/
class Db
{
/**
* 数据库配置
* @var array
*/
protected static $config = [];
/**
* 查询类名
* @var string
*/
protected static $query;
/**
* 查询类自动映射
* @var array
*/
protected static $queryMap = [
'mongo' => '\\think\\db\Mongo',
];
/**
* 查询次数
* @var integer
*/
public static $queryTimes = 0;
/**
* 执行次数
* @var integer
*/
public static $executeTimes = 0;
/**
* 缓存对象
* @var object
*/
protected static $cacheHandler;
public static function setConfig($config = [])
{
self::$config = array_merge(self::$config, $config);
}
public static function getConfig($name = null)
{
if ($name) {
return isset(self::$config[$name]) ? self::$config[$name] : null;
} else {
return self::$config;
}
}
public static function setQuery($query)
{
self::$query = $query;
}
/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
public static function parseName($name, $type = 0, $ucfirst = true)
{
if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name);
} else {
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
public static function setCacheHandler($cacheHandler)
{
self::$cacheHandler = $cacheHandler;
}
public static function getCacheHandler()
{
return self::$cacheHandler;
}
public static function __callStatic($method, $args)
{
if (!self::$query) {
$type = strtolower(self::getConfig('type'));
$class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query';
self::$query = $class;
}
$class = self::$query;
return call_user_func_array([new $class, $method], $args);
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
// +----------------------------------------------------------------------
namespace think;
class Exception extends \Exception
{
/**
* 保存异常页面显示的额外Debug数据
* @var array
*/
protected $data = [];
/**
* 设置异常额外的Debug数据
* 数据将会显示为下面的格式
*
* Exception Data
* --------------------------------------------------
* Label 1
* key1 value1
* key2 value2
* Label 2
* key1 value1
* key2 value2
*
* @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组
*/
final protected function setData($label, array $data)
{
$this->data[$label] = $data;
}
/**
* 获取异常额外Debug数据
* 主要用于输出到异常页面便于调试
* @return array 由setData设置的Debug数据
*/
final public function getData()
{
return $this->data;
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\db\Query;
/**
* Class Model
* @package think
* @mixin Query
* @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器
*/
abstract class Model implements \JsonSerializable, \ArrayAccess
{
use model\concern\Attribute;
use model\concern\RelationShip;
use model\concern\ModelEvent;
use model\concern\TimeStamp;
use model\concern\Conversion;
/**
* 数据是否存在
* @var bool
*/
private $exists = false;
/**
* 是否强制更新所有数据
* @var bool
*/
private $force = false;
/**
* 更新条件
* @var array
*/
private $updateWhere;
/**
* 数据库配置信息
* @var array|string
*/
protected $connection = [];
/**
* 数据库查询对象类名
* @var string
*/
protected $query;
/**
* 模型名称
* @var string
*/
protected $name;
/**
* 数据表名称
* @var string
*/
protected $table;
/**
* 写入自动完成定义
* @var array
*/
protected $auto = [];
/**
* 新增自动完成定义
* @var array
*/
protected $insert = [];
/**
* 更新自动完成定义
* @var array
*/
protected $update = [];
/**
* 初始化过的模型.
* @var array
*/
protected static $initialized = [];
/**
* 是否从主库读取(主从分布式有效)
* @var array
*/
protected static $readMaster;
/**
* 查询对象实例
* @var Query
*/
protected $queryInstance;
/**
* 错误信息
* @var mixed
*/
protected $error;
/**
* 软删除字段默认值
* @var mixed
*/
protected $defaultSoftDelete;
/**
* 全局查询范围
* @var array
*/
protected $globalScope = [];
/**
* 架构函数
* @access public
* @param array|object $data 数据
*/
public function __construct($data = [])
{
if (is_object($data)) {
$this->data = get_object_vars($data);
} else {
$this->data = $data;
}
if ($this->disuse) {
// 废弃字段
foreach ((array) $this->disuse as $key) {
if (array_key_exists($key, $this->data)) {
unset($this->data[$key]);
}
}
}
// 记录原始数据
$this->origin = $this->data;
$config = Db::getConfig();
if (empty($this->name)) {
// 当前模型名
$name = str_replace('\\', '/', static::class);
$this->name = basename($name);
if (!empty($config['class_suffix'])) {
$suffix = basename(dirname($name));
$this->name = substr($this->name, 0, -strlen($suffix));
}
}
if (is_null($this->autoWriteTimestamp)) {
// 自动写入时间戳
$this->autoWriteTimestamp = $config['auto_timestamp'];
}
if (is_null($this->dateFormat)) {
// 设置时间戳格式
$this->dateFormat = $config['datetime_format'];
}
if (is_null($this->resultSetType)) {
$this->resultSetType = $config['resultset_type'];
}
if (is_null($this->query)) {
// 设置查询对象
$this->query = $config['query'];
}
if (!empty($this->connection) && is_array($this->connection)) {
// 设置模型的数据库连接
$this->connection = array_merge($config, $this->connection);
}
if ($this->observerClass) {
// 注册模型观察者
static::observe($this->observerClass);
}
// 执行初始化操作
$this->initialize();
}
/**
* 是否从主库读取数据(主从分布有效)
* @access public
* @param bool $all 是否所有模型有效
* @return $this
*/
public function readMaster($all = false)
{
$model = $all ? '*' : static::class;
static::$readMaster[$model] = true;
return $this;
}
/**
* 创建新的模型实例
* @access public
* @param array|object $data 数据
* @param bool $isUpdate 是否为更新
* @param mixed $where 更新条件
* @return Model
*/
public function newInstance($data = [], $isUpdate = false, $where = null)
{
return (new static($data))->isUpdate($isUpdate, $where);
}
/**
* 创建模型的查询对象
* @access protected
* @return Query
*/
protected function buildQuery()
{
// 设置当前模型 确保查询返回模型对象
$class = $this->query;
$query = (new $class())->connect($this->connection)
->model($this)
->json($this->json, $this->jsonAssoc)
->setJsonFieldType($this->jsonType);
if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) {
$query->master(true);
}
// 设置当前数据表和模型名
if (!empty($this->table)) {
$query->table($this->table);
} else {
$query->name($this->name);
}
if (!empty($this->pk)) {
$query->pk($this->pk);
}
return $query;
}
/**
* 获取当前模型的数据库查询对象
* @access public
* @param Query $query 查询对象实例
* @return $this
*/
public function setQuery($query)
{
$this->queryInstance = $query;
return $this;
}
/**
* 获取当前模型的数据库查询对象
* @access public
* @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称)
* @return Query
*/
public function db($useBaseQuery = true)
{
if ($this->queryInstance) {
return $this->queryInstance;
}
$query = $this->buildQuery();
// 软删除
if (property_exists($this, 'withTrashed') && !$this->withTrashed) {
$this->withNoTrashed($query);
}
// 全局作用域
if (true === $useBaseQuery && method_exists($this, 'base')) {
call_user_func_array([$this, 'base'], [ & $query]);
}
$globalScope = is_array($useBaseQuery) && $useBaseQuery ?: $this->globalScope;
if ($globalScope) {
$query->scope($globalScope);
}
// 返回当前模型的数据库查询对象
return $query;
}
/**
* 初始化模型
* @access protected
* @return void
*/
protected function initialize()
{
if (!isset(static::$initialized[static::class])) {
static::$initialized[static::class] = true;
static::init();
}
}
/**
* 初始化处理
* @access protected
* @return void
*/
protected static function init()
{}
/**
* 更新是否强制写入数据 而不做比较
* @access public
* @param bool $force
* @return $this
*/
public function force($force = true)
{
$this->force = $force;
return $this;
}
/**
* 判断force
* @access public
* @return bool
*/
public function isForce()
{
return $this->force;
}
/**
* 设置数据是否存在
* @access public
* @param bool $exists
* @return $this
*/
public function exists($exists)
{
$this->exists = $exists;
return $this;
}
/**
* 判断数据是否存在数据库
* @access public
* @return bool
*/
public function isExists()
{
return $this->exists;
}
/**
* 数据自动完成
* @access protected
* @param array $auto 要自动更新的字段列表
* @return void
*/
protected function autoCompleteData($auto = [])
{
foreach ($auto as $field => $value) {
if (is_integer($field)) {
$field = $value;
$value = null;
}
if (!isset($this->data[$field])) {
$default = null;
} else {
$default = $this->data[$field];
}
$this->setAttr($field, !is_null($value) ? $value : $default);
}
}
/**
* 保存当前数据对象
* @access public
* @param array $data 数据
* @param array $where 更新条件
* @param string $sequence 自增序列名
* @return false
*/
public function save($data = [], $where = [], $sequence = null)
{
if (is_string($data)) {
$sequence = $data;
$data = [];
}
if (!$this->checkBeforeSave($data, $where)) {
return false;
}
$result = $this->exists ? $this->updateData($where) : $this->insertData($sequence);
if (false === $result) {
return false;
}
// 写入回调
$this->trigger('after_write');
// 重新记录原始数据
$this->origin = $this->data;
$this->set = [];
return true;
}
/**
* 解析查询条件
* @access protected
* @param array|null $where 保存条件
* @return array|null
*/
protected static function parseWhere($where)
{
if (is_array($where) && key($where) !== 0) {
$item = [];
foreach ($where as $key => $val) {
$item[] = [$key, '=', $val];
}
return $item;
}
return $where;
}
/**
* 写入之前检查数据
* @access protected
* @param array $data 数据
* @param array $where 保存条件
* @return bool
*/
protected function checkBeforeSave($data, $where)
{
if (!empty($data)) {
// 数据对象赋值
foreach ($data as $key => $value) {
$this->setAttr($key, $value, $data);
}
if (!empty($where)) {
$this->exists = true;
$this->updateWhere = self::parseWhere($where);
}
}
// 数据自动完成
$this->autoCompleteData($this->auto);
// 事件回调
if (false === $this->trigger('before_write')) {
return false;
}
return true;
}
/**
* 检查数据是否允许写入
* @access protected
* @param array $autoFields 自动完成的字段列表
* @return array
*/
protected function checkAllowFields($append = [])
{
// 检测字段
if (empty($this->field) || true === $this->field) {
$query = $this->db(false);
$table = $this->table ?: $query->getTable();
$this->field = $query->getConnection()->getTableFields($table);
$field = $this->field;
} else {
$field = array_merge($this->field, $append);
if ($this->autoWriteTimestamp) {
array_push($field, $this->createTime, $this->updateTime);
}
}
if ($this->disuse) {
// 废弃字段
$field = array_diff($field, (array) $this->disuse);
}
return $field;
}
/**
* 保存写入数据
* @access protected
* @param array $where 保存条件
* @return int|false
*/
protected function updateData($where)
{
// 自动更新
$this->autoCompleteData($this->update);
// 事件回调
if (false === $this->trigger('before_update')) {
return false;
}
// 获取有更新的数据
$data = $this->getChangedData();
if (empty($data)) {
// 关联更新
if (isset($this->relationWrite)) {
$this->autoRelationUpdate();
}
return 0;
} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
// 自动写入更新时间
$data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
$this->data[$this->updateTime] = $data[$this->updateTime];
}
if (empty($where) && !empty($this->updateWhere)) {
$where = $this->updateWhere;
}
// 检查允许字段
$allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update));
// 保留主键数据
foreach ($this->data as $key => $val) {
if ($this->isPk($key)) {
$data[$key] = $val;
}
}
$pk = $this->getPk();
foreach ((array) $pk as $key) {
if (isset($data[$key])) {
$array[] = [$key, '=', $data[$key]];
unset($data[$key]);
}
}
if (!empty($array)) {
$where = $array;
}
if ($this->relationWrite) {
foreach ($this->relationWrite as $name => $val) {
if (is_array($val)) {
foreach ($val as $key) {
if (isset($data[$key])) {
unset($data[$key]);
}
}
}
}
}
$db = $this->db(false);
$db->startTrans();
try {
// 模型更新
$result = $db->where($where)
->strict(false)
->field($allowFields)
->update($data);
// 关联更新
if (isset($this->relationWrite)) {
$this->autoRelationUpdate();
}
$db->commit();
// 更新回调
$this->trigger('after_update');
return $result;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
* 新增写入数据
* @access protected
* @param string $sequence 自增名
* @return int|false
*/
protected function insertData($sequence)
{
// 自动写入
$this->autoCompleteData($this->insert);
// 时间戳自动写入
$this->checkTimeStampWrite();
if (false === $this->trigger('before_insert')) {
return false;
}
// 检查允许字段
$allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert));
$db = $this->db(false);
$db->startTrans();
try {
$result = $db->strict(false)
->field($allowFields)
->insert($this->data);
// 获取自动增长主键
if ($result && $insertId = $db->getLastInsID($sequence)) {
$pk = $this->getPk();
foreach ((array) $pk as $key) {
if (!isset($this->data[$key]) || '' == $this->data[$key]) {
$this->data[$key] = $insertId;
}
}
}
// 关联写入
if (isset($this->relationWrite)) {
$this->autoRelationInsert();
}
$db->commit();
// 标记为更新
$this->exists = true;
// 新增回调
$this->trigger('after_insert');
return $result;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
* 字段值(延迟)增长
* @access public
* @param string $field 字段名
* @param integer $step 增长值
* @param integer $lazyTime 延时时间(s)
* @return integer|true
* @throws Exception
*/
public function setInc($field, $step = 1, $lazyTime = 0)
{
// 读取更新条件
$where = $this->getWhere();
$result = $this->db(false)->where($where)->setInc($field, $step, $lazyTime);
if (true !== $result) {
$this->data[$field] += $step;
}
return $result;
}
/**
* 字段值(延迟)增长
* @access public
* @param string $field 字段名
* @param integer $step 增长值
* @param integer $lazyTime 延时时间(s)
* @return integer|true
* @throws Exception
*/
public function setDec($field, $step = 1, $lazyTime = 0)
{
// 读取更新条件
$where = $this->getWhere();
$result = $this->db(false)->where($where)->setDec($field, $step, $lazyTime);
if (true !== $result) {
$this->data[$field] -= $step;
}
return $result;
}
/**
* 获取当前的更新条件
* @access protected
* @return mixed
*/
protected function getWhere()
{
// 删除条件
$pk = $this->getPk();
if (is_string($pk) && isset($this->data[$pk])) {
$where[] = [$pk, '=', $this->data[$pk]];
} elseif (!empty($this->updateWhere)) {
$where = $this->updateWhere;
} else {
$where = null;
}
return $where;
}
/**
* 保存多个数据到当前数据对象
* @access public
* @param array $dataSet 数据
* @param boolean $replace 是否自动识别更新和写入
* @return Collection|false
* @throws \Exception
*/
public function saveAll($dataSet, $replace = true)
{
$result = [];
$db = $this->db(false);
$db->startTrans();
try {
$pk = $this->getPk();
if (is_string($pk) && $replace) {
$auto = true;
}
foreach ($dataSet as $key => $data) {
if (!empty($auto) && isset($data[$pk])) {
$result[$key] = self::update($data, [], $this->field);
} else {
$result[$key] = self::create($data, $this->field);
}
}
$db->commit();
return $this->toCollection($result);
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
* 是否为更新数据
* @access public
* @param mixed $update
* @param mixed $where
* @return $this
*/
public function isUpdate($update = true, $where = null)
{
if (is_bool($update)) {
$this->exists = $update;
if (!empty($where)) {
$this->updateWhere = $where;
}
} else {
$this->exists = true;
$this->updateWhere = $update;
}
return $this;
}
/**
* 删除当前的记录
* @access public
* @return bool
*/
public function delete()
{
if (!$this->exists || false === $this->trigger('before_delete')) {
return false;
}
// 读取更新条件
$where = $this->getWhere();
$db = $this->db(false);
$db->startTrans();
try {
// 删除当前模型数据
$db->where($where)->delete();
// 关联删除
if (!empty($this->relationWrite)) {
$this->autoRelationDelete();
}
$db->commit();
$this->trigger('after_delete');
$this->exists = false;
return true;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
* 设置自动完成的字段( 规则通过修改器定义)
* @access public
* @param array $fields 需要自动完成的字段
* @return $this
*/
public function auto($fields)
{
$this->auto = $fields;
return $this;
}
/**
* 写入数据
* @access public
* @param array $data 数据数组
* @param array|true $field 允许字段
* @return $this
*/
public static function create($data = [], $field = null)
{
$model = new static();
if (!empty($field)) {
$model->allowField($field);
}
$model->isUpdate(false)->save($data, []);
return $model;
}
/**
* 更新数据
* @access public
* @param array $data 数据数组
* @param array $where 更新条件
* @param array|true $field 允许字段
* @return $this
*/
public static function update($data = [], $where = [], $field = null)
{
$model = new static();
if (!empty($field)) {
$model->allowField($field);
}
$result = $model->isUpdate(true)->save($data, $where);
return $model;
}
/**
* 删除记录
* @access public
* @param mixed $data 主键列表 支持闭包查询条件
* @return bool
*/
public static function destroy($data)
{
$model = new static();
$query = $model->db();
if (empty($data) && 0 !== $data) {
return false;
} elseif (is_array($data) && key($data) !== 0) {
$query->where(self::parseWhere($data));
$data = null;
} elseif ($data instanceof \Closure) {
$data($query);
$data = null;
}
$resultSet = $query->select($data);
if ($resultSet) {
foreach ($resultSet as $data) {
$data->delete();
}
}
return true;
}
/**
* 获取错误信息
* @access public
* @return mixed
*/
public function getError()
{
return $this->error;
}
/**
* 解序列化后处理
*/
public function __wakeup()
{
$this->initialize();
}
public function __debugInfo()
{
return [
'data' => $this->data,
'relation' => $this->relation,
];
}
/**
* 修改器 设置数据对象的值
* @access public
* @param string $name 名称
* @param mixed $value 值
* @return void
*/
public function __set($name, $value)
{
$this->setAttr($name, $value);
}
/**
* 获取器 获取数据对象的值
* @access public
* @param string $name 名称
* @return mixed
*/
public function __get($name)
{
return $this->getAttr($name);
}
/**
* 检测数据对象的值
* @access public
* @param string $name 名称
* @return boolean
*/
public function __isset($name)
{
try {
return !is_null($this->getAttr($name));
} catch (InvalidArgumentException $e) {
return false;
}
}
/**
* 销毁数据对象的值
* @access public
* @param string $name 名称
* @return void
*/
public function __unset($name)
{
unset($this->data[$name], $this->relation[$name]);
}
// ArrayAccess
public function offsetSet($name, $value)
{
$this->setAttr($name, $value);
}
public function offsetExists($name)
{
return $this->__isset($name);
}
public function offsetUnset($name)
{
$this->__unset($name);
}
public function offsetGet($name)
{
return $this->getAttr($name);
}
/**
* 设置是否使用全局查询范围
* @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称)
* @access public
* @return Query
*/
public static function useGlobalScope($use)
{
$model = new static();
return $model->db($use);
}
public function __call($method, $args)
{
if ('withattr' == strtolower($method)) {
return call_user_func_array([$this, 'withAttribute'], $args);
}
return call_user_func_array([$this->db(), $method], $args);
}
public static function __callStatic($method, $args)
{
$model = new static();
return call_user_func_array([$model->db(), $method], $args);
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;
use Traversable;
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
/** @var bool 是否为简洁模式 */
protected $simple = false;
/** @var Collection 数据集 */
protected $items;
/** @var integer 当前页 */
protected $currentPage;
/** @var integer 最后一页 */
protected $lastPage;
/** @var integer|null 数据总数 */
protected $total;
/** @var integer 每页的数量 */
protected $listRows;
/** @var bool 是否有下一页 */
protected $hasMore;
/** @var array 一些配置 */
protected $options = [
'var_page' => 'page',
'path' => '/',
'query' => [],
'fragment' => '',
];
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
{
$this->options = array_merge($this->options, $options);
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
$this->simple = $simple;
$this->listRows = $listRows;
if (!$items instanceof Collection) {
$items = Collection::make($items);
}
if ($simple) {
$this->currentPage = $this->setCurrentPage($currentPage);
$this->hasMore = count($items) > ($this->listRows);
$items = $items->slice(0, $this->listRows);
} else {
$this->total = $total;
$this->lastPage = (int) ceil($total / $listRows);
$this->currentPage = $this->setCurrentPage($currentPage);
$this->hasMore = $this->currentPage < $this->lastPage;
}
$this->items = $items;
}
/**
* @param $items
* @param $listRows
* @param null $currentPage
* @param bool $simple
* @param null $total
* @param array $options
* @return Paginator
*/
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
{
return new static($items, $listRows, $currentPage, $total, $simple, $options);
}
protected function setCurrentPage($currentPage)
{
if (!$this->simple && $currentPage > $this->lastPage) {
return $this->lastPage > 0 ? $this->lastPage : 1;
}
return $currentPage;
}
/**
* 获取页码对应的链接
*
* @param $page
* @return string
*/
protected function url($page)
{
if ($page <= 0) {
$page = 1;
}
if (strpos($this->options['path'], '[PAGE]') === false) {
$parameters = [$this->options['var_page'] => $page];
$path = $this->options['path'];
} else {
$parameters = [];
$path = str_replace('[PAGE]', $page, $this->options['path']);
}
if (count($this->options['query']) > 0) {
$parameters = array_merge($this->options['query'], $parameters);
}
$url = $path;
if (!empty($parameters)) {
$url .= '?' . urldecode(http_build_query($parameters, null, '&'));
}
return $url . $this->buildFragment();
}
/**
* 自动获取当前页码
* @param string $varPage
* @param int $default
* @return int
*/
public static function getCurrentPage($varPage = 'page', $default = 1)
{
$page = isset($_REQUEST[$varPage]) ? $_REQUEST[$varPage] : 1;
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
return $page;
}
return $default;
}
/**
* 自动获取当前的path
* @return string
*/
public static function getCurrentPath()
{
if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
$url = $_SERVER['HTTP_X_REWRITE_URL'];
} elseif (isset($_SERVER['REQUEST_URI'])) {
$url = $_SERVER['REQUEST_URI'];
} elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
$url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
} else {
$url = '';
}
return strpos($url, '?') ? strstr($url, '?', true) : $url;
}
public function total()
{
if ($this->simple) {
throw new \DomainException('not support total');
}
return $this->total;
}
public function listRows()
{
return $this->listRows;
}
public function currentPage()
{
return $this->currentPage;
}
public function lastPage()
{
if ($this->simple) {
throw new \DomainException('not support last');
}
return $this->lastPage;
}
/**
* 数据是否足够分页
* @return boolean
*/
public function hasPages()
{
return !(1 == $this->currentPage && !$this->hasMore);
}
/**
* 创建一组分页链接
*
* @param int $start
* @param int $end
* @return array
*/
public function getUrlRange($start, $end)
{
$urls = [];
for ($page = $start; $page <= $end; $page++) {
$urls[$page] = $this->url($page);
}
return $urls;
}
/**
* 设置URL锚点
*
* @param string|null $fragment
* @return $this
*/
public function fragment($fragment)
{
$this->options['fragment'] = $fragment;
return $this;
}
/**
* 添加URL参数
*
* @param array|string $key
* @param string|null $value
* @return $this
*/
public function appends($key, $value = null)
{
if (!is_array($key)) {
$queries = [$key => $value];
} else {
$queries = $key;
}
foreach ($queries as $k => $v) {
if ($k !== $this->options['var_page']) {
$this->options['query'][$k] = $v;
}
}
return $this;
}
/**
* 构造锚点字符串
*
* @return string
*/
protected function buildFragment()
{
return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
}
/**
* 渲染分页html
* @return mixed
*/
abstract public function render();
public function items()
{
return $this->items->all();
}
public function getCollection()
{
return $this->items;
}
public function isEmpty()
{
return $this->items->isEmpty();
}
/**
* 给每个元素执行个回调
*
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
$result = $callback($item, $key);
if (false === $result) {
break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
}
}
return $this;
}
/**
* Retrieve an external iterator
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
*/
public function getIterator()
{
return new ArrayIterator($this->items->all());
}
/**
* Whether a offset exists
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return $this->items->offsetExists($offset);
}
/**
* Offset to retrieve
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->items->offsetGet($offset);
}
/**
* Offset to set
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->items->offsetSet($offset, $value);
}
/**
* Offset to unset
* @param mixed $offset
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
$this->items->offsetUnset($offset);
}
/**
* Count elements of an object
*/
public function count()
{
return $this->items->count();
}
public function __toString()
{
return (string) $this->render();
}
public function toArray()
{
try {
$total = $this->total();
} catch (\DomainException $e) {
$total = null;
}
return [
'total' => $total,
'per_page' => $this->listRows(),
'current_page' => $this->currentPage(),
'last_page' => $this->lastPage,
'data' => $this->items->toArray(),
];
}
/**
* Specify data which should be serialized to JSON
*/
public function jsonSerialize()
{
return $this->toArray();
}
public function __call($name, $arguments)
{
return call_user_func_array([$this->getCollection(), $name], $arguments);
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
Db::setConfig([
'type' => 'mysql',
// 服务器地址
'hostname' => 'rm-m5eblhc9o3515i25n.mysql.rds.aliyuncs.com',
// 数据库名
'database' => 'fupai',
// 用户名
'username' => 'db136s1ehvo1yn73',
// 密码
'password' => 'cxz307311SJK',
// 端口
'hostport' => '3306',
// 数据库编码默认采用utf8
'charset' => 'utf8mb4',
// 数据库表前缀
'prefix' => 'fp_',
// 数据库调试模式
'debug' => false,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型
'resultset_type' => '',
// 自动写入时间戳字段
'auto_timestamp' => false,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要进行SQL性能分析
'sql_explain' => false,
// Builder类
'builder' => '',
// Query类
'query' => '\\think\\db\\Query',
// 是否需要断线重连
'break_reconnect' => false,
// 默认分页设置
'paginate' => [
'type' => 'bootstrap',
'var_page' => 'page',
'list_rows' => 15,
]
]);
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db;
use PDO;
use think\Exception;
abstract class Builder
{
// connection对象实例
protected $connection;
// 查询表达式映射
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'];
// 查询表达式解析
protected $parser = [
'parseCompare' => ['=', '<>', '>', '>=', '<', '<='],
'parseLike' => ['LIKE', 'NOT LIKE'],
'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],
'parseIn' => ['NOT IN', 'IN'],
'parseExp' => ['EXP'],
'parseNull' => ['NOT NULL', 'NULL'],
'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
'parseExists' => ['NOT EXISTS', 'EXISTS'],
'parseColumn' => ['COLUMN'],
];
// SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 架构函数
* @access public
* @param Connection $connection 数据库连接对象实例
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* 获取当前的连接对象实例
* @access public
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* 注册查询表达式解析
* @access public
* @param string $name 解析方法
* @param array $parser 匹配表达式数据
* @return $this
*/
public function bindParser($name, $parser)
{
$this->parser[$name] = $parser;
return $this;
}
/**
* 数据分析
* @access protected
* @param Query $query 查询对象
* @param array $data 数据
* @param array $fields 字段信息
* @param array $bind 参数绑定
* @return array
*/
protected function parseData(Query $query, $data = [], $fields = [], $bind = [])
{
if (empty($data)) {
return [];
}
$options = $query->getOptions();
// 获取绑定信息
if (empty($bind)) {
$bind = $this->connection->getFieldsBind($options['table']);
}
if (empty($fields)) {
if ('*' == $options['field']) {
$fields = array_keys($bind);
} else {
$fields = $options['field'];
}
}
$result = [];
foreach ($data as $key => $val) {
if ('*' != $options['field'] && !in_array($key, $fields, true)) {
continue;
}
$item = $this->parseKey($query, $key, true);
if ($val instanceof Expression) {
$result[$item] = $val->getValue();
continue;
} elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) {
$val = json_encode($val, JSON_UNESCAPED_UNICODE);
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
$val = $val->__toString();
}
if (false !== strpos($key, '->')) {
list($key, $name) = explode('->', $key);
$item = $this->parseKey($query, $key);
$result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')';
} elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
} elseif (is_null($val)) {
$result[$item] = 'NULL';
} elseif (is_array($val) && !empty($val)) {
switch (strtoupper($val[0])) {
case 'INC':
$result[$item] = $item . ' + ' . floatval($val[1]);
break;
case 'DEC':
$result[$item] = $item . ' - ' . floatval($val[1]);
break;
case 'EXP':
throw new Exception('not support data:[' . $val[0] . ']');
}
} elseif (is_scalar($val)) {
// 过滤非标量数据
$result[$item] = $this->parseDataBind($query, $key, $val, $bind);
}
}
return $result;
}
/**
* 数据绑定处理
* @access protected
* @param Query $query 查询对象
* @param string $key 字段名
* @param mixed $data 数据
* @param array $bind 绑定数据
* @return string
*/
protected function parseDataBind(Query $query, $key, $data, $bind = [])
{
if ($data instanceof Expression) {
return $data->getValue();
}
$query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
return '?';
}
/**
* 字段名分析
* @access public
* @param Query $query 查询对象
* @param mixed $key 字段名
* @param bool $strict 严格检测
* @return string
*/
public function parseKey(Query $query, $key, $strict = false)
{
return $key instanceof Expression ? $key->getValue() : $key;
}
/**
* field分析
* @access protected
* @param Query $query 查询对象
* @param mixed $fields
* @return string
*/
protected function parseField(Query $query, $fields)
{
if ('*' == $fields || empty($fields)) {
$fieldsStr = '*';
} elseif (is_array($fields)) {
// 支持 'field1'=>'field2' 这样的字段别名定义
$array = [];
foreach ($fields as $key => $field) {
if (!is_numeric($key)) {
$array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true);
} else {
$array[] = $this->parseKey($query, $field);
}
}
$fieldsStr = implode(',', $array);
}
return $fieldsStr;
}
/**
* table分析
* @access protected
* @param Query $query 查询对象
* @param mixed $tables
* @return string
*/
protected function parseTable(Query $query, $tables)
{
$item = [];
$options = $query->getOptions();
foreach ((array) $tables as $key => $table) {
if (!is_numeric($key)) {
$key = $this->connection->parseSqlTable($key);
$item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);
} else {
$table = $this->connection->parseSqlTable($table);
if (isset($options['alias'][$table])) {
$item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]);
} else {
$item[] = $this->parseKey($query, $table);
}
}
}
return implode(',', $item);
}
/**
* where分析
* @access protected
* @param Query $query 查询对象
* @param mixed $where 查询条件
* @return string
*/
protected function parseWhere(Query $query, $where)
{
$options = $query->getOptions();
$whereStr = $this->buildWhere($query, $where);
if (!empty($options['soft_delete'])) {
// 附加软删除条件
list($field, $condition) = $options['soft_delete'];
$binds = $this->connection->getFieldsBind($options['table']);
$whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
$whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds);
}
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
}
/**
* 生成查询条件SQL
* @access public
* @param Query $query 查询对象
* @param mixed $where
* @param array $options
* @return string
*/
public function buildWhere(Query $query, $where)
{
if (empty($where)) {
$where = [];
}
$whereStr = '';
$binds = $this->connection->getFieldsBind($query->getOptions('table'));
foreach ($where as $logic => $val) {
$str = [];
foreach ($val as $value) {
if ($value instanceof Expression) {
$str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )';
continue;
}
if (is_array($value)) {
if (key($value) !== 0) {
throw new Exception('where express error:' . var_export($value, true));
}
$field = array_shift($value);
} elseif (!($value instanceof \Closure)) {
throw new Exception('where express error:' . var_export($value, true));
}
if ($value instanceof \Closure) {
// 使用闭包查询
$newQuery = $query->newQuery()->setConnection($this->connection);
$value($newQuery);
$whereClause = $this->buildWhere($query, $newQuery->getOptions('where'));
if (!empty($whereClause)) {
$str[] = ' ' . $logic . ' ( ' . $whereClause . ' )';
}
} elseif (is_array($field)) {
array_unshift($value, $field);
$str2 = [];
foreach ($value as $item) {
$str2[] = $this->parseWhereItem($query, array_shift($item), $item, $logic, $binds);
}
$str[] = ' ' . $logic . ' ( ' . implode(' AND ', $str2) . ' )';
} elseif (strpos($field, '|')) {
// 不同字段使用相同查询条件(OR)
$array = explode('|', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($query, $k, $value, '', $binds);
}
$str[] = ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )';
} elseif (strpos($field, '&')) {
// 不同字段使用相同查询条件(AND)
$array = explode('&', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($query, $k, $value, '', $binds);
}
$str[] = ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )';
} else {
// 对字段使用表达式查询
$field = is_string($field) ? $field : '';
$str[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $logic, $binds);
}
}
$whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str);
}
return $whereStr;
}
// where子单元分析
protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [])
{
// 字段分析
$key = $field ? $this->parseKey($query, $field, true) : '';
// 查询规则和条件
if (!is_array($val)) {
$val = is_null($val) ? ['NULL', ''] : ['=', $val];
}
list($exp, $value) = $val;
// 对一个字段使用多个查询条件
if (is_array($exp)) {
$item = array_pop($val);
// 传入 or 或者 and
if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) {
$rule = $item;
} else {
array_push($val, $item);
}
foreach ($val as $k => $item) {
$str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds);
}
return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
}
// 检测操作符
$exp = strtoupper($exp);
if (isset($this->exp[$exp])) {
$exp = $this->exp[$exp];
}
if ($value instanceof Expression) {
} elseif (is_object($value) && method_exists($value, '__toString')) {
// 对象数据写入
$value = $value->__toString();
}
if (strpos($field, '->')) {
$jsonType = $query->getJsonFieldType($field);
$bindType = $this->connection->getFieldBindType($jsonType);
} else {
$bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
}
if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
$query->bind($value, $bindType);
$value = '?';
}
// 解析查询表达式
foreach ($this->parser as $fun => $parse) {
if (in_array($exp, $parse)) {
$whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND');
break;
}
}
if (!isset($whereStr)) {
throw new Exception('where express error:' . $exp);
}
return $whereStr;
}
/**
* 模糊查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @param string $logic
* @return string
*/
protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic)
{
// 模糊匹配
if (is_array($value)) {
foreach ($value as $item) {
$bind[] = [$item, $bindType];
$array[] = $key . ' ' . $exp . ' ?';
}
$query->bind($bind);
$whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
} else {
$whereStr = $key . ' ' . $exp . ' ' . $value;
}
return $whereStr;
}
/**
* 表达式查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param Expression $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType)
{
// 表达式查询
return '( ' . $key . ' ' . $value->getValue() . ' )';
}
/**
* 表达式查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param array $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType)
{
// 字段比较查询
list($op, $field2) = $value;
if (!in_array($op, ['=', '<>', '>', '>=', '<', '<='])) {
throw new Exception('where express error:' . var_export($value, true));
}
return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field2, true) . ' )';
}
/**
* Null查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType)
{
// NULL 查询
return $key . ' IS ' . $exp;
}
/**
* 范围查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType)
{
// BETWEEN 查询
$data = is_array($value) ? $value : explode(',', $value);
$bind[] = [$data[0], $bindType];
$bind[] = [$data[1], $bindType];
$query->bind($bind);
return $key . ' ' . $exp . ' ? AND ? ';
}
/**
* Exists查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType)
{
// EXISTS 查询
if ($value instanceof \Closure) {
$value = $this->parseClosure($query, $value, false);
} elseif ($value instanceof Expression) {
$value = $value->getValue();
} else {
throw new Exception('where express error:' . $value);
}
return $exp . ' (' . $value . ')';
}
/**
* 时间比较查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType)
{
return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType);
}
/**
* 大小比较查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType)
{
if (is_array($value)) {
throw new Exception('where express error:' . $exp . var_export($value, true));
}
// 比较运算
if ($value instanceof \Closure) {
$value = $this->parseClosure($query, $value);
}
return $key . ' ' . $exp . ' ' . $value;
}
/**
* 时间范围查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType)
{
if (is_string($value)) {
$value = explode(',', $value);
}
return $key . ' ' . substr($exp, 0, -4)
. $this->parseDateTime($query, $value[0], $field, $bindType)
. ' AND '
. $this->parseDateTime($query, $value[1], $field, $bindType);
}
/**
* IN查询
* @access protected
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param mixed $value
* @param string $field
* @param integer $bindType
* @return string
*/
protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType)
{
// IN 查询
if ($value instanceof \Closure) {
$value = $this->parseClosure($query, $value, false);
} else {
$value = array_unique(is_array($value) ? $value : explode(',', $value));
$bind = [];
$array = [];
foreach ($value as $k => $v) {
$bind[] = [$v, $bindType];
$array[] = '?';
}
$zone = implode(',', $array);
$query->bind($bind);
$value = empty($zone) ? "''" : $zone;
}
return $key . ' ' . $exp . ' (' . $value . ')';
}
/**
* 闭包子查询
* @access protected
* @param Query $query 查询对象
* @param \Closure $call
* @param bool $show
* @return string
*/
protected function parseClosure(Query $query, $call, $show = true)
{
$newQuery = $query->newQuery()->setConnection($this->connection);
$call($newQuery);
return $newQuery->buildSql($show);
}
/**
* 日期时间条件解析
* @access protected
* @param Query $query 查询对象
* @param string $value
* @param string $key
* @param integer $bindType
* @return string
*/
protected function parseDateTime(Query $query, $value, $key, $bindType = null)
{
$options = $query->getOptions();
// 获取时间字段类型
if (strpos($key, '.')) {
list($table, $key) = explode('.', $key);
if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
$table = $pos;
}
} else {
$table = $options['table'];
}
$type = $this->connection->getTableInfo($table, 'type');
if (isset($type[$key])) {
$info = $type[$key];
}
if (isset($info)) {
if (is_string($value)) {
$value = strtotime($value) ?: $value;
}
if (preg_match('/(datetime|timestamp)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d H:i:s', $value);
} elseif (preg_match('/(date)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d', $value);
}
}
$query->bind($value, $bindType);
return '?';
}
/**
* limit分析
* @access protected
* @param Query $query 查询对象
* @param mixed $limit
* @return string
*/
protected function parseLimit(Query $query, $limit)
{
return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
}
/**
* join分析
* @access protected
* @param Query $query 查询对象
* @param array $join
* @return string
*/
protected function parseJoin(Query $query, $join)
{
$joinStr = '';
if (!empty($join)) {
foreach ($join as $item) {
list($table, $type, $on) = $item;
$condition = [];
foreach ((array) $on as $val) {
if ($val instanceof Expression) {
$condition[] = $val->getValue();
} elseif (strpos($val, '=')) {
list($val1, $val2) = explode('=', $val, 2);
$condition[] = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2);
} else {
$condition[] = $val;
}
}
$table = $this->parseTable($query, $table);
$joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition);
}
}
return $joinStr;
}
/**
* order分析
* @access protected
* @param Query $query 查询对象
* @param mixed $order
* @return string
*/
protected function parseOrder(Query $query, $order)
{
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) {
$array[] = $this->parseOrderField($query, $key, $val);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand($query);
} elseif (is_string($val)) {
if (is_numeric($key)) {
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} else {
$sort = $val;
}
if (preg_match('/^[\w\.]+$/', $key)) {
$sort = strtoupper($sort);
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$array[] = $this->parseKey($query, $key, true) . $sort;
} else {
throw new Exception('order express error:' . $key);
}
}
}
return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
}
/**
* orderField分析
* @access protected
* @param Query $query 查询对象
* @param mixed $key
* @param array $val
* @return string
*/
protected function parseOrderField($query, $key, $val)
{
if (isset($val['sort'])) {
$sort = $val['sort'];
unset($val['sort']);
} else {
$sort = '';
}
$sort = strtoupper($sort);
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$options = $query->getOptions();
$bind = $this->connection->getFieldsBind($options['table']);
foreach ($val as $k => $item) {
$val[$k] = $this->parseDataBind($query, $key, $item, $bind);
}
return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort;
}
/**
* group分析
* @access protected
* @param Query $query 查询对象
* @param mixed $group
* @return string
*/
protected function parseGroup(Query $query, $group)
{
return !empty($group) ? ' GROUP BY ' . $this->parseKey($query, $group) : '';
}
/**
* having分析
* @access protected
* @param Query $query 查询对象
* @param string $having
* @return string
*/
protected function parseHaving(Query $query, $having)
{
return !empty($having) ? ' HAVING ' . $having : '';
}
/**
* comment分析
* @access protected
* @param Query $query 查询对象
* @param string $comment
* @return string
*/
protected function parseComment(Query $query, $comment)
{
if (false !== strpos($comment, '*/')) {
$comment = strstr($comment, '*/', true);
}
return !empty($comment) ? ' /* ' . $comment . ' */' : '';
}
/**
* distinct分析
* @access protected
* @param Query $query 查询对象
* @param mixed $distinct
* @return string
*/
protected function parseDistinct(Query $query, $distinct)
{
return !empty($distinct) ? ' DISTINCT ' : '';
}
/**
* union分析
* @access protected
* @param Query $query 查询对象
* @param mixed $union
* @return string
*/
protected function parseUnion(Query $query, $union)
{
if (empty($union)) {
return '';
}
$type = $union['type'];
unset($union['type']);
foreach ($union as $u) {
if ($u instanceof \Closure) {
$sql[] = $type . ' ' . $this->parseClosure($query, $u);
} elseif (is_string($u)) {
$sql[] = $type . ' ( ' . $this->connection->parseSqlTable($u) . ' )';
}
}
return ' ' . implode(' ', $sql);
}
/**
* index分析,可在操作链中指定需要强制使用的索引
* @access protected
* @param Query $query 查询对象
* @param mixed $index
* @return string
*/
protected function parseForce(Query $query, $index)
{
if (empty($index)) {
return '';
}
if (is_array($index)) {
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
}
/**
* 设置锁机制
* @access protected
* @param Query $query 查询对象
* @param bool|string $lock
* @return string
*/
protected function parseLock(Query $query, $lock = false)
{
if (is_bool($lock)) {
return $lock ? ' FOR UPDATE ' : '';
} elseif (is_string($lock) && !empty($lock)) {
return ' ' . trim($lock) . ' ';
}
}
/**
* 生成查询SQL
* @access public
* @param Query $query 查询对象
* @return string
*/
public function select(Query $query)
{
$options = $query->getOptions();
return str_replace(
['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
[
$this->parseTable($query, $options['table']),
$this->parseDistinct($query, $options['distinct']),
$this->parseField($query, $options['field']),
$this->parseJoin($query, $options['join']),
$this->parseWhere($query, $options['where']),
$this->parseGroup($query, $options['group']),
$this->parseHaving($query, $options['having']),
$this->parseOrder($query, $options['order']),
$this->parseLimit($query, $options['limit']),
$this->parseUnion($query, $options['union']),
$this->parseLock($query, $options['lock']),
$this->parseComment($query, $options['comment']),
$this->parseForce($query, $options['force']),
],
$this->selectSql);
}
/**
* 生成Insert SQL
* @access public
* @param Query $query 查询对象
* @param bool $replace 是否replace
* @return string
*/
public function insert(Query $query, $replace = false)
{
$options = $query->getOptions();
// 分析并处理数据
$data = $this->parseData($query, $options['data']);
if (empty($data)) {
return '';
}
$fields = array_keys($data);
$values = array_values($data);
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($query, $options['table']),
implode(' , ', $fields),
implode(' , ', $values),
$this->parseComment($query, $options['comment']),
],
$this->insertSql);
}
/**
* 生成insertall SQL
* @access public
* @param Query $query 查询对象
* @param array $dataSet 数据集
* @param bool $replace 是否replace
* @return string
*/
public function insertAll(Query $query, $dataSet, $replace = false)
{
$options = $query->getOptions();
// 获取合法的字段
if ('*' == $options['field']) {
$allowFields = $this->connection->getTableFields($options['table']);
} else {
$allowFields = $options['field'];
}
// 获取绑定信息
$bind = $this->connection->getFieldsBind($options['table']);
foreach ($dataSet as $data) {
$data = $this->parseData($query, $data, $allowFields, $bind);
$values[] = 'SELECT ' . implode(',', array_values($data));
if (!isset($insertFields)) {
$insertFields = array_keys($data);
}
}
$fields = [];
foreach ($insertFields as $field) {
$fields[] = $this->parseKey($query, $field);
}
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($query, $options['table']),
implode(' , ', $fields),
implode(' UNION ALL ', $values),
$this->parseComment($query, $options['comment']),
],
$this->insertAllSql);
}
/**
* 生成slect insert SQL
* @access public
* @param Query $query 查询对象
* @param array $fields 数据
* @param string $table 数据表
* @return string
*/
public function selectInsert(Query $query, $fields, $table)
{
if (is_string($fields)) {
$fields = explode(',', $fields);
}
foreach ($fields as &$field) {
$field = $this->parseKey($query, $field, true);
}
return 'INSERT INTO ' . $this->parseTable($query, $table) . ' (' . implode(',', $fields) . ') ' . $this->select($query);
}
/**
* 生成update SQL
* @access public
* @param Query $query 查询对象
* @return string
*/
public function update(Query $query)
{
$options = $query->getOptions();
$table = $this->parseTable($query, $options['table']);
$data = $this->parseData($query, $options['data']);
if (empty($data)) {
return '';
}
foreach ($data as $key => $val) {
$set[] = $key . ' = ' . $val;
}
return str_replace(
['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($query, $options['table']),
implode(' , ', $set),
$this->parseJoin($query, $options['join']),
$this->parseWhere($query, $options['where']),
$this->parseOrder($query, $options['order']),
$this->parseLimit($query, $options['limit']),
$this->parseLock($query, $options['lock']),
$this->parseComment($query, $options['comment']),
],
$this->updateSql);
}
/**
* 生成delete SQL
* @access public
* @param Query $query 查询对象
* @return string
*/
public function delete(Query $query)
{
$options = $query->getOptions();
return str_replace(
['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($query, $options['table']),
!empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '',
$this->parseJoin($query, $options['join']),
$this->parseWhere($query, $options['where']),
$this->parseOrder($query, $options['order']),
$this->parseLimit($query, $options['limit']),
$this->parseLock($query, $options['lock']),
$this->parseComment($query, $options['comment']),
],
$this->deleteSql);
}
}