DB::connection()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Kore : Simple And Minimal Framework
4
 *
5
 */
6
7
namespace Kore;
8
9
use Kore\Config;
10
use Kore\Log;
11
use PDO;
12
13
/**
14
 * DB class
15
 *
16
 * Managing database operations
17
 */
18
class DB
19
{
20
    /**
21
     * PDO instance
22
     *
23
     * @var PDO
24
     */
25
    protected $pdo;
26
27
    /**
28
     * __construct method
29
     *
30
     * @param string $dbconfig DB configuration key
31
     * @return void
32
     */
33
    final private function __construct($dbconfig)
34
    {
35
        $this->connect($dbconfig);
36
    }
37
38
    /**
39
     * __clone method
40
     *
41
     * @return void
42
     * @throws \Exception Thrown in when __clone is called
43
     */
44
    final public function __clone()
45
    {
46
        throw new \Exception('__clone is not allowed!');
47
    }
48
49
    /**
50
     * Get DB instance corresponding to the DB configuration key
51
     *
52
     * @param string $dbconfig DB configuration key
53
     * @return self DB instance
54
     */
55
    public static function connection($dbconfig='database')
56
    {
57
        static $instances = array();
58
        if (empty($instances[$dbconfig])) {
59
            $instances[$dbconfig] = new static($dbconfig);
60
        }
61
        return $instances[$dbconfig];
62
    }
63
64
    /**
65
     * Access DB
66
     *
67
     * @param string $dbconfig DB configuration key
68
     * @return void
69
     * @throws \Exception Thrown in when the DB configuration does not exist
70
     * @throws \PDOException Thrown when DB access fails
71
     */
72
    protected function connect($dbconfig)
73
    {
74
        $config = Config::get($dbconfig);
75
        if ($config === null) {
76
            throw new \Exception("Database config $dbconfig is not found!");
77
        }
78
        $host = $config['host'];
79
        $db = $config['db'];
80
        $port = $config['port'];
81
        $dsn = sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8', $host, $port, $db);
82
83
        $user = $config['user'];
84
        $pass = $config['pass'];
85
86
        $options = array(
87
            PDO::ATTR_EMULATE_PREPARES => false,
88
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
89
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
90
        );
91
92
        $this->pdo = new PDO($dsn, $user, $pass, $options);
93
    }
94
95
    /**
96
     * Get PDO instance
97
     *
98
     * @return PDO PDO instance
99
     */
100
    public function getPdo()
101
    {
102
        return $this->pdo;
103
    }
104
105
    /**
106
     * Run the select statement
107
     *
108
     * @param string $query SQL query
109
     * @param array<mixed> $params SQL query parameters
110
     * @return array<mixed> query results
111
     */
112
    public function select($query, $params = array())
113
    {
114
        $stm = $this->execute($query, $params);
115
116
        $result = $stm->fetchAll();
117
        return $result !== false ? $result : array();
118
    }
119
120
    /**
121
     * Run the select statement, get only one record
122
     *
123
     * @param string $query SQL query
124
     * @param array<mixed> $params SQL query parameters
125
     * @return array<mixed> query results
126
     */
127
    public function selectFirst($query, $params = array())
128
    {
129
        $stm = $this->execute($query, $params);
130
        
131
        $result = $stm->fetch();
132
        return $result !== false ? $result: array();
133
    }
134
135
    /**
136
     * Run the select count statement
137
     *
138
     * @param string $query SQL query
139
     * @param array<mixed> $params SQL query parameters
140
     * @return mixed query results
141
     */
142
    public function count($query, $params = array())
143
    {
144
        $stm = $this->execute($query, $params);
145
146
        $result = $stm->fetchColumn();
147
        return $result !== false ? $result : 0;
148
    }
149
150
    /**
151
     * Run the insert statement
152
     *
153
     * @param string $query SQL query
154
     * @param array<mixed> $params SQL query parameters
155
     * @return string last insert ID
156
     */
157
    public function insert($query, $params = array())
158
    {
159
        $this->execute($query, $params);
160
161
        return $this->pdo->lastInsertId();
162
    }
163
164
    /**
165
     * Run the update statement
166
     *
167
     * @param string $query SQL query
168
     * @param array<mixed> $params SQL query parameters
169
     * @return int number of updated records
170
     */
171
    public function update($query, $params = array())
172
    {
173
        $stm = $this->execute($query, $params);
174
175
        return $stm->rowCount();
176
    }
177
178
    /**
179
     * Run the delete statement
180
     *
181
     * @param string $query SQL query
182
     * @param array<mixed> $params SQL query parameters
183
     * @return int number of deleted records
184
     */
185
    public function delete($query, $params = array())
186
    {
187
        $stm = $this->execute($query, $params);
188
189
        return $stm->rowCount();
190
    }
191
192
    /**
193
     * Run in a transaction
194
     *
195
     * Rollback if an exception is raised in the callback.
196
     * @param callable $callback callback processing
197
     * @return void
198
     * @throws \Exception
199
     */
200
    public function transaction($callback)
201
    {
202
        try {
203
            $this->beginTransaction();
204
            $callback();
205
            $this->commit();
206
        } catch (\Exception $e) {
207
            $this->rollback();
208
            throw $e;
209
        }
210
    }
211
212
    /**
213
     * Begin transaction
214
     *
215
     * @return void
216
     */
217
    public function beginTransaction()
218
    {
219
        $this->pdo->beginTransaction();
220
    }
221
222
    /**
223
     * Commit
224
     *
225
     * @return void
226
     */
227
    public function commit()
228
    {
229
        $this->pdo->commit();
230
    }
231
232
    /**
233
     * Rollback
234
     *
235
     * @return void
236
     */
237
    public function rollback()
238
    {
239
        $this->pdo->rollback();
240
    }
241
242
    /**
243
     * Get the IN clause
244
     *
245
     * @param string $marker parameter marker
246
     * @param array<mixed> $values value specified in the IN clause
247
     * @return array<mixed> IN clause information
248
     *                      array(
249
     *                          'IN (:MARKER_0, :MARKER_1, :MARKER_2)',
250
     *                          array(
251
     *                              'MARKER_0' => 'value',
252
     *                              'MARKER_1' => 'value',
253
     *                              'MARKER_2' => 'value'
254
     *                          )
255
     *                      )
256
     */
257
    public static function getInClause($marker, $values)
258
    {
259
        $inClause = 'IN (';
260
        $params = array();
261
        foreach ($values as $i => $value) {
262
            if ($i !== 0) {
263
                $inClause .= ', ';
264
            }
265
            $key = $marker.'_'.$i;
266
            $inClause .= ':'.$key;
267
            $params[$key] = $value;
268
        }
269
        $inClause .= ')';
270
        return array($inClause, $params);
271
    }
272
273
    /**
274
     * Run the query statement
275
     *
276
     * @param string $query SQL query
277
     * @param array<mixed> $params SQL query parameters
278
     * @return \PDOStatement \PDOStatement object
279
     */
280
    public function execute($query, $params = array())
281
    {
282
        $stm = $this->pdo->prepare($query);
283
284
        $keys = array();
285
        $values = array();
286
        foreach ($params as $key => $value) {
287
            $parameter = ":{$key}";
288
            $keys[] = $parameter;
289
            $values[] = $this->debugValue($value);
290
            $stm->bindValue($parameter, $value, $this->dataType($value));
291
        }
292
293
        $start = microtime(true);
294
        $stm->execute();
295
        $end = microtime(true);
296
        /** @phpstan-ignore-next-line */
297
        Log::debug(sprintf('%f - %s', $end-$start, str_replace($keys, $values, preg_replace('/[\n\t\s]+/', ' ', $query))));
298
        return $stm;
299
    }
300
301
    /**
302
     * Get the debug value from the variable
303
     *
304
     * @param mixed $value variable
305
     * @return mixed debug value
306
     */
307
    private function debugValue($value)
308
    {
309
        switch (gettype($value)) {
310
            case 'string':
311
                $debugValue = "'$value'";
312
                break;
313
            case 'boolean':
314
                $debugValue = $value ? 'true' : 'false';
315
                break;
316
            case 'NULL':
317
                $debugValue = 'NULL';
318
                break;
319
            default:
320
                $debugValue = $value;
321
        
322
        }
323
        return $debugValue;
324
    }
325
326
    /**
327
     * Get the data_type from the variable
328
     *
329
     * @param mixed $value variable
330
     * @return int data_type
331
     */
332
    private function dataType($value)
333
    {
334
        switch (gettype($value)) {
335
            case 'boolean':
336
                $datatype = PDO::PARAM_BOOL;
337
                break;
338
            case 'integer':
339
                $datatype = PDO::PARAM_INT;
340
                break;
341
            case 'string':
342
                $datatype = PDO::PARAM_STR;
343
                break;
344
            case 'NULL':
345
                $datatype = PDO::PARAM_NULL;
346
                break;
347
            default:
348
                $datatype = PDO::PARAM_STR;
349
        }
350
        return $datatype;
351
    }
352
}
353