Completed
Push — master ( ebcddb...625177 )
by Kanto
14s queued 11s
created

DB::beginTransaction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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