1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* The PMF_DB_Mysqli class provides methods and functions for MySQL 5.x and |
5
|
|
|
* MariaDB 5.x databases. |
6
|
|
|
* |
7
|
|
|
* PHP Version 5.5 |
8
|
|
|
* |
9
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public License, |
10
|
|
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can |
11
|
|
|
* obtain one at http://mozilla.org/MPL/2.0/. |
12
|
|
|
* |
13
|
|
|
* @category phpMyFAQ |
14
|
|
|
* |
15
|
|
|
* @author Thorsten Rinne <[email protected]> |
16
|
|
|
* @author David Soria Parra <[email protected]> |
17
|
|
|
* @copyright 2005-2016 phpMyFAQ Team |
18
|
|
|
* @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 |
19
|
|
|
* |
20
|
|
|
* @link http://www.phpmyfaq.de |
21
|
|
|
*/ |
22
|
|
|
if (!defined('IS_VALID_PHPMYFAQ')) { |
23
|
|
|
exit(); |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* PMF_DB_Mysqli. |
28
|
|
|
* |
29
|
|
|
* @category phpMyFAQ |
30
|
|
|
* |
31
|
|
|
* @author Thorsten Rinne <[email protected]> |
32
|
|
|
* @author David Soria Parra <[email protected]> |
33
|
|
|
* @copyright 2005-2016 phpMyFAQ Team |
34
|
|
|
* @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 |
35
|
|
|
* |
36
|
|
|
* @link http://www.phpmyfaq.de |
37
|
|
|
*/ |
38
|
|
|
class PMF_DB_Mysqli implements PMF_DB_Driver |
39
|
|
|
{ |
40
|
|
|
/** |
41
|
|
|
* The connection object. |
42
|
|
|
* |
43
|
|
|
* @var mysqli |
44
|
|
|
*/ |
45
|
|
|
private $conn = false; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* The query log string. |
49
|
|
|
* |
50
|
|
|
* @var string |
51
|
|
|
*/ |
52
|
|
|
private $sqllog = ''; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Tables. |
56
|
|
|
* |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
public $tableNames = []; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Connects to the database. |
63
|
|
|
* |
64
|
|
|
* @param string $host Hostname or path to socket |
65
|
|
|
* @param string $user Username |
66
|
|
|
* @param string $password Password |
67
|
|
|
* @param string $database Database name |
68
|
|
|
* |
69
|
|
|
* @throws PMF_Exception |
70
|
|
|
* |
71
|
|
|
* @return bool true, if connected, otherwise false |
72
|
|
|
*/ |
73
|
|
|
public function connect($host, $user, $password, $database = '') |
74
|
|
|
{ |
75
|
|
|
if (substr($host, 0, 1) === '/') { |
76
|
|
|
// Connect to MySQL via socket |
77
|
|
|
$this->conn = new mysqli(null, $user, $password, null, null, $host); |
78
|
|
|
} else { |
79
|
|
|
// Connect to MySQL via network |
80
|
|
|
$this->conn = new mysqli($host, $user, $password); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
if ($this->conn->connect_error) { |
84
|
|
|
PMF_Db::errorPage($this->conn->connect_errno.': '.$this->conn->connect_error); |
85
|
|
|
die(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
// change character set to UTF-8 |
89
|
|
|
if (!$this->conn->set_charset('utf8')) { |
90
|
|
|
PMF_Db::errorPage($this->error()); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
if ('' !== $database) { |
94
|
|
|
if (!$this->conn->select_db($database)) { |
95
|
|
|
throw new PMF_Exception('Cannot connect to database '.$database); |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return true; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* This function sends a query to the database. |
104
|
|
|
* |
105
|
|
|
* @param string $query |
106
|
|
|
* @param int $offset |
107
|
|
|
* @param int $rowCount |
108
|
|
|
* |
109
|
|
|
* @return mysqli_result $result |
110
|
|
|
*/ |
111
|
|
View Code Duplication |
public function query($query, $offset = 0, $rowCount = 0) |
|
|
|
|
112
|
|
|
{ |
113
|
|
|
if (DEBUG) { |
114
|
|
|
$this->sqllog .= PMF_Utils::debug($query); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
if (0 < $rowCount) { |
118
|
|
|
$query .= sprintf(' LIMIT %d,%d', $offset, $rowCount); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$result = $this->conn->query($query); |
122
|
|
|
|
123
|
|
|
if (!$result) { |
124
|
|
|
$this->sqllog .= $this->error(); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return $result; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Escapes a string for use in a query. |
132
|
|
|
* |
133
|
|
|
* @param string |
134
|
|
|
* |
135
|
|
|
* @return string |
136
|
|
|
*/ |
137
|
|
|
public function escape($string) |
138
|
|
|
{ |
139
|
|
|
return $this->conn->real_escape_string($string); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Fetch a result row as an object. |
144
|
|
|
* |
145
|
|
|
* This function fetches a result row as an object. |
146
|
|
|
* |
147
|
|
|
* @param mixed $result |
148
|
|
|
* |
149
|
|
|
* @return mixed |
150
|
|
|
*/ |
151
|
|
|
public function fetchObject($result) |
152
|
|
|
{ |
153
|
|
|
return $result->fetch_object(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Fetch a result row as an object. |
158
|
|
|
* |
159
|
|
|
* This function fetches a result as an associative array. |
160
|
|
|
* |
161
|
|
|
* @param mixed $result |
162
|
|
|
* |
163
|
|
|
* @return array |
164
|
|
|
*/ |
165
|
|
|
public function fetchArray($result) |
166
|
|
|
{ |
167
|
|
|
return $result->fetch_assoc(); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Fetches a complete result as an object. |
172
|
|
|
* |
173
|
|
|
* @param resource $result Resultset |
174
|
|
|
* |
175
|
|
|
* @throws Exception |
176
|
|
|
* |
177
|
|
|
* @return array |
178
|
|
|
*/ |
179
|
|
View Code Duplication |
public function fetchAll($result) |
180
|
|
|
{ |
181
|
|
|
$ret = []; |
182
|
|
|
if (false === $result) { |
183
|
|
|
throw new Exception('Error while fetching result: '.$this->error()); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
while ($row = $this->fetchObject($result)) { |
187
|
|
|
$ret[] = $row; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return $ret; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Number of rows in a result. |
195
|
|
|
* |
196
|
|
|
* @param mixed $result |
197
|
|
|
* |
198
|
|
|
* @return int |
199
|
|
|
*/ |
200
|
|
|
public function numRows($result) |
201
|
|
|
{ |
202
|
|
|
if ($result instanceof mysqli_result) { |
203
|
|
|
return $result->num_rows; |
204
|
|
|
} else { |
205
|
|
|
return 0; |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Logs the queries. |
211
|
|
|
* |
212
|
|
|
* @return string |
213
|
|
|
*/ |
214
|
|
|
public function log() |
215
|
|
|
{ |
216
|
|
|
return $this->sqllog; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* This function returns the table status. |
221
|
|
|
* |
222
|
|
|
* @param string $prefix Table prefix |
223
|
|
|
* |
224
|
|
|
* @return array |
225
|
|
|
*/ |
226
|
|
|
public function getTableStatus($prefix = '') |
227
|
|
|
{ |
228
|
|
|
$status = []; |
229
|
|
|
foreach ($this->getTableNames($prefix) as $table) { |
230
|
|
|
$status[$table] = $this->getOne('SELECT count(*) FROM '.$table); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return $status; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* This function is a replacement for MySQL's auto-increment so that |
238
|
|
|
* we don't need it anymore. |
239
|
|
|
* |
240
|
|
|
* @param string $table The name of the table |
241
|
|
|
* @param string $id The name of the ID column |
242
|
|
|
* |
243
|
|
|
* @return int |
244
|
|
|
*/ |
245
|
|
|
public function nextId($table, $id) |
246
|
|
|
{ |
247
|
|
|
$select = sprintf(' |
248
|
|
|
SELECT |
249
|
|
|
MAX(%s) AS current_id |
250
|
|
|
FROM |
251
|
|
|
%s', |
252
|
|
|
$id, |
253
|
|
|
$table); |
254
|
|
|
|
255
|
|
|
$result = $this->query($select); |
256
|
|
|
|
257
|
|
|
if ($result instanceof mysqli_result) { |
258
|
|
|
$current = $result->fetch_row(); |
259
|
|
|
} else { |
260
|
|
|
$current = [0]; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
return $current[0] + 1; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Returns the error string. |
268
|
|
|
* |
269
|
|
|
* @return string |
270
|
|
|
*/ |
271
|
|
|
public function error() |
272
|
|
|
{ |
273
|
|
|
return $this->conn->error; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Returns the client version string. |
278
|
|
|
* |
279
|
|
|
* @return string |
280
|
|
|
*/ |
281
|
|
|
public function clientVersion() |
282
|
|
|
{ |
283
|
|
|
return $this->conn->get_client_info(); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Returns the server version string. |
288
|
|
|
* |
289
|
|
|
* @return string |
290
|
|
|
*/ |
291
|
|
|
public function serverVersion() |
292
|
|
|
{ |
293
|
|
|
return $this->conn->server_info; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Returns an array with all table names. |
298
|
|
|
* |
299
|
|
|
* @todo Have to be refactored because of https://github.com/thorsten/phpMyFAQ/issues/965 |
300
|
|
|
* |
301
|
|
|
* @param string $prefix Table prefix |
302
|
|
|
* |
303
|
|
|
* @return array |
304
|
|
|
*/ |
305
|
|
View Code Duplication |
public function getTableNames($prefix = '') |
306
|
|
|
{ |
307
|
|
|
return $this->tableNames = [ |
308
|
|
|
$prefix.'faqadminlog', |
309
|
|
|
$prefix.'faqattachment', |
310
|
|
|
$prefix.'faqattachment_file', |
311
|
|
|
$prefix.'faqcaptcha', |
312
|
|
|
$prefix.'faqcategories', |
313
|
|
|
$prefix.'faqcategory_group', |
314
|
|
|
$prefix.'faqcategory_user', |
315
|
|
|
$prefix.'faqcategoryrelations', |
316
|
|
|
$prefix.'faqchanges', |
317
|
|
|
$prefix.'faqcomments', |
318
|
|
|
$prefix.'faqconfig', |
319
|
|
|
$prefix.'faqdata', |
320
|
|
|
$prefix.'faqdata_group', |
321
|
|
|
$prefix.'faqdata_revisions', |
322
|
|
|
$prefix.'faqdata_tags', |
323
|
|
|
$prefix.'faqdata_user', |
324
|
|
|
$prefix.'faqglossary', |
325
|
|
|
$prefix.'faqgroup', |
326
|
|
|
$prefix.'faqgroup_right', |
327
|
|
|
$prefix.'faqinstances', |
328
|
|
|
$prefix.'faqinstances_config', |
329
|
|
|
$prefix.'faqnews', |
330
|
|
|
$prefix.'faqquestions', |
331
|
|
|
$prefix.'faqright', |
332
|
|
|
$prefix.'faqsearches', |
333
|
|
|
$prefix.'faqsessions', |
334
|
|
|
$prefix.'faqstopwords', |
335
|
|
|
$prefix.'faqtags', |
336
|
|
|
$prefix.'faquser', |
337
|
|
|
$prefix.'faquser_group', |
338
|
|
|
$prefix.'faquser_right', |
339
|
|
|
$prefix.'faquserdata', |
340
|
|
|
$prefix.'faquserlogin', |
341
|
|
|
$prefix.'faqvisits', |
342
|
|
|
$prefix.'faqvoting', |
343
|
|
|
]; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Closes the connection to the database. |
348
|
|
|
*/ |
349
|
|
|
public function close() |
350
|
|
|
{ |
351
|
|
|
if (is_resource($this->conn)) { |
352
|
|
|
$this->conn->close(); |
|
|
|
|
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Destructor. |
358
|
|
|
*/ |
359
|
|
|
public function __destruct() |
360
|
|
|
{ |
361
|
|
|
if (is_resource($this->conn)) { |
362
|
|
|
$this->conn->close(); |
|
|
|
|
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* @return string |
368
|
|
|
*/ |
369
|
|
|
public function now() |
370
|
|
|
{ |
371
|
|
|
return 'NOW()'; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Returns just one row. |
376
|
|
|
* |
377
|
|
|
* @param string |
378
|
|
|
* |
379
|
|
|
* @return string |
380
|
|
|
*/ |
381
|
|
|
private function getOne($query) |
382
|
|
|
{ |
383
|
|
|
$row = $this->conn->query($query)->fetch_row(); |
384
|
|
|
|
385
|
|
|
return $row[0]; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
} |
389
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.