Passed
Branch master (0c354d)
by Ryan
01:21
created

Database::extendedInsert()   C

Complexity

Conditions 12
Paths 13

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12.0948

Importance

Changes 0
Metric Value
cc 12
eloc 25
nc 13
nop 4
dl 0
loc 40
ccs 21
cts 23
cp 0.913
crap 12.0948
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Godsgood33\Php_Db;
3
4
use Monolog\Logger;
5
use Monolog\Formatter\LineFormatter;
6
use Monolog\Handler\StreamHandler;
7
use Exception;
8
use mysqli;
9
10
/**
11
 * Constant to define that we want to return an object
12
 *
13
 * @var int
14
 */
15
define('MYSQLI_OBJECT', 4);
16
17
/**
18
 * Constant to return consistent date format
19
 *
20
 * @var string
21
 */
22
define('MYSQL_DATE', 'Y-m-d');
23
24
/**
25
 * Constant to return consistent datetime format
26
 *
27
 * @var string
28
 */
29
define('MYSQL_DATETIME', 'Y-m-d H:i:s');
30
31
/**
32
 * A generic database class
33
 *
34
 * @author Ryan Prather <[email protected]>
35
 */
36
class Database
37
{
38
39
    /**
40
     * Constant defining a SELECT query
41
     *
42
     * @var integer
43
     */
44
    const SELECT = 1;
45
46
    /**
47
     * Constant defining a SELECT COUNT query
48
     *
49
     * @var integer
50
     */
51
    const SELECT_COUNT = 2;
52
53
    /**
54
     * Constant defining a CREATE TABLE query
55
     *
56
     * @var integer
57
     */
58
    const CREATE_TABLE = 3;
59
60
    /**
61
     * Constant defining DROP query
62
     *
63
     * @var integer
64
     */
65
    const DROP = 4;
66
67
    /**
68
     * Constant defining DELETE query
69
     *
70
     * @var integer
71
     */
72
    const DELETE = 5;
73
74
    /**
75
     * Constant defining INSERT query
76
     *
77
     * @var integer
78
     */
79
    const INSERT = 6;
80
81
    /**
82
     * Constant defining REPLACE query
83
     *
84
     * @var integer
85
     */
86
    const REPLACE = 7;
87
88
    /**
89
     * Constant defining UPDATE query
90
     *
91
     * @var integer
92
     */
93
    const UPDATE = 8;
94
95
    /**
96
     * Constant defining EXTENDED INSERT query
97
     *
98
     * @var integer
99
     */
100
    const EXTENDED_INSERT = 9;
101
102
    /**
103
     * Constant defining EXTENDED REPLACE query
104
     *
105
     * @var integer
106
     */
107
    const EXTENDED_REPLACE = 10;
108
109
    /**
110
     * Constant defining EXTENDED UPDATE query
111
     *
112
     * @var integer
113
     */
114
    const EXTENDED_UPDATE = 11;
115
116
    /**
117
     * Constant defining ALTER TABLE query
118
     *
119
     * @var integer
120
     */
121
    const ALTER_TABLE = 12;
122
123
    /**
124
     * Constant defining action for alter table statement
125
     *
126
     * @var integer
127
     */
128
    const ADD_COLUMN = 1;
129
130
    /**
131
     * Constant defining action for alter table statement
132
     *
133
     * @var integer
134
     */
135
    const DROP_COLUMN = 2;
136
137
    /**
138
     * Constant defining action for alter table statement
139
     *
140
     * @var integer
141
     */
142
    const MODIFY_COLUMN = 3;
143
144
    /**
145
     * Constant defining action to add a constraint
146
     * 
147
     * @var integer
148
     */
149
    const ADD_CONSTRAINT = 4;
150
151
    /**
152
     * Constant defining a TRUNCATE TABLE query
153
     *
154
     * @var integer
155
     */
156
    const TRUNCATE = 13;
157
158
    /**
159
     * The mysqli connection
160
     *
161
     * @access protected
162
     * @var \mysqli
163
     */
164
    protected $_c;
165
166
    /**
167
     * To store the SQL statement
168
     *
169
     * @access private
170
     * @var string
171
     */
172
    private $_sql = null;
173
174
    /**
175
     * A variable to store the type of query that is being run
176
     *
177
     * @access private
178
     * @var int
179
     */
180
    private $_queryType = null;
181
182
    /**
183
     * The result of the query
184
     *
185
     * @access protected
186
     * @var mixed
187
     */
188
    protected $_result = null;
189
190
    /**
191
     * Log level
192
     *
193
     * @access private
194
     * @var string
195
     */
196
    private $_logLevel = Logger::ERROR;
197
198
    /**
199
     * Variable to store the logger
200
     *
201
     * @access private
202
     * @var \Monolog\Logger
203
     */
204
    private $_logger = null;
205
206
    /**
207
     * Path for the logger to log the file
208
     *
209
     * @access private
210
     * @var string
211
     */
212
    private $_logPath = null;
213
214
    /**
215
     * Variable to store the most recent insert ID from an insert query
216
     *
217
     * @access protected
218
     * @var mixed
219
     */
220
    protected $_insertId = null;
221
222
    /**
223
     * Variable to decide if we need to automatically run the queries after generating them
224
     *
225
     * @access public
226
     * @staticvar
227
     * @var boolean
228
     */
229
    public static $autorun = false;
230
231
    /**
232
     * Variable to decide if the system should create a CLI logger in addition to the file logger
233
     *
234
     * @access public
235
     * @staticvar
236
     * @var boolean
237
     */
238
    public static $cliLog = false;
239
240
    /**
241
     * Constructor
242
     *
243
     * @param string $strLogPath
244
     *            [optional]
245
     * @param \mysqli $dbh
246
     *            [optional]
247
     *            [by ref]
248
     *            mysqli object to perform queries.
249
     * @param int $intLogLevel
250
     */
251 86
    public function __construct($strLogPath = __DIR__, mysqli &$dbh = null, $intLogLevel = null)
252
    {
253 86
        if(! is_null($dbh) && is_a($dbh, 'mysqli')) {
254 1
            $this->_c = $dbh;
255 86
        } elseif(!defined('PHP_DB_SERVER') || !defined('PHP_DB_USER') || !defined('PHP_DB_PWD') || !defined('PHP_DB_SCHEMA')) {
256
            throw new Exception("Please create and include a constant file with the following constants defining your DB connection (PHP_DB_SERVER, PHP_DB_USER, PHP_DB_PWD, PHP_DB_SCHEMA)", E_USER_ERROR);
257 86
        } elseif(defined('PHP_DB_ENCRYPT') && (!defined('PHP_DB_ENCRYPT_ALGORITHM') || !defined('PHP_DB_ENCRYPT_SALT'))) {
258
            throw new Exception("Missing required PHP_DB_ENCRYPT_ALGORITHM or PHP_DB_ENCRYPT_SALT constants");
259
        }
260
        
261 86
        if(defined('PHP_DB_ENCRYPT') && PHP_DB_ENCRYPT) {
262 86
            $pwd = $this->decrypt(PHP_DB_PWD);
0 ignored issues
show
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_PWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
263
        } else {
264
            $pwd = PHP_DB_PWD;
265
        }
266
267 86
        $this->_c = new mysqli(PHP_DB_SERVER, PHP_DB_USER, $pwd, PHP_DB_SCHEMA);
0 ignored issues
show
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_SCHEMA was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_SERVER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
268
        
269 86
        if ($this->_c->connect_errno) {
270
            throw new Exception("Could not create database class due to error {$this->_c->connect_error}", E_ERROR);
271
        }
272
273 86
        $this->_logPath = $strLogPath;
274 86
        touch($this->_logPath . "/db.log");
275
276 86
        if(!defined("PHP_DB_LOG_LEVEL") && is_null($intLogLevel)) {
277
            $this->_logLevel = Logger::ERROR;
278 86
        } elseif(!is_null($intLogLevel)) {
279
            $this->_logLevel = $intLogLevel;
280 86
        } elseif(defined('PHP_DB_LOG_LEVEL')) {
281 86
            $this->_logLevel = PHP_DB_LOG_LEVEL;
0 ignored issues
show
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_LOG_LEVEL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
282
        }
283
284 86
        $this->_logger = new Logger('db', [
285 86
            new StreamHandler(realpath($this->_logPath . "/db.log"), $this->_logLevel)
0 ignored issues
show
Bug introduced by
$this->_logLevel of type string is incompatible with the type integer expected by parameter $level of Monolog\Handler\StreamHandler::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
            new StreamHandler(realpath($this->_logPath . "/db.log"), /** @scrutinizer ignore-type */ $this->_logLevel)
Loading history...
286
        ]);
287
288 86
        if (PHP_SAPI == 'cli' && self::$cliLog) {
289
            $stream = new StreamHandler(STDOUT, $this->_logLevel);
290
            $stream->setFormatter(new LineFormatter("%datetime% %level_name% %message%" . PHP_EOL, "H:i:s.u"));
291
            $this->_logger->pushHandler($stream);
292
        }
293
294 86
        $this->_logger->info("Database connected");
295 86
        $this->_logger->debug("Connection details:", [
296 86
            'Server' => PHP_DB_SERVER,
297 86
            'User'   => PHP_DB_USER,
298 86
            'Schema' => PHP_DB_SCHEMA
299
        ]);
300
301 86
        $this->setVar("time_zone", "+00:00");
302 86
        $this->setVar("sql_mode", "");
303 86
    }
304
305
    /**
306
     * Function to make sure that the database is connected
307
     *
308
     * @return boolean
309
     */
310 1
    public function isConnected()
311
    {
312 1
        $this->_logger->debug("Pinging server");
313 1
        return $this->_c->ping();
314
    }
315
316
    /**
317
     * Setter function for _logger
318
     *
319
     * @param Logger $log
320
     */
321
    public function setLogger(Logger $log)
322
    {
323
        $this->_logger->debug("Setting logger");
324
        $this->_logger = $log;
325
    }
326
327
    /**
328
     * Getter function for _logLevel
329
     *
330
     * @return string
331
     */
332 1
    public function getLogLevel()
333
    {
334 1
        $level = $this->_logLevel;
335
336 1
        $this->_logger->debug("Getting log level ({$level})");
337 1
        return $level;
338
    }
339
340
    /**
341
     * Function to set the log level just in case there needs to be a change to the default log level
342
     *
343
     * @param string $strLevel
344
     */
345 1
    public function setLogLevel($strLevel)
346
    {
347 1
        $this->_logger->debug("Setting log level to {$strLevel}");
348 1
        $this->_logLevel = $strLevel;
349
350 1
        $handles = [];
351
352 1
        foreach ($this->_logger->getHandlers() as $h) {
353
            $h->/** @scrutinizer ignore-call */
354 1
                setLevel($strLevel);
355 1
            $handles[] = $h;
356
        }
357
358 1
        $this->_logger->setHandlers($handles);
359 1
    }
360
361
    /**
362
     * Getter function for _queryType
363
     *
364
     * @return int
365
     */
366 2
    public function getQueryType()
367
    {
368 2
        return $this->_queryType;
369
    }
370
371
    /**
372
     * Setter function for _queryType
373
     *
374
     * @param int $qt
375
     */
376 1
    public function setQueryType($qt)
377
    {
378 1
        $this->_queryType = $qt;
379 1
    }
380
381
    /**
382
     * Getter function for _sql
383
     *
384
     * @return string
385
     */
386 43
    public function getSql()
387
    {
388 43
        return $this->_sql;
389
    }
390
391
    /**
392
     * Function to return the currently selected database schema
393
     *
394
     * @return string
395
     */
396 1
    public function getSchema()
397
    {
398 1
        if ($res = $this->_c->query("SELECT DATABASE()")) {
399 1
            $row = $res->fetch_row();
400
401 1
            $this->_logger->debug("Getting schema {$row[0]}");
402 1
            return $row[0];
403
        }
404
    }
405
406
    /**
407
     * Function to set schema
408
     *
409
     * @param string $strSchema
410
     */
411 2
    public function setSchema($strSchema)
412
    {
413 2
        $this->_logger->debug("Setting schema to {$strSchema}");
414 2
        if (! $this->_c->select_db($strSchema)) {
415 1
            $this->_logger->emergency("Unknown schema {$strSchema}", [debug_backtrace()]);
416 1
            return false;
417
        }
418 1
        return true;
419
    }
420
421
    /**
422
     * Method to set a MYSQL variable
423
     *
424
     * @param string $strName
425
     * @param string $strVal
426
     *
427
     * @return boolean
428
     */
429 86
    public function setVar($strName, $strVal)
430
    {
431 86
        if (! $strName ) {
432 1
            $this->_logger->debug("name is blank", [
433 1
                'name'  => $strName
434
            ]);
435 1
            return false;
436
        }
437
438 86
        $this->_logger->debug("Setting {$strName} = '{$strVal}'");
439
440 86
        if ($this->_c->real_query("SET $strName = {$this->_escape($strVal)}")) {
441 86
            return true;
442
        } else {
443
            $this->_logger->error("Failed to set variable {$this->_c->error}");
444
            return false;
445
        }
446
    }
447
448
    /**
449
     * Function to execute the statement
450
     *
451
     * @param mixed $return
452
     *            [optional]
453
     *            MYSQLI constant to control what is returned from the mysqli_result object
454
     * @param string $class
455
     *            [optional]
456
     *            Class to use when returning object
457
     * @param string $strSql
458
     *            [optional]
459
     *            Optional SQL query
460
     *
461
     * @throws \Exception
462
     * @throws \InvalidArgumentException
463
     *
464
     * @return mixed
465
     */
466 7
    public function execute($return = MYSQLI_OBJECT, $strSql = null)
467
    {
468 7
        if (! is_null($strSql)) {
469
            $this->_sql = $strSql;
470
        }
471
472 7
        $this->_result = false;
473 7
        $query = 'SELECT';
474 7
        switch ($this->_queryType) {
475 7
            case self::SELECT_COUNT:
476
                $query = 'SELECT COUNT';
477
                break;
478 7
            case self::INSERT:
479 7
            case self::EXTENDED_INSERT:
480
                $query = 'INSERT';
481
                break;
482 7
            case self::UPDATE:
483 7
            case self::EXTENDED_UPDATE:
484
                $query = 'UPDATE';
485
                break;
486 7
            case self::REPLACE:
487 7
            case self::EXTENDED_REPLACE:
488
                $query = 'REPLACE';
489
                break;
490 7
            case self::DROP:
491
                $query = 'DROP';
492
                break;
493 7
            case self::DELETE:
494
                $query = 'DELETE';
495
                break;
496 7
            case self::CREATE_TABLE:
497 3
                $query = 'CREATE TABLE';
498 3
                break;
499 4
            case self::TRUNCATE:
500
                $query = 'TRUNCATE';
501
                break;
502
        }
503
504 7
        if (is_a($this->_c, 'mysqli')) {
505 7
            if (! $this->_c->ping()) {
506
                require_once 'DBConfig.php';
507
                $this->_c = null;
508 7
                $this->_c = new mysqli(PHP_DB_SERVER, PHP_DB_USER, PHP_DB_PWD, PHP_DB_SCHEMA);
0 ignored issues
show
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_PWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_SCHEMA was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_SERVER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Godsgood33\Php_Db\PHP_DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
509
            }
510
        } else {
511
            throw new Exception('Database was not connected', E_ERROR);
512
        }
513
514 7
        $this->_logger->info("Executing {$query} query");
515 7
        $this->_logger->debug($this->_sql);
516
517
        try {
518 7
            if (in_array($this->_queryType, [
519 7
                self::SELECT,
520 7
                self::SELECT_COUNT
521
            ])) {
522 4
                $this->_result = $this->_c->query($this->_sql);
523 4
                if ($this->_c->error) {
524
                    $this->_logger->error("There is an error {$this->_c->error}");
525
                    $this->_logger->debug("Errored on query", [$this->_sql]);
526 4
                    throw new Exception("There was an error {$this->_c->error}", E_ERROR);
527
                }
528
            } else {
529 3
                $this->_result = $this->_c->real_query($this->_sql);
530 3
                if ($this->_c->errno) {
531
                    $this->_logger->error("There was an error {$this->_c->error}");
532
                    $this->_logger->debug("Errored on query", [$this->_sql]);
533
                    throw new Exception("There was an error {$this->_c->error}", E_ERROR);
534
                }
535
            }
536
537 7
            $this->_logger->debug("Checking for query results");
538 7
            $this->_result = $this->checkResults($return);
539
        } catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
540
541 7
        return $this->_result;
542
    }
543
544
    /**
545
     * Function to check the results and return what is expected
546
     *
547
     * @param mixed $returnType
548
     *            [optional]
549
     *            Optional return mysqli_result return type
550
     *
551
     * @return mixed
552
     */
553 7
    protected function checkResults($returnType)
554
    {
555 7
        $res = null;
556
557 7
        if (in_array($this->_queryType, [Database::CREATE_TABLE, Database::ALTER_TABLE, Database::TRUNCATE, Database::DROP])) {
558 3
            $res = $this->_result;
559
        }
560 4
        elseif (in_array($this->_queryType, [Database::INSERT, Database::EXTENDED_INSERT, Database::DELETE, Database::UPDATE, Database::EXTENDED_UPDATE, Database::REPLACE, Database::EXTENDED_REPLACE])) {
561
            $res = $this->_c->affected_rows;
562
563
            if (in_array($this->_queryType, [Database::INSERT, Database::REPLACE])) {
564
                $this->_insertId = $this->_c->insert_id;
565
            }
566
        }
567 4
        elseif ($this->_queryType == Database::SELECT_COUNT) {
568
            if (! is_a($this->_result, 'mysqli_result')) {
569
                $this->_logger->error("Error with return on query");
570
                return null;
571
            }
572
573
            if ($this->_result->num_rows == 1) {
574
                $row = $this->_result->fetch_assoc();
575
                if (isset($row['count'])) {
576
                    $this->_logger->debug("Returning SELECT_COUNT query", [
577
                        'count' => $row['count']
578
                    ]);
579
                    $res = $row['count'];
580
                }
581
            } elseif ($this->_result->num_rows > 1) {
582
                $this->_logger->debug("Returning SELECT_COUNT query", [
583
                    'count' => $this->_result->num_rows
584
                ]);
585
                $res = $this->_result->num_rows;
586
            }
587
588
            mysqli_free_result($this->_result);
589
        }
590
        else {
591 4
            $method = "mysqli_fetch_object";
592 4
            if($returnType == MYSQLI_ASSOC) {
593 1
                $method = "mysqli_fetch_assoc";
594 3
            } elseif ($returnType == MYSQLI_NUM) {
595 1
                $method = "mysqli_fetch_array";
596
            }
597
598 4
            if (is_a($this->_result, 'mysqli_result')) {
599 4
                if($this->_result->num_rows > 1) {
600 3
                    $res = [];
601 3
                    while ($row = call_user_func($method, $this->_result)) {
602 3
                        $res[] = $row;
603
                    }
604
                } else {
605 4
                    $res = call_user_func($method, $this->_result);
606
                }
607
            } else {
608
                $this->_logger->error("Error with return on query");
609
                return null;
610
            }
611
        }
612
613 7
        if ($this->_c->error) {
614
            $this->_logger->error("Encountered a SQL error", ['error' => $this->_c->error, 'list' => $this->_c->error_list]);
615
            $this->_logger->debug("Debug", ['debug' => debug_backtrace()]);
616
            return null;
617
        }
618
619 7
        return $res;
620
    }
621
622
    /**
623
     * Function to pass through calling the query function (used for backwards compatibility and for more complex queries that aren't currently supported)
624
     * Nothing is escaped
625
     *
626
     * @param string $strSql
627
     *            [optional]
628
     *            Optional query to pass in and execute
629
     *
630
     * @return \mysqli_result|boolean
631
     */
632 1
    public function query($strSql = null)
633
    {
634 1
        if (is_null($strSql)) {
635
            return $this->_c->query($this->_sql);
636
        } else {
637 1
            return $this->_c->query($strSql);
638
        }
639
    }
640
641
    /**
642
     * A function to build a select query
643
     *
644
     * @param string $strTableName
645
     *            The table to query
646
     * @param array|string $fields
647
     *            [optional]
648
     *            Optional array of fields to return (defaults to '*')
649
     * @param array $arrWhere
650
     *            [optional]
651
     *            Optional 2-dimensional array to build where clause from
652
     * @param array $arrFlags
653
     *            [optional]
654
     *            Optional 2-dimensional array to allow other flags
655
     *
656
     * @see Database::flags()
657
     *
658
     * @throws Exception
659
     *
660
     * @return mixed
661
     */
662 24
    public function select($strTableName, $fields = null, $arrWhere = [], $arrFlags = [])
663
    {
664 24
        $this->_sql = null;
665 24
        $this->_queryType = self::SELECT;
666
667 24
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
668 23
            $this->_logger->debug("Starting SELECT query of {$strTableName}", [
669 23
                'fields' => $this->fields($fields)
670
            ]);
671 22
            $this->_sql = "SELECT " . $this->fields($fields) . " FROM $strTableName";
672
        } else {
673 1
            $this->_logger->emergency("Table name is invalid or wrong type", [debug_backtrace()]);
674 1
            throw new Exception("Table name is invalid");
675
        }
676
677 22
        if (isset($arrFlags['joins']) && is_array($arrFlags['joins']) && count($arrFlags['joins'])) {
678
            $this->_logger->debug("Adding joins", [
679
                'joins' => implode(' ', $arrFlags['joins'])
680
            ]);
681
            $this->_sql .= " " . implode(" ", $arrFlags['joins']);
682
        } else {
683 22
            $this->_logger->debug("No joins");
684
        }
685
686 22
        $where = $this->parseClause($arrWhere);
687
688 20
        if (! is_null($where) && is_array($where) && count($where)) {
0 ignored issues
show
introduced by
The condition is_null($where) is always false.
Loading history...
introduced by
The condition is_array($where) is always true.
Loading history...
689 8
            $where_str = " WHERE";
690 8
            $this->_logger->debug("Parsing where clause and adding to query");
691 8
            foreach ($where as $x => $w) {
692 8
                if($x > 0) {
693 1
                    $where_str .= " {$w->sqlOperator}";
694
                }
695 8
                $where_str .= $w;
696
            }
697 8
            if (strlen($where_str) > strlen(" WHERE")) {
698 8
                $this->_sql .= $where_str;
699
            }
700
        }
701
702 20
        if (is_array($arrFlags) && count($arrFlags)) {
703
            $this->_logger->debug("Parsing flags and adding to query", $arrFlags);
704
            $this->_sql .= $this->flags($arrFlags);
705
        }
706
707 20
        if (self::$autorun) {
708
            return $this->execute();
709
        }
710
711 20
        return $this->_sql;
712
    }
713
714
    /**
715
     * Function to build a query to check the number of rows in a table
716
     *
717
     * @param string $strTableName
718
     *            The table to query
719
     * @param array $arrWhere
720
     *            [optional]
721
     *            Optional 2-dimensional array to build where clause
722
     * @param array $arrFlags
723
     *            [optional]
724
     *            Optional 2-dimensional array to add flags
725
     *
726
     * @see Database::flags()
727
     *
728
     * @return string|NULL
729
     */
730 3
    public function selectCount($strTableName, $arrWhere = [], $arrFlags = [])
731
    {
732 3
        $this->_sql = null;
733 3
        $this->_queryType = self::SELECT_COUNT;
734
735 3
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
736 2
            $this->_sql = "SELECT COUNT(1) AS 'count' FROM $strTableName";
737
        } else {
738 1
            $this->_logger->emergency("Table name is invalid or wrong type", [debug_backtrace()]);
739 1
            throw new Exception("Table name is invalid");
740
        }
741
742 2
        if (isset($arrFlags['joins']) && is_array($arrFlags['joins'])) {
743 1
            $this->_sql .= " " . implode(" ", $arrFlags['joins']);
744
        }
745
746 2
        $where = $this->parseClause($arrWhere);
747
748 2
        if (! is_null($where) && is_array($where) && count($where)) {
0 ignored issues
show
introduced by
The condition is_null($where) is always false.
Loading history...
introduced by
The condition is_array($where) is always true.
Loading history...
749 1
            $where_str = " WHERE";
750 1
            $this->_logger->debug("Parsing where clause and adding to query");
751 1
            foreach ($where as $x => $w) {
752 1
                if($x > 0) {
753
                    $where_str .= " {$w->sqlOperator}";
754
                }
755 1
                $where_str .= $w;
756
            }
757 1
            if (strlen($where_str) > strlen(" WHERE")) {
758 1
                $this->_sql .= $where_str;
759
            }
760
        }
761
762 2
        if (is_array($arrFlags) && count($arrFlags)) {
763 1
            $this->_sql .= $this->flags($arrFlags);
764
        }
765
766 2
        if (self::$autorun) {
767
            return $this->execute();
768
        }
769
770 2
        return $this->_sql;
771
    }
772
773
    /**
774
     * Function to build an insert query statement
775
     *
776
     * @param string $strTableName
777
     * @param array|string $arrParams
778
     * @param boolean $blnToIgnore
779
     *
780
     * @return string|NULL
781
     */
782 8
    public function insert($strTableName, $arrParams = null, $blnToIgnore = false)
783
    {
784 8
        $this->_sql = null;
785 8
        $this->_queryType = self::INSERT;
786
787 8
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_string($strTableName) is always true.
Loading history...
introduced by
The condition is_null($strTableName) is always false.
Loading history...
788 7
            $this->_sql = "INSERT" . ($blnToIgnore ? " IGNORE" : "") . " INTO {$strTableName}";
789
        } else {
790 1
            throw new Exception("Table name is invalid");
791
        }
792
793 7
        if (is_array($arrParams) && count($arrParams)) {
794 4
            if(is_array($arrParams) && count($arrParams)) {
795 4
                $this->_sql .= " (`" . implode("`,`", array_keys($arrParams)) . "`)";
796
            }
797 4
            $this->_sql .= " VALUES (" . implode(",", array_map([
798 4
                $this,
799 4
                '_escape'
800 4
            ], array_values($arrParams))) . ")";
801 3
        } elseif (is_string($arrParams) && strpos(strtolower($arrParams), 'select') !== false) {
802 1
            $this->_sql .= " {$arrParams}";
803 2
        } elseif (is_object($arrParams)) {
804 2
            $interfaces = \class_implements($arrParams);
805 2
            if(in_array("Godsgood33\Php_Db\DBInterface", $interfaces) && is_callable(get_class($arrParams) . "::insert")) {
806 1
                $params = \call_user_func([$arrParams, "insert"]);
807 1
                $this->_sql .= " (`" . implode("`,`", array_keys($params)) . "`) VALUES ";
808 1
                $this->_sql .= "(" . implode(",", array_map([$this, '_escape'], array_values($params))) . ")";
809
            } else {
810 2
                throw new Exception("Object does not implement the DBInterface interface and methods");
811
            }
812
        } else {
813
            throw new Exception("Invalid type passed to insert " . gettype($arrParams));
814
        }
815
816 6
        if (self::$autorun) {
817
            return $this->execute();
818
        }
819
820 6
        return $this->_sql;
821
    }
822
823
    /**
824
     * Function to create an extended insert query statement
825
     *
826
     * @param string $strTableName
827
     *            The table name that the data is going to be inserted on
828
     * @param array $arrFields
829
     *            An array of field names that each value represents
830
     * @param array|string $params
831
     *            An array of array of values or a string with a SELECT statement to populate the insert with
832
     * @param boolean $blnToIgnore
833
     *            [optional]
834
     *            Boolean to decide if we need to use the INSERT IGNORE INTO syntax
835
     *
836
     * @return NULL|string Returns the SQL if self::$autorun is set to false, else it returns the output from running.
837
     */
838 4
    public function extendedInsert($strTableName, $arrFields, $params, $blnToIgnore = false)
839
    {
840 4
        $this->_sql = null;
841 4
        $this->_queryType = self::EXTENDED_INSERT;
842
843 4
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
844 3
            $this->_sql = "INSERT " . ($blnToIgnore ? "IGNORE " : "") . "INTO $strTableName " . "(`" . implode("`,`", $arrFields) . "`)";
845
        } else {
846 1
            throw new Exception("Table name is invalid");
847
        }
848
849 3
        if (is_array($params) && count($params)) {
850 3
            $this->_sql .= " VALUES ";
851 3
            if (isset($params[0]) && is_array($params[0])) {
852 3
                foreach ($params as $p) {
853 3
                    if (count($p) != count($arrFields)) {
854 2
                        $this->_logger->emergency("Inconsistent number of fields to values in extendedInsert", [
855 2
                            $p,
856 2
                            debug_backtrace()
857
                        ]);
858 2
                        throw new Exception("Inconsistent number of fields in fields and values in extendedInsert " . print_r($p, true));
859
                    }
860 2
                    $this->_sql .= "(" . implode(",", array_map([$this, '_escape'], array_values($p))) . ")";
861
862 2
                    if ($p != end($params)) {
863 2
                        $this->_sql .= ",";
864
                    }
865
                }
866
            } else {
867 1
                $this->_sql .= "(" . implode("),(", array_map([$this, '_escape'], array_values($params))) . ")";
868
            }
869
        } else {
870
            throw new Exception("Invalid param type");
871
        }
872
873 1
        if (self::$autorun) {
874
            return $this->execute();
875
        }
876
877 1
        return $this->_sql;
878
    }
879
880
    /**
881
     * Build a statement to update a table
882
     *
883
     * @param string $strTableName
884
     *            The table name to update
885
     * @param array $arrParams
886
     *            Name/value pairs of the field name and value
887
     * @param array $arrWhere
888
     *            [optional]
889
     *            Two-dimensional array to create where clause
890
     * @param array $arrFlags
891
     *            [optional]
892
     *            Two-dimensional array to create other flag options (joins, order, and group)
893
     *
894
     * @see Database::flags()
895
     *
896
     * @return NULL|string
897
     */
898 8
    public function update($strTableName, $arrParams, $arrWhere = [], $arrFlags = [])
899
    {
900 8
        $this->_sql = "UPDATE ";
901 8
        $this->_queryType = self::UPDATE;
902
903 8
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
904 7
            $this->_sql .= $strTableName;
905
906 7
            if (isset($arrFlags['joins']) && is_array($arrFlags['joins'])) {
907 1
                $this->_sql .= " " . implode(" ", $arrFlags['joins']);
908 1
                unset($arrFlags['joins']);
909
            }
910
911 7
            $this->_sql .= " SET ";
912
        } else {
913 1
            throw new Exception("Table name is invalid");
914
        }
915
916 7
        if (is_array($arrParams) && count($arrParams)) {
917 4
            $keys = array_keys($arrParams);
918 4
            foreach ($arrParams as $f => $p) {
919 4
                $field = $f;
920 4
                if ((strpos($f, "`") === false) && (strpos($f, ".") === false) && (strpos($f, "*") === false) && (stripos($f, " as ") === false)) {
921 3
                    $field = "`{$f}`";
922
                }
923
924 4
                if (! is_null($p)) {
925 4
                    $this->_sql .= "$field={$this->_escape($p)}";
926
                } else {
927
                    $this->_sql .= "$field=NULL";
928
                }
929
930 4
                if($f != end($keys)) {
931
                    $this->_sql .= ",";
932
                }
933
            }
934 3
        } elseif (is_object($arrParams)) {
935 2
            $interfaces = \class_implements($arrParams);
936 2
            if(in_array("Godsgood33\Php_Db\DBInterface", $interfaces) && is_callable(get_class($arrParams) . "::update")) {
937 1
                $this->_sql .= \call_user_func([$arrParams, "update"]);
938
            } else {
939 2
                throw new Exception("Params is an object that doesn't implement DBInterface");
940
            }
941
        } else {
942 1
            throw new Exception("No fields to update");
943
        }
944
945 5
        $where = $this->parseClause($arrWhere);
946
        
947 5
        if (! is_null($where) && is_array($where) && count($where)) {
0 ignored issues
show
introduced by
The condition is_null($where) is always false.
Loading history...
introduced by
The condition is_array($where) is always true.
Loading history...
948 1
            $where_str = " WHERE";
949 1
            $this->_logger->debug("Parsing where clause and adding to query");
950 1
            foreach ($where as $x => $w) {
951 1
                if($x > 0) {
952
                    $where_str .= " {$w->sqlOperator}";
953
                }
954 1
                $where_str .= $w;
955
            }
956 1
            if (strlen($where_str) > strlen(" WHERE")) {
957 1
                $this->_sql .= $where_str;
958
            }
959
        }
960
961 5
        if (! is_null($arrFlags) && is_array($arrFlags) && count($arrFlags)) {
962 1
            $this->_sql .= $this->flags($arrFlags);
963
        }
964
965 5
        if (self::$autorun) {
966
            return $this->execute();
967
        }
968
969 5
        return $this->_sql;
970
    }
971
972
    /**
973
     * Function to offer an extended updated functionality by using two different tables.
974
     *
975
     * @param string $strTableToUpdate
976
     *            The table that you want to update (alias 'tbu' is automatically added)
977
     * @param string $strOriginalTable
978
     *            The table with the data you want to overwrite to_be_updated table (alias 'o' is automatically added)
979
     * @param string $strLinkField
980
     *            The common index value between them that will join the fields
981
     * @param array|string $arrParams
982
     *            If string only a single field is updated (tbu.$params = o.$params)
983
     *            If array each element in the array is a field to be updated (tbu.$param = o.$param)
984
     *
985
     * @return mixed
986
     */
987 3
    public function extendedUpdate($strTableToUpdate, $strOriginalTable, $strLinkField, $arrParams)
988
    {
989 3
        $this->_sql = "UPDATE ";
990 3
        $this->_queryType = self::EXTENDED_UPDATE;
991
992 3
        if (! is_null($strTableToUpdate) && ! is_null($strOriginalTable) && ! is_null($strLinkField)) {
0 ignored issues
show
introduced by
The condition is_null($strOriginalTable) is always false.
Loading history...
introduced by
The condition is_null($strLinkField) is always false.
Loading history...
introduced by
The condition is_null($strTableToUpdate) is always false.
Loading history...
993 3
            $this->_sql .= "$strTableToUpdate tbu INNER JOIN $strOriginalTable o USING ($strLinkField) SET ";
994
        } else {
995
            throw new Exception("Missing necessary fields");
996
        }
997
998 3
        if (is_array($arrParams) && count($arrParams)) {
999 1
            foreach ($arrParams as $param) {
1000 1
                if ($param != $strLinkField) {
1001 1
                    $this->_sql .= "tbu.`$param` = o.`$param`,";
1002
                }
1003
            }
1004 1
            $this->_sql = substr($this->_sql, 0, - 1);
1005 2
        } elseif (is_string($arrParams)) {
1006 1
            $this->_sql .= "tbu.`$arrParams` = o.`$arrParams`";
1007
        } else {
1008 1
            throw new Exception("Do not understand datatype " . gettype($arrParams), E_ERROR);
1009
        }
1010
1011 2
        if (self::$autorun) {
1012
            return $this->execute();
1013
        }
1014
1015 2
        return $this->_sql;
1016
    }
1017
1018
    /**
1019
     * Function to build a replace query
1020
     *
1021
     * @param string $strTableName
1022
     *            The table to update
1023
     * @param array $arrParams
1024
     *            Name/value pair to insert
1025
     *
1026
     * @return NULL|string
1027
     */
1028 2
    public function replace($strTableName, $arrParams)
1029
    {
1030 2
        $this->_sql = null;
1031 2
        $this->_queryType = self::REPLACE;
1032
1033 2
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
1034 1
            $this->_sql = "REPLACE INTO $strTableName ";
1035
        } else {
1036 1
            throw new Exception("Table name is invalid");
1037
        }
1038
1039 1
        if(is_array($arrParams) && count($arrParams)) {
1040 1
            $keys = array_keys($arrParams);
1041 1
            $vals = array_values($arrParams);
1042
1043 1
            $this->_sql .= "(`" . implode("`,`", $keys) . "`)";
1044 1
            $this->_sql .= " VALUES (" . implode(",", array_map([
1045 1
                $this,
1046 1
                '_escape'
1047 1
            ], array_values($vals))) . ")";
1048
        } elseif (is_object($arrParams)) {
1049
            $interfaces = class_implements($arrParams);
1050
            if(in_array("Godsgood33\Php_Db\DBInterface", $interfaces) && is_callable(get_class($arrParams) . "::replace")) {
1051
                $params = \call_user_func([$arrParams, "replace"]);
1052
                $this->_sql .= "(`" . implode("`,`", array_keys($params)) . "`) VALUES ";
1053
                $this->_sql .= "(" . implode(",", array_map([$this, '_escape'], array_values($params))) . ")";
1054
            }
1055
        }
1056
1057 1
        if (self::$autorun) {
1058
            return $this->execute();
1059
        }
1060
1061 1
        return $this->_sql;
1062
    }
1063
1064
    /**
1065
     * Function to build an extended replace statement
1066
     *
1067
     * @param string $strTableName
1068
     *            Table name to update
1069
     * @param array $arrFields
1070
     *            Array of fields
1071
     * @param array $arrParams
1072
     *            Two-dimensional array of values
1073
     *
1074
     * @return NULL|string
1075
     */
1076 2
    public function extendedReplace($strTableName, $arrFields, $arrParams)
1077
    {
1078 2
        $this->_sql = null;
1079 2
        $this->_queryType = self::EXTENDED_REPLACE;
1080
1081 2
        if (! is_array($arrFields) || ! count($arrFields)) {
0 ignored issues
show
introduced by
The condition is_array($arrFields) is always true.
Loading history...
1082 1
            throw new Exception("Error with the field type");
1083
        }
1084
1085 1
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_string($strTableName) is always true.
Loading history...
introduced by
The condition is_null($strTableName) is always false.
Loading history...
1086 1
            $this->_sql = "REPLACE INTO $strTableName " . "(`" . implode("`,`", $arrFields) . "`)";
1087
        } else {
1088
            throw new Exception("Table name is invalid");
1089
        }
1090
1091 1
        if (is_array($arrParams) && count($arrParams)) {
1092 1
            $this->_sql .= " VALUES ";
1093 1
            foreach ($arrParams as $p) {
1094 1
                $this->_sql .= "(" . implode(",", array_map([
1095 1
                    $this,
1096 1
                    '_escape'
1097 1
                ], array_values($p))) . ")";
1098
1099 1
                if ($p != end($arrParams)) {
1100 1
                    $this->_sql .= ",";
1101
                }
1102
            }
1103
        }
1104
1105 1
        if (self::$autorun) {
1106
            return $this->execute();
1107
        }
1108
1109 1
        return $this->_sql;
1110
    }
1111
1112
    /**
1113
     * Function to build a delete statement
1114
     *
1115
     * @param string $strTableName
1116
     *            Table name to act on
1117
     * @param array $arrFields
1118
     *            [optional]
1119
     *            Optional list of fields to delete (used when including multiple tables)
1120
     * @param array $arrWhere
1121
     *            [optional]
1122
     *            Optional 2-dimensional array to build where clause from
1123
     * @param array $arrJoins
1124
     *            [optional]
1125
     *            Optional 2-dimensional array to add other flags
1126
     *
1127
     * @see Database::flags()
1128
     *
1129
     * @return string|NULL
1130
     */
1131 4
    public function delete($strTableName, $arrFields = [], $arrWhere = [], $arrJoins = [])
1132
    {
1133 4
        $this->_sql = "DELETE";
1134 4
        $this->_queryType = self::DELETE;
1135
1136 4
        $this->_logger->debug("Deleting table data");
1137
1138 4
        if (! is_null($arrFields) && is_array($arrFields) && count($arrFields)) {
0 ignored issues
show
introduced by
The condition is_array($arrFields) is always true.
Loading history...
introduced by
The condition is_null($arrFields) is always false.
Loading history...
1139 1
            $this->_sql .= " " . implode(",", $arrFields);
1140
        }
1141
1142 4
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_string($strTableName) is always true.
Loading history...
introduced by
The condition is_null($strTableName) is always false.
Loading history...
1143 3
            $this->_sql .= " FROM $strTableName";
1144
        } else {
1145 1
            throw new Exception("Table name is invalid");
1146
        }
1147
1148 3
        if (! is_null($arrJoins) && is_array($arrJoins) && count($arrJoins)) {
0 ignored issues
show
introduced by
The condition is_array($arrJoins) is always true.
Loading history...
introduced by
The condition is_null($arrJoins) is always false.
Loading history...
1149 1
            $this->_sql .= " " . implode(" ", $arrJoins);
1150
        }
1151
1152 3
        $where = $this->parseClause($arrWhere);
1153
1154 3
        if (! is_null($where) && is_array($where) && count($where)) {
0 ignored issues
show
introduced by
The condition is_null($where) is always false.
Loading history...
introduced by
The condition is_array($where) is always true.
Loading history...
1155 1
            $where_str = " WHERE";
1156 1
            $this->_logger->debug("Parsing where clause and adding to query");
1157 1
            foreach ($where as $x => $w) {
1158 1
                if($x > 0) {
1159
                    $where_str .= " {$w->sqlOperator}";
1160
                }
1161 1
                $where_str .= $w;
1162
            }
1163 1
            if (strlen($where_str) > strlen(" WHERE")) {
1164 1
                $this->_sql .= $where_str;
1165
            }
1166
        }
1167
1168 3
        if (self::$autorun) {
1169
            return $this->execute();
1170
        }
1171
1172 3
        return $this->_sql;
1173
    }
1174
1175
    /**
1176
     * Function to build a drop table statement (automatically executes)
1177
     *
1178
     * @param string $strTableName
1179
     *            Table to drop
1180
     * @param string $strType
1181
     *            [optional]
1182
     *            Type of item to drop ('table', 'view') (defaulted to 'table')
1183
     * @param boolean $blnIsTemp
1184
     *            [optional]
1185
     *            Optional boolean if this is a temporary table
1186
     *
1187
     * @return string|NULL
1188
     */
1189 5
    public function drop($strTableName, $strType = 'table', $blnIsTemp = false)
1190
    {
1191 5
        $this->_sql = null;
1192 5
        $this->_queryType = self::DROP;
1193
1194 5
        switch ($strType) {
1195 5
            case 'table':
1196 3
                $strType = 'TABLE';
1197 3
                break;
1198 2
            case 'view':
1199 1
                $strType = 'VIEW';
1200 1
                break;
1201
            default:
1202 1
                throw new Exception("Invalid type " . gettype($strType), E_ERROR);
1203
        }
1204
1205 4
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
1206 3
            $this->_sql = "DROP" . ($blnIsTemp ? " TEMPORARY" : "") . " $strType IF EXISTS `{$strTableName}`";
1207
        } else {
1208 1
            throw new Exception("Table name is invalid");
1209
        }
1210
1211 3
        if (self::$autorun) {
1212
            return $this->execute();
1213
        }
1214
1215 3
        return $this->_sql;
1216
    }
1217
1218
    /**
1219
     * Function to build a truncate table statement (automatically executes)
1220
     *
1221
     * @param string $strTableName
1222
     *            Table to truncate
1223
     *
1224
     * @throws Exception
1225
     *
1226
     * @return string|NULL
1227
     */
1228 2
    public function truncate($strTableName)
1229
    {
1230 2
        $this->_sql = null;
1231 2
        $this->_queryType = self::TRUNCATE;
1232
1233 2
        if (! is_null($strTableName) && is_string($strTableName)) {
0 ignored issues
show
introduced by
The condition is_null($strTableName) is always false.
Loading history...
introduced by
The condition is_string($strTableName) is always true.
Loading history...
1234 1
            $this->_sql = "TRUNCATE TABLE $strTableName";
1235
        } else {
1236 1
            throw new Exception("Table name is invalid");
1237
        }
1238
1239 1
        if (self::$autorun) {
1240
            return $this->execute();
1241
        }
1242
1243 1
        return $this->_sql;
1244
    }
1245
1246
    /**
1247
     * Function to build a create temporary table statement
1248
     *
1249
     * @param string $strTableName
1250
     *            Name to give the table when creating
1251
     * @param boolean $blnIsTemp
1252
     *            [optional]
1253
     *            Optional boolean to make the table a temporary table
1254
     * @param mixed $strSelect
1255
     *            [optional]
1256
     *            Optional parameter if null uses last built statement
1257
     *            If string, will be made the SQL statement executed to create the table
1258
     *            If array, 2-dimensional array with "field", "datatype" values to build table fields
1259
     *
1260
     * @return NULL|string
1261
     */
1262 3
    public function createTable($strTableName, $blnIsTemp = false, $strSelect = null)
1263
    {
1264 3
        $this->_queryType = self::CREATE_TABLE;
1265
1266 3
        if (is_null($strSelect) && ! is_null($this->_sql) && substr($this->_sql, 0, 6) == 'SELECT') {
1267 1
            $this->_sql = "CREATE" . ($blnIsTemp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $strTableName AS ($this->_sql)";
1268 2
        } elseif (! is_null($strTableName) && is_string($strTableName) && is_string($strSelect)) {
0 ignored issues
show
introduced by
The condition is_string($strTableName) is always true.
Loading history...
introduced by
The condition is_null($strTableName) is always false.
Loading history...
1269 1
            $this->_sql = "CREATE" . ($blnIsTemp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $strTableName AS ($strSelect)";
1270 1
        } elseif (! is_null($strTableName) && is_string($strTableName) && is_array($strSelect)) {
1271 1
            $this->_sql = "CREATE" . ($blnIsTemp ? " TEMPORARY" : "") . " TABLE IF NOT EXISTS $strTableName (";
1272
1273 1
            foreach ($strSelect as $field) {
1274 1
                $default = null;
1275 1
                if (isset($field['default'])) {
1276 1
                    $default = (is_null($field['default']) ? "" : " DEFAULT '{$field['default']}'");
1277
                }
1278 1
                $this->_sql .= "`{$field['field']}` {$field['datatype']}" . $default . (isset($field['option']) ? " {$field['option']}" : '') . ",";
1279
            }
1280 1
            $this->_sql = substr($this->_sql, 0, - 1) . ")";
1281
        }
1282
1283 3
        if (self::$autorun) {
1284
            return $this->execute();
1285
        }
1286
1287 3
        return $this->_sql;
1288
    }
1289
1290
    /**
1291
     * Function to create a table using a stdClass object derived from JSON
1292
     *
1293
     * @param \stdClass $json
1294
     *
1295
     * @example /examples/create_table_json.json
1296
     *
1297
     */
1298 3
    public function createTableJson($json)
1299
    {
1300 3
        $this->_queryType = self::CREATE_TABLE;
1301 3
        $this->_c->select_db($json->schema);
1302
1303 3
        $this->_sql = "CREATE TABLE IF NOT EXISTS `{$json->name}` (";
1304 3
        foreach ($json->fields as $field) {
1305 3
            $this->_sql .= "`{$field->name}` {$field->dataType}";
1306
1307 3
            if ($field->dataType == 'enum' && isset($field->values)) {
1308 1
                $this->_sql .= "('" . implode("','", $field->values) . "')";
1309
            }
1310
1311 3
            if (isset($field->ai) && $field->ai) {
1312 3
                $this->_sql .= " AUTO_INCREMENT";
1313
            }
1314
1315 3
            if (isset($field->nn) && $field->nn) {
1316 3
                $this->_sql .= " NOT NULL";
1317 2
            } elseif(isset($field->default)) {
1318 2
                if (strtolower($field->default) == 'null') {
1319 1
                    $this->_sql .= " DEFAULT NULL";
1320 1
                } elseif (strlen($field->default)) {
1321 1
                    $this->_sql .= " DEFAULT '{$field->default}'";
1322
                }
1323
            }
1324
1325 3
            if ($field != end($json->fields)) {
1326 2
                $this->_sql .= ",";
1327
            }
1328
        }
1329
1330 3
        if (isset($json->index) && count($json->index)) {
1331 1
            foreach ($json->index as $ind) {
1332 1
                $ref = null;
1333 1
                if(is_array($ind->ref)) {
1334
                    $ref = "";
1335
                    foreach($ind->ref as $r) {
1336
                        $ref .= "`{$r}` ASC,";
1337
                    }
1338
                    $ref = substr($ref, 0, -1);
1339 1
                } elseif(is_string($ind->ref)) {
1340 1
                    $ref = $ind->ref;
1341
                }
1342 1
                if(!is_null($ref)) {
1343 1
                    $this->_sql .= ", " . strtoupper($ind->type) . " `{$ind->id}` (`{$ref}`)";
1344
                }
1345
            }
1346
        }
1347
1348 3
        if (isset($json->constraints) && count($json->constraints)) {
1349
            foreach ($json->constraints as $con) {
1350
                $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));
1351
            }
1352
        }
1353
1354 3
        if (isset($json->unique) && count($json->unique)) {
1355 1
            $this->_sql .= ", UNIQUE(`" . implode("`,`", $json->unique) . "`)";
1356
        }
1357
1358 3
        if (isset($json->primary_key) && count($json->primary_key)) {
1359 3
            $this->_sql .= ", PRIMARY KEY(`" . implode("`,`", $json->primary_key) . "`))";
1360
        } else {
1361
            if (substr($this->_sql, - 1) == ',') {
1362
                $this->_sql = substr($this->_sql, 0, - 1);
1363
            }
1364
1365
            $this->_sql .= ")";
1366
        }
1367
1368 3
        $this->execute();
1369 3
    }
1370
1371
    /**
1372
     * Function to alter a existing table
1373
     *
1374
     * @param string $strTableName
1375
     *            Table to alter
1376
     * @param int $intAction
1377
     *            What action should be taken
1378
     * @param mixed $arrParams
1379
     *            For add column this is a stdClass object that has the same elements as the example json
1380
     *
1381
     * @return mixed
1382
     */
1383 3
    public function alterTable($strTableName, $intAction, $arrParams)
1384
    {
1385 3
        $this->_queryType = self::ALTER_TABLE;
1386 3
        $this->_sql = "ALTER TABLE $strTableName";
1387 3
        if ($intAction == self::ADD_COLUMN) {
1388 1
            $nn = (isset($arrParams->nn) && $arrParams->nn ? " NOT NULL" : "");
1389 1
            $default = null;
1390 1
            if ($arrParams->default === null) {
1391 1
                $default = " DEFAULT NULL";
1392
            } elseif (strlen($arrParams->default)) {
1393
                $default = " DEFAULT {$this->_escape($arrParams->default)}";
1394
            }
1395 1
            $this->_sql .= " ADD COLUMN `{$arrParams->name}` {$arrParams->dataType}" . $nn . $default;
1396 2
        } elseif ($intAction == self::DROP_COLUMN) {
1397 1
            $this->_sql .= " DROP COLUMN ";
1398 1
            foreach ($arrParams as $col) {
1399 1
                $this->_sql .= "`{$col->name}`";
1400
1401 1
                if ($col != end($arrParams)) {
1402
                    $this->_sql .= ",";
1403
                }
1404
            }
1405 1
        } elseif ($intAction == self::MODIFY_COLUMN) {
1406 1
            $this->_sql .= " MODIFY COLUMN";
1407 1
            $nn = (isset($arrParams->nn) && $arrParams->nn ? " NOT NULL" : "");
1408 1
            $default = null;
1409 1
            if ($arrParams->default === null) {
1410
                $default = " DEFAULT NULL";
1411 1
            } elseif (strlen($arrParams->default)) {
1412 1
                $default = " DEFAULT {$this->_escape($arrParams->default)}";
1413
            }
1414 1
            $this->_sql .= " `{$arrParams->name}` `{$arrParams->new_name}` {$arrParams->dataType}" . $nn . $default;
1415
        } elseif ($intAction == self::ADD_CONSTRAINT) {
1416
            if(is_array($arrParams->field) && is_array($arrParams->local)) {
1417
                $field = "`" . implode("`,`", $arrParams->field) . "`";
1418
                $local = "`" . implode("`,`", $arrParams->local) . "`";
1419
            } elseif(is_string($arrParams->field) && is_string($arrParams->local)) {
1420
                $field = "`{$arrParams->field}`";
1421
                $local = "`{$arrParams->local}`";
1422
            } else {
1423
                $this->_logger->critical("Error in reading constraint field");
1424
                throw new Exception("Error in reading constraint field");
1425
            }
1426
            $this->_sql .= " ADD CONSTRAINT `{$arrParams->id}` FOREIGN KEY ({$local}) REFERENCES `{$arrParams->schema}`.`{$arrParams->table}` ({$field}) ON DELETE {$arrParams->delete} ON UPDATE {$arrParams->update}";
1427
        }
1428
1429 3
        if (self::$autorun) {
1430
            return $this->execute();
1431
        }
1432
1433 3
        return $this->_sql;
1434
    }
1435
1436
    /**
1437
     * Check to see if a field in a table exists
1438
     *
1439
     * @param string $strTableName
1440
     *            Table to check
1441
     * @param string $strFieldName
1442
     *            Field name to find
1443
     *
1444
     * @return boolean Returns TRUE if field is found in that schema and table, otherwise FALSE
1445
     */
1446 2
    public function fieldExists($strTableName, $strFieldName)
1447
    {
1448 2
        $fdata = $this->fieldData($strTableName);
1449
1450 2
        if (is_array($fdata) && count($fdata)) {
1451 2
            foreach ($fdata as $field) {
1452 2
                if ($field->name == $strFieldName) {
1453 1
                    return true;
1454
                }
1455
            }
1456
        }
1457
1458 1
        return false;
1459
    }
1460
1461
    /**
1462
     * Function to get the column data (datatype, flags, defaults, etc)
1463
     *
1464
     * @param string $strTableName
1465
     *            Table to query
1466
     * @param mixed $field
1467
     *            [optional]
1468
     *            Optional field to retrieve data (if null, returns data from all fields)
1469
     *
1470
     * @return array
1471
     */
1472 3
    public function fieldData($strTableName, $field = null)
1473
    {
1474 3
        if (is_null($field)) {
1475 3
            $res = $this->_c->query("SELECT * FROM $strTableName LIMIT 1");
1476 1
        } elseif (is_array($field)) {
1477 1
            $res = $this->_c->query("SELECT `" . implode("`,`", $field) . "` FROM $strTableName LIMIT 1");
1478 1
        } elseif (is_string($field)) {
1479 1
            $res = $this->_c->query("SELECT $field FROM $strTableName LIMIT 1");
1480
        } else {
1481 1
            return null;
1482
        }
1483
1484 3
        $fields = null;
1485 3
        if (is_a($res, 'mysqli_result')) {
1486 3
            $fields = $res->fetch_fields();
1487 3
            foreach ($fields as $i => $f) {
1488 3
                $fields["{$f->name}"] = $f;
1489 3
                unset($fields[$i]);
1490
            }
1491
        }
1492
1493 3
        return $fields;
1494
    }
1495
1496
    /**
1497
     * Function to check that all field parameters are set correctly
1498
     *
1499
     * @param object $field_data
1500
     * @param object $check
1501
     * @param array $pks
1502
     * @param object $index
1503
     *
1504
     * @return array|string
1505
     */
1506
    public function fieldCheck($field_data, $check, $pks, $index)
1507
    {
1508
        $default = null;
1509
        $ret = null;
1510
1511
        $nn = ($check->nn ? " NOT NULL" : null);
1512
        if ($check->default === null) {
1513
            $default = " DEFAULT NULL";
1514
        } elseif (strlen($check->default)) {
1515
            $default = " DEFAULT '{$check->default}'";
1516
        }
1517
1518
        if ($field_data->type != $check->type && $check->type != MYSQLI_TYPE_ENUM) {
1519
            $this->_logger->notice("Wrong datatype", [
1520
                'name' => $field_data->name,
1521
                'datatype' => $check->dataType
1522
            ]);
1523
            $ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}";
1524
        } elseif (! is_null($check->length) && $field_data->length != $check->length) {
1525
            $this->_logger->notice("Incorrect size", [
1526
                'name' => $field_data->name,
1527
                'current' => $field_data->length,
1528
                'new_size' => $check->length
1529
            ]);
1530
            $ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}";
1531
        } elseif ($check->type == MYSQLI_TYPE_ENUM && ! ($field_data->flags & MYSQLI_ENUM_FLAG)) {
1532
            $this->_logger->notice("Setting ENUM type", [
1533
                'name' => $field_data->name,
1534
                'values' => implode(",", $check->values)
1535
            ]);
1536
            $ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}('" . implode("','", $check->values) . "')" . "{$nn}{$default}";
1537
        }
1538
1539
        if (! is_null($index) && count($index)) {
1540
            foreach ($index as $ind) {
1541
                if ($check->name == $ind->ref && ! ($field_data->flags & MYSQLI_MULTIPLE_KEY_FLAG)) {
1542
                    $this->_logger->debug("Missing index", [
1543
                        'name' => $field_data->name
1544
                    ]);
1545
                    $ret .= ($ret ? "," : "") . " ADD INDEX `{$ind->id}` (`{$ind->ref}` ASC)";
1546
                }
1547
            }
1548
        }
1549
1550
        if (in_array($check->name, $pks) && ! ($field_data->flags & MYSQLI_PRI_KEY_FLAG)) {
1551
            $this->_logger->debug("Setting PKs", [
1552
                'keys' => implode(',', $pks)
1553
            ]);
1554
            $ret .= ($ret ? "," : "") . " DROP PRIMARY KEY, ADD PRIMARY KEY(`" . implode("`,`", $pks) . "`)";
1555
        }
1556
1557
        return $ret;
1558
    }
1559
1560
    /**
1561
     * Function to check for the existence of a table within a schema
1562
     *
1563
     * @param string $strSchema
1564
     *            The schema to search in
1565
     * @param string $strTableName
1566
     *            Table to search for
1567
     *
1568
     * @return integer|boolean Returns number of tables that match if table is found in that schema, otherwise FALSE
1569
     */
1570 3
    public function tableExists($strSchema, $strTableName)
1571
    {
1572 3
        if (! $this->_c->select_db($strSchema)) {
1573
            fwrite(STDOUT, $this->_c->error . PHP_EOL);
1574
        }
1575 3
        $sql = "SHOW TABLES LIKE '{$strTableName}'";
1576
1577 3
        if ($res = $this->_c->query($sql)) {
1578 3
            if (gettype($res) == 'object' && is_a($res, 'mysqli_result') && $res->num_rows) {
1579 3
                return $res->num_rows;
1580
            }
1581
        } else {
1582
            if ($this->_c->errno) {
1583
                fwrite(STDOUT, $this->_c->error . PHP_EOL);
1584
            }
1585
        }
1586
1587 1
        return false;
1588
    }
1589
1590
    /**
1591
     * Function to detect if string is a JSON object or not
1592
     *
1593
     * @param string $strVal
1594
     *
1595
     * @return boolean
1596
     */
1597
    public function isJson($strVal)
1598
    {
1599
        json_decode($strVal);
1600
        return (json_last_error() == JSON_ERROR_NONE);
1601
    }
1602
1603
    /**
1604
     * Function to escape SQL characters to prevent SQL injection
1605
     *
1606
     * @param mixed $val
1607
     *            Value to escape
1608
     * @param boolean $blnEscape
1609
     *            Decide if we should escape or not
1610
     *
1611
     * @return string Escaped value
1612
     */
1613 86
    public function _escape($val, $blnEscape = true)
1614
    {
1615 86
        if (is_null($val) || (is_string($val) && strtolower($val) == 'null')) {
1616 1
            return 'NULL';
1617 86
        } elseif (is_numeric($val) || is_string($val)) {
1618 86
            if (stripos($val, "IF(") !== false) {
1619
                return $val;
1620 86
            } elseif ($blnEscape) {
1621 86
                return "'{$this->_c->real_escape_string($val)}'";
1622
            }
1623
            return "'{$val}'";
1624 6
        } elseif (is_a($val, 'DateTime')) {
1625 1
            return "'{$val->format(MYSQL_DATETIME)}'";
1626 5
        } elseif (is_bool($val)) {
1627 1
            return $val ? "'1'" : "'0'";
1628 4
        } elseif (is_array($val)) {
1629 1
            $ret = [];
1630 1
            foreach($val as $v) {
1631 1
                $ret[] = $this->_escape($v);
1632
            }
1633 1
            return "(" . implode(",", $ret) . ")";
1634 3
        } elseif (is_object($val) && method_exists($val, '_escape')) {
1635 2
            $ret = call_user_func([
1636 2
                $val,
1637 2
                '_escape'
1638
            ]);
1639 2
            if ($ret !== false) {
1640 1
                return $ret;
1641
            } else {
1642 1
                throw new Exception("Error in return from _escape method in " . get_class($val), E_ERROR);
1643
            }
1644
        }
1645
1646 1
        throw new Exception("Unknown datatype to escape in SQL string {$this->_sql} " . gettype($val), E_ERROR);
1647
    }
1648
1649
    /**
1650
     * Function to retrieve all results
1651
     *
1652
     * @param int $intResultType
1653
     *
1654
     * @return mixed
1655
     */
1656
    protected function fetchAll($intResultType = MYSQLI_ASSOC)
1657
    {
1658
        $res = [];
1659
        if (method_exists('mysqli_result', 'fetch_all')) { // Compatibility layer with PHP < 5.3
1660
            $res = $this->_result->fetch_all($intResultType);
1661
        } else {
1662
            while ($tmp = $this->_result->fetch_array($intResultType)) {
1663
                $res[] = $tmp;
1664
            }
1665
        }
1666
1667
        return $res;
1668
    }
1669
1670
    /**
1671
     * Function to populate the fields for the SQL
1672
     *
1673
     * @param array|string $fields
1674
     *            [optional]
1675
     *            Optional array of fields to string together to create a field list
1676
     *
1677
     * @return string
1678
     */
1679 23
    protected function fields($fields = null)
1680
    {
1681 23
        $ret = null;
1682
1683 23
        if (is_array($fields) && count($fields) && isset($fields[0]) && is_string($fields[0])) {
1684 2
            foreach ($fields as $field) {
1685 2
                if ((strpos($field, '`') === false) && (strpos($field, '.') === false) && (strpos($field, '*') === false) && (strpos($field, 'JSON_') === false) && (stripos($field, ' as ') === false)) {
1686 2
                    $ret .= "`$field`,";
1687
                } else {
1688
                    $ret .= "$field,";
1689
                }
1690
            }
1691 2
            $ret = substr($ret, - 1) == ',' ? substr($ret, 0, - 1) : $ret;
1692 21
        } elseif (is_string($fields)) {
1693 4
            $ret = $fields;
1694 17
        } elseif (is_null($fields)) {
1695 16
            $ret = "*";
1696
        } else {
1697 1
            throw new \InvalidArgumentException("Invalid field type");
1698
        }
1699
1700 22
        return $ret;
1701
    }
1702
1703
    /**
1704
     * Function to parse the flags
1705
     *
1706
     * @param array $flags
1707
     *            Two-dimensional array to added flags
1708
     *
1709
     *            <code>
1710
     *            [
1711
     *            &nbsp;&nbsp;'joins' => [
1712
     *            &nbsp;&nbsp;&nbsp;&nbsp;"JOIN table2 t2 ON t2.id=t1.id"
1713
     *            &nbsp;&nbsp;],
1714
     *            &nbsp;&nbsp;'group' => 'field',
1715
     *            &nbsp;&nbsp;'having' => 'field',
1716
     *            &nbsp;&nbsp;'order' => 'field',
1717
     *            &nbsp;&nbsp;'start' => 0,
1718
     *            &nbsp;&nbsp;'limit' => 0
1719
     *            ]
1720
     *            </code>
1721
     *
1722
     * @see Database::groups()
1723
     * @see Database::having()
1724
     * @see Database::order()
1725
     *
1726
     * @return string
1727
     */
1728 2
    protected function flags($arrFlags)
1729
    {
1730 2
        $ret = '';
1731
1732 2
        if (isset($arrFlags['group'])) {
1733
            $ret .= $this->groups($arrFlags['group']);
1734
        }
1735
1736 2
        if (isset($arrFlags['having']) && is_array($arrFlags['having'])) {
1737
            $having = " HAVING";
1738
            foreach ($arrFlags['having'] as $x => $h) {
1739
                $having .= $this->parseClause($h, $x);
0 ignored issues
show
Unused Code introduced by
The call to Godsgood33\Php_Db\Database::parseClause() has too many arguments starting with $x. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1739
                $having .= $this->/** @scrutinizer ignore-call */ parseClause($h, $x);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1740
            }
1741
            if (strlen($having) > strlen(" HAVING")) {
1742
                $ret .= $having;
1743
            }
1744
        }
1745
1746 2
        if (isset($arrFlags['order'])) {
1747
            $ret .= $this->order($arrFlags['order']);
1748
        }
1749
1750 2
        if (isset($arrFlags['limit']) && (is_string($arrFlags['limit']) || is_numeric($arrFlags['limit']))) {
1751 1
            $ret .= " LIMIT ";
1752 1
            if (isset($arrFlags['start']) && (is_string($arrFlags['start']) || is_numeric($arrFlags['start']))) {
1753
                $ret .= "{$arrFlags['start']},";
1754
            }
1755 1
            $ret .= "{$arrFlags['limit']}";
1756
        }
1757
1758 2
        return $ret;
1759
    }
1760
1761
    /**
1762
     * Function to parse SQL GROUP BY statements
1763
     *
1764
     * @param mixed $groups
1765
     *
1766
     * @return string
1767
     */
1768
    protected function groups($groups)
1769
    {
1770
        $ret = '';
1771
        if (is_array($groups) && count($groups)) {
1772
            $ret .= " GROUP BY";
1773
1774
            foreach ($groups as $grp) {
1775
                $ret .= " $grp";
1776
1777
                if ($grp != end($groups)) {
1778
                    $ret .= ",";
1779
                }
1780
            }
1781
        } elseif (is_string($groups)) {
1782
            $ret .= " GROUP BY {$groups}";
1783
        } else {
1784
            throw (new Exception("Error in datatype for groups " . gettype($groups), E_ERROR));
1785
        }
1786
1787
        return $ret;
1788
    }
1789
1790
    /**
1791
     * Function to parse SQL ORDER BY statements
1792
     *
1793
     * @param mixed $order
1794
     *
1795
     * @return string
1796
     */
1797
    protected function order($order)
1798
    {
1799
        $ret = '';
1800
        if (is_array($order)) {
1801
            $ret .= " ORDER BY";
1802
1803
            foreach ($order as $ord) {
1804
                $ret .= " {$ord['field']} {$ord['sort']}";
1805
1806
                if ($ord != end($order)) {
1807
                    $ret .= ",";
1808
                }
1809
            }
1810
        } elseif (is_string($order)) {
1811
            $ret .= " ORDER BY {$order}";
1812
        }
1813
1814
        return $ret;
1815
    }
1816
1817
    /**
1818
     * Function to see if a constraint exists
1819
     *
1820
     * @param string $strConstraintId
1821
     *
1822
     * @return boolean
1823
     */
1824
    public function isConstraint($strConstraintId)
1825
    {
1826
        $res = $this->_c->query("SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = '{$strConstraintId}'");
1827
1828
        if ($res->num_rows) {
1829
            return true;
1830
        }
1831
1832
        return false;
1833
    }
1834
1835
    /**
1836
     * Method to add a where clause
1837
     * 
1838
     * @param DBWhere|array:DBWhere $where
0 ignored issues
show
Documentation Bug introduced by
The doc comment $where at position 0 could not be parsed: Unknown type name '$where' at position 0 in $where.
Loading history...
1839
     * 
1840
     * @return boolean|array:DBWhere
1841
     */
1842 32
    public function parseClause($where) 
1843
    {
1844 32
        $ret = [];
1845 32
        $interfaces = [];
1846 32
        if(is_object($where)) {
1847 8
            $interfaces = \class_implements($where);
1848
        }
1849 32
        if(is_array($where)) {
1850 23
            foreach($where as $k => $w) {
1851 5
                if(!is_a($w, 'Godsgood33\Php_Db\DBWhere')) {
1852
                    return false;
1853
                }
1854 5
                $v = $this->_escape($w->value, $w->escape);
0 ignored issues
show
Bug Best Practice introduced by
The property escape does not exist on Godsgood33\Php_Db\DBWhere. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property value does not exist on Godsgood33\Php_Db\DBWhere. Since you implemented __get, consider adding a @property annotation.
Loading history...
1855 5
                $where[$k]->value = $v;
1856
1857 5
                $ret[] = $where[$k];
1858
            }
1859 9
        } elseif(is_a($where, 'Godsgood33\Php_Db\DBWhere')) {
1860 7
            $v = $this->_escape($where->value, $where->escape);
1861 5
            $where->value = $v;
0 ignored issues
show
Bug Best Practice introduced by
The property value does not exist on Godsgood33\Php_Db\DBWhere. Since you implemented __set, consider adding a @property annotation.
Loading history...
1862 5
            $ret[] = $where;
1863 2
        } elseif(in_array("Godsgood33\Php_Db\DBInterface", $interfaces) && is_callable(get_class($where) . "::where")) {
1864 1
            $params = \call_user_func([$where, "where"]);
1865 1
            if(!is_a($params, 'Godsgood33\Php_Db\DBWhere')) {
1866
                $this->_logger->warning("DBWhere object NOT returned from " . get_class($where) . " object");
1867
                return $ret;
1868
            }
1869 1
            $v = $this->_escape($params->value, $params->escape);
1870 1
            $params->value = $v;
1871 1
            $ret[] = $params;
1872
        } else {
1873 1
            $this->_logger->warning("Failed to get where from", [$where]);
1874
        }
1875
1876 30
        return $ret;
1877
    }
1878
1879
    /**
1880
     * Encryption algorithm
1881
     *
1882
     * @param string $data
1883
     * @param string $key
1884
     * 
1885
     * @throws Exception
1886
     * 
1887
     * @return string
1888
     */
1889
    public static function encrypt($data, $salt = null)
1890
    {
1891
        if(!defined('PHP_DB_ENCRYPT_SALT') || !defined('PHP_DB_ENCRYPT_ALGORITHM')) {
1892
            throw new Exception("Need to declare and populate PHP_DB_ENCRYPT_SALT and PHP_DB_ENCRYPT_ALGORITHM");
1893
        }
1894
1895
        // Remove the base64 encoding from our key
1896
        if (is_null($salt)) {
1897
            $encryption_key = base64_decode(PHP_DB_ENCRYPT_SALT);
1898
        } else {
1899
            $encryption_key = base64_decode($salt);
1900
        }
1901
        // Generate an initialization vector
1902
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(PHP_DB_ENCRYPT_ALGORITHM));
1903
        // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
1904
        $encrypted = openssl_encrypt($data, PHP_DB_ENCRYPT_ALGORITHM, $encryption_key, 0, $iv);
1905
        // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
1906
        return base64_encode($encrypted . '::' . $iv);
1907
    }
1908
1909
    /**
1910
     * Decryption algorithm
1911
     *
1912
     * @param string $data
1913
     * 
1914
     * @throws Exception
1915
     * 
1916
     * @return string
1917
     */
1918 86
    public static function decrypt($data)
1919
    {
1920 86
        if(!defined('PHP_DB_ENCRYPT_SALT') || !defined('PHP_DB_ENCRYPT_ALGORITHM')) {
1921
            throw new Exception("Need to declare and populate PHP_DB_ENCRYPT_SALT and PHP_DB_ENCRYPT_ALGORITHM");
1922
        }
1923
1924
        // Remove the base64 encoding from our key
1925 86
        $encryption_key = base64_decode(PHP_DB_ENCRYPT_SALT);
1926
1927
        // To decrypt, split the encrypted data from our IV - our unique separator used was "::"
1928 86
        list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
1929 86
        $plaintext = openssl_decrypt($encrypted_data, PHP_DB_ENCRYPT_ALGORITHM, $encryption_key, 0, $iv);
1930 86
        return $plaintext;
1931
    }
1932
}
1933