Completed
Push — 6.0 ( 36224d...52d3ce )
by liu
02:38
created

Connection::execute()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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

601
                return $this->connect(/** @scrutinizer ignore-type */ $autoConnection, $linkNum);
Loading history...
602
            } else {
603
                throw $e;
604
            }
605
        }
606
    }
607
608
    /**
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...
609
     * 创建PDO实例
610
     * @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...
611
     * @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...
612
     * @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...
613
     * @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...
614
     * @return PDO
615
     */
616
    protected function createPdo($dsn, $username, $password, $params)
617
    {
618
        return new PDO($dsn, $username, $password, $params);
619
    }
620
621
    /**
622
     * 释放查询结果
623
     * @access public
624
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
625
    public function free(): void
626
    {
627
        $this->PDOStatement = null;
628
    }
629
630
    /**
631
     * 获取PDO对象
632
     * @access public
633
     * @return \PDO|false
634
     */
635
    public function getPdo()
636
    {
637
        if (!$this->linkID) {
638
            return false;
639
        }
640
641
        return $this->linkID;
642
    }
643
644
    /**
645
     * 执行查询 使用生成器返回数据
646
     * @access public
647
     * @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...
648
     * @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...
649
     * @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...
650
     * @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...
651
     * @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...
652
     * @return \Generator
653
     */
654
    public function getCursor(Query $query, string $sql, array $bind = [], $model = null, $condition = null)
655
    {
656
        $this->queryPDOStatement($query, $sql, $bind);
657
658
        // 返回结果集
659
        while ($result = $this->PDOStatement->fetch($this->fetchType)) {
660
            if ($model) {
661
                yield $model->newInstance($result, true, $condition);
0 ignored issues
show
Unused Code introduced by
The call to think\Model::newInstance() has too many arguments starting with $condition. ( Ignorable by Annotation )

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

661
                yield $model->/** @scrutinizer ignore-call */ newInstance($result, true, $condition);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

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