1
|
|
|
<?php /** MicroDataBaseConnection */ |
2
|
|
|
|
3
|
|
|
namespace Micro\Db; |
4
|
|
|
|
5
|
|
|
use Micro\Base\Exception; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Connection class file. |
9
|
|
|
* |
10
|
|
|
* @author Oleg Lunegov <[email protected]> |
11
|
|
|
* @link https://github.com/linpax/microphp-framework |
12
|
|
|
* @copyright Copyright (c) 2013 Oleg Lunegov |
13
|
|
|
* @license https://github.com/linpax/microphp-framework/blob/master/LICENSE |
14
|
|
|
* @package Micro |
15
|
|
|
* @subpackage Db |
16
|
|
|
* @version 1.0 |
17
|
|
|
* @since 1.0 |
18
|
|
|
*/ |
19
|
|
|
class DbConnection extends Connection |
20
|
|
|
{ |
21
|
|
|
/** @var \PDO|null $conn Connection to DB */ |
22
|
|
|
protected $conn; |
23
|
|
|
/** @var string $tableSchema */ |
24
|
|
|
protected $tableSchema = 'public'; |
25
|
|
|
|
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Construct for this class |
29
|
|
|
* |
30
|
|
|
* @access public |
31
|
|
|
* |
32
|
|
|
* @param array $config |
33
|
|
|
* @param array $options |
34
|
|
|
* |
35
|
|
|
* @result void |
36
|
|
|
* @throws Exception |
37
|
|
|
*/ |
38
|
|
|
public function __construct(array $config = [], array $options = []) |
39
|
|
|
{ |
40
|
|
|
parent::__construct(); |
41
|
|
|
|
42
|
|
|
if (!empty($config['schema'])) { |
43
|
|
|
$this->tableSchema = $config['schema']; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
try { |
47
|
|
|
$this->conn = new \PDO($config['connectionString'], $config['username'], $config['password'], |
48
|
|
|
$options |
49
|
|
|
); |
50
|
|
|
} catch (\PDOException $e) { |
51
|
|
|
if (!array_key_exists('ignoreFail', $config) || !$config['ignoreFail']) { |
52
|
|
|
throw new Exception('Connect to DB failed: '.$e->getMessage()); |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Destructor for this class |
59
|
|
|
* |
60
|
|
|
* @access public |
61
|
|
|
* @return void |
62
|
|
|
*/ |
63
|
|
|
public function __destruct() |
64
|
|
|
{ |
65
|
|
|
$this->conn = null; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @inheritdoc |
70
|
|
|
*/ |
71
|
|
|
public function rawQuery($query = '', array $params = [], $fetchType = \PDO::FETCH_ASSOC, $fetchClass = 'Model') |
72
|
|
|
{ |
73
|
|
|
$sth = $this->conn->prepare($query); |
74
|
|
|
|
75
|
|
|
if ($fetchType === \PDO::FETCH_CLASS) { |
76
|
|
|
/** @noinspection PhpMethodParametersCountMismatchInspection */ |
77
|
|
|
$sth->setFetchMode($fetchType, ucfirst($fetchClass), ['new' => false]); |
78
|
|
|
} else { |
79
|
|
|
$sth->setFetchMode($fetchType); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
foreach ($params AS $name => $value) { |
83
|
|
|
$sth->bindValue($name, $value); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$sth->execute(); |
87
|
|
|
|
88
|
|
|
return $sth->fetchAll(); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @inheritdoc |
93
|
|
|
*/ |
94
|
|
|
public function listDatabases() |
95
|
|
|
{ |
96
|
|
|
$sth = $this->conn->query('SHOW DATABASES;'); |
97
|
|
|
|
98
|
|
|
$result = []; |
99
|
|
|
foreach ($sth->fetchAll() AS $row) { |
100
|
|
|
$result[] = $row[0]; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
return $result; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @inheritdoc |
108
|
|
|
*/ |
109
|
|
|
public function infoDatabase($dbName) |
110
|
|
|
{ |
111
|
|
|
$sth = $this->conn->query("SHOW TABLE STATUS FROM {$dbName};"); |
112
|
|
|
|
113
|
|
|
$result = []; |
114
|
|
|
foreach ($sth->fetchAll() AS $row) { |
115
|
|
|
$result[] = [ |
116
|
|
|
'name' => $row['Name'], |
117
|
|
|
'engine' => $row['Engine'], |
118
|
|
|
'rows' => $row['Rows'], |
119
|
|
|
'length' => $row['Avg_row_length'], |
120
|
|
|
'increment' => $row['Auto_increment'], |
121
|
|
|
'collation' => $row['Collation'] |
122
|
|
|
]; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
return $result; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @inheritdoc |
130
|
|
|
*/ |
131
|
|
|
public function tableExists($table) |
132
|
|
|
{ |
133
|
|
|
return in_array($table, $this->listTables(), false); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @inheritdoc |
138
|
|
|
*/ |
139
|
|
|
public function listTables() |
140
|
|
|
{ |
141
|
|
|
$sql = 'SHOW TABLES;'; |
142
|
|
|
|
143
|
|
|
if ($this->getDriverType() == 'pgsql') { |
144
|
|
|
$sql = 'SELECT table_name FROM information_schema.tables WHERE table_schema = \'' . $this->tableSchema . '\''; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $this->conn->query($sql)->fetchAll(\PDO::FETCH_COLUMN, 0); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
public function getDriverType() |
151
|
|
|
{ |
152
|
|
|
return $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* @inheritdoc |
157
|
|
|
*/ |
158
|
|
|
public function createTable($name, array $elements = [], $params = '') |
159
|
|
|
{ |
160
|
|
|
return $this->conn->exec( |
161
|
|
|
sprintf('SELECT TABLE IF NOT EXISTS `%s` (%s) %s;', $name, implode(',', $elements), $params) |
162
|
|
|
); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @inheritdoc |
167
|
|
|
*/ |
168
|
|
|
public function clearTable($name) |
169
|
|
|
{ |
170
|
|
|
return $this->conn->exec("TRUNCATE {$name};"); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @inheritdoc |
175
|
|
|
*/ |
176
|
|
|
public function removeTable($name) |
177
|
|
|
{ |
178
|
|
|
return $this->conn->exec("DROP TABLE {$name};"); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @inheritdoc |
183
|
|
|
*/ |
184
|
|
|
public function fieldExists($field, $table) |
185
|
|
|
{ |
186
|
|
|
foreach ($this->listFields($table) AS $tbl) { |
187
|
|
|
if ($tbl['field'] === $field) { |
188
|
|
|
return true; |
189
|
|
|
} |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return false; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @inheritdoc |
197
|
|
|
*/ |
198
|
|
|
public function listFields($table) |
199
|
|
|
{ |
200
|
|
|
$sth = $this->conn->query("SHOW COLUMNS FROM {$table};"); |
201
|
|
|
|
202
|
|
|
$result = []; |
203
|
|
|
foreach ($sth->fetchAll(\PDO::FETCH_ASSOC) as $row) { |
204
|
|
|
$result[] = [ |
205
|
|
|
'field' => $row['Field'], |
206
|
|
|
'type' => $row['Type'], |
207
|
|
|
'null' => $row['Null'], |
208
|
|
|
'key' => $row['Key'], |
209
|
|
|
'default' => $row['Default'], |
210
|
|
|
'extra' => $row['Extra'] |
211
|
|
|
]; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
return $result; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @inheritdoc |
219
|
|
|
*/ |
220
|
|
|
public function fieldInfo($field, $table) |
221
|
|
|
{ |
222
|
|
|
return $this->conn->query("SELECT {$field} FROM {$table} LIMIT 1;")->getColumnMeta(0); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* @inheritdoc |
227
|
|
|
*/ |
228
|
|
|
public function switchDatabase($dbName) |
229
|
|
|
{ |
230
|
|
|
if ($this->conn->exec("USE {$dbName};") !== false) { |
231
|
|
|
return true; |
232
|
|
|
} else { |
233
|
|
|
return false; |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @inheritdoc |
239
|
|
|
*/ |
240
|
|
|
public function insert($table, array $line = [], $multi = false) |
241
|
|
|
{ |
242
|
|
|
$fields = '`'.implode('`, `', array_keys($multi ? $line[0] : $line)).'`'; |
243
|
|
|
$values = ':'.implode(', :', array_keys($multi ? $line[0] : $line)); |
244
|
|
|
$rows = $multi ? $line : [$line]; |
245
|
|
|
$id = null; |
246
|
|
|
|
247
|
|
|
if ($rows) { |
|
|
|
|
248
|
|
|
$this->conn->beginTransaction(); |
249
|
|
|
|
250
|
|
|
$dbh = null; |
251
|
|
|
foreach ($rows AS $row) { |
252
|
|
|
$dbh = $this->conn->prepare("INSERT INTO {$table} ({$fields}) VALUES ({$values});")->execute($row); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
$id = $dbh ? $this->conn->lastInsertId() : false; |
256
|
|
|
$this->conn->commit(); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return $id ?: false; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @inheritdoc |
264
|
|
|
*/ |
265
|
|
|
public function update($table, array $elements = [], $conditions = '') |
266
|
|
|
{ |
267
|
|
|
$keys = array_keys($elements); |
268
|
|
|
if (0 === count($keys)) { |
269
|
|
|
return false; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
$valStr = []; |
273
|
|
|
foreach ($keys as $key) { |
274
|
|
|
$valStr[] = '`'.$key.'` = :'.$key; |
275
|
|
|
} |
276
|
|
|
$valStr = implode(',', $valStr); |
277
|
|
|
|
278
|
|
|
if ($conditions) { |
279
|
|
|
$conditions = 'WHERE '.$conditions; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
return $this->conn->prepare("UPDATE {$table} SET {$valStr} {$conditions};")->execute($elements); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @inheritdoc |
287
|
|
|
*/ |
288
|
|
|
public function delete($table, $conditions, array $ph = []) |
289
|
|
|
{ |
290
|
|
|
return $this->conn->prepare("DELETE FROM {$table} WHERE {$conditions};")->execute($ph); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* @inheritdoc |
295
|
|
|
*/ |
296
|
|
|
public function exists($table, array $params = []) |
297
|
|
|
{ |
298
|
|
|
$keys = []; |
299
|
|
|
foreach ($params AS $key => $val) { |
300
|
|
|
$keys[] = $table . '.' . $key . '=\'' . $val . '\''; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$sth = $this->conn->prepare('SELECT * FROM ' . $table . ' WHERE ' . implode(' AND ', $keys) . ' LIMIT 1;'); |
304
|
|
|
/** @noinspection PdoApiUsageInspection */ |
305
|
|
|
$sth->execute(); |
306
|
|
|
|
307
|
|
|
return (bool)$sth->rowCount(); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @inheritdoc |
312
|
|
|
*/ |
313
|
|
|
public function count($subQuery = '', $table = '') |
314
|
|
|
{ |
315
|
|
|
if ($subQuery) { |
316
|
|
|
$sth = $this->conn->prepare("SELECT COUNT(*) FROM ({$subQuery}) AS m;"); |
317
|
|
|
} elseif ($table) { |
318
|
|
|
$sth = $this->conn->prepare("SELECT COUNT(*) FROM {$table} AS m;"); |
319
|
|
|
} else { |
320
|
|
|
return false; |
321
|
|
|
} |
322
|
|
|
if ($sth->execute()) { |
323
|
|
|
return $sth->fetchColumn(); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
return false; |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.