Completed
Push — 6.0 ( 77cbae...03c51c )
by liu
03:09
created

Connection::delete()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 12
nop 1
dl 0
loc 30
ccs 0
cts 15
cp 0
crap 42
rs 9.2222
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
            if ($config['debug']) {
596
                $startTime             = microtime(true);
597
                $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params);
598
                // 记录数据库连接信息
599
                $this->log('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']);
600
            } else {
601
                $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params);
602
            }
603
604
            return $this->links[$linkNum];
605
        } catch (\PDOException $e) {
606
            if ($autoConnection) {
607
                $this->log->error($e->getMessage());
608
                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

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