Connection   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 384
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 83
c 1
b 0
f 0
dl 0
loc 384
rs 9.44
wmc 37

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A connect() 0 19 4
A __sleep() 0 3 1
A prepareConnection() 0 6 2
A setObserver() 0 5 1
A countQuery() 0 3 1
A getObserver() 0 3 1
A query() 0 5 1
A prefix() 0 5 1
A quote() 0 3 1
A isConnected() 0 3 1
A beginTransaction() 0 6 2
A lastInsertId() 0 7 2
A getPdo() 0 4 1
A __toString() 0 3 1
A arrayQuote() 0 7 3
A begin() 0 3 1
A getName() 0 3 1
A commit() 0 7 2
A getType() 0 3 1
A rollBack() 0 8 2
A setType() 0 3 1
A onBeforeSystemShutdown() 0 8 4
A getConfig() 0 3 1
1
<?php
2
namespace suda\database\connection;
3
4
use PDO;
5
use PDOException;
6
use suda\database\DataSource;
7
use suda\database\statement\Statement;
8
use suda\database\statement\QueryAccess;
9
use suda\database\exception\SQLException;
10
use suda\database\connection\observer\Observer;
11
use suda\database\connection\observer\NullObserver;
12
13
/**
14
 * 数据表链接对象
15
 *
16
 */
17
abstract class Connection
18
{
19
    /**
20
     * @var string
21
     */
22
    protected $type = 'mysql';
23
24
    /**
25
     * Config
26
     *
27
     * @var array
28
     */
29
    protected $config;
30
31
    /**
32
     * @var int
33
     */
34
    protected $queryCount = 0;
35
36
    /**
37
     * @var PDO
38
     */
39
    protected $pdo;
40
41
    /**
42
     * @var int
43
     */
44
    protected $transaction = 0;
45
46
    /**
47
     * @var
48
     */
49
    protected $id;
50
51
    /**
52
     * @var int
53
     */
54
    protected static $connectionCount = 0;
55
56
    /**
57
     * 链接别名
58
     *
59
     * @var string
60
     */
61
    protected $name;
62
63
    /**
64
     * 性能观测
65
     *
66
     * @var Observer
67
     */
68
    protected $observer;
69
70
    /**
71
     * 创建连接
72
     *
73
     * @param array $config
74
     * @param string $name
75
     */
76
    public function __construct(array $config, string $name = null)
77
    {
78
        $this->config = $config;
79
        $this->name = $name ?? 'anonymous';
80
        $this->observer = new NullObserver;
81
        register_shutdown_function(function () {
82
            $this->onBeforeSystemShutdown();
83
        });
84
    }
85
86
    /**
87
     * @return mixed
88
     */
89
    abstract public function getDsn();
90
91
    /**
92
     * @return PDO
93
     */
94
    abstract public function createPDO(): PDO;
95
96
    /**
97
     * 连接服务器
98
     *
99
     * @return bool
100
     * @throws SQLException
101
     */
102
    public function connect()
103
    {
104
        // 链接数据库
105
        if (null === $this->pdo && $this->getConfig('enable', true)) {
106
            try {
107
                $time = microtime(true);
108
                $this->pdo = $this->createPDO();
109
                $this->observer->connectDatabase(microtime(true) - $time);
110
                $this->id = static::$connectionCount;
111
                static::$connectionCount ++;
112
            } catch (PDOException $e) {
113
                throw new SQLException(sprintf(
114
                    "%s connect database error:%s",
115
                    $this->getName(),
116
                    $e->getMessage()
117
                ), $e->getCode(), $e);
118
            }
119
        }
120
        return $this->isConnected();
121
    }
122
123
    /**
124
     * @return array
125
     */
126
    public function __sleep()
127
    {
128
        return ['type', 'config', 'observer'];
129
    }
130
131
    /**
132
     * 获取PDO
133
     * @ignore-dump
134
     * @return PDO
135
     * @throws SQLException
136
     */
137
    public function getPdo()
138
    {
139
        $this->prepareConnection();
140
        return $this->pdo;
141
    }
142
143
    /**
144
     * 连接数据库
145
     *
146
     * @return void
147
     */
148
    protected function prepareConnection() {
149
        if (!$this->connect()) {
150
            throw new SQLException(sprintf(
151
                "%s data source is not connected",
152
                $this->getName()
153
            ), SQLException::ERR_NO_CONNECTION);
154
        }
155
    }
156
157
    /**
158
     * 获取配置
159
     *
160
     * @param string $name
161
     * @param mixed $default
162
     * @return mixed
163
     */
164
    public function getConfig(string $name, $default = null)
165
    {
166
        return $this->config[$name] ?? $default;
167
    }
168
169
    /**
170
     * 获取最后一次插入的主键ID(用于自增值
171
     *
172
     * @param string|null $name
173
     * @return string
174
     */
175
    public function lastInsertId(?string $name = null):string
176
    {
177
        $this->prepareConnection();
178
        if (null === $name) {
179
            return $this->pdo->lastInsertId();
180
        } else {
181
            return $this->pdo->lastInsertId($name);
182
        }
183
    }
184
185
    /**
186
     * 事务系列,开启事务
187
     *
188
     * @return void
189
     */
190
    public function begin()
191
    {
192
        $this->beginTransaction();
193
    }
194
195
    /**
196
     * 事务系列,开启事务
197
     *
198
     * @return void
199
     */
200
    public function beginTransaction()
201
    {
202
        $this->prepareConnection();
203
        $this->transaction ++;
204
        if ($this->transaction == 1) {
205
            $this->pdo->beginTransaction();
206
        }
207
    }
208
209
    /**
210
     * 判断是否连接
211
     *
212
     * @return boolean
213
     */
214
    public function isConnected():bool
215
    {
216
        return $this->pdo !== null;
217
    }
218
219
    /**
220
     * 事务系列,提交事务
221
     *
222
     * @return void
223
     */
224
    public function commit()
225
    {
226
        $this->prepareConnection();
227
        if ($this->transaction == 1) {
228
            $this->pdo->commit();
229
        }
230
        $this->transaction--;
231
    }
232
233
    /**
234
     * 事务系列,撤销事务
235
     *
236
     * @return void
237
     */
238
    public function rollBack()
239
    {
240
        $this->prepareConnection();
241
        if ($this->transaction == 1) {
242
            $this->transaction = 0;
243
            $this->pdo->rollBack();
244
        } else {
245
            $this->transaction--;
246
        }
247
    }
248
249
    /**
250
     * 事务关闭检测
251
     *
252
     * @return void
253
     * @throws SQLException
254
     */
255
    protected function onBeforeSystemShutdown()
256
    {
257
        if ($this->pdo && ($this->transaction > 0 || $this->pdo->inTransaction())) {
258
            throw new SQLException(sprintf(
259
                "SQL transaction is open (%d) in connection %s",
260
                $this->transaction,
261
                $this->__toString()
262
            ), SQLException::ERR_TRANSACTION);
263
        }
264
    }
265
266
    /**
267
     * 查询SQL
268
     *
269
     * @param Statement $statement
270
     * @return mixed
271
     * @throws SQLException
272
     */
273
    public function query(Statement $statement)
274
    {
275
        $source = new DataSource();
276
        $source->add($this);
277
        return (new QueryAccess($source))->run($statement);
278
    }
279
280
    /**
281
     * 转义字符
282
     *
283
     * @param $string
284
     * @return string
285
     * @throws SQLException
286
     */
287
    public function quote($string)
288
    {
289
        return $this->getPdo()->quote($string);
290
    }
291
292
    /**
293
     * 转义字符
294
     *
295
     * @param array $array
296
     * @return string
297
     * @throws SQLException
298
     */
299
    public function arrayQuote(array $array)
300
    {
301
        $temp = array();
302
        foreach ($array as $value) {
303
            $temp[] = is_int($value) ? $value : $this->quote($value);
304
        }
305
        return implode(',', $temp);
306
    }
307
308
    /**
309
     * 统计查询数量
310
     *
311
     * @return void
312
     */
313
    public function countQuery()
314
    {
315
        $this->queryCount++;
316
    }
317
318
    /**
319
     * @return string
320
     */
321
    public function __toString()
322
    {
323
        return $this->getName();
324
    }
325
326
    /**
327
     * @param string $name
328
     * @return mixed
329
     */
330
    abstract public function switchDatabase(string $name);
331
332
    /**
333
     * @param string $name
334
     * @return mixed
335
     */
336
    abstract public function rawTableName(string $name);
337
338
    /**
339
     * Get 链接别名
340
     *
341
     * @return  string
342
     */
343
    public function getName()
344
    {
345
        return 'DataConnection['.$this->name.'][at '.$this->getDsn().']';
346
    }
347
348
    /**
349
     * Get 性能观测
350
     *
351
     * @return  Observer
352
     */
353
    public function getObserver():Observer
354
    {
355
        return $this->observer;
356
    }
357
358
    /**
359
     * Set 性能观测
360
     *
361
     * @param  Observer  $observer  性能观测
362
     *
363
     * @return  self
364
     */
365
    public function setObserver(Observer $observer)
366
    {
367
        $this->observer = $observer;
368
369
        return $this;
370
    }
371
372
373
    /**
374
     * @return string
375
     */
376
    public function getType(): string
377
    {
378
        return $this->type;
379
    }
380
381
    /**
382
     * @param string $type
383
     */
384
    public function setType(string $type): void
385
    {
386
        $this->type = $type;
387
    }
388
389
390
    /**
391
     * 自动填充前缀
392
     *
393
     * @param string $query
394
     * @return string
395
     */
396
    public function prefix(string $query):string
397
    {
398
        // _:table 前缀控制
399
        $prefix = $this->getConfig('prefix', '');
400
        return preg_replace('/_:(\w+)/', $prefix.'$1', $query);
401
    }
402
}
403