1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* |
5
|
|
|
*/ |
6
|
|
|
namespace Godsgood33\Php_Db; |
7
|
|
|
|
8
|
|
|
use Katzgrau\KLogger\Logger; |
9
|
|
|
use Psr\Log\LogLevel; |
10
|
|
|
use Exception; |
11
|
|
|
use mysqli; |
12
|
|
|
require_once 'DBConfig.php'; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* A generic database class |
16
|
|
|
* |
17
|
|
|
* @author Ryan Prather |
18
|
|
|
*/ |
19
|
|
|
class Database |
20
|
|
|
{ |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Constant defining a SELECT query |
24
|
|
|
* |
25
|
|
|
* @var integer |
26
|
|
|
*/ |
27
|
|
|
const SELECT = 1; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Constant defining a SELECT COUNT query |
31
|
|
|
* |
32
|
|
|
* @var integer |
33
|
|
|
*/ |
34
|
|
|
const SELECT_COUNT = 2; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Constant defining a CREATE TABLE query |
38
|
|
|
* |
39
|
|
|
* @var integer |
40
|
|
|
*/ |
41
|
|
|
const CREATE_TABLE = 3; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Constant defining DROP query |
45
|
|
|
* |
46
|
|
|
* @var integer |
47
|
|
|
*/ |
48
|
|
|
const DROP = 4; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Constant defining DELETE query |
52
|
|
|
* |
53
|
|
|
* @var integer |
54
|
|
|
*/ |
55
|
|
|
const DELETE = 5; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Constant defining INSERT query |
59
|
|
|
* |
60
|
|
|
* @var integer |
61
|
|
|
*/ |
62
|
|
|
const INSERT = 6; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Constant defining REPLACE query |
66
|
|
|
* |
67
|
|
|
* @var integer |
68
|
|
|
*/ |
69
|
|
|
const REPLACE = 7; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Constant defining UPDATE query |
73
|
|
|
* |
74
|
|
|
* @var integer |
75
|
|
|
*/ |
76
|
|
|
const UPDATE = 8; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Constant defining EXTENDED INSERT query |
80
|
|
|
* |
81
|
|
|
* @var integer |
82
|
|
|
*/ |
83
|
|
|
const EXTENDED_INSERT = 9; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Constant defining EXTENDED REPLACE query |
87
|
|
|
* |
88
|
|
|
* @var integer |
89
|
|
|
*/ |
90
|
|
|
const EXTENDED_REPLACE = 10; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Constant defining EXTENDED UPDATE query |
94
|
|
|
* |
95
|
|
|
* @var integer |
96
|
|
|
*/ |
97
|
|
|
const EXTENDED_UPDATE = 11; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Constant defining ALTER TABLE query |
101
|
|
|
* |
102
|
|
|
* @var integer |
103
|
|
|
*/ |
104
|
|
|
const ALTER_TABLE = 12; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Constant defining a TRUNCATE TABLE query |
108
|
|
|
* |
109
|
|
|
* @var integer |
110
|
|
|
*/ |
111
|
|
|
const TRUNCATE = 13; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* The mysqli connection |
115
|
|
|
* |
116
|
|
|
* @var \mysqli |
117
|
|
|
*/ |
118
|
|
|
private $c; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* To store the SQL statement |
122
|
|
|
* |
123
|
|
|
* @var string |
124
|
|
|
*/ |
125
|
|
|
public $sql = null; |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* A string to store the type of query that is being run |
129
|
|
|
* |
130
|
|
|
* @var int |
131
|
|
|
*/ |
132
|
|
|
private $query_type = null; |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* The result of the query |
136
|
|
|
* |
137
|
|
|
* @var mixed |
138
|
|
|
*/ |
139
|
|
|
private $result = null; |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Log level |
143
|
|
|
* |
144
|
|
|
* @var LogLevel |
145
|
|
|
*/ |
146
|
|
|
public $log_level = LogLevel::DEBUG; |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Variable to store the logger |
150
|
|
|
* |
151
|
|
|
* @var \Katzgrau\KLogger\Logger |
152
|
|
|
*/ |
153
|
|
|
private $logger = null; |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Variable to decide if we need to automatically run the queries after generating them |
157
|
|
|
* |
158
|
|
|
* @var boolean |
159
|
|
|
*/ |
160
|
|
|
public static $autorun = false; |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Constructor |
164
|
|
|
* |
165
|
|
|
* @param \mysqli $dbh |
166
|
|
|
* [optional] |
167
|
|
|
* [by ref] |
168
|
|
|
* mysqli object to perform queries. |
169
|
|
|
*/ |
170
|
|
|
public function __construct(&$dbh = null) |
171
|
|
|
{ |
172
|
|
|
if (! is_null($dbh) && is_a($dbh, "mysqli")) { |
173
|
|
|
$this->c = $dbh; |
174
|
|
|
} else { |
175
|
|
|
$this->c = new mysqli(PHP_DB_SERVER, PHP_DB_USER, PHP_DB_PWD, PHP_DB_SCHEMA); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
if ($this->c->connect_errno) { |
179
|
|
|
throw new Exception("Could not create database class due to error {$this->c->error}", E_ERROR); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$this->logger = new Logger("./", $this->log_level, [ |
|
|
|
|
183
|
|
|
'filename' => 'db.log', |
184
|
|
|
'dateFormat' => 'Y-m-d H:i:s.u', |
185
|
|
|
'logFormat' => "[{date}] {level}{level-padding} {message} {context}" |
186
|
|
|
]); |
187
|
|
|
|
188
|
|
|
$this->set_var("time_zone", "+00:00"); |
189
|
|
|
$this->set_var("sql_mode", ""); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Function to make sure that the database is connected |
194
|
|
|
* |
195
|
|
|
* @return boolean |
196
|
|
|
*/ |
197
|
|
|
public function is_connected() |
198
|
|
|
{ |
199
|
|
|
return $this->c->ping(); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Function to set the log level just in case there needs to be a change to the default log level |
204
|
|
|
* |
205
|
|
|
* @param LogLevel $level |
206
|
|
|
*/ |
207
|
|
|
public function set_log_level(LogLevel $level) |
208
|
|
|
{ |
209
|
|
|
$this->log_level = $level; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Function to return the currently selected database schema |
214
|
|
|
* |
215
|
|
|
* @return string |
216
|
|
|
*/ |
217
|
|
|
public function get_schema() |
218
|
|
|
{ |
219
|
|
|
if ($res = $this->c->query("SELECT DATABASE()")) { |
220
|
|
|
$row = $res->fetch_row(); |
221
|
|
|
return $row[0]; |
222
|
|
|
} |
223
|
|
|
return null; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Function to set schema |
228
|
|
|
* |
229
|
|
|
* @param string $schema |
230
|
|
|
*/ |
231
|
|
|
public function set_schema(string $schema) |
232
|
|
|
{ |
233
|
|
|
if (! $this->c->select_db($schema)) { |
234
|
|
|
throw new Exception("Failed to change databases to {$schema}", E_ERROR); |
235
|
|
|
} |
236
|
|
|
return true; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Method to set a MYSQL variable |
241
|
|
|
* |
242
|
|
|
* @param string $name |
243
|
|
|
* @param string $val |
244
|
|
|
* @return boolean |
245
|
|
|
*/ |
246
|
|
|
public function set_var(string $name, string $val) |
247
|
|
|
{ |
248
|
|
|
if (! $name || ! $val) { |
249
|
|
|
return false; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
return $this->c->real_query("SET $name = {$this->_escape($val)}"); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Function to execute the statement |
257
|
|
|
* |
258
|
|
|
* @param mixed $return |
259
|
|
|
* [optional] |
260
|
|
|
* MYSQLI constant to control what is returned from the mysqli_result object |
261
|
|
|
* @param string $sql |
262
|
|
|
* [optional] |
263
|
|
|
* Optional SQL query |
264
|
|
|
* |
265
|
|
|
* @return mixed |
266
|
|
|
*/ |
267
|
|
|
public function execute($return = MYSQLI_ASSOC, $class = null, $sql = null) |
268
|
|
|
{ |
269
|
|
|
if (! is_null($sql)) { |
270
|
|
|
$this->sql = $sql; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
if (is_a($this->c, 'mysqli')) { |
274
|
|
|
if (! $this->c->ping()) { |
275
|
|
|
$this->c = null; |
276
|
|
|
$this->c = new mysqli(PHP_DB_SERVER, PHP_DB_USER, PHP_DB_PWD, PHP_DB_SCHEMA); |
277
|
|
|
} |
278
|
|
|
} else { |
279
|
|
|
throw new Exception('Database was not connected', E_ERROR); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
$this->logger->debug($this->sql); |
283
|
|
|
|
284
|
|
|
try { |
285
|
|
|
if (in_array($this->query_type, [ |
286
|
|
|
self::SELECT, |
287
|
|
|
self::SELECT_COUNT |
288
|
|
|
])) { |
289
|
|
|
$this->result = $this->c->query($this->sql); |
290
|
|
View Code Duplication |
if ($this->c->error) { |
|
|
|
|
291
|
|
|
$this->log("There is an error " . $this->c->error, LogLevel::ERROR); |
292
|
|
|
throw new Exception("There was an error " . $this->c->error, E_ERROR); |
293
|
|
|
} |
294
|
|
|
} else { |
295
|
|
|
$this->result = $this->c->real_query($this->sql); |
296
|
|
View Code Duplication |
if ($this->c->errno) { |
|
|
|
|
297
|
|
|
$this->log("There was an error " . $this->c->error, LogLevel::ERROR); |
298
|
|
|
throw new Exception("There was an error " . $this->c->error, E_ERROR); |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
if ($return == MYSQLI_OBJECT && ! is_null($class) && class_exists($class)) { |
303
|
|
|
$this->result = $this->check_results($return, $class); |
304
|
|
|
} elseif ($return == MYSQLI_OBJECT && is_null($class)) { |
305
|
|
|
$this->result = $this->check_results($return, 'stdClass'); |
306
|
|
|
} else { |
307
|
|
|
$this->result = $this->check_results(MYSQLI_ASSOC); |
308
|
|
|
} |
309
|
|
|
} catch (Exception $e) { |
310
|
|
|
// die($e->getTraceAsString()); |
|
|
|
|
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
return $this->result; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Function to check the results and return what is expected |
318
|
|
|
* |
319
|
|
|
* @param mixed $return_type |
320
|
|
|
* [optional] |
321
|
|
|
* Optional return mysqli_result return type |
322
|
|
|
* |
323
|
|
|
* @return mixed |
324
|
|
|
*/ |
325
|
|
|
private function check_results($return_type = MYSQLI_ASSOC, $class = null) |
326
|
|
|
{ |
327
|
|
|
$res = null; |
328
|
|
|
|
329
|
|
|
switch ($this->query_type) { |
330
|
|
|
case self::SELECT_COUNT: |
331
|
|
|
if (! is_a($this->result, 'mysqli_result')) { |
332
|
|
|
$this->log("Error with return on query", LogLevel::ERROR); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
if ($this->result->num_rows == 1) { |
336
|
|
|
$row = $this->result->fetch_assoc(); |
337
|
|
|
if (isset($row['count'])) { |
338
|
|
|
$res = $row['count']; |
339
|
|
|
} |
340
|
|
|
} elseif ($this->result->num_rows > 1) { |
341
|
|
|
$res = $this->result->num_rows; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
mysqli_free_result($this->result); |
345
|
|
|
|
346
|
|
|
return $res; |
347
|
|
|
case self::SELECT: |
348
|
|
|
if (! is_a($this->result, 'mysqli_result')) { |
349
|
|
|
$this->log("Error with return on query", LogLevel::ERROR); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
if ($return_type == MYSQLI_OBJECT && ! is_null($class) && class_exists($class)) { |
353
|
|
|
if ($this->result->num_rows == 1) { |
354
|
|
|
$res = $this->result->fetch_object($class); |
355
|
|
|
} elseif ($this->result->num_rows > 1) { |
356
|
|
|
while ($row = $this->result->fetch_object($class)) { |
357
|
|
|
$res[] = $row; |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
} else { |
361
|
|
|
if ($this->result->num_rows == 1) { |
362
|
|
|
$res = $this->result->fetch_array($return_type); |
363
|
|
|
} elseif ($this->result->num_rows > 1) { |
364
|
|
|
$res = $this->fetch_all($return_type); |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
mysqli_free_result($this->result); |
369
|
|
|
|
370
|
|
|
return $res; |
371
|
|
|
case self::INSERT: |
372
|
|
|
if ($this->c->error) { |
373
|
|
|
$this->log("Database Error " . $this->c->error, LogLevel::ERROR); |
374
|
|
|
return 0; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
if ($this->c->insert_id) { |
378
|
|
|
return $this->c->insert_id; |
379
|
|
|
} elseif ($this->c->affected_rows) { |
380
|
|
|
return $this->c->affected_rows; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
return 1; |
384
|
|
|
case self::EXTENDED_INSERT: |
385
|
|
|
case self::EXTENDED_REPLACE: |
386
|
|
|
case self::EXTENDED_UPDATE: |
387
|
|
|
case self::REPLACE: |
388
|
|
|
case self::UPDATE: |
389
|
|
|
case self::DELETE: |
390
|
|
|
case self::ALTER_TABLE: |
|
|
|
|
391
|
|
|
if ($this->c->error && $this->c->errno == 1060) { |
392
|
|
|
return ($this->c->affected_rows ? $this->c->affected_rows : true); |
393
|
|
|
} elseif ($this->c->error) { |
394
|
|
|
$this->log("Database Error " . $this->c->error, LogLevel::ERROR); |
395
|
|
|
return false; |
396
|
|
|
} elseif ($this->c->affected_rows) { |
397
|
|
|
return $this->c->affected_rows; |
398
|
|
|
} else { |
399
|
|
|
return true; |
400
|
|
|
} |
401
|
|
|
case self::CREATE_TABLE: |
402
|
|
|
case self::DROP: |
403
|
|
|
case self::TRUNCATE: |
404
|
|
|
return true; |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* Function to pass through calling the query function (used for backwards compatibility and for more complex queries that aren't currently supported) |
410
|
|
|
* Nothing is escaped |
411
|
|
|
* |
412
|
|
|
* @param string $sql |
413
|
|
|
* [optional] |
414
|
|
|
* Optional query to pass in and execute |
415
|
|
|
* |
416
|
|
|
* @return \mysqli_result |
417
|
|
|
*/ |
418
|
|
|
public function query($sql = null) |
419
|
|
|
{ |
420
|
|
|
if (is_null($sql)) { |
421
|
|
|
return $this->c->query($this->sql); |
422
|
|
|
} else { |
423
|
|
|
return $this->c->query($sql); |
424
|
|
|
} |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* A function to build a select query |
429
|
|
|
* |
430
|
|
|
* @param string $table_name |
431
|
|
|
* The table to query |
432
|
|
|
* @param array $fields |
433
|
|
|
* [optional] |
434
|
|
|
* Optional array of fields to return (defaults to '*') |
435
|
|
|
* @param array $where |
436
|
|
|
* [optional] |
437
|
|
|
* Optional 2-dimensional array to build where clause from |
438
|
|
|
* @param array $flags |
439
|
|
|
* [optional] |
440
|
|
|
* Optional 2-dimensional array to allow other flags |
441
|
|
|
* |
442
|
|
|
* @see Database::where() |
443
|
|
|
* @see Database::flags() |
444
|
|
|
* |
445
|
|
|
* @return mixed |
446
|
|
|
*/ |
447
|
|
|
public function select($table_name, $fields = null, $where = null, $flags = null) |
448
|
|
|
{ |
449
|
|
|
$this->sql = null; |
450
|
|
|
$this->query_type = self::SELECT; |
451
|
|
|
|
452
|
|
|
if (! is_null($table_name) && is_string($table_name)) { |
453
|
|
|
$this->sql = "SELECT " . $this->fields($fields) . " FROM $table_name"; |
454
|
|
|
} else { |
455
|
|
|
throw new Exception("Table name is invalid", E_ERROR); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
View Code Duplication |
if (isset($flags['joins']) && is_array($flags['joins'])) { |
|
|
|
|
459
|
|
|
$this->sql .= " " . implode(" ", $flags['joins']); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
View Code Duplication |
if (! is_null($where) && is_array($where) && count($where)) { |
|
|
|
|
463
|
|
|
$this->sql .= $this->where($where); |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
if (count($flags)) { |
467
|
|
|
$this->sql .= $this->flags($flags); |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
if (self::$autorun) { |
471
|
|
|
return $this->execute(MYSQLI_BOTH); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
return $this->sql; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* Function to build a query to check the number of rows in a table |
479
|
|
|
* |
480
|
|
|
* @param string $table_name |
481
|
|
|
* The table to query |
482
|
|
|
* @param array $where |
483
|
|
|
* [optional] |
484
|
|
|
* Optional 2-dimensional array to build where clause |
485
|
|
|
* @param array $flags |
486
|
|
|
* [optional] |
487
|
|
|
* Optional 2-dimensional array to add flags |
488
|
|
|
* |
489
|
|
|
* @see Database::where() |
490
|
|
|
* @see Database::flags() |
491
|
|
|
* |
492
|
|
|
* @return string|NULL |
493
|
|
|
*/ |
494
|
|
|
public function select_count($table_name, $where = null, $flags = null) |
495
|
|
|
{ |
496
|
|
|
$this->sql = null; |
497
|
|
|
$this->query_type = self::SELECT_COUNT; |
498
|
|
|
|
499
|
|
|
if (! is_null($table_name) && is_string($table_name)) { |
500
|
|
|
$this->sql = "SELECT COUNT(1) AS 'count' FROM $table_name"; |
501
|
|
|
} else { |
502
|
|
|
return null; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
View Code Duplication |
if (isset($flags['joins']) && is_array($flags['joins'])) { |
|
|
|
|
506
|
|
|
$this->sql .= " " . implode(" ", $flags['joins']); |
507
|
|
|
} |
508
|
|
|
|
509
|
|
View Code Duplication |
if (! is_null($where) && is_array($where) && count($where)) { |
|
|
|
|
510
|
|
|
$this->sql .= $this->where($where); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
if (count($flags)) { |
514
|
|
|
$this->sql .= $this->flags($flags); |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
if (self::$autorun) { |
518
|
|
|
return $this->execute(MYSQLI_BOTH); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
return $this->sql; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* Function to build an insert query statement |
526
|
|
|
* |
527
|
|
|
* @param string $table_name |
528
|
|
|
* @param array $params |
529
|
|
|
* @param boolean $to_ignore |
530
|
|
|
* |
531
|
|
|
* @return string|NULL |
532
|
|
|
*/ |
533
|
|
|
public function insert($table_name, $params = null, $to_ignore = false) |
534
|
|
|
{ |
535
|
|
|
$this->sql = null; |
536
|
|
|
$this->query_type = self::INSERT; |
537
|
|
|
|
538
|
|
|
if (! is_null($table_name) && is_string($table_name)) { |
539
|
|
|
$this->sql = "INSERT" . ($to_ignore ? " IGNORE" : "") . " INTO $table_name" . (is_array($params) && count($params) ? " (`" . implode("`,`", array_keys($params)) . "`)" : null); |
540
|
|
|
} else { |
541
|
|
|
throw (new Exception("Missing table name in insert function", E_ERROR)); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
if (is_array($params) && count($params)) { |
545
|
|
|
$this->sql .= " VALUES (" . implode(",", array_map([ |
546
|
|
|
$this, |
547
|
|
|
'_escape' |
548
|
|
|
], array_values($params))) . ")"; |
549
|
|
|
} elseif (is_string($params) && stripos($params, 'SELECT') !== false) { |
550
|
|
|
$this->sql .= " {$params}"; |
551
|
|
|
} else { |
552
|
|
|
throw (new Exception("Invalid type passed to insert " . gettype($params), E_ERROR)); |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
if (self::$autorun) { |
556
|
|
|
return $this->execute(MYSQLI_BOTH); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
return $this->sql; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Function to create an extended insert query statement |
564
|
|
|
* |
565
|
|
|
* @param string $table_name |
566
|
|
|
* The table name that the data is going to be inserted on |
567
|
|
|
* @param array $fields |
568
|
|
|
* An array of field names that each value represents |
569
|
|
|
* @param array|string $params |
570
|
|
|
* An array of array of values or a string with a SELECT statement to populate the insert with |
571
|
|
|
* @param boolean $to_ignore |
572
|
|
|
* [optional] |
573
|
|
|
* Boolean to decide if we need to use the INSERT IGNORE INTO syntax |
574
|
|
|
* |
575
|
|
|
* @return NULL|string Returns the SQL if self::$autorun is set to false, else it returns the output from running. |
576
|
|
|
*/ |
577
|
|
|
public function extended_insert($table_name, $fields, $params, $to_ignore = false) |
578
|
|
|
{ |
579
|
|
|
$this->sql = null; |
580
|
|
|
$this->query_type = self::EXTENDED_INSERT; |
581
|
|
|
|
582
|
|
|
if (! is_null($table_name) && is_string($table_name)) { |
583
|
|
|
$this->sql = "INSERT " . ($to_ignore ? "IGNORE " : "") . "INTO $table_name " . "(`" . implode("`,`", $fields) . "`)"; |
584
|
|
|
} else { |
585
|
|
|
throw (new Exception("Missing table name in extended_insert", E_ERROR)); |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
if (is_array($params) && count($params)) { |
589
|
|
|
$this->sql .= " VALUES "; |
590
|
|
|
if (isset($params[0]) && is_array($params[0])) { |
591
|
|
|
foreach ($params as $p) { |
592
|
|
|
if (count($p) != count($fields)) { |
593
|
|
|
throw (new Exception("Inconsistent number of fields in fields and values in extended_insert " . print_r($p, true), E_ERROR)); |
594
|
|
|
} |
595
|
|
|
$this->sql .= "(" . implode(",", array_map([ |
596
|
|
|
$this, |
597
|
|
|
'_escape' |
598
|
|
|
], array_values($p))) . ")"; |
599
|
|
|
|
600
|
|
|
if ($p != end($params)) { |
601
|
|
|
$this->sql .= ","; |
602
|
|
|
} |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
if (self::$autorun) { |
608
|
|
|
return $this->execute(MYSQLI_BOTH); |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
return $this->sql; |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
/** |
615
|
|
|
* Build a statement to update a table |
616
|
|
|
* |
617
|
|
|
* @param string $table_name |
618
|
|
|
* The table name to update |
619
|
|
|
* @param array $params |
620
|
|
|
* Name/value pairs of the field name and value |
621
|
|
|
* @param array $where |
622
|
|
|
* [optional] |
623
|
|
|
* Two-dimensional array to create where clause |
624
|
|
|
* @param array $flags |
625
|
|
|
* [optional] |
626
|
|
|
* Two-dimensional array to create other flag options (joins, order, and group) |
627
|
|
|
* |
628
|
|
|
* @see Database::where() |
629
|
|
|
* @see Database::flags() |
630
|
|
|
* |
631
|
|
|
* @return NULL|string |
632
|
|
|
*/ |
633
|
|
|
public function update($table_name, $params, $where = null, $flags = null) |
634
|
|
|
{ |
635
|
|
|
$this->sql = "UPDATE "; |
636
|
|
|
$this->query_type = self::UPDATE; |
637
|
|
|
|
638
|
|
|
if (! is_null($table_name) && is_string($table_name)) { |
639
|
|
|
$this->sql .= $table_name; |
640
|
|
|
|
641
|
|
View Code Duplication |
if (isset($flags['joins'])) { |
|
|
|
|
642
|
|
|
$this->sql .= " " . implode(" ", $flags['joins']); |
643
|
|
|
unset($flags['joins']); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
$this->sql .= " SET "; |
647
|
|
|
} else { |
648
|
|
|
throw new Exception("Invalid table name datatype", E_ERROR); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
foreach ($params as $f => $p) { |
652
|
|
|
if ((strpos($f, "`") === false) && (strpos($f, ".") === false) && (strpos($f, "*") === false) && (stripos($f, " as ") === false)) { |
653
|
|
|
$f = "`{$f}`"; |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
if (! is_null($p)) { |
657
|
|
|
$this->sql .= "$f={$this->_escape($p)},"; |
658
|
|
|
} else { |
659
|
|
|
$this->sql .= "$f=NULL,"; |
660
|
|
|
} |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
$this->sql = substr($this->sql, 0, - 1); |
664
|
|
|
|
665
|
|
View Code Duplication |
if (! is_null($where) && is_array($where) && count($where)) { |
|
|
|
|
666
|
|
|
$this->sql .= $this->where($where); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
View Code Duplication |
if (! is_null($flags) && is_array($flags) && count($flags)) { |
|
|
|
|
670
|
|
|
$this->sql .= $this->flags($flags); |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
if (self::$autorun) { |
674
|
|
|
return $this->execute(MYSQLI_BOTH); |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
return $this->sql; |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
/** |
681
|
|
|
* Function to offer an extended updated functionality by using two different tables. |
682
|
|
|
* |
683
|
|
|
* @param string $to_be_updated |
684
|
|
|
* The table that you want to update (alias 'tbu' is automatically added) |
685
|
|
|
* @param string $original |
686
|
|
|
* The table with the data you want to overwrite to_be_updated table (alias 'o' is automatically added) |
687
|
|
|
* @param string $using |
688
|
|
|
* The common index value between them that will join the fields |
689
|
|
|
* @param array|string $params |
690
|
|
|
* If string only a single field is updated (tbu.$params = o.$params) |
691
|
|
|
* If array each element in the array is a field to be updated (tbu.$param = o.$param) |
692
|
|
|
* |
693
|
|
|
* @return mixed |
694
|
|
|
*/ |
695
|
|
|
public function extended_update($to_be_updated, $original, $using, $params) |
696
|
|
|
{ |
697
|
|
|
$this->sql = "UPDATE "; |
698
|
|
|
$this->query_type = self::EXTENDED_UPDATE; |
699
|
|
|
|
700
|
|
|
if (! is_null($to_be_updated) && ! is_null($original) && ! is_null($using)) { |
701
|
|
|
$this->sql .= "$to_be_updated tbu INNER JOIN $original o USING ($using) SET "; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
if (is_array($params) && count($params)) { |
705
|
|
|
foreach ($params as $param) { |
706
|
|
|
if ($param != $using) { |
707
|
|
|
$this->sql .= "tbu.`$param` = o.`$param`,"; |
708
|
|
|
} |
709
|
|
|
} |
710
|
|
|
$this->sql = substr($this->sql, 0, - 1); |
711
|
|
|
} elseif (is_string($params)) { |
712
|
|
|
$this->sql .= "tbu.`$params` = o.`$params`"; |
713
|
|
|
} else { |
714
|
|
|
throw new Exception("Do not understand datatype " . gettype($params), E_ERROR); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
if (self::$autorun) { |
718
|
|
|
return $this->execute(MYSQL_BOTH); |
719
|
|
|
} |
720
|
|
|
|
721
|
|
|
return $this->sql; |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
/** |
725
|
|
|
* Function to build a replace query |
726
|
|
|
* |
727
|
|
|
* @param string $table_name |
728
|
|
|
* The table to update |
729
|
|
|
* @param array $params |
730
|
|
|
* Name/value pair to insert |
731
|
|
|
* |
732
|
|
|
* @return NULL|string |
733
|
|
|
*/ |
734
|
|
|
public function replace($table_name, $params) |
735
|
|
|
{ |
736
|
|
|
$this->sql = null; |
737
|
|
|
$this->query_type = self::REPLACE; |
738
|
|
|
|
739
|
|
View Code Duplication |
if (! is_null($table_name) && is_string($table_name)) { |
|
|
|
|
740
|
|
|
$this->sql = "REPLACE INTO $table_name " . "(`" . implode("`,`", array_keys($params)) . "`)"; |
741
|
|
|
} else { |
742
|
|
|
throw (new Exception("Table name is not valid", E_ERROR)); |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
$this->sql .= " VALUES (" . implode(",", array_map([ |
746
|
|
|
$this, |
747
|
|
|
'_escape' |
748
|
|
|
], array_values($params))) . ")"; |
749
|
|
|
|
750
|
|
|
if (self::$autorun) { |
751
|
|
|
return $this->execute(MYSQLI_BOTH); |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
return $this->sql; |
755
|
|
|
} |
756
|
|
|
|
757
|
|
|
/** |
758
|
|
|
* Function to build an extended replace statement |
759
|
|
|
* |
760
|
|
|
* @param string $table_name |
761
|
|
|
* Table name to update |
762
|
|
|
* @param array $fields |
763
|
|
|
* Array of fields |
764
|
|
|
* @param array $params |
765
|
|
|
* Two-dimensional array of values |
766
|
|
|
* |
767
|
|
|
* @return NULL|string |
768
|
|
|
*/ |
769
|
|
|
public function extended_replace($table_name, $fields, $params) |
770
|
|
|
{ |
771
|
|
|
$this->sql = null; |
772
|
|
|
$this->query_type = self::EXTENDED_REPLACE; |
773
|
|
|
|
774
|
|
View Code Duplication |
if (! is_null($table_name) && is_string($table_name)) { |
|
|
|
|
775
|
|
|
$this->sql = "REPLACE INTO $table_name " . "(`" . implode("`,`", $fields) . "`)"; |
776
|
|
|
} else { |
777
|
|
|
throw (new Exception("Table name is not valid", E_ERROR)); |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
if (is_array($params) && count($params)) { |
781
|
|
|
$this->sql .= " VALUES "; |
782
|
|
|
foreach ($params as $p) { |
783
|
|
|
$this->sql .= "(" . implode(",", array_map([ |
784
|
|
|
$this, |
785
|
|
|
'_escape' |
786
|
|
|
], array_values($p))) . ")"; |
787
|
|
|
|
788
|
|
|
if ($p != end($params)) { |
789
|
|
|
$this->sql .= ","; |
790
|
|
|
} |
791
|
|
|
} |
792
|
|
|
} |
793
|
|
|
|
794
|
|
|
if (self::$autorun) { |
795
|
|
|
return $this->execute(MYSQLI_BOTH); |
796
|
|
|
} |
797
|
|
|
|
798
|
|
|
return $this->sql; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* Function to build a delete statement |
803
|
|
|
* |
804
|
|
|
* @param string $table_name |
805
|
|
|
* Table name to act on |
806
|
|
|
* @param array $fields |
807
|
|
|
* [optional] |
808
|
|
|
* Optional list of fields to delete (used when including multiple tables) |
809
|
|
|
* @param array $where |
810
|
|
|
* [optional] |
811
|
|
|
* Optional 2-dimensional array to build where clause from |
812
|
|
|
* @param array $joins |
813
|
|
|
* [optional] |
814
|
|
|
* Optional 2-dimensional array to add other flags |
815
|
|
|
* |
816
|
|
|
* @see Database::where() |
817
|
|
|
* @see Database::flags() |
818
|
|
|
* |
819
|
|
|
* @return string|NULL |
820
|
|
|
*/ |
821
|
|
|
public function delete($table_name, $fields = null, $where = null, $joins = null) |
822
|
|
|
{ |
823
|
|
|
$this->sql = "DELETE"; |
824
|
|
|
$this->query_type = self::DELETE; |
825
|
|
|
|
826
|
|
|
if (! is_null($fields) && is_array($fields)) { |
827
|
|
|
$this->sql .= " " . implode(",", $fields); |
828
|
|
|
} |
829
|
|
|
|
830
|
|
View Code Duplication |
if (! is_null($table_name) && is_string($table_name)) { |
|
|
|
|
831
|
|
|
$this->sql .= " FROM $table_name"; |
832
|
|
|
} else { |
833
|
|
|
throw (new Exception("Failed to create delete query, no table name", E_ERROR)); |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
if (! is_null($joins) && is_array($joins) && count($joins)) { |
837
|
|
|
$this->sql .= " " . implode(" ", $joins); |
838
|
|
|
} |
839
|
|
|
|
840
|
|
View Code Duplication |
if (! is_null($where) && is_array($where) && count($where)) { |
|
|
|
|
841
|
|
|
$this->sql .= $this->where($where); |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
if (self::$autorun) { |
845
|
|
|
return $this->execute(MYSQLI_BOTH); |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
return $this->sql; |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
/** |
852
|
|
|
* Function to build a drop table statement (automatically executes) |
853
|
|
|
* |
854
|
|
|
* @param string $name |
855
|
|
|
* Table to drop |
856
|
|
|
* @param string $type |
857
|
|
|
* [optional] |
858
|
|
|
* Type of item to drop ('table', 'view') (defaulted to 'table') |
859
|
|
|
* @param boolean $is_tmp |
860
|
|
|
* [optional] |
861
|
|
|
* Optional boolean if this is a temporary table |
862
|
|
|
* |
863
|
|
|
* @return string|NULL |
864
|
|
|
*/ |
865
|
|
|
public function drop($name, $type = 'table', $is_tmp = false) |
866
|
|
|
{ |
867
|
|
|
$this->sql = null; |
868
|
|
|
$this->query_type = self::DROP; |
869
|
|
|
|
870
|
|
|
switch ($type) { |
871
|
|
|
case 'table': |
872
|
|
|
$type = 'TABLE'; |
873
|
|
|
break; |
874
|
|
|
case 'view': |
875
|
|
|
$type = 'VIEW'; |
876
|
|
|
break; |
877
|
|
|
default: |
878
|
|
|
throw new Exception("Invalid type " . gettype($type), E_ERROR); |
879
|
|
|
} |
880
|
|
|
|
881
|
|
|
if (! is_null($name) && is_string($name)) { |
882
|
|
|
$this->sql = "DROP" . ($is_tmp ? " TEMPORARY" : "") . " $type IF EXISTS `$name`"; |
883
|
|
|
} else { |
884
|
|
|
throw new Exception("Table name is invalid", E_ERROR); |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
if (self::$autorun) { |
888
|
|
|
return $this->execute(MYSQLI_BOTH); |
889
|
|
|
} |
890
|
|
|
|
891
|
|
|
return $this->sql; |
892
|
|
|
} |
893
|
|
|
|
894
|
|
|
/** |
895
|
|
|
* Function to build a truncate table statement (automatically executes) |
896
|
|
|
* |
897
|
|
|
* @param string $table_name |
898
|
|
|
* Table to truncate |
899
|
|
|
* |
900
|
|
|
* @return string|NULL |
901
|
|
|
*/ |
902
|
|
|
public function truncate($table_name) |
903
|
|
|
{ |
904
|
|
|
$this->sql = null; |
905
|
|
|
$this->query_type = self::TRUNCATE; |
906
|
|
|
|
907
|
|
View Code Duplication |
if (! is_null($table_name) && is_string($table_name)) { |
|
|
|
|
908
|
|
|
$this->sql = "TRUNCATE TABLE $table_name"; |
909
|
|
|
} else { |
910
|
|
|
throw new Exception("Table name is invalid", E_ERROR); |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
if (self::$autorun) { |
914
|
|
|
return $this->execute(MYSQLI_BOTH); |
915
|
|
|
} |
916
|
|
|
|
917
|
|
|
return $this->sql; |
918
|
|
|
} |
919
|
|
|
|
920
|
|
|
/** |
921
|
|
|
* Function to build a create temporary table statement |
922
|
|
|
* |
923
|
|
|
* @param string $table_name |
924
|
|
|
* Name to give the table when creating |
925
|
|
|
* @param boolean $is_tmp |
926
|
|
|
* [optional] |
927
|
|
|
* Optional boolean to make the table a temporary table |
928
|
|
|
* @param mixed $select |
929
|
|
|
* [optional] |
930
|
|
|
* Optional parameter if null uses last built statement |
931
|
|
|
* If string, will be made the SQL statement executed to create the table |
932
|
|
|
* If array, 2-dimensional array with "field", "datatype" values to build table fields |
933
|
|
|
* |
934
|
|
|
* @return NULL|string |
935
|
|
|
*/ |
936
|
|
|
public function create_table($table_name, $is_tmp = false, $select = null) |
937
|
|
|
{ |
938
|
|
|
$this->query_type = self::CREATE_TABLE; |
939
|
|
|
|
940
|
|
|
if (is_null($select) && ! is_null($this->sql) && substr($this->sql, 0, 6) == 'SELECT') { |
941
|
|
|
$this->sql = "CREATE" . ($is_tmp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name AS ($this->sql)"; |
942
|
|
|
} |
943
|
|
|
if (! is_null($table_name) && is_string($table_name) && is_string($select)) { |
944
|
|
|
$this->sql = "CREATE" . ($is_tmp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name AS ($select)"; |
945
|
|
|
} elseif (! is_null($table_name) && is_string($table_name) && is_array($select)) { |
946
|
|
|
$this->sql = "CREATE" . ($is_tmp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name ("; |
947
|
|
|
|
948
|
|
|
foreach ($select as $field) { |
949
|
|
|
$default = null; |
950
|
|
|
if (isset($field['default'])) { |
951
|
|
|
$default = (is_null($field['default']) ? "" : " DEFAULT '{$field['default']}'"); |
952
|
|
|
} |
953
|
|
|
$this->sql .= "`{$field['field']}` {$field['datatype']}" . $default . (isset($field['option']) ? " {$field['option']}" : '') . ","; |
954
|
|
|
} |
955
|
|
|
$this->sql = substr($this->sql, 0, - 1) . ")"; |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
if (self::$autorun) { |
959
|
|
|
return $this->execute(); |
960
|
|
|
} |
961
|
|
|
|
962
|
|
|
return $this->sql; |
963
|
|
|
} |
964
|
|
|
|
965
|
|
|
/** |
966
|
|
|
* Function to create a table using a stdClass object derived from JSON |
967
|
|
|
* |
968
|
|
|
* @param \stdClass $json |
969
|
|
|
* |
970
|
|
|
* @example /examples/create_table_json.json |
971
|
|
|
* |
972
|
|
|
*/ |
973
|
|
|
public function create_table_json($json) |
974
|
|
|
{ |
975
|
|
|
$this->query_type = self::CREATE_TABLE; |
976
|
|
|
$this->c->select_db($json->schema); |
977
|
|
|
|
978
|
|
|
$this->sql = "CREATE TABLE IF NOT EXISTS `{$json->name}` ("; |
979
|
|
|
foreach ($json->fields as $field) { |
980
|
|
|
$this->sql .= "`{$field->name}` {$field->dataType}"; |
981
|
|
|
|
982
|
|
|
if ($field->dataType == 'enum') { |
983
|
|
|
$this->sql .= "('" . implode("','", $field->values) . "')"; |
984
|
|
|
} |
985
|
|
|
|
986
|
|
|
if ($field->ai) { |
987
|
|
|
$this->sql .= " AUTO_INCREMENT"; |
988
|
|
|
} |
989
|
|
|
|
990
|
|
|
if ($field->nn) { |
991
|
|
|
$this->sql .= " NOT NULL"; |
992
|
|
|
} else { |
993
|
|
|
if ($field->default === null) { |
994
|
|
|
$this->sql .= " DEFAULT NULL"; |
995
|
|
|
} elseif (strlen($field->default)) { |
996
|
|
|
$this->sql .= " DEFAULT '{$field->default}'"; |
997
|
|
|
} |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
if ($field != end($json->fields)) { |
1001
|
|
|
$this->sql .= ","; |
1002
|
|
|
} |
1003
|
|
|
} |
1004
|
|
|
|
1005
|
|
|
if (isset($json->index) && count($json->index)) { |
1006
|
|
|
foreach ($json->index as $ind) { |
1007
|
|
|
$this->sql .= ", " . strtoupper($ind->type) . " `{$ind->id}` (`{$ind->ref}`)"; |
1008
|
|
|
} |
1009
|
|
|
} |
1010
|
|
|
|
1011
|
|
|
if (isset($json->constraints) && count($json->constraints)) { |
1012
|
|
|
foreach ($json->constraints as $con) { |
1013
|
|
|
$this->sql .= ", CONSTRAINT `{$con->id}` " . "FOREIGN KEY (`{$con->local}`) " . "REFERENCES `{$con->schema}`.`{$con->table}` (`{$con->field}`) " . "ON DELETE " . (is_null($con->delete) ? "NO ACTION" : strtoupper($con->delete)) . " " . "ON UPDATE " . (is_null($con->update) ? "NO ACTION" : strtoupper($con->update)); |
1014
|
|
|
} |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
if (isset($json->unique) && count($json->unique)) { |
1018
|
|
|
$this->sql .= ", UNIQUE(`" . implode("`,`", $json->unique) . "`)"; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
if (isset($json->primary_key) && count($json->primary_key)) { |
1022
|
|
|
$this->sql .= ", PRIMARY KEY(`" . implode("`,`", $json->primary_key) . "`))"; |
1023
|
|
|
} else { |
1024
|
|
|
if (substr($this->sql, - 1) == ',') { |
1025
|
|
|
$this->sql = substr($this->sql, 0, - 1); |
1026
|
|
|
} |
1027
|
|
|
|
1028
|
|
|
$this->sql .= ")"; |
1029
|
|
|
} |
1030
|
|
|
|
1031
|
|
|
$this->execute(MYSQLI_BOTH); |
1032
|
|
|
} |
1033
|
|
|
|
1034
|
|
|
/** |
1035
|
|
|
* Function to alter a existing table |
1036
|
|
|
* |
1037
|
|
|
* @param string $table_name |
1038
|
|
|
* Table to alter |
1039
|
|
|
* @param string $action |
1040
|
|
|
* What action should be taken ('add-column', 'drop-column', 'modify-column') |
1041
|
|
|
* @param mixed $params |
1042
|
|
|
* For add column this is a stdClass object that has the same elements as the example json |
1043
|
|
|
* |
1044
|
|
|
* @return mixed |
1045
|
|
|
*/ |
1046
|
|
|
public function alter_table($table_name, $action, $params) |
1047
|
|
|
{ |
1048
|
|
|
$this->query_type = self::ALTER_TABLE; |
1049
|
|
|
$this->sql = "ALTER TABLE $table_name"; |
1050
|
|
|
if ($action == 'add-column') { |
1051
|
|
|
$nn = ($params->nn ? " NOT NULL" : ""); |
1052
|
|
|
$default = null; |
1053
|
|
View Code Duplication |
if ($params->default === null) { |
|
|
|
|
1054
|
|
|
$default = " DEFAULT NULL"; |
1055
|
|
|
} elseif (strlen($params->default)) { |
1056
|
|
|
$default = " DEFAULT {$this->_escape($params->default)}"; |
1057
|
|
|
} |
1058
|
|
|
$this->sql .= " ADD COLUMN `{$params->name}` {$params->dataType}" . $nn . $default; |
1059
|
|
|
} elseif ($action == 'drop-column') { |
1060
|
|
|
$this->sql .= " DROP COLUMN "; |
1061
|
|
|
foreach ($params as $col) { |
1062
|
|
|
$this->sql .= "`{$col->name}`"; |
1063
|
|
|
|
1064
|
|
|
if ($col != end($params)) { |
1065
|
|
|
$this->sql .= ","; |
1066
|
|
|
} |
1067
|
|
|
} |
1068
|
|
|
} elseif ($action == 'modify-column') { |
1069
|
|
|
$this->sql .= " MODIFY COLUMN"; |
1070
|
|
|
$nn = ($params->nn ? " NOT NULL" : ""); |
1071
|
|
|
$default = null; |
1072
|
|
View Code Duplication |
if ($params->default === null) { |
|
|
|
|
1073
|
|
|
$default = " DEFAULT NULL"; |
1074
|
|
|
} elseif (strlen($params->default)) { |
1075
|
|
|
$default = " DEFAULT {$this->_escape($params->default)}"; |
1076
|
|
|
} |
1077
|
|
|
$this->sql .= " `{$params->name}` `{$params->new_name}` {$params->dataType}" . $nn . $default; |
1078
|
|
|
} |
1079
|
|
|
|
1080
|
|
|
if (self::$autorun) { |
1081
|
|
|
return $this->execute(); |
1082
|
|
|
} |
1083
|
|
|
|
1084
|
|
|
return $this->sql; |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
|
|
/** |
1088
|
|
|
* Check to see if a field in a table exists |
1089
|
|
|
* |
1090
|
|
|
* @param string $table_name |
1091
|
|
|
* Table to check |
1092
|
|
|
* @param string $field_name |
1093
|
|
|
* Field name to find |
1094
|
|
|
* |
1095
|
|
|
* @return boolean Returns TRUE if field is found in that schema and table, otherwise FALSE |
1096
|
|
|
*/ |
1097
|
|
|
public function field_exists($table_name, $field_name) |
1098
|
|
|
{ |
1099
|
|
|
$fdata = $this->field_data($table_name); |
1100
|
|
|
|
1101
|
|
|
if (is_array($fdata) && count($fdata)) { |
1102
|
|
|
foreach ($fdata as $field) { |
1103
|
|
|
if ($field->name == $field_name) { |
1104
|
|
|
return true; |
1105
|
|
|
} |
1106
|
|
|
} |
1107
|
|
|
} |
1108
|
|
|
|
1109
|
|
|
return false; |
1110
|
|
|
} |
1111
|
|
|
|
1112
|
|
|
/** |
1113
|
|
|
* Function to get the column data (datatype, flags, defaults, etc) |
1114
|
|
|
* |
1115
|
|
|
* @param string $table_name |
1116
|
|
|
* Table to query |
1117
|
|
|
* @param mixed $field |
1118
|
|
|
* [optional] |
1119
|
|
|
* Optional field to retrieve data (if null, returns data from all fields) |
1120
|
|
|
* |
1121
|
|
|
* @return array |
1122
|
|
|
*/ |
1123
|
|
|
public function field_data($table_name, $field = null) |
1124
|
|
|
{ |
1125
|
|
|
if (is_null($field)) { |
1126
|
|
|
$res = $this->c->query("SELECT * FROM $table_name LIMIT 1"); |
1127
|
|
|
} elseif (is_array($field)) { |
1128
|
|
|
$res = $this->c->query("SELECT `" . implode("`,`", $field) . "` FROM $table_name LIMIT 1"); |
1129
|
|
|
} elseif (is_string($field)) { |
1130
|
|
|
$res = $this->c->query("SELECT $field FROM $table_name LIMIT 1"); |
1131
|
|
|
} else { |
1132
|
|
|
return null; |
1133
|
|
|
} |
1134
|
|
|
|
1135
|
|
|
$fields = null; |
1136
|
|
|
if (is_a($res, 'mysqli_result')) { |
1137
|
|
|
$fields = $res->fetch_fields(); |
1138
|
|
|
foreach ($fields as $i => $f) { |
1139
|
|
|
$fields["{$f->name}"] = $f; |
1140
|
|
|
unset($fields[$i]); |
1141
|
|
|
} |
1142
|
|
|
} |
1143
|
|
|
|
1144
|
|
|
return $fields; |
1145
|
|
|
} |
1146
|
|
|
|
1147
|
|
|
/** |
1148
|
|
|
* Function to check that all field parameters are set correctly |
1149
|
|
|
* |
1150
|
|
|
* @param object $field_data |
1151
|
|
|
* @param object $check |
1152
|
|
|
* @param object $pks |
1153
|
|
|
* @param object $index |
1154
|
|
|
* |
1155
|
|
|
* @return array |
1156
|
|
|
*/ |
1157
|
|
|
public function field_check($field_data, $check, $pks, $index) |
1158
|
|
|
{ |
1159
|
|
|
$default = null; |
1160
|
|
|
$ret = null; |
1161
|
|
|
|
1162
|
|
|
$nn = ($check->nn ? " NOT NULL" : null); |
1163
|
|
|
if ($check->default === null) { |
1164
|
|
|
$default = " DEFAULT NULL"; |
1165
|
|
|
} elseif (strlen($check->default)) { |
1166
|
|
|
$default = " DEFAULT '{$check->default}'"; |
1167
|
|
|
} |
1168
|
|
|
|
1169
|
|
|
if ($field_data->type != $check->type && $check->type != MYSQLI_TYPE_ENUM) { |
1170
|
|
|
$this->log("{$field_data->name} wrong datatype, changing to {$check->dataType}", LogLevel::NOTICE); |
1171
|
|
|
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}"; |
1172
|
|
|
} elseif (! is_null($check->length) && $field_data->length != $check->length) { |
1173
|
|
|
$this->log("{$field_data->name} incorrect size ({$field_data->length} != {$check->length})", LogLevel::NOTICE); |
1174
|
|
|
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}"; |
1175
|
|
|
} elseif ($check->type == MYSQLI_TYPE_ENUM && ! ($field_data->flags & MYSQLI_ENUM_FLAG)) { |
1176
|
|
|
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}('" . implode("','", $check->values) . "')" . "{$nn}{$default}"; |
1177
|
|
|
} |
1178
|
|
|
|
1179
|
|
|
if (! is_null($index) && count($index)) { |
1180
|
|
|
foreach ($index as $ind) { |
1181
|
|
|
if ($check->name == $ind->ref && ! ($field_data->flags & MYSQLI_MULTIPLE_KEY_FLAG)) { |
1182
|
|
|
$this->log("{$field_data->name} is not an index", LogLevel::NOTICE); |
1183
|
|
|
$ret .= ($ret ? "," : "") . " ADD INDEX `{$ind->id}` (`{$ind->ref}` ASC)"; |
1184
|
|
|
} |
1185
|
|
|
} |
1186
|
|
|
} |
1187
|
|
|
|
1188
|
|
|
if (in_array($check->name, $pks) && ! ($field_data->flags & MYSQLI_PRI_KEY_FLAG)) { |
1189
|
|
|
$ret .= ($ret ? "," : "") . " DROP PRIMARY KEY, ADD PRIMARY KEY(`" . implode("`,`", $pks) . "`)"; |
1190
|
|
|
} |
1191
|
|
|
|
1192
|
|
|
return $ret; |
1193
|
|
|
} |
1194
|
|
|
|
1195
|
|
|
/** |
1196
|
|
|
* Function to check for the existence of a table within a schema |
1197
|
|
|
* |
1198
|
|
|
* @param string $schema |
1199
|
|
|
* The schema to search in |
1200
|
|
|
* @param string $table_name |
1201
|
|
|
* Table to search for |
1202
|
|
|
* |
1203
|
|
|
* @return boolean Returns number of tables that match if table is found in that schema, otherwise FALSE |
1204
|
|
|
*/ |
1205
|
|
|
public function table_exists(string $schema, string $table_name) |
1206
|
|
|
{ |
1207
|
|
|
if (! $this->c->select_db($schema)) { |
1208
|
|
|
fwrite(STDOUT, $this->c->error . PHP_EOL); |
1209
|
|
|
} |
1210
|
|
|
$sql = "SHOW TABLES LIKE '{$table_name}'"; |
1211
|
|
|
|
1212
|
|
|
if ($res = $this->c->query($sql)) { |
1213
|
|
|
if (is_a($res, 'mysqli_result') && $res->num_rows) { |
1214
|
|
|
return $res->num_rows; |
1215
|
|
|
} |
1216
|
|
|
} else { |
1217
|
|
|
if ($this->c->errno) { |
1218
|
|
|
fwrite(STDOUT, $this->c->error . PHP_EOL); |
1219
|
|
|
} |
1220
|
|
|
} |
1221
|
|
|
|
1222
|
|
|
return false; |
1223
|
|
|
} |
1224
|
|
|
|
1225
|
|
|
/** |
1226
|
|
|
* Function to detect if string is a JSON object or not |
1227
|
|
|
* |
1228
|
|
|
* @param string $val |
1229
|
|
|
* |
1230
|
|
|
* @return boolean |
1231
|
|
|
*/ |
1232
|
|
|
private function isJson($val) |
1233
|
|
|
{ |
1234
|
|
|
json_decode($val); |
1235
|
|
|
return (json_last_error() == JSON_ERROR_NONE); |
1236
|
|
|
} |
1237
|
|
|
|
1238
|
|
|
/** |
1239
|
|
|
* Function to escape SQL characters to prevent SQL injection |
1240
|
|
|
* |
1241
|
|
|
* @param mixed $val |
1242
|
|
|
* Value to escape |
1243
|
|
|
* |
1244
|
|
|
* @return string Escaped value |
1245
|
|
|
*/ |
1246
|
|
|
private function _escape($val) |
1247
|
|
|
{ |
1248
|
|
|
if (is_null($val) || (is_string($val) && $val == 'NULL')) { |
1249
|
|
|
return 'NULL'; |
1250
|
|
|
} elseif (is_numeric($val) || is_string($val)) { |
1251
|
|
|
if ($this->isJson($val)) { |
1252
|
|
|
return "'{$this->c->real_escape_string($val)}'"; |
1253
|
|
|
} elseif (strtolower($val) == 'now()') { |
1254
|
|
|
return $val; |
1255
|
|
|
} elseif (preg_match("/\.`\w+`/", $val)) { |
1256
|
|
|
return $val; |
1257
|
|
|
} |
1258
|
|
|
return "'{$this->c->real_escape_string($val)}'"; |
1259
|
|
|
} elseif (is_a($val, 'DateTime')) { |
1260
|
|
|
return "'{$val->format(MYSQL_DATETIME)}'"; |
1261
|
|
|
} elseif (is_bool($val)) { |
1262
|
|
|
return $val ? "'1'" : "'0'"; |
1263
|
|
|
} elseif (gettype($val) == 'object' && method_exists($val, '_escape')) { |
1264
|
|
|
$ret = call_user_func([ |
1265
|
|
|
$val, |
1266
|
|
|
'_escape' |
1267
|
|
|
]); |
1268
|
|
|
if ($ret !== false) { |
1269
|
|
|
return $ret; |
1270
|
|
|
} else { |
1271
|
|
|
throw new Exception("Error in return from _escape method in " . get_class($val), E_ERROR); |
1272
|
|
|
} |
1273
|
|
|
} elseif (gettype($val) == 'object') { |
1274
|
|
|
$this->log("Unknown object to escape " . get_class($val) . " in SQL string {$this->sql}", LogLevel::ERROR); |
1275
|
|
|
} |
1276
|
|
|
|
1277
|
|
|
throw new Exception("Unknown datatype to escape in SQL string {$this->sql} " . gettype($val), E_ERROR); |
1278
|
|
|
} |
1279
|
|
|
|
1280
|
|
|
/** |
1281
|
|
|
* Function to retrieve all results |
1282
|
|
|
* |
1283
|
|
|
* @param string $resulttype |
1284
|
|
|
* |
1285
|
|
|
* @return mixed |
1286
|
|
|
*/ |
1287
|
|
|
private function fetch_all($resulttype = MYSQLI_ASSOC) |
1288
|
|
|
{ |
1289
|
|
|
$res = []; |
1290
|
|
|
if (method_exists('mysqli_result', 'fetch_all')) { // Compatibility layer with PHP < 5.3 |
1291
|
|
|
$res = $this->result->fetch_all($resulttype); |
1292
|
|
|
} else { |
1293
|
|
|
while ($tmp = $this->result->fetch_array($resulttype)) { |
1294
|
|
|
$res[] = $tmp; |
1295
|
|
|
} |
1296
|
|
|
} |
1297
|
|
|
|
1298
|
|
|
return $res; |
1299
|
|
|
} |
1300
|
|
|
|
1301
|
|
|
/** |
1302
|
|
|
* Function to populate the fields for the SQL |
1303
|
|
|
* |
1304
|
|
|
* @param array $fields |
1305
|
|
|
* [optional] |
1306
|
|
|
* Optional array of fields to string together to create a field list |
1307
|
|
|
* |
1308
|
|
|
* @return string |
1309
|
|
|
*/ |
1310
|
|
|
private function fields($fields = null) |
1311
|
|
|
{ |
1312
|
|
|
$str_fields = null; |
1313
|
|
|
|
1314
|
|
|
if (is_array($fields) && count($fields)) { |
1315
|
|
|
foreach ($fields as $field) { |
1316
|
|
|
if ((strpos($field, '`') === false) && (strpos($field, '.') === false) && (strpos($field, '*') === false) && (strpos($field, 'JSON_') === false) && (stripos($field, ' as ') === false)) { |
1317
|
|
|
$str_fields .= "`$field`,"; |
1318
|
|
|
} else { |
1319
|
|
|
$str_fields .= "$field,"; |
1320
|
|
|
} |
1321
|
|
|
} |
1322
|
|
|
$str_fields = substr($str_fields, 0, - 1); |
1323
|
|
|
} elseif (is_string($fields)) { |
1324
|
|
|
$str_fields = $fields; |
1325
|
|
|
} elseif (is_null($fields)) { |
1326
|
|
|
$str_fields = "*"; |
1327
|
|
|
} |
1328
|
|
|
|
1329
|
|
|
return $str_fields; |
1330
|
|
|
} |
1331
|
|
|
|
1332
|
|
|
/** |
1333
|
|
|
* Function to create the where statement for the SQL |
1334
|
|
|
* |
1335
|
|
|
* @param array $where |
1336
|
|
|
* Two-dimensional array to use to build the where clause |
1337
|
|
|
* |
1338
|
|
|
* <code> |
1339
|
|
|
* [<br /> |
1340
|
|
|
* [<br /> |
1341
|
|
|
* 'field' => 'field_name',<br /> |
1342
|
|
|
* 'op' => '=', // (defaults to '=', common operations or IN, NOT_IN, BETWEEN, LIKE, NOT_LIKE, IS, & IS_NOT constants)<br /> |
1343
|
|
|
* 'value' => 'field_value',<br /> |
1344
|
|
|
* 'sql_op' => 'AND', // NOT required for first element (common SQL operators AND, OR, NOR)<br /> |
1345
|
|
|
* 'open-paren' => true, // optional to add a paren '(' BEFORE clause<br /> |
1346
|
|
|
* 'close-paren' => true, // optional to add a paren ')' AFTER clause<br /> |
1347
|
|
|
* 'low' => '1', // LOW value only used in BETWEEN clause<br /> |
1348
|
|
|
* 'high' => '100', // HIGH value only used in BETWEEN clause<br /> |
1349
|
|
|
* 'case_insensitive' => true // optional boolean to set the parameters to LOWER to do case insenstive comparison |
1350
|
|
|
* ],<br /> |
1351
|
|
|
* [<br /> |
1352
|
|
|
* ...<br /> |
1353
|
|
|
* ],<br /> |
1354
|
|
|
* ...<br /> |
1355
|
|
|
* ] |
1356
|
|
|
* </code> |
1357
|
|
|
* |
1358
|
|
|
* @return string |
1359
|
|
|
*/ |
1360
|
|
|
private function where($where) |
1361
|
|
|
{ |
1362
|
|
|
$ret = " WHERE"; |
1363
|
|
|
if (! is_array($where) || ! count($where) || ! isset($where[0])) { |
1364
|
|
|
$this->log("Invalid where array clause", LogLevel::WARNING); |
1365
|
|
|
return; |
1366
|
|
|
} |
1367
|
|
|
|
1368
|
|
|
foreach ($where as $x => $w) { |
1369
|
|
|
$ret .= $this->parse_clause($w, $x); |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
if ($ret == " WHERE") { |
1373
|
|
|
$ret = ''; |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
|
|
return $ret; |
1377
|
|
|
} |
1378
|
|
|
|
1379
|
|
|
/** |
1380
|
|
|
* Function to parse the flags |
1381
|
|
|
* |
1382
|
|
|
* @param array $flags |
1383
|
|
|
* Two-dimensional array to added flags |
1384
|
|
|
* |
1385
|
|
|
* <code> |
1386
|
|
|
* [ |
1387
|
|
|
* 'joins' => [ |
1388
|
|
|
* "JOIN table2 t2 ON t2.id=t1.id" |
1389
|
|
|
* ], |
1390
|
|
|
* 'group' => 'field', |
1391
|
|
|
* 'having' => 'field', |
1392
|
|
|
* 'order' => 'field', |
1393
|
|
|
* 'start' => 0, |
1394
|
|
|
* 'limit' => 0 |
1395
|
|
|
* ] |
1396
|
|
|
* </code> |
1397
|
|
|
* |
1398
|
|
|
* @see Database::groups() |
1399
|
|
|
* @see Database::having() |
1400
|
|
|
* @see Database::order() |
1401
|
|
|
* |
1402
|
|
|
* @return string |
1403
|
|
|
*/ |
1404
|
|
|
private function flags($flags) |
1405
|
|
|
{ |
1406
|
|
|
$ret = ''; |
1407
|
|
|
|
1408
|
|
|
if (isset($flags['group'])) { |
1409
|
|
|
$ret .= $this->groups($flags['group']); |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
if (isset($flags['having']) && is_array($flags['having'])) { |
1413
|
|
|
$ret .= $this->having($flags['having']); |
1414
|
|
|
} |
1415
|
|
|
|
1416
|
|
|
if (isset($flags['order'])) { |
1417
|
|
|
$ret .= $this->order($flags['order']); |
1418
|
|
|
} |
1419
|
|
|
|
1420
|
|
|
if (isset($flags['limit']) && (is_string($flags['limit']) || is_numeric($flags['limit']))) { |
1421
|
|
|
$ret .= " LIMIT "; |
1422
|
|
|
if (isset($flags['start']) && (is_string($flags['start']) || is_numeric($flags['start']))) { |
1423
|
|
|
$ret .= "{$flags['start']},"; |
1424
|
|
|
} |
1425
|
|
|
$ret .= "{$flags['limit']}"; |
1426
|
|
|
} |
1427
|
|
|
|
1428
|
|
|
return $ret; |
1429
|
|
|
} |
1430
|
|
|
|
1431
|
|
|
/** |
1432
|
|
|
* Function to parse SQL GROUP BY statements |
1433
|
|
|
* |
1434
|
|
|
* @param mixed $groups |
1435
|
|
|
* |
1436
|
|
|
* @return string |
1437
|
|
|
*/ |
1438
|
|
|
private function groups($groups) |
1439
|
|
|
{ |
1440
|
|
|
$ret = ''; |
1441
|
|
|
if (is_array($groups) && count($groups)) { |
1442
|
|
|
$ret .= " GROUP BY"; |
1443
|
|
|
|
1444
|
|
|
foreach ($groups as $grp) { |
1445
|
|
|
$ret .= " $grp"; |
1446
|
|
|
|
1447
|
|
|
if ($grp != end($groups)) { |
1448
|
|
|
$ret .= ","; |
1449
|
|
|
} |
1450
|
|
|
} |
1451
|
|
|
} elseif (is_string($groups)) { |
1452
|
|
|
$ret .= " GROUP BY {$groups}"; |
1453
|
|
|
} else { |
1454
|
|
|
throw (new Exception("Error in datatype for groups " . gettype($groups), E_ERROR)); |
1455
|
|
|
} |
1456
|
|
|
|
1457
|
|
|
return $ret; |
1458
|
|
|
} |
1459
|
|
|
|
1460
|
|
|
/** |
1461
|
|
|
* Function to parse SQL HAVING statements (same format as WHERE) |
1462
|
|
|
* |
1463
|
|
|
* @param mixed $having |
1464
|
|
|
* |
1465
|
|
|
* @return string |
1466
|
|
|
* |
1467
|
|
|
* @see Database::where() |
1468
|
|
|
*/ |
1469
|
|
|
private function having($having) |
1470
|
|
|
{ |
1471
|
|
|
if (! is_array($having) || ! count($having) || ! isset($having[0]) || ! is_array($having[0])) { |
1472
|
|
|
$this->log("Invalid having parameter", LogLevel::WARNING, $having); |
1473
|
|
|
return; |
1474
|
|
|
} |
1475
|
|
|
|
1476
|
|
|
$ret = " HAVING"; |
1477
|
|
|
foreach ($having as $x => $h) { |
1478
|
|
|
$ret .= $this->parse_clause($h, $x); |
1479
|
|
|
} |
1480
|
|
|
|
1481
|
|
|
if ($ret == " HAVING") { |
1482
|
|
|
$ret = ''; |
1483
|
|
|
} |
1484
|
|
|
|
1485
|
|
|
return $ret; |
1486
|
|
|
} |
1487
|
|
|
|
1488
|
|
|
/** |
1489
|
|
|
* Function to parse SQL ORDER BY statements |
1490
|
|
|
* |
1491
|
|
|
* @param mixed $order |
1492
|
|
|
* |
1493
|
|
|
* @return string |
1494
|
|
|
*/ |
1495
|
|
|
private function order($order) |
1496
|
|
|
{ |
1497
|
|
|
$ret = ''; |
1498
|
|
|
if (is_array($order)) { |
1499
|
|
|
$ret .= " ORDER BY"; |
1500
|
|
|
|
1501
|
|
|
foreach ($order as $ord) { |
1502
|
|
|
$ret .= " {$ord['field']} {$ord['sort']}"; |
1503
|
|
|
|
1504
|
|
|
if ($ord != end($order)) { |
1505
|
|
|
$ret .= ","; |
1506
|
|
|
} |
1507
|
|
|
} |
1508
|
|
|
} elseif (is_string($order)) { |
1509
|
|
|
$ret .= " ORDER BY {$order}"; |
1510
|
|
|
} |
1511
|
|
|
|
1512
|
|
|
return $ret; |
1513
|
|
|
} |
1514
|
|
|
|
1515
|
|
|
/** |
1516
|
|
|
* Function to see if a constraint exists |
1517
|
|
|
* |
1518
|
|
|
* @param string $con_id |
1519
|
|
|
* |
1520
|
|
|
* @return boolean |
1521
|
|
|
*/ |
1522
|
|
|
private function is_constraint($con_id) |
|
|
|
|
1523
|
|
|
{ |
1524
|
|
|
$res = $this->c->query("SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = '$con_id'"); |
1525
|
|
|
|
1526
|
|
|
if ($res->num_rows) { |
1527
|
|
|
return true; |
1528
|
|
|
} |
1529
|
|
|
|
1530
|
|
|
return false; |
1531
|
|
|
} |
1532
|
|
|
|
1533
|
|
|
/** |
1534
|
|
|
* Function to call logger and log activity |
1535
|
|
|
* |
1536
|
|
|
* @param string $msg |
1537
|
|
|
* @param LogLevel $level |
1538
|
|
|
* [optional] |
1539
|
|
|
* @param array $context |
1540
|
|
|
* [optional] |
1541
|
|
|
*/ |
1542
|
|
|
public function log($msg, $level = LogLevel::ERROR, $context = []) |
1543
|
|
|
{ |
1544
|
|
|
if ($level == LogLevel::INFO) { |
1545
|
|
|
$this->logger->info($msg, $context); |
1546
|
|
|
} elseif ($level == LogLevel::WARNING) { |
1547
|
|
|
$this->logger->warning($msg, $context); |
1548
|
|
|
} elseif ($level == LogLevel::ERROR) { |
1549
|
|
|
$this->logger->error($msg, $context); |
1550
|
|
|
} elseif ($level == LogLevel::NOTICE) { |
1551
|
|
|
$this->logger->notice($msg, $context); |
1552
|
|
|
} elseif ($level == LogLevel::DEBUG) { |
1553
|
|
|
$this->logger->debug($msg, $context); |
1554
|
|
|
} |
1555
|
|
|
} |
1556
|
|
|
|
1557
|
|
|
/** |
1558
|
|
|
* Function to parse where and having clauses |
1559
|
|
|
* |
1560
|
|
|
* @param mixed $clause |
1561
|
|
|
* @param int $index |
1562
|
|
|
*/ |
1563
|
|
|
private function parse_clause($clause, $index) |
1564
|
|
|
{ |
1565
|
|
|
$ret = null; |
1566
|
|
|
|
1567
|
|
|
if (! isset($clause['field']) && isset($clause['close-paren']) && $clause['close-paren']) { |
1568
|
|
|
$ret .= ")"; |
1569
|
|
|
return $ret; |
1570
|
|
|
} elseif ($index > 0 && ! isset($clause['sql_op'])) { |
1571
|
|
|
$this->log("Missing sql_op field to identify how current and previous WHERE clause statements should be linked ('AND', 'OR', 'XOR', etc), skipped", LogLevel::WARNING, $clause); |
1572
|
|
|
return; |
1573
|
|
|
} |
1574
|
|
|
|
1575
|
|
|
$op = '='; |
1576
|
|
|
if (isset($clause['op'])) { |
1577
|
|
|
$op = $clause['op']; |
1578
|
|
|
} |
1579
|
|
|
|
1580
|
|
|
switch ($op) { |
1581
|
|
View Code Duplication |
case BETWEEN: |
|
|
|
|
1582
|
|
|
if (! isset($clause['field']) || ! isset($clause['low']) || ! isset($clause['high'])) { |
1583
|
|
|
$this->log("Missing field, low, or high for BETWEEN where clause, skipping", LogLevel::WARNING, $clause); |
1584
|
|
|
return; |
1585
|
|
|
} |
1586
|
|
|
break; |
1587
|
|
View Code Duplication |
default: |
|
|
|
|
1588
|
|
|
if (! isset($clause['field']) || ! isset($clause['value'])) { |
1589
|
|
|
$this->log("Missing field or value for WHERE clause, skipping", LogLevel::WARNING, $clause); |
1590
|
|
|
return; |
1591
|
|
|
} |
1592
|
|
|
} |
1593
|
|
|
|
1594
|
|
|
if ($index > 0) { |
1595
|
|
|
$ret .= " {$clause['sql_op']}"; |
1596
|
|
|
} |
1597
|
|
|
|
1598
|
|
|
if (isset($clause['open-paren']) && $clause['open-paren']) { |
1599
|
|
|
$ret .= " ("; |
1600
|
|
|
} |
1601
|
|
|
|
1602
|
|
|
if (isset($clause['backticks']) && ! $clause['backticks']) { |
1603
|
|
|
$field = $clause['field']; |
1604
|
|
|
} else { |
1605
|
|
|
$field = "`{$clause['field']}`"; |
1606
|
|
|
} |
1607
|
|
|
|
1608
|
|
|
if ($op == IN || $op == NOT_IN) { |
1609
|
|
|
if (is_string($clause['value'])) { |
1610
|
|
|
$ret .= " {$field} {$op} " . (strpos($clause['value'], '(') !== false ? $clause['value'] : "({$clause['value']})"); |
1611
|
|
|
} elseif (is_array($clause['value'])) { |
1612
|
|
|
$ret .= " {$field} {$op} (" . implode(",", array_map([ |
1613
|
|
|
$this, |
1614
|
|
|
'_escape' |
1615
|
|
|
], $clause['value'])) . ")"; |
1616
|
|
|
} else { |
1617
|
|
|
$this->log("Invalid datatype for IN WHERE clause, only string and array allowed " . gettype($clause['value']), LogLevel::ERROR, $clause); |
1618
|
|
|
throw new Exception("Invalid datatype for IN WHERE clause", E_ERROR); |
1619
|
|
|
} |
1620
|
|
|
} elseif ($op == BETWEEN) { |
1621
|
|
|
$ret .= " {$field} BETWEEN {$this->_escape($clause['low'])} AND {$this->_escape($clause['high'])}"; |
1622
|
|
|
} else { |
1623
|
|
|
if (isset($clause['escape']) && ! $clause['escape']) { |
1624
|
|
|
$value = $clause['value']; |
1625
|
|
|
} else { |
1626
|
|
|
$value = $this->_escape($clause['value']); |
1627
|
|
|
} |
1628
|
|
|
|
1629
|
|
|
if (isset($clause['case_insensitive']) && $clause['case_insensitive']) { |
1630
|
|
|
$ret .= " LOWER({$field}) {$op} LOWER({$this->_escape($clause['value'])})"; |
1631
|
|
|
} elseif (preg_match("/\(SELECT/", $clause['value'])) { |
1632
|
|
|
$ret .= " {$field} {$op} {$clause['value']}"; |
1633
|
|
|
} else { |
1634
|
|
|
$ret .= " {$field} {$op} {$value}"; |
1635
|
|
|
} |
1636
|
|
|
} |
1637
|
|
|
|
1638
|
|
|
if (isset($clause['close-paren']) && $clause['close-paren']) { |
1639
|
|
|
$ret .= ")"; |
1640
|
|
|
} |
1641
|
|
|
|
1642
|
|
|
return $ret; |
1643
|
|
|
} |
1644
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.