Completed
Push — 6.0 ( 52d3ce...17b267 )
by liu
02:46
created

Connection   F

Complexity

Total Complexity 256

Size/Duplication

Total Lines 1889
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 609
dl 0
loc 1889
ccs 0
cts 582
cp 0
rs 1.991
c 0
b 0
f 0
wmc 256

67 Methods

Rating   Name   Duplication   Size   Complexity  
A initialize() 0 2 1
A insert() 0 34 6
A getError() 0 14 3
A queryPDOStatement() 0 7 3
A getLastSql() 0 3 1
B initConnect() 0 20 7
A getFieldsBind() 0 3 1
A procedure() 0 14 3
A getResult() 0 12 2
B update() 0 28 8
A getPk() 0 3 1
A getBuilder() 0 3 1
A bindParam() 0 20 5
B find() 0 39 7
B getPDOStatement() 0 41 6
A execute() 0 11 4
A insertAll() 0 34 5
A lazyWrite() 0 18 4
A createPdo() 0 3 1
A __construct() 0 15 2
A log() 0 4 2
B getFieldBindType() 0 17 7
A close() 0 10 1
B value() 0 40 8
A listen() 0 3 1
A setDb() 0 5 1
A parseSavepoint() 0 3 1
A isBreak() 0 15 4
A getConfig() 0 6 2
B delete() 0 29 7
A parseSavepointRollBack() 0 3 1
A getNumRows() 0 3 1
A setBuilder() 0 5 1
A batchQuery() 0 17 3
A transaction() 0 15 3
B multiConnect() 0 44 10
C column() 0 64 14
A getCursor() 0 10 3
A aggregate() 0 11 5
A getQueryClass() 0 3 2
A getLastInsID() 0 3 1
A pdo() 0 7 1
C connect() 0 47 10
A setConfig() 0 3 1
A cacheData() 0 7 2
A rollback() 0 13 4
B bindValue() 0 25 8
B query() 0 28 8
A getBuilderClass() 0 3 2
B triggerSql() 0 21 7
A startTrans() 0 20 6
A select() 0 31 6
A free() 0 3 1
B getRealSql() 0 21 10
A getTableFields() 0 3 1
C getTableInfo() 0 58 14
A cursor() 0 12 1
A parseCache() 0 21 4
A selectInsert() 0 6 1
A __destruct() 0 7 1
A debug() 0 19 6
A getFieldsType() 0 9 3
A getCacheData() 0 4 1
A commit() 0 9 2
A fieldCase() 0 16 4
A getPdo() 0 7 2
A supportSavepoint() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Connection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Connection, and based on these observations, apply Extract Interface, too.

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

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

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