Passed
Push — master ( 566a3a...fbef46 )
by 世昌
02:19
created

Connection::beginTransaction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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