Completed
Push — 6.0 ( 7fd508...a1342f )
by liu
09:18
created

Connection::connect()   B

Complexity

Conditions 9
Paths 177

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 26
nc 177
nop 3
dl 0
loc 43
ccs 0
cts 22
cp 0
crap 90
rs 7.4138
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think\db;
14
15
use PDO;
16
use PDOStatement;
17
use think\Cache;
18
use think\cache\CacheItem;
19
use think\Container;
20
use think\Db;
21
use think\db\exception\BindParamException;
22
use think\Exception;
23
use think\exception\PDOException;
24
use think\Log;
25
26
/**
27
 * 数据库连接基础类
28
 */
5 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
29
abstract class Connection
30
{
31
    const PARAM_FLOAT = 21;
32
33
    /**
34
     * PDO操作实例
35
     * @var PDOStatement
36
     */
37
    protected $PDOStatement;
38
39
    /**
40
     * 当前SQL指令
41
     * @var string
42
     */
43
    protected $queryStr = '';
44
45
    /**
46
     * 返回或者影响记录数
47
     * @var int
48
     */
49
    protected $numRows = 0;
50
51
    /**
52
     * 事务指令数
53
     * @var int
54
     */
55
    protected $transTimes = 0;
56
57
    /**
58
     * 错误信息
59
     * @var string
60
     */
61
    protected $error = '';
62
63
    /**
64
     * 数据库连接ID 支持多个连接
65
     * @var PDO[]
66
     */
67
    protected $links = [];
68
69
    /**
70
     * 当前连接ID
71
     * @var PDO
72
     */
73
    protected $linkID;
74
75
    /**
76
     * 当前读连接ID
77
     * @var PDO
78
     */
79
    protected $linkRead;
80
81
    /**
82
     * 当前写连接ID
83
     * @var PDO
84
     */
85
    protected $linkWrite;
86
87
    /**
88
     * 查询结果类型
89
     * @var int
90
     */
91
    protected $fetchType = PDO::FETCH_ASSOC;
92
93
    /**
94
     * 字段属性大小写
95
     * @var int
96
     */
97
    protected $attrCase = PDO::CASE_LOWER;
98
99
    /**
100
     * 数据表信息
101
     * @var array
102
     */
103
    protected $info = [];
104
105
    /**
106
     * 查询开始时间
107
     * @var float
108
     */
109
    protected $queryStartTime;
110
111
    /**
112
     * Builder对象
113
     * @var Builder
114
     */
115
    protected $builder;
116
117
    /**
118
     * Db对象
119
     * @var Db
120
     */
121
    protected $db;
122
123
    /**
124
     * 是否读取主库
125
     * @var bool
126
     */
127
    protected $readMaster = false;
128
129
    /**
130
     * 数据库连接参数配置
131
     * @var array
132
     */
133
    protected $config = [
134
        // 数据库类型
135
        'type'            => '',
136
        // 服务器地址
137
        'hostname'        => '',
138
        // 数据库名
139
        'database'        => '',
140
        // 用户名
141
        'username'        => '',
142
        // 密码
143
        'password'        => '',
144
        // 端口
145
        'hostport'        => '',
146
        // 连接dsn
147
        'dsn'             => '',
148
        // 数据库连接参数
149
        'params'          => [],
150
        // 数据库编码默认采用utf8
151
        'charset'         => 'utf8',
152
        // 数据库表前缀
153
        'prefix'          => '',
154
        // 数据库调试模式
155
        'debug'           => false,
156
        // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
157
        'deploy'          => 0,
158
        // 数据库读写是否分离 主从式有效
159
        'rw_separate'     => false,
160
        // 读写分离后 主服务器数量
161
        'master_num'      => 1,
162
        // 指定从服务器序号
163
        'slave_no'        => '',
164
        // 模型写入后自动读取主服务器
165
        'read_master'     => false,
166
        // 是否严格检查字段是否存在
167
        'fields_strict'   => true,
168
        // 自动写入时间戳字段
169
        'auto_timestamp'  => false,
170
        // 时间字段取出后的默认时间格式
171
        'datetime_format' => 'Y-m-d H:i:s',
172
        // 是否需要进行SQL性能分析
173
        'sql_explain'     => false,
174
        // Builder类
175
        'builder'         => '',
176
        // Query类
177
        'query'           => '',
178
        // 是否需要断线重连
179
        'break_reconnect' => false,
180
        // 断线标识字符串
181
        'break_match_str' => [],
182
    ];
183
184
    /**
185
     * PDO连接参数
186
     * @var array
187
     */
188
    protected $params = [
189
        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
190
        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
191
        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
192
        PDO::ATTR_STRINGIFY_FETCHES => false,
193
        PDO::ATTR_EMULATE_PREPARES  => false,
194
    ];
195
196
    /**
197
     * 参数绑定类型映射
198
     * @var array
199
     */
200
    protected $bindType = [
201
        'string'  => PDO::PARAM_STR,
202
        'str'     => PDO::PARAM_STR,
203
        'integer' => PDO::PARAM_INT,
204
        'int'     => PDO::PARAM_INT,
205
        'boolean' => PDO::PARAM_BOOL,
206
        'bool'    => PDO::PARAM_BOOL,
207
        'float'   => self::PARAM_FLOAT,
208
    ];
209
210
    /**
211
     * 服务器断线标识字符
212
     * @var array
213
     */
214
    protected $breakMatchStr = [
215
        'server has gone away',
216
        'no connection to the server',
217
        'Lost connection',
218
        'is dead or not enabled',
219
        'Error while sending',
220
        'decryption failed or bad record mac',
221
        'server closed the connection unexpectedly',
222
        'SSL connection has been closed unexpectedly',
223
        'Error writing data to the connection',
224
        'Resource deadlock avoided',
225
        'failed with errno',
226
    ];
227
228
    /**
229
     * 绑定参数
230
     * @var array
231
     */
232
    protected $bind = [];
233
234
    /**
235
     * 缓存对象
236
     * @var Cache
237
     */
238
    protected $cache;
239
240
    /**
241
     * 日志对象
242
     * @var Log
243
     */
244
    protected $log;
245
246
    /**
247
     * 架构函数 读取数据库配置信息
248
     * @access public
249
     * @param Cache $cache 缓存对象
1 ignored issue
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
250
     * @param Log   $log 日志对象
1 ignored issue
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
251
     * @param array $config 数据库配置数组
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
252
     */
253
    public function __construct(Cache $cache, Log $log, array $config = [])
254
    {
255
        if (!empty($config)) {
256
            $this->config = array_merge($this->config, $config);
257
        }
258
259
        // 创建Builder对象
260
        $class = $this->getBuilderClass();
261
262
        $this->builder = new $class($this);
263
        $this->cache   = $cache;
264
        $this->log     = $log;
265
266
        // 执行初始化操作
267
        $this->initialize();
268
    }
269
270
    /**
271
     * 初始化
272
     * @access protected
273
     * @return void
274
     */
275
    protected function initialize(): void
276
    {
277
    }
278
279
    /**
280
     * 获取当前连接器类对应的Query类
281
     * @access public
282
     * @return string
283
     */
284
    public function getQueryClass(): string
285
    {
286
        return $this->getConfig('query') ?: Query::class;
287
    }
288
289
    /**
290
     * 获取当前连接器类对应的Builder类
291
     * @access public
292
     * @return string
293
     */
294
    public function getBuilderClass(): string
295
    {
296
        return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type'));
297
    }
298
299
    /**
300
     * 设置当前的数据库Builder对象
301
     * @access protected
302
     * @param Builder $builder
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
303
     * @return void
304
     */
305
    protected function setBuilder(Builder $builder): void
306
    {
307
        $this->builder = $builder;
308
    }
309
310
    /**
311
     * 获取当前的builder实例对象
312
     * @access public
313
     * @return Builder
314
     */
315
    public function getBuilder(): Builder
316
    {
317
        return $this->builder;
318
    }
319
320
    /**
321
     * 设置当前的数据库Db对象
322
     * @access public
323
     * @param Db $db
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
324
     * @return void
325
     */
326
    public function setDb(Db $db): void
327
    {
328
        $this->db = $db;
329
    }
330
331
    /**
332
     * 解析pdo连接的dsn信息
333
     * @access protected
334
     * @param array $config 连接信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
335
     * @return string
336
     */
337
    abstract protected function parseDsn(array $config);
338
339
    /**
340
     * 取得数据表的字段信息
341
     * @access public
342
     * @param string $tableName 数据表名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
343
     * @return array
344
     */
345
    abstract public function getFields(string $tableName);
346
347
    /**
348
     * 取得数据库的表信息
349
     * @access public
350
     * @param string $dbName 数据库名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
351
     * @return array
352
     */
353
    abstract public function getTables(string $dbName);
354
355
    /**
356
     * SQL性能分析
357
     * @access protected
358
     * @param string $sql SQL语句
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
359
     * @return array
360
     */
361
    abstract protected function getExplain(string $sql);
362
363
    /**
364
     * 对返数据表字段信息进行大小写转换出来
365
     * @access public
366
     * @param array $info 字段信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
367
     * @return array
368
     */
369
    public function fieldCase(array $info): array
370
    {
371
        // 字段大小写转换
372
        switch ($this->attrCase) {
373
            case PDO::CASE_LOWER:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
374
                $info = array_change_key_case($info);
375
                break;
376
            case PDO::CASE_UPPER:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
377
                $info = array_change_key_case($info, CASE_UPPER);
378
                break;
379
            case PDO::CASE_NATURAL:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
380
            default:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
381
                // 不做转换
382
        }
383
384
        return $info;
385
    }
386
387
    /**
388
     * 获取字段绑定类型
389
     * @access public
390
     * @param string $type 字段类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
391
     * @return integer
392
     */
393
    public function getFieldBindType(string $type): int
394
    {
395
        if (in_array($type, ['integer', 'string', 'float', 'boolean', 'bool', 'int', 'str'])) {
396
            $bind = $this->bindType[$type];
397
        } elseif (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
398
            $bind = PDO::PARAM_STR;
399
        } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
400
            $bind = self::PARAM_FLOAT;
401
        } elseif (preg_match('/(int|serial|bit)/is', $type)) {
402
            $bind = PDO::PARAM_INT;
403
        } elseif (preg_match('/bool/is', $type)) {
404
            $bind = PDO::PARAM_BOOL;
405
        } else {
406
            $bind = PDO::PARAM_STR;
407
        }
408
409
        return $bind;
410
    }
411
412
    /**
413
     * 获取数据表信息
414
     * @access public
415
     * @param mixed  $tableName 数据表名 留空自动获取
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
416
     * @param string $fetch     获取信息类型 包括 fields type bind pk
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
417
     * @return mixed
418
     */
419
    public function getTableInfo($tableName, string $fetch = '')
420
    {
421
        if (is_array($tableName)) {
422
            $tableName = key($tableName) ?: current($tableName);
423
        }
424
425
        if (strpos($tableName, ',')) {
426
            // 多表不获取字段信息
427
            return [];
428
        }
429
430
        // 修正子查询作为表名的问题
431
        if (strpos($tableName, ')')) {
432
            return [];
433
        }
434
435
        list($tableName) = explode(' ', $tableName);
436
437
        if (!strpos($tableName, '.')) {
438
            $schema = $this->getConfig('database') . '.' . $tableName;
439
        } else {
440
            $schema = $tableName;
441
        }
442
443
        if (!isset($this->info[$schema])) {
444
            // 读取缓存
445
            $cacheFile = Container::pull('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php';
446
447
            if (!$this->config['debug'] && is_file($cacheFile)) {
448
                $info = include $cacheFile;
449
            } else {
450
                $info = $this->getFields($tableName);
451
            }
452
453
            $fields = array_keys($info);
454
            $bind   = $type   = [];
455
456
            foreach ($info as $key => $val) {
457
                // 记录字段类型
458
                $type[$key] = $val['type'];
459
                $bind[$key] = $this->getFieldBindType($val['type']);
460
461
                if (!empty($val['primary'])) {
462
                    $pk[] = $key;
463
                }
464
            }
465
466
            if (isset($pk)) {
467
                // 设置主键
468
                $pk = count($pk) > 1 ? $pk : $pk[0];
469
            } else {
470
                $pk = null;
471
            }
472
473
            $this->info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk];
474
        }
475
476
        return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema];
477
    }
478
479
    /**
480
     * 获取数据表的主键
481
     * @access public
482
     * @param mixed $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
483
     * @return string|array
484
     */
485
    public function getPk($tableName)
486
    {
487
        return $this->getTableInfo($tableName, 'pk');
488
    }
489
490
    /**
491
     * 获取数据表字段信息
492
     * @access public
493
     * @param mixed $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
494
     * @return array
495
     */
496
    public function getTableFields($tableName): array
497
    {
498
        return $this->getTableInfo($tableName, 'fields');
499
    }
500
501
    /**
502
     * 获取数据表字段类型
503
     * @access public
504
     * @param mixed  $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
505
     * @param string $field     字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
506
     * @return array|string
507
     */
508
    public function getFieldsType($tableName, string $field = null)
509
    {
510
        $result = $this->getTableInfo($tableName, 'type');
511
512
        if ($field && isset($result[$field])) {
513
            return $result[$field];
514
        }
515
516
        return $result;
517
    }
518
519
    /**
520
     * 获取数据表绑定信息
521
     * @access public
522
     * @param mixed $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
523
     * @return array
524
     */
525
    public function getFieldsBind($tableName): array
526
    {
527
        return $this->getTableInfo($tableName, 'bind');
528
    }
529
530
    /**
531
     * 获取数据库的配置参数
532
     * @access public
533
     * @param string $config 配置名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
534
     * @return mixed
535
     */
536
    public function getConfig(string $config = '')
537
    {
538
        if ('' === $config) {
539
            return $this->config;
540
        }
541
        return $this->config[$config] ?? null;
542
    }
543
544
    /**
545
     * 设置数据库的配置参数
546
     * @access public
547
     * @param array $config 配置
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
548
     * @return void
549
     */
550
    public function setConfig(array $config): void
551
    {
552
        $this->config = array_merge($this->config, $config);
553
    }
554
555
    /**
556
     * 连接数据库方法
557
     * @access public
558
     * @param array      $config         连接参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
559
     * @param integer    $linkNum        连接序号
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
560
     * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
561
     * @return PDO
562
     * @throws Exception
563
     */
564
    public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO
565
    {
566
        if (isset($this->links[$linkNum])) {
567
            return $this->links[$linkNum];
568
        }
569
570
        if (empty($config)) {
571
            $config = $this->config;
572
        } else {
573
            $config = array_merge($this->config, $config);
574
        }
575
576
        // 连接参数
577
        if (isset($config['params']) && is_array($config['params'])) {
578
            $params = $config['params'] + $this->params;
579
        } else {
580
            $params = $this->params;
581
        }
582
583
        // 记录当前字段属性大小写设置
584
        $this->attrCase = $params[PDO::ATTR_CASE];
585
586
        if (!empty($config['break_match_str'])) {
587
            $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']);
588
        }
589
590
        try {
591
            if (empty($config['dsn'])) {
592
                $config['dsn'] = $this->parseDsn($config);
593
            }
594
595
            $startTime             = microtime(true);
596
            $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params);
597
            // 记录数据库连接信息
598
            $this->log('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']);
599
600
            return $this->links[$linkNum];
601
        } catch (\PDOException $e) {
602
            if ($autoConnection) {
603
                $this->log->error($e->getMessage());
604
                return $this->connect($autoConnection, $linkNum);
0 ignored issues
show
Bug introduced by
It seems like $autoConnection can also be of type true; however, parameter $config of think\db\Connection::connect() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

604
                return $this->connect(/** @scrutinizer ignore-type */ $autoConnection, $linkNum);
Loading history...
605
            } else {
606
                throw $e;
607
            }
608
        }
609
    }
610
611
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $dsn should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $username should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $password should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $params should have a doc-comment as per coding-style.
Loading history...
612
     * 创建PDO实例
613
     * @param $dsn
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
614
     * @param $username
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
615
     * @param $password
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
616
     * @param $params
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
617
     * @return PDO
618
     */
619
    protected function createPdo($dsn, $username, $password, $params)
620
    {
621
        return new PDO($dsn, $username, $password, $params);
622
    }
623
624
    /**
625
     * 释放查询结果
626
     * @access public
627
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
628
    public function free(): void
629
    {
630
        $this->PDOStatement = null;
631
    }
632
633
    /**
634
     * 获取PDO对象
635
     * @access public
636
     * @return \PDO|false
637
     */
638
    public function getPdo()
639
    {
640
        if (!$this->linkID) {
641
            return false;
642
        }
643
644
        return $this->linkID;
645
    }
646
647
    /**
648
     * 执行查询 使用生成器返回数据
649
     * @access public
650
     * @param Query        $query     查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
651
     * @param string       $sql       sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
652
     * @param array        $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
653
     * @param \think\Model $model     模型对象实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
654
     * @param array        $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
655
     * @return \Generator
656
     */
657
    public function getCursor(Query $query, string $sql, array $bind = [], $model = null, $condition = null)
658
    {
659
        $this->queryPDOStatement($query, $sql, $bind);
660
661
        // 返回结果集
662
        while ($result = $this->PDOStatement->fetch($this->fetchType)) {
663
            if ($model) {
664
                yield $model->newInstance($result, $condition)->setQuery($query);
665
            } else {
666
                yield $result;
667
            }
668
        }
669
    }
670
671
    /**
672
     * 执行查询 返回数据集
673
     * @access public
674
     * @param Query  $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
675
     * @param string $sql   sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
676
     * @param array  $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
677
     * @param bool   $cache 是否支持缓存
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
678
     * @return array
679
     * @throws BindParamException
680
     * @throws \PDOException
681
     * @throws \Exception
682
     * @throws \Throwable
683
     */
684
    public function query(Query $query, string $sql, array $bind = [], bool $cache = false): array
685
    {
686
        // 分析查询表达式
687
        $options = $query->parseOptions();
688
689
        if ($cache && !empty($options['cache'])) {
690
            $cacheItem = $this->parseCache($query, $options['cache']);
691
            $resultSet = $this->cache->get($cacheItem->getKey());
692
693
            if (false !== $resultSet) {
694
                return $resultSet;
695
            }
696
        }
697
698
        $master    = !empty($options['master']) ? true : false;
699
        $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
700
701
        $this->getPDOStatement($sql, $bind, $master, $procedure);
702
703
        $resultSet = $this->getResult($procedure);
704
705
        if (isset($cacheItem) && $resultSet) {
706
            // 缓存数据集
707
            $cacheItem->set($resultSet);
708
            $this->cacheData($cacheItem);
709
        }
710
711
        return $resultSet;
712
    }
713
714
    /**
715
     * 执行查询但只返回PDOStatement对象
716
     * @access public
717
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
718
     * @return \PDOStatement
719
     */
720
    public function pdo(Query $query): PDOStatement
721
    {
722
        $bind = $query->getBind();
723
        // 生成查询SQL
724
        $sql = $this->builder->select($query);
725
726
        return $this->queryPDOStatement($query, $sql, $bind);
727
    }
728
729
    /**
730
     * 执行查询但只返回PDOStatement对象
731
     * @access public
732
     * @param string $sql       sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
733
     * @param array  $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
734
     * @param bool   $master    是否在主服务器读操作
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
735
     * @param bool   $procedure 是否为存储过程调用
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
736
     * @return PDOStatement
737
     * @throws BindParamException
738
     * @throws \PDOException
739
     * @throws \Exception
740
     * @throws \Throwable
741
     */
742
    public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement
743
    {
744
        $this->initConnect($this->readMaster ?: $master);
745
746
        // 记录SQL语句
747
        $this->queryStr = $sql;
748
749
        $this->bind = $bind;
750
751
        $this->db->updateQueryTimes();
752
753
        try {
754
            // 调试开始
755
            $this->debug(true);
756
757
            // 预处理
758
            $this->PDOStatement = $this->linkID->prepare($sql);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->linkID->prepare($sql) can also be of type boolean. However, the property $PDOStatement is declared as type PDOStatement. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
759
760
            // 参数绑定
761
            if ($procedure) {
762
                $this->bindParam($bind);
763
            } else {
764
                $this->bindValue($bind);
765
            }
766
767
            // 执行查询
768
            $this->PDOStatement->execute();
769
770
            // 调试结束
771
            $this->debug(false, '', $master);
772
773
            return $this->PDOStatement;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->PDOStatement could return the type boolean which is incompatible with the type-hinted return PDOStatement. Consider adding an additional type-check to rule them out.
Loading history...
774
        } catch (\Throwable | \Exception $e) {
775
            if ($this->isBreak($e)) {
776
                return $this->close()->getPDOStatement($sql, $bind, $master, $procedure);
777
            }
778
779
            if ($e instanceof \PDOException) {
780
                throw new PDOException($e, $this->config, $this->getLastsql());
781
            } else {
782
                throw $e;
783
            }
784
        }
785
    }
786
787
    /**
788
     * 执行语句
789
     * @access public
790
     * @param Query  $query  查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
791
     * @param string $sql    sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
792
     * @param array  $bind   参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
793
     * @param bool   $origin 是否原生查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
794
     * @return int
795
     * @throws BindParamException
796
     * @throws \PDOException
797
     * @throws \Exception
798
     * @throws \Throwable
799
     */
800
    public function execute(Query $query, string $sql, array $bind = [], bool $origin = false): int
801
    {
802
        $this->queryPDOStatement($query->master(true), $sql, $bind);
803
804
        if (!$origin && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
805
            $this->readMaster = true;
806
        }
807
808
        $this->numRows = $this->PDOStatement->rowCount();
809
810
        return $this->numRows;
811
    }
812
813
    protected function queryPDOStatement(Query $query, string $sql, array $bind = []): PDOStatement
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function queryPDOStatement()
Loading history...
814
    {
815
        $options   = $query->getOptions();
816
        $master    = !empty($options['master']) ? true : false;
817
        $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
818
819
        return $this->getPDOStatement($sql, $bind, $master, $procedure);
820
    }
821
822
    /**
823
     * 查找单条记录
824
     * @access public
825
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
826
     * @return array
827
     * @throws DbException
828
     * @throws ModelNotFoundException
829
     * @throws DataNotFoundException
830
     */
831
    public function find(Query $query): array
832
    {
833
        // 分析查询表达式
834
        $options = $query->parseOptions();
835
836
        if (!empty($options['cache'])) {
837
            // 判断查询缓存
838
            $cacheItem = $this->parseCache($query, $options['cache']);
839
            $key       = $cacheItem->getKey();
840
        }
841
842
        if (isset($key)) {
843
            $result = $this->cache->get($key);
844
845
            if (false !== $result) {
846
                return $result;
847
            }
848
        }
849
850
        // 生成查询SQL
851
        $sql = $this->builder->select($query, true);
852
853
        // 事件回调
854
        $result = $this->db->trigger('before_find', $query);
855
856
        if (!$result) {
857
            // 执行查询
858
            $resultSet = $this->query($query, $sql, $query->getBind());
859
860
            $result = $resultSet[0] ?? [];
861
        }
862
863
        if (isset($cacheItem) && $result) {
864
            // 缓存数据
865
            $cacheItem->set($result);
866
            $this->cacheData($cacheItem);
867
        }
868
869
        return $result;
870
    }
871
872
    /**
873
     * 使用游标查询记录
874
     * @access public
875
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
876
     * @return \Generator
877
     */
878
    public function cursor(Query $query)
879
    {
880
        // 分析查询表达式
881
        $options = $query->parseOptions();
882
883
        // 生成查询SQL
884
        $sql = $this->builder->select($query);
885
886
        $condition = $options['where']['AND'] ?? null;
887
888
        // 执行查询操作
889
        return $this->getCursor($query, $sql, $query->getBind(), $query->getModel(), $condition);
890
    }
891
892
    /**
893
     * 查找记录
894
     * @access public
895
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
896
     * @return array
897
     * @throws DbException
898
     * @throws ModelNotFoundException
899
     * @throws DataNotFoundException
900
     */
901
    public function select(Query $query): array
902
    {
903
        // 分析查询表达式
904
        $options = $query->parseOptions();
905
906
        if (!empty($options['cache'])) {
907
            $cacheItem = $this->parseCache($query, $options['cache']);
908
            $resultSet = $this->getCacheData($cacheItem);
909
910
            if (false !== $resultSet) {
911
                return $resultSet;
912
            }
913
        }
914
915
        // 生成查询SQL
916
        $sql = $this->builder->select($query);
917
918
        $resultSet = $this->db->trigger('before_select', $query);
919
920
        if (!$resultSet) {
921
            // 执行查询操作
922
            $resultSet = $this->query($query, $sql, $query->getBind());
923
        }
924
925
        if (isset($cacheItem) && false !== $resultSet) {
926
            // 缓存数据集
927
            $cacheItem->set($resultSet);
928
            $this->cacheData($cacheItem);
929
        }
930
931
        return $resultSet;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $resultSet could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
932
    }
933
934
    /**
935
     * 插入记录
936
     * @access public
937
     * @param Query   $query        查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
938
     * @param boolean $getLastInsID 返回自增主键
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
939
     * @return mixed
940
     */
941
    public function insert(Query $query, bool $getLastInsID = false)
942
    {
943
        // 分析查询表达式
944
        $options = $query->parseOptions();
945
946
        // 生成SQL语句
947
        $sql = $this->builder->insert($query);
948
949
        // 执行操作
950
        $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind());
951
952
        if ($result) {
953
            $sequence  = $options['sequence'] ?? null;
954
            $lastInsId = $query->getLastInsID($sequence);
955
956
            $data = $options['data'];
957
958
            if ($lastInsId) {
959
                $pk = $query->getPk();
960
                if (is_string($pk)) {
961
                    $data[$pk] = $lastInsId;
962
                }
963
            }
964
965
            $query->setOption('data', $data);
966
967
            $this->db->trigger('after_insert', $query);
968
969
            if ($getLastInsID) {
970
                return $lastInsId;
971
            }
972
        }
973
974
        return $result;
975
    }
976
977
    /**
978
     * 批量插入记录
979
     * @access public
980
     * @param Query   $query   查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
981
     * @param mixed   $dataSet 数据集
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
982
     * @param integer $limit   每次写入数据限制
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
983
     * @return integer
984
     * @throws \Exception
985
     * @throws \Throwable
986
     */
987
    public function insertAll(Query $query, array $dataSet = [], int $limit = 0): int
988
    {
989
        if (!is_array(reset($dataSet))) {
990
            return 0;
991
        }
992
993
        $query->parseOptions();
994
995
        if ($limit) {
996
            // 分批写入 自动启动事务支持
997
            $this->startTrans();
998
999
            try {
1000
                $array = array_chunk($dataSet, $limit, true);
1001
                $count = 0;
1002
1003
                foreach ($array as $item) {
1004
                    $sql = $this->builder->insertAll($query, $item);
1005
                    $count += $this->execute($query, $sql, $query->getBind());
1006
                }
1007
1008
                // 提交事务
1009
                $this->commit();
1010
            } catch (\Exception | \Throwable $e) {
1011
                $this->rollback();
1012
                throw $e;
1013
            }
1014
1015
            return $count;
1016
        }
1017
1018
        $sql = $this->builder->insertAll($query, $dataSet);
1019
1020
        return $this->execute($query, $sql, $query->getBind());
1021
    }
1022
1023
    /**
1024
     * 通过Select方式插入记录
1025
     * @access public
1026
     * @param Query  $query  查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1027
     * @param array  $fields 要插入的数据表字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1028
     * @param string $table  要插入的数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1029
     * @return integer
1030
     * @throws PDOException
1031
     */
1032
    public function selectInsert(Query $query, array $fields, string $table): int
1033
    {
1034
        // 分析查询表达式
1035
        $query->parseOptions();
1036
1037
        $sql = $this->builder->selectInsert($query, $fields, $table);
1038
1039
        return $this->execute($query, $sql, $query->getBind());
1040
    }
1041
1042
    /**
1043
     * 更新记录
1044
     * @access public
1045
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1046
     * @return integer
1047
     * @throws Exception
1048
     * @throws PDOException
1049
     */
1050
    public function update(Query $query): int
1051
    {
1052
        $options = $query->parseOptions();
1053
1054
        if (isset($options['cache'])) {
1055
            $cacheItem = $this->parseCache($query, $options['cache']);
1056
            $key       = $cacheItem->getKey();
1057
            $tag       = $cacheItem->getTag();
1058
        }
1059
1060
        // 生成UPDATE SQL语句
1061
        $sql = $this->builder->update($query);
1062
1063
        // 检测缓存
1064
        if (isset($key) && $this->cache->get($key)) {
1065
            // 删除缓存
1066
            $this->cache->delete($key);
1067
        } elseif (!empty($tag)) {
1068
            $this->cache->tag($tag)->clear();
1069
        }
1070
1071
        // 执行操作
1072
        $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind());
1073
1074
        if ($result) {
1075
            $this->db->trigger('after_update', $query);
1076
        }
1077
1078
        return $result;
1079
    }
1080
1081
    /**
1082
     * 删除记录
1083
     * @access public
1084
     * @param Query $query 查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1085
     * @return int
1086
     * @throws Exception
1087
     * @throws PDOException
1088
     */
1089
    public function delete(Query $query): int
1090
    {
1091
        // 分析查询表达式
1092
        $options = $query->parseOptions();
1093
1094
        if (isset($options['cache'])) {
1095
            $cacheItem = $this->parseCache($query, $options['cache']);
1096
            $key       = $cacheItem->getKey();
1097
            $tag       = $cacheItem->getTag();
1098
        }
1099
1100
        // 生成删除SQL语句
1101
        $sql = $this->builder->delete($query);
1102
1103
        // 检测缓存
1104
        if (isset($key) && $this->cache->get($key)) {
1105
            // 删除缓存
1106
            $this->cache->delete($key);
1107
        } elseif (!empty($tag)) {
1108
            $this->cache->tag($tag)->clear();
1109
        }
1110
1111
        // 执行操作
1112
        $result = $this->execute($query, $sql, $query->getBind());
1113
1114
        if ($result) {
1115
            $this->db->trigger('after_delete', $query);
1116
        }
1117
1118
        return $result;
1119
    }
1120
1121
    /**
1122
     * 得到某个字段的值
1123
     * @access public
1124
     * @param Query  $query   查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1125
     * @param string $field   字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1126
     * @param mixed  $default 默认值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1127
     * @param bool   $one     返回一个值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1128
     * @return mixed
1129
     */
1130
    public function value(Query $query, string $field, $default = null, bool $one = true)
1131
    {
1132
        $options = $query->parseOptions();
1133
1134
        if (isset($options['field'])) {
1135
            $query->removeOption('field');
1136
        }
1137
1138
        $query->setOption('field', (array) $field);
1139
1140
        if (!empty($options['cache'])) {
1141
            $cacheItem = $this->parseCache($query, $options['cache']);
1142
            $result    = $this->getCacheData($cacheItem);
1143
1144
            if (false !== $result) {
1145
                return $result;
1146
            }
1147
        }
1148
1149
        // 生成查询SQL
1150
        $sql = $this->builder->select($query, $one);
1151
1152
        if (isset($options['field'])) {
1153
            $query->setOption('field', $options['field']);
1154
        } else {
1155
            $query->removeOption('field');
1156
        }
1157
1158
        // 执行查询操作
1159
        $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']);
1160
1161
        $result = $pdo->fetchColumn();
1162
1163
        if (isset($cacheItem) && false !== $result) {
1164
            // 缓存数据
1165
            $cacheItem->set($result);
1166
            $this->cacheData($cacheItem);
1167
        }
1168
1169
        return false !== $result ? $result : $default;
1170
    }
1171
1172
    /**
1173
     * 得到某个字段的值
1174
     * @access public
1175
     * @param Query  $query     查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1176
     * @param string $aggregate 聚合方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1177
     * @param mixed  $field     字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1178
     * @param bool   $force     强制转为数字类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1179
     * @return mixed
1180
     */
1181
    public function aggregate(Query $query, string $aggregate, $field, bool $force = false)
1182
    {
1183
        if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) {
1184
            list($distinct, $field) = explode(' ', $field);
1185
        }
1186
1187
        $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate);
1188
1189
        $result = $this->value($query, $field, 0, false);
1190
1191
        return $force ? (float) $result : $result;
1192
    }
1193
1194
    /**
1195
     * 得到某个列的数组
1196
     * @access public
1197
     * @param Query  $query  查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1198
     * @param string $column 字段名 多个字段用逗号分隔
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1199
     * @param string $key    索引
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1200
     * @return array
1201
     */
1202
    public function column(Query $query, string $column, string $key = ''): array
1203
    {
1204
        $options = $query->parseOptions();
1205
1206
        if (isset($options['field'])) {
1207
            $query->removeOption('field');
1208
        }
1209
1210
        if ($key && '*' != $column) {
1211
            $field = $key . ',' . $column;
1212
        } else {
1213
            $field = $column;
1214
        }
1215
1216
        $field = array_map('trim', explode(',', $field));
1217
1218
        $query->setOption('field', $field);
1219
1220
        if (!empty($options['cache'])) {
1221
            // 判断查询缓存
1222
            $cacheItem = $this->parseCache($query, $options['cache']);
1223
            $result    = $this->getCacheData($cacheItem);
1224
1225
            if (false !== $result) {
1226
                return $result;
1227
            }
1228
        }
1229
1230
        // 生成查询SQL
1231
        $sql = $this->builder->select($query);
1232
1233
        if (isset($options['field'])) {
1234
            $query->setOption('field', $options['field']);
1235
        } else {
1236
            $query->removeOption('field');
1237
        }
1238
1239
        // 执行查询操作
1240
        $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']);
1241
1242
        $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC);
1243
1244
        if (empty($resultSet)) {
1245
            $result = [];
1246
        } elseif (('*' == $column || strpos($column, ',')) && $key) {
1247
            $result = array_column($resultSet, null, $key);
1248
        } else {
1249
            $fields = array_keys($resultSet[0]);
1250
            $key    = $key ?: array_shift($fields);
1251
1252
            if (strpos($key, '.')) {
1253
                list($alias, $key) = explode('.', $key);
1254
            }
1255
1256
            $result = array_column($resultSet, $column, $key);
1257
        }
1258
1259
        if (isset($cacheItem)) {
1260
            // 缓存数据
1261
            $cacheItem->set($result);
1262
            $this->cacheData($cacheItem);
1263
        }
1264
1265
        return $result;
1266
    }
1267
1268
    /**
1269
     * 根据参数绑定组装最终的SQL语句 便于调试
1270
     * @access public
1271
     * @param string $sql  带参数绑定的sql语句
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1272
     * @param array  $bind 参数绑定列表
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1273
     * @return string
1274
     */
1275
    public function getRealSql(string $sql, array $bind = []): string
1276
    {
1277
        foreach ($bind as $key => $val) {
1278
            $value = is_array($val) ? $val[0] : $val;
1279
            $type  = is_array($val) ? $val[1] : PDO::PARAM_STR;
1280
1281
            if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) {
1282
                $value = '\'' . addslashes($value) . '\'';
1283
            } elseif (PDO::PARAM_INT == $type && '' === $value) {
1284
                $value = 0;
1285
            }
1286
1287
            // 判断占位符
1288
            $sql = is_numeric($key) ?
1289
            substr_replace($sql, $value, strpos($sql, '?'), 1) :
1290
            substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key));
1291
        }
1292
1293
        return rtrim($sql);
1294
    }
1295
1296
    /**
1297
     * 参数绑定
1298
     * 支持 ['name'=>'value','id'=>123] 对应命名占位符
1299
     * 或者 ['value',123] 对应问号占位符
1300
     * @access public
1301
     * @param array $bind 要绑定的参数列表
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1302
     * @return void
1303
     * @throws BindParamException
1304
     */
1305
    protected function bindValue(array $bind = []): void
1306
    {
1307
        foreach ($bind as $key => $val) {
1308
            // 占位符
1309
            $param = is_numeric($key) ? $key + 1 : ':' . $key;
1310
1311
            if (is_array($val)) {
1312
                if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
1313
                    $val[0] = 0;
1314
                } elseif (self::PARAM_FLOAT == $val[1]) {
1315
                    $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
1316
                    $val[1] = PDO::PARAM_STR;
1317
                }
1318
1319
                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
1320
            } else {
1321
                $result = $this->PDOStatement->bindValue($param, $val);
1322
            }
1323
1324
            if (!$result) {
1325
                throw new BindParamException(
1326
                    "Error occurred  when binding parameters '{$param}'",
1327
                    $this->config,
1328
                    $this->getLastsql(),
1329
                    $bind
1330
                );
1331
            }
1332
        }
1333
    }
1334
1335
    /**
1336
     * 存储过程的输入输出参数绑定
1337
     * @access public
1338
     * @param array $bind 要绑定的参数列表
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1339
     * @return void
1340
     * @throws BindParamException
1341
     */
1342
    protected function bindParam(array $bind): void
1343
    {
1344
        foreach ($bind as $key => $val) {
1345
            $param = is_numeric($key) ? $key + 1 : ':' . $key;
1346
1347
            if (is_array($val)) {
1348
                array_unshift($val, $param);
1349
                $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val);
1350
            } else {
1351
                $result = $this->PDOStatement->bindValue($param, $val);
1352
            }
1353
1354
            if (!$result) {
1355
                $param = array_shift($val);
1356
1357
                throw new BindParamException(
1358
                    "Error occurred  when binding parameters '{$param}'",
1359
                    $this->config,
1360
                    $this->getLastsql(),
1361
                    $bind
1362
                );
1363
            }
1364
        }
1365
    }
1366
1367
    /**
1368
     * 获得数据集数组
1369
     * @access protected
1370
     * @param bool $procedure 是否存储过程
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1371
     * @return array
1372
     */
1373
    protected function getResult(bool $procedure = false): array
1374
    {
1375
        if ($procedure) {
1376
            // 存储过程返回结果
1377
            return $this->procedure();
1378
        }
1379
1380
        $result = $this->PDOStatement->fetchAll($this->fetchType);
1381
1382
        $this->numRows = count($result);
1383
1384
        return $result;
1385
    }
1386
1387
    /**
1388
     * 获得存储过程数据集
1389
     * @access protected
1390
     * @return array
1391
     */
1392
    protected function procedure(): array
1393
    {
1394
        $item = [];
1395
1396
        do {
1397
            $result = $this->getResult();
1398
            if (!empty($result)) {
1399
                $item[] = $result;
1400
            }
1401
        } while ($this->PDOStatement->nextRowset());
1402
1403
        $this->numRows = count($item);
1404
1405
        return $item;
1406
    }
1407
1408
    /**
1409
     * 执行数据库事务
1410
     * @access public
1411
     * @param callable $callback 数据操作方法回调
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1412
     * @return mixed
1413
     * @throws PDOException
1414
     * @throws \Exception
1415
     * @throws \Throwable
1416
     */
1417
    public function transaction(callable $callback)
1418
    {
1419
        $this->startTrans();
1420
1421
        try {
1422
            $result = null;
1423
            if (is_callable($callback)) {
1424
                $result = call_user_func_array($callback, [$this]);
1425
            }
1426
1427
            $this->commit();
1428
            return $result;
1429
        } catch (\Exception | \Throwable $e) {
1430
            $this->rollback();
1431
            throw $e;
1432
        }
1433
    }
1434
1435
    /**
1436
     * 启动事务
1437
     * @access public
1438
     * @return void
1439
     * @throws \PDOException
1440
     * @throws \Exception
1441
     */
1442
    public function startTrans(): void
1443
    {
1444
        $this->initConnect(true);
1445
1446
        ++$this->transTimes;
1447
1448
        try {
1449
            if (1 == $this->transTimes) {
1450
                $this->linkID->beginTransaction();
1451
            } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
1452
                $this->linkID->exec(
1453
                    $this->parseSavepoint('trans' . $this->transTimes)
1454
                );
1455
            }
1456
        } catch (\Exception $e) {
1457
            if ($this->isBreak($e)) {
1458
                --$this->transTimes;
1459
                $this->close()->startTrans();
1460
            }
1461
            throw $e;
1462
        }
1463
    }
1464
1465
    /**
1466
     * 用于非自动提交状态下面的查询提交
1467
     * @access public
1468
     * @return void
1469
     * @throws PDOException
1470
     */
1471
    public function commit(): void
1472
    {
1473
        $this->initConnect(true);
1474
1475
        if (1 == $this->transTimes) {
1476
            $this->linkID->commit();
1477
        }
1478
1479
        --$this->transTimes;
1480
    }
1481
1482
    /**
1483
     * 事务回滚
1484
     * @access public
1485
     * @return void
1486
     * @throws PDOException
1487
     */
1488
    public function rollback(): void
1489
    {
1490
        $this->initConnect(true);
1491
1492
        if (1 == $this->transTimes) {
1493
            $this->linkID->rollBack();
1494
        } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
1495
            $this->linkID->exec(
1496
                $this->parseSavepointRollBack('trans' . $this->transTimes)
1497
            );
1498
        }
1499
1500
        $this->transTimes = max(0, $this->transTimes - 1);
1501
    }
1502
1503
    /**
1504
     * 是否支持事务嵌套
1505
     * @return bool
1506
     */
1507
    protected function supportSavepoint(): bool
1508
    {
1509
        return false;
1510
    }
1511
1512
    /**
1513
     * 生成定义保存点的SQL
1514
     * @access protected
1515
     * @param string $name 标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1516
     * @return string
1517
     */
1518
    protected function parseSavepoint(string $name): string
1519
    {
1520
        return 'SAVEPOINT ' . $name;
1521
    }
1522
1523
    /**
1524
     * 生成回滚到保存点的SQL
1525
     * @access protected
1526
     * @param string $name 标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1527
     * @return string
1528
     */
1529
    protected function parseSavepointRollBack(string $name): string
1530
    {
1531
        return 'ROLLBACK TO SAVEPOINT ' . $name;
1532
    }
1533
1534
    /**
1535
     * 批处理执行SQL语句
1536
     * 批处理的指令都认为是execute操作
1537
     * @access public
1538
     * @param Query $query    查询对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1539
     * @param array $sqlArray SQL批处理指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1540
     * @param array $bind     参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1541
     * @return bool
1542
     */
1543
    public function batchQuery(Query $query, array $sqlArray = [], array $bind = []): bool
1544
    {
1545
        // 自动启动事务支持
1546
        $this->startTrans();
1547
1548
        try {
1549
            foreach ($sqlArray as $sql) {
1550
                $this->execute($query, $sql, $bind);
1551
            }
1552
            // 提交事务
1553
            $this->commit();
1554
        } catch (\Exception $e) {
1555
            $this->rollback();
1556
            throw $e;
1557
        }
1558
1559
        return true;
1560
    }
1561
1562
    /**
1563
     * 关闭数据库(或者重新连接)
1564
     * @access public
1565
     * @return $this
1566
     */
1567
    public function close()
1568
    {
1569
        $this->linkID    = null;
1570
        $this->linkWrite = null;
1571
        $this->linkRead  = null;
1572
        $this->links     = [];
1573
1574
        $this->free();
1575
1576
        return $this;
1577
    }
1578
1579
    /**
1580
     * 是否断线
1581
     * @access protected
1582
     * @param \PDOException|\Exception $e 异常对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1583
     * @return bool
1584
     */
1585
    protected function isBreak($e): bool
1586
    {
1587
        if (!$this->config['break_reconnect']) {
1588
            return false;
1589
        }
1590
1591
        $error = $e->getMessage();
1592
1593
        foreach ($this->breakMatchStr as $msg) {
1594
            if (false !== stripos($error, $msg)) {
1595
                return true;
1596
            }
1597
        }
1598
1599
        return false;
1600
    }
1601
1602
    /**
1603
     * 获取最近一次查询的sql语句
1604
     * @access public
1605
     * @return string
1606
     */
1607
    public function getLastSql(): string
1608
    {
1609
        return $this->getRealSql($this->queryStr, $this->bind);
1610
    }
1611
1612
    /**
1613
     * 获取最近插入的ID
1614
     * @access public
1615
     * @param string $sequence 自增序列名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1616
     * @return string
1617
     */
1618
    public function getLastInsID(string $sequence = null): string
1619
    {
1620
        return $this->linkID->lastInsertId($sequence);
1621
    }
1622
1623
    /**
1624
     * 获取返回或者影响的记录数
1625
     * @access public
1626
     * @return integer
1627
     */
1628
    public function getNumRows(): int
1629
    {
1630
        return $this->numRows;
1631
    }
1632
1633
    /**
1634
     * 获取最近的错误信息
1635
     * @access public
1636
     * @return string
1637
     */
1638
    public function getError(): string
1639
    {
1640
        if ($this->PDOStatement) {
1641
            $error = $this->PDOStatement->errorInfo();
1642
            $error = $error[1] . ':' . $error[2];
1643
        } else {
1644
            $error = '';
1645
        }
1646
1647
        if ('' != $this->queryStr) {
1648
            $error .= "\n [ SQL语句 ] : " . $this->getLastsql();
1649
        }
1650
1651
        return $error;
1652
    }
1653
1654
    /**
1655
     * 数据库调试 记录当前SQL及分析性能
1656
     * @access protected
1657
     * @param boolean $start  调试开始标记 true 开始 false 结束
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1658
     * @param string  $sql    执行的SQL语句 留空自动获取
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1659
     * @param bool    $master 主从标记
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1660
     * @return void
1661
     */
1662
    protected function debug(bool $start, string $sql = '', bool $master = false): void
1663
    {
1664
        if (!empty($this->config['debug'])) {
1665
            // 开启数据库调试模式
1666
            if ($start) {
1667
                $this->queryStartTime = microtime(true);
1668
            } else {
1669
                // 记录操作结束时间
1670
                $runtime = number_format((microtime(true) - $this->queryStartTime), 6);
1671
                $sql     = $sql ?: $this->getLastsql();
1672
                $result  = [];
1673
1674
                // SQL性能分析
1675
                if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) {
1676
                    $result = $this->getExplain($sql);
1677
                }
1678
1679
                // SQL监听
1680
                $this->triggerSql($sql, $runtime, $result, $master);
1681
            }
1682
        }
1683
    }
1684
1685
    /**
1686
     * 触发SQL事件
1687
     * @access protected
1688
     * @param string $sql     SQL语句
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1689
     * @param string $runtime SQL运行时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1690
     * @param mixed  $explain SQL分析
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1691
     * @param bool   $master  主从标记
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1692
     * @return void
1693
     */
1694
    protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void
1695
    {
1696
        $listen = $this->db->getListen();
1697
        if (!empty($listen)) {
1698
            foreach ($listen as $callback) {
1699
                if (is_callable($callback)) {
1700
                    $callback($sql, $runtime, $explain, $master);
1701
                }
1702
            }
1703
        } else {
1704
            if ($this->config['deploy']) {
1705
                // 分布式记录当前操作的主从
1706
                $master = $master ? 'master|' : 'slave|';
1707
            } else {
1708
                $master = '';
1709
            }
1710
1711
            // 未注册监听则记录到日志中
1712
            $this->log('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]');
1713
1714
            if (!empty($explain)) {
1715
                $this->log('[ EXPLAIN : ' . var_export($explain, true) . ' ]');
1716
            }
1717
        }
1718
    }
1719
1720
    /**
1721
     * 记录SQL日志
1722
     * @access protected
1723
     * @param string $log  SQL日志信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1724
     * @param string $type 日志类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1725
     * @return void
1726
     */
1727
    protected function log($log, $type = 'sql'): void
1728
    {
1729
        if ($this->config['debug']) {
1730
            $this->log->record($log, $type);
1731
        }
1732
    }
1733
1734
    /**
1735
     * 初始化数据库连接
1736
     * @access protected
1737
     * @param boolean $master 是否主服务器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1738
     * @return void
1739
     */
1740
    protected function initConnect(bool $master = true): void
1741
    {
1742
        if (!empty($this->config['deploy'])) {
1743
            // 采用分布式数据库
1744
            if ($master || $this->transTimes) {
1745
                if (!$this->linkWrite) {
1746
                    $this->linkWrite = $this->multiConnect(true);
1747
                }
1748
1749
                $this->linkID = $this->linkWrite;
1750
            } else {
1751
                if (!$this->linkRead) {
1752
                    $this->linkRead = $this->multiConnect(false);
1753
                }
1754
1755
                $this->linkID = $this->linkRead;
1756
            }
1757
        } elseif (!$this->linkID) {
1758
            // 默认单数据库
1759
            $this->linkID = $this->connect();
1760
        }
1761
    }
1762
1763
    /**
1764
     * 连接分布式服务器
1765
     * @access protected
1766
     * @param boolean $master 主服务器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1767
     * @return PDO
1768
     */
1769
    protected function multiConnect(bool $master = false): PDO
1770
    {
1771
        $config = [];
1772
1773
        // 分布式数据库配置解析
1774
        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
1775
            $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name];
1776
        }
1777
1778
        // 主服务器序号
1779
        $m = floor(mt_rand(0, $this->config['master_num'] - 1));
1780
1781
        if ($this->config['rw_separate']) {
1782
            // 主从式采用读写分离
1783
            if ($master) // 主服务器写入
0 ignored issues
show
Coding Style introduced by
Expected "if (...) {\n"; found "if (...) // 主服务器写入\n {\n"
Loading history...
1784
            {
1785
                $r = $m;
1786
            } elseif (is_numeric($this->config['slave_no'])) {
1787
                // 指定服务器读
1788
                $r = $this->config['slave_no'];
1789
            } else {
1790
                // 读操作连接从服务器 每次随机连接的数据库
1791
                $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1));
1792
            }
1793
        } else {
1794
            // 读写操作不区分服务器 每次随机连接的数据库
1795
            $r = floor(mt_rand(0, count($config['hostname']) - 1));
1796
        }
1797
        $dbMaster = false;
1798
1799
        if ($m != $r) {
1800
            $dbMaster = [];
1801
            foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
1802
                $dbMaster[$name] = $config[$name][$m] ?? $config[$name][0];
1803
            }
1804
        }
1805
1806
        $dbConfig = [];
1807
1808
        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
1809
            $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0];
1810
        }
1811
1812
        return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster);
1813
    }
1814
1815
    /**
1816
     * 析构方法
1817
     * @access public
1818
     */
1819
    public function __destruct()
1820
    {
1821
        // 释放查询
1822
        $this->free();
1823
1824
        // 关闭连接
1825
        $this->close();
1826
    }
1827
1828
    /**
1829
     * 缓存数据
1830
     * @access protected
1831
     * @param CacheItem $cacheItem 缓存Item
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1832
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1833
    protected function cacheData(CacheItem $cacheItem): void
1834
    {
1835
        if ($cacheItem->getTag()) {
1836
            $this->cache->tag($cacheItem->getTag());
1837
        }
1838
1839
        $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire());
1840
    }
1841
1842
    /**
1843
     * 获取缓存数据
1844
     * @access protected
1845
     * @param CacheItem $cacheItem
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1846
     * @return mixed
1847
     */
1848
    protected function getCacheData(CacheItem $cacheItem)
1849
    {
1850
        // 判断查询缓存
1851
        return $this->cache->get($cacheItem->getKey());
1852
    }
1853
1854
    protected function parseCache(Query $query, array $cache): CacheItem
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function parseCache()
Loading history...
1855
    {
1856
        list($key, $expire, $tag) = $cache;
1857
1858
        if ($key instanceof CacheItem) {
1859
            $cacheItem = $key;
1860
        } else {
1861
            if (true === $key) {
1862
                if (!empty($query->getOptions('key'))) {
1863
                    $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key');
1864
                } else {
1865
                    $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false)));
1866
                }
1867
            }
1868
1869
            $cacheItem = new CacheItem($key);
1870
            $cacheItem->expire($expire);
1871
            $cacheItem->tag($tag);
1872
        }
1873
1874
        return $cacheItem;
1875
    }
1876
1877
    /**
1878
     * 延时更新检查 返回false表示需要延时
1879
     * 否则返回实际写入的数值
1880
     * @access public
1881
     * @param string  $type     自增或者自减
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1882
     * @param string  $guid     写入标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1883
     * @param float   $step     写入步进值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1884
     * @param integer $lazyTime 延时时间(s)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1885
     * @return false|integer
1886
     */
1887
    public function lazyWrite(string $type, string $guid, float $step, int $lazyTime)
1888
    {
1889
        if (!$this->cache->has($guid . '_time')) {
1890
            // 计时开始
1891
            $this->cache->set($guid . '_time', time(), 0);
1892
            $this->cache->$type($guid, $step);
1893
        } elseif (time() > $this->cache->get($guid . '_time') + $lazyTime) {
1894
            // 删除缓存
1895
            $value = $this->cache->$type($guid, $step);
1896
            $this->cache->delete($guid);
1897
            $this->cache->delete($guid . '_time');
1898
            return 0 === $value ? false : $value;
1899
        } else {
1900
            // 更新缓存
1901
            $this->cache->$type($guid, $step);
1902
        }
1903
1904
        return false;
1905
    }
1906
}
1907