GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Failed
Push — 3.0.x ( 7946ec...895393 )
by Nicolas
04:19
created

Database::showIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
7
/**
8
 * The Database class acts as a wrapper for connecting to the Database
9
 * in Symphony.
10
 *
11
 * It provides many methods that maps directly to their PDO equivalent.
12
 * It also provides many factory methods to help developers creates instances
13
 * of `DatabaseStatement` and their specialized child classes.
14
 *
15
 * Symphony uses a prefix to namespace it's tables in a
16
 * database, allowing it play nice with other applications installed on the
17
 * database.
18
 *
19
 * An error that occur during a query throw a `DatabaseException`.
20
 * By default, Symphony logs all queries to be used for Profiling and Debug
21
 * devkit extensions when a Developer is logged in. When a developer is not
22
 * logged in, all queries and errors are made available with delegates.
23
 */
24
class Database
25
{
26
    /**
27
     * Constant to indicate whether the query is a write operation.
28
     *
29
     * @deprecated @since Symphony 3.0.0
30
     * @var int
31
     */
32
    const __WRITE_OPERATION__ = 0;
33
34
    /**
35
     * Constant to indicate whether the query is a write operation
36
     *
37
     * @deprecated @since Symphony 3.0.0
38
     * @var int
39
     */
40
    const __READ_OPERATION__ = 1;
41
42
    /**
43
     * An instance of the current PDO object
44
     *
45
     * @var PDO
46
     */
47
    private $conn = null;
48
49
    /**
50
     * The array of log messages
51
     *
52
     * @var array
53
     */
54
    private $log = [];
55
56
    /**
57
     * The number of queries this class has executed, defaults to 0.
58
     *
59
     * @var int
60
     */
61
    private $queryCount = 0;
62
63
    /**
64
     * The default configuration values
65
     *
66
     * @var array
67
     */
68
    private $config = [
69
        'host' => null,
70
        'port' => null,
71
        'user' => null,
72
        'password' => null,
73
        'db' => null,
74
        'driver' => null,
75
        'charset' => null,
76
        'collate' => null,
77
        'engine' => null,
78
        'tbl_prefix' => null,
79
        'query_caching' => null,
80
        'query_logging' => null,
81
        'options' => [],
82
    ];
83
84
    /**
85
     * The last executed query
86
     * @var string;
87
     */
88
    private $lastQuery;
89
90
    /**
91
     * The md5 hash of the last executed query
92
     * @var string;
93
     */
94
    private $lastQueryHash;
95
96
    /**
97
     * The values used with the last executed query
98
     * @var array
99
     */
100
    private $lastQueryValues;
101
102
    /**
103
     * The unsafe mode of the last executed query
104
     * @var bool
105
     */
106
    private $lastQuerySafe;
107
108
    /**
109
     * The version of the SQL server
110
     * @var string
111
     */
112
    private $version;
113
114
    /**
115
     * Creates a new Database object given an associative array of configuration
116
     * parameters in `$config`, which should include
117
     * `driver`, `host`, `port`, `user`, `password`, `db` and an optional
118
     * array of PDO options in `options`.
119
     *
120
     * @param array $config
121
     */
122
    public function __construct(array $config = [])
123
    {
124
        $this->config = array_merge($this->config, $config);
125
    }
126
127
    /**
128
     * Magic function that will flush the logs and close the underlying database
129
     * connection when the Database class is destroyed.
130
     *
131
     * @link http://php.net/manual/en/language.oop5.decon.php
132
     */
133
    public function __destruct()
134
    {
135
        unset($this->conn);
136
        $this->flush();
137
    }
138
139
    /**
140
     * Getter for all the log entries.
141
     *
142
     * @return array
143
     */
144
    public function getLogs()
145
    {
146
        return $this->log;
147
    }
148
149
    /**
150
     * Resets `$this->lastQuery`, `$this->lastQueryHash`, `$this->lastQueryValues` and
151
     * `$this->lastQuerySafe` to their empty values.
152
     * Called on each query and when the class is destroyed.
153
     *
154
     * @return Database
155
     *  The current instance.
156
     */
157
    public function flush()
158
    {
159
        $this->lastQuery = null;
160
        $this->lastQueryHash = null;
161
        $this->lastQueryValues = null;
162
        $this->lastQuerySafe = null;
163
        return $this;
164
    }
165
166
    /**
167
     * Based on the configuration values set in the constructor,
168
     * this method will properly format the values to get a valid DSN
169
     * connection string.
170
     *
171
     * @return string
172
     *  The generated DNS connection string
173
     */
174
    public function getDSN()
175
    {
176
        $config = &$this->config;
177
        if ($config['host'] === 'unix_socket') {
178
           return sprintf(
179
                '%s:unix_socket=%s;dbname=%s;charset=%s',
180
                $config['driver'],
181
                General::intval($config['port']) === -1 ? $config['port'] : '',
182
                $config['db'],
183
                $config['charset']
184
            );
185
        }
186
        return sprintf(
187
            '%s:dbname=%s;host=%s;port=%d;charset=%s',
188
            $config['driver'],
189
            $config['db'],
190
            $config['host'],
191
            General::intval($config['port']),
192
            $config['charset']
193
        );
194
    }
195
196
    /**
197
     * Getter for the version of the SQL server.
198
     *
199
     * @return string
200
     */
201
    public function getVersion()
202
    {
203
        return $this->version;
204
    }
205
206
    /**
207
     * Creates a PDO connection to the desired database given the current config.
208
     * This will also set the error mode to be exceptions,
209
     * which are handled by this class.
210
     *
211
     * @link http://www.php.net/manual/en/pdo.drivers.php
212
     * @param array $options
213
     * @return Database
214
     *  The current instance if connection was successful.
215
     * @throws DatabaseException
216
     */
217
    public function connect()
218
    {
219
        try {
220
            $config = $this->config;
221
            $this->conn = new PDO(
222
                $this->getDSN(),
223
                $config['user'],
224
                $config['password'],
225
                is_array($config['options']) ? $config['options'] : []
226
            );
227
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
228
            $this->conn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
229
            $this->version = $this->select(['VERSION()'])->execute()->variable(0);
230
        } catch (PDOException $ex) {
231
            $this->throwDatabaseError($ex);
232
        }
233
234
        return $this;
235
    }
236
237
    /**
238
     * Checks if the connection was already made successfully.
239
     *
240
     * @return boolean
241
     *  true if the connection was made, false otherwise
242
     */
243
    public function isConnected()
244
    {
245
        return $this->conn && $this->conn instanceof PDO;
246
    }
247
248
    /**
249
     * Issues a call to `connect()` if the current instance is not already
250
     * connected. Does nothing if already connected.
251
     *
252
     * @see isConnected()
253
     * @return Database
254
     *  The current instance.
255
     * @throws DatabaseException
256
     */
257
    private function autoConnect()
258
    {
259
        if (!$this->isConnected()) {
260
            $this->connect();
261
        }
262
        return $this;
263
    }
264
265
    /**
266
     * Returns the number of queries that has been executed since
267
     * the creation of the object.
268
     *
269
     * @return int
270
     *  The total number of query executed.
271
     */
272
    public function queryCount()
273
    {
274
        return $this->queryCount;
275
    }
276
277
    /**
278
     * Sets query caching to true. This will prepend all SELECT
279
     * queries with SQL_CACHE. Symphony by default enables caching. It
280
     * can be turned off by setting the query_cache parameter to 'off' in the
281
     * Symphony config file.
282
     *
283
     * @link https://dev.mysql.com/doc/refman/5.1/en/query-cache.html
284
     * @deprecated The query cache is deprecated as of MySQL 5.7.20,
285
     * and is removed in MySQL 8.0.
286
     * @link https://dev.mysql.com/doc/refman/5.7/en/query-cache-in-select.html
287
     * @return Database
288
     *  The current instance
289
     */
290
    public function enableCaching()
291
    {
292
        $this->config['query_caching'] = true;
293
        return $this;
294
    }
295
296
    /**
297
     * Sets query caching to false. This will prepend all SELECT
298
     * queries will SQL_NO_CACHE.
299
     *
300
     * @deprecated The query cache is deprecated as of MySQL 5.7.20,
301
     * and is removed in MySQL 8.0.
302
     * @link https://dev.mysql.com/doc/refman/5.7/en/query-cache-in-select.html
303
     * @return Database
304
     *  The current instance
305
     */
306
    public function disableCaching()
307
    {
308
        $this->config['query_caching'] = false;
309
        return $this;
310
    }
311
312
    /**
313
     * Returns boolean if query caching is enabled or not.
314
     *
315
     * @deprecated The query cache is deprecated as of MySQL 5.7.20,
316
     * and is removed in MySQL 8.0.
317
     * @link https://dev.mysql.com/doc/refman/5.7/en/query-cache-in-select.html
318
     * @return boolean
319
     */
320
    public function isCachingEnabled()
321
    {
322
        return in_array($this->config['query_caching'], ['on', true], true);
323
    }
324
325
    /**
326
     * Symphony uses a prefix for all it's database tables so it can live peacefully
327
     * on the same database as other applications. By default this is sym_, but it
328
     * can be changed when Symphony is installed.
329
     *
330
     * @param string $prefix
331
     *  The table prefix for Symphony, by default this is sym_
332
     * @return Database
333
     *  The current instance
334
     */
335
    public function setPrefix($prefix)
336
    {
337
        $this->config['tbl_prefix'] = $prefix;
338
        return $this;
339
    }
340
341
    /**
342
     * Returns the prefix used by Symphony for this Database instance.
343
     *
344
     * @see __construct()
345
     * @since Symphony 2.4
346
     * @return string
347
     */
348
    public function getPrefix()
349
    {
350
        return $this->config['tbl_prefix'];
351
    }
352
353
    /**
354
     * Sets query logging to true.
355
     *
356
     * @return Database
357
     *  The current instance
358
     */
359
    public function enableLogging()
360
    {
361
        $this->config['query_logging'] = true;
362
        return $this;
363
    }
364
365
    /**
366
     * Sets query logging to false.
367
     *
368
     * @return Database
369
     *  The current instance
370
     */
371
    public function disableLogging()
372
    {
373
        $this->config['query_logging'] = false;
374
        return $this;
375
    }
376
377
    /**
378
     * Returns true if logging of queries is enabled.
379
     *
380
     * @return boolean
381
     */
382
    public function isLoggingEnabled()
383
    {
384
        return in_array($this->config['query_logging'], ['on', true], true);
385
    }
386
387
    /**
388
     * Sets the Database connection to use this timezone instead of the default
389
     * Database server timezone.
390
     *
391
     * @throws DatabaseException
392
     * @link https://dev.mysql.com/doc/refman/5.6/en/time-zone-support.html
393
     * @link https://github.com/symphonycms/symphony-2/issues/1726
394
     * @since Symphony 2.3.3
395
     * @param string $timezone
396
     *  PHP's Human readable timezone, such as Australia/Brisbane.
397
     * @return boolean
398
     */
399
    public function setTimeZone($timezone = null)
400
    {
401
        // This should throw, default value should be removed
402
        if (!$timezone) {
403
            return true;
404
        }
405
406
        // What is the time now in the install timezone
407
        $symphony_date = new DateTime('now', new DateTimeZone($timezone));
408
409
        // MySQL wants the offset to be in the format +/-H:I, getOffset returns offset in seconds
410
        $utc = new DateTime('now ' . $symphony_date->getOffset() . ' seconds', new DateTimeZone("UTC"));
411
412
        // Get the difference between the symphony install timezone and UTC
413
        $offset = $symphony_date->diff($utc)->format('%R%H:%I');
414
415
        return $this->set('time_zone')
416
                    ->value((string)$offset)
417
                    ->execute()
418
                    ->success();
419
    }
420
421
    /**
422
     * This function will clean a string using the `PDO::quote` function
423
     * taking into account the current database character encoding.
424
     *
425
     * If not connected to the database, it will default to PHP's `addslashes`.
426
     * This is useful for unit tests.
427
     *
428
     * This function does not encode _ or %.
429
     *
430
     * This function should not be used. Instead, pass your data in the proper
431
     * function that will delegate to SQL parameters.
432
     *
433
     * @deprecated @since Symphony 3.0.0
434
     * @see DatabaseStatement::appendValues()
435
     * @param string $value
436
     *  The string to be encoded into an escaped SQL string
437
     * @return string
438
     *  The escaped SQL string
439
     */
440
    public function quote($value)
441
    {
442
        if (!$this->isConnected()) {
443
            return "'" . addslashes($value) . "'";
444
        }
445
        return $this->conn->quote($value);
446
    }
447
448
    /**
449
     * This function will apply the `quote` function to an associative
450
     * array of data, encoding only the value, not the key. This function
451
     * can handle recursive arrays. This function manipulates the given
452
     * parameter by reference.
453
     *
454
     * This function should not be used. Instead, pass your data in the proper
455
     * function that will delegate to SQL parameters.
456
     *
457
     * @deprecated @since Symphony 3.0.0
458
     * @see quote
459
     * @param array $array
460
     *  The associative array of data to encode, this parameter is manipulated
461
     *  by reference.
462
     */
463
    public function quoteFields(array &$array)
464
    {
465
        foreach ($array as $key => $val) {
466
            // Handle arrays with more than 1 level
467
            if (is_array($val)) {
468
                $this->quoteFields($val);
0 ignored issues
show
Deprecated Code introduced by
The function Database::quoteFields() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

468
                /** @scrutinizer ignore-deprecated */ $this->quoteFields($val);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
469
            } elseif (!$val || strlen(trim($val)) === 0) {
470
                $array[$key] = 'NULL';
471
            } else {
472
                $array[$key] = $this->quote($val);
0 ignored issues
show
Deprecated Code introduced by
The function Database::quote() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

472
                $array[$key] = /** @scrutinizer ignore-deprecated */ $this->quote($val);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
473
            }
474
        }
475
    }
476
477
    /**
478
     * This function takes `$table` and `$field` names and returns true
479
     * if the `$table` contains a column named `$field`.
480
     *
481
     * @since Symphony 2.3
482
     * @see describe
483
     * @link  https://dev.mysql.com/doc/refman/en/describe.html
484
     * @param string $table
485
     *  The table name
486
     * @param string $field
487
     *  The field name
488
     * @throws DatabaseException
489
     * @return boolean
490
     *  true if `$table` contains `$field`, false otherwise
491
     */
492
    public function tableContainsField($table, $field)
493
    {
494
        return $this->describe($table)
495
                    ->field($field)
496
                    ->execute()
497
                    ->next() !== null;
498
    }
499
500
    /**
501
     * This function takes `$table` and returns boolean
502
     * if it exists or not.
503
     *
504
     * @since Symphony 2.3.4
505
     * @see show
506
     * @link  https://dev.mysql.com/doc/refman/en/show-tables.html
507
     * @param string $table
508
     *  The table name
509
     * @throws DatabaseException
510
     * @return boolean
511
     *  true if `$table` exists, false otherwise
512
     */
513
    public function tableExists($table)
514
    {
515
        return $this->show()
516
                    ->like($table)
517
                    ->execute()
518
                    ->next() !== null;
519
    }
520
521
    /**
522
     * Factory method that creates a new, empty statement.
523
     *
524
     * @param string $action
525
     *  The SQL clause name. Default to empty string.
526
     * @return DatabaseStatement
527
     */
528
    public function statement($action = '')
529
    {
530
        return new DatabaseStatement($this, $action);
531
    }
532
533
    /**
534
     * Factory method that creates a new `SELECT ...` statement.
535
     *
536
     * @param array $projection
537
     *  The columns to select.
538
     *  If no projection gets added, it defaults to `DatabaseQuery::getDefaultProjection()`.
539
     * @return DatabaseQuery
540
     */
541
    public function select(array $projection = [])
542
    {
543
        return new DatabaseQuery($this, $projection);
544
    }
545
546
    /**
547
     * Factory method that creates a new `SHOW TABLES` statement.
548
     *
549
     * @return DatabaseShow
550
     */
551
    public function show()
552
    {
553
        return new DatabaseShow($this);
554
    }
555
556
    /**
557
     * Factory method that creates a new `SHOW COLUMNS` statement.
558
     *
559
     * @return DatabaseShow
560
     */
561
    public function showColumns()
562
    {
563
        return new DatabaseShow($this, 'COLUMNS');
564
    }
565
566
    /**
567
     * Factory method that creates a new `SHOW INDEX` statement.
568
     *
569
     * @return DatabaseShow
570
     */
571
    public function showIndex()
572
    {
573
        return new DatabaseShow($this, 'INDEX');
574
    }
575
576
    /**
577
     * Factory method that creates a new `INSERT` statement.
578
     *
579
     * @param string $table
580
     *  The name of the table to act on, including the tbl prefix which will be changed
581
     *  to the Database table prefix.
582
     *  @deprecated Symphony 3.0.0
583
     *  If $table is an array, it is treated as the fields values
584
     *  Use DatabaseInsert::values()
585
     * @param string $table
586
     *  The name of the table to act on, including the tbl prefix which will be changed
587
     *  to the Database table prefix.
588
     *  @deprecated Symphony 3.0.0
589
     *  This parameter is deprecated and will be removed.
590
     *  Use the first parameter and DatabaseInsert::values()
591
     * @param bool $updateOnDuplicate
592
     *  If set to true, data will updated if any key constraints are found that cause
593
     *  conflicts. Defaults to false
594
     *  @deprecated Symphony 3.0.0
595
     *  This parameter is deprecated and will be removed.
596
     *  Use DatabaseInsert::updateOnDuplicateKey()
597
     * @return DatabaseInsert
598
     */
599
    public function insert($table, ...$oldParams)
600
    {
601
        // Compat layer
602
        if (is_array($table)) {
0 ignored issues
show
introduced by
The condition is_array($table) is always false.
Loading history...
603
            if (isset($oldParams[0]) && isset($oldParams[1])) {
604
                return $this->_insert($table, $oldParams[0], $oldParams[1]);
605
            }
606
            return $this->_insert($table, $oldParams[0]);
607
        }
608
        return new DatabaseInsert($this, $table);
609
    }
610
611
    /**
612
     * Returns the last insert ID from the previous query. This is
613
     * the value from an auto_increment field.
614
     * If the lastInsertId is empty or not a valid integer, -1 is returned.
615
     *
616
     * @return int
617
     *  The last interested row's ID
618
     */
619
    public function getInsertID()
620
    {
621
        return General::intval($this->conn->lastInsertId());
622
    }
623
624
    /**
625
     * Factory method that creates a new `UPDATE` statement.
626
     *
627
     * @param string $table
628
     *  The name of the table to act on, including the tbl prefix which will be changed
629
     *  to the Database table prefix.
630
     * @param string $where
631
     *  An unsanitized WHERE condition.
632
     *  @deprecated Symphony 3.0.0
633
     *  This parameter is deprecated and will be removed.
634
     *  Use DatabaseUpdate::where()
635
     * @return DatabaseUpdate
636
     */
637
    public function update($table, ...$oldParams)
638
    {
639
        // Compat layer
640
        if (is_array($table)) {
0 ignored issues
show
introduced by
The condition is_array($table) is always false.
Loading history...
641
            if (isset($oldParams[0]) && isset($oldParams[1])) {
642
                return $this->_update($table, $oldParams[0], $oldParams[1]);
643
            }
644
            return $this->_update($table, $oldParams[0]);
645
        }
646
        return new DatabaseUpdate($this, $table);
647
    }
648
649
    /**
650
     * Factory method that creates a new `DELETE` statement.
651
     *
652
     * @param string $table
653
     *  The name of the table to act on, including the tbl prefix which will be changed
654
     *  to the Database table prefix.
655
     * @param string $where
656
     *  An unsanitized WHERE condition.
657
     *  @deprecated Symphony 3.0.0
658
     *  This parameter is deprecated and will be removed.
659
     *  Use DatabaseDelete::where()
660
     * @return DatabaseDelete
661
     */
662
    public function delete($table, $where = null)
663
    {
664
        $stm = new DatabaseDelete($this, $table);
665
        // Compat layer
666
        if ($where) {
667
            $where = $stm->replaceTablePrefix($where);
668
            $stm->unsafe()->unsafeAppendSQLPart('where', "WHERE $where");
669
            return $stm->execute()->success();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stm->execute()->success() returns the type boolean which is incompatible with the documented return type DatabaseDelete.
Loading history...
670
        }
671
        return $stm;
672
    }
673
674
    /**
675
     * Factory method that creates a new `DROP` statement.
676
     *
677
     * @param string $table
678
     * @return DatabaseDrop
679
     */
680
    public function drop($table)
681
    {
682
        return new DatabaseDrop($this, $table);
683
    }
684
685
    /**
686
     * Factory method that creates a new `DESCRIBE` statement.
687
     *
688
     * @param string $table
689
     * @return DatabaseDescribe
690
     */
691
    public function describe($table)
692
    {
693
        return new DatabaseDescribe($this, $table);
694
    }
695
696
    /**
697
     * Factory method that creates a new `CREATE TABLE` statement.
698
     * Also sets the charset, collate and engine values using the
699
     * instance configuration.
700
     *
701
     * @param string $table
702
     * @return DatabaseCreate
703
     */
704
    public function create($table)
705
    {
706
        return (new DatabaseCreate($this, $table))
707
            ->charset($this->config['charset'])
708
            ->collate($this->config['collate'])
709
            ->engine($this->config['engine']);
710
    }
711
712
    /**
713
     * Factory method that creates a new `ALTER TABLE` statement.
714
     * Also sets the collate value using the instance configuration.
715
     *
716
     * @param string $table
717
     * @return DatabaseAlter
718
     */
719
    public function alter($table)
720
    {
721
        return (new DatabaseAlter($this, $table))
722
            ->collate($this->config['collate']);
723
    }
724
725
    /**
726
     * Factory method that creates a new `OPTIMIZE TABLE` statement.
727
     *
728
     * @param string $table
729
     * @return DatabaseOptimize
730
     */
731
    public function optimize($table)
732
    {
733
        return new DatabaseOptimize($this, $table);
734
    }
735
736
    /**
737
     * Factory method that creates a new `TRUNCATE TABLE` statement.
738
     *
739
     * @param string $table
740
     * @return DatabaseTruncate
741
     */
742
    public function truncate($table)
743
    {
744
        return new DatabaseTruncate($this, $table);
745
    }
746
747
    /**
748
     * Factory method that creates a new `SET` statement.
749
     *
750
     * @param string $variable
751
     * @return DatabaseSet
752
     */
753
    public function set($variable)
754
    {
755
        return new DatabaseSet($this, $variable);
756
    }
757
758
    /**
759
     * Begins a new transaction.
760
     * This method calls `autoConnect()` before forwarding the call to PDO.
761
     *
762
     * @return boolean
763
     */
764
    public function beginTransaction()
765
    {
766
        $this->autoConnect();
767
        return $this->conn->beginTransaction();
768
    }
769
770
    /**
771
     * Commits the lastly created transaction.
772
     * This method calls `autoConnect()` before forwarding the call to PDO.
773
     *
774
     * @return boolean
775
     */
776
    public function commit()
777
    {
778
        $this->autoConnect();
779
        return $this->conn->commit();
780
    }
781
782
    /**
783
     * Rollbacks the lastly created transaction.
784
     * This method calls `autoConnect()` before forwarding the call to PDO.
785
     *
786
     * @return boolean
787
     */
788
    public function rollBack()
789
    {
790
        $this->autoConnect();
791
        return $this->conn->rollBack();
792
    }
793
794
    /**
795
     * Check if we are currently in a transaction.
796
     * This method calls `autoConnect()` before forwarding the call to PDO.
797
     *
798
     * @return boolean
799
     */
800
    public function inTransaction()
801
    {
802
        $this->autoConnect();
803
        return $this->conn->inTransaction();
804
    }
805
806
    /**
807
     * Factory method that creates a new DatabaseTransaction object.
808
     * $tx will be called with a single parameter: the instance of the current Database object.
809
     *
810
     * @param callable $tx
811
     *  The code to execute in the transaction
812
     * @return DatabaseTransaction
813
     */
814
    public function transaction($tx)
815
    {
816
        return new DatabaseTransaction($this, $tx);
817
    }
818
819
    /**
820
     * @internal
821
     * Finds the best possible PDO::PARAM_* value to bind with, based on the PHP type.
822
     *
823
     * @param mixed $value
824
     *  The value on which to deduce its PDO type
825
     * @return int
826
     *  Either PDO::PARAM_NULL, PDO::PARAM_INT, PDO::PARAM_BOOL or PDO::PARAM_STR
827
     */
828
    public function deducePDOParamType($value)
829
    {
830
        if ($value === null) {
831
            return PDO::PARAM_NULL;
832
        } elseif (is_numeric($value)) {
833
            return PDO::PARAM_INT;
834
        } elseif (is_bool($value)) {
835
            return PDO::PARAM_BOOL;
836
        }
837
        return PDO::PARAM_STR;
838
    }
839
840
    /**
841
     * Given a DatabaseStatement, it will execute it and return
842
     * its result, by calling `DatabaseStatement::result()`.
843
     * Any error will throw a DatabaseException.
844
     *
845
     * Developers are encouraged to call `DatabaseStatement::execute()` instead,
846
     * because it will make sure to set required state properly.
847
     *
848
     * @see validateSQLQuery()
849
     * @see DatabaseStatement::execute()
850
     * @see DatabaseStatement::result()
851
     * @param string $query
852
     * @return DatabaseStatementResult
853
     * @throws DatabaseException
854
     */
855
    public function execute(DatabaseStatement $stm)
856
    {
857
        $this->autoConnect();
858
859
        if ($this->isLoggingEnabled()) {
860
            $start = precision_timer();
861
        }
862
863
        $query = $stm->generateSQL();
864
        $values = $stm->getValues();
865
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
866
867
        // Cleanup from last time, set some logging parameters
868
        $this->flush();
869
        $this->lastQuery = $stm->generateFormattedSQL();
870
        $this->lastQueryHash = md5($query . $start);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $start does not seem to be defined for all execution paths leading up to this point.
Loading history...
871
        $this->lastQueryValues = $values;
872
        $this->lastQuerySafe = $stm->isSafe();
873
874
        try {
875
            // Validate the query
876
            $this->validateSQLQuery($query, $stm->isSafe());
877
            // Prepare the query
878
            $pstm = $this->conn->prepare($query);
879
            // Bind all values
880
            foreach ($values as $param => $value) {
881
                if (General::intval($param) !== -1) {
882
                    $param = $param + 1;
883
                } else {
884
                    $param = ":$param";
885
                }
886
                $pstm->bindValue($param, $value, $this->deducePDOParamType($value));
887
            }
888
            // Execute it
889
            $result = $pstm->execute();
890
            $this->queryCount++;
891
        } catch (PDOException $ex) {
892
            $this->throwDatabaseError($ex);
893
            return;
894
        }
895
896
        // Check for errors
897
        if ($this->conn->errorCode() !== PDO::ERR_NONE) {
898
            $this->throwDatabaseError();
899
            return;
900
        }
901
902
        // Log the query
903
        if ($this->isLoggingEnabled()) {
904
            $this->logLastQuery(precision_timer('stop', $start));
905
        }
906
907
        return $stm->results($result, $pstm);
0 ignored issues
show
Bug introduced by
It seems like $pstm can also be of type boolean; however, parameter $stm of DatabaseStatement::results() does only seem to accept PDOStatement, maybe add an additional type check? ( Ignorable by Annotation )

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

907
        return $stm->results($result, /** @scrutinizer ignore-type */ $pstm);
Loading history...
908
    }
909
910
    /**
911
     * @internal
912
     * This method checks for common pattern of SQL injection, like `--`, `'` and `;`.
913
     *
914
     * @see execute()
915
     * @param string $query
916
     *  The query to test.
917
     * @param boolean $strict
918
     *  Perform extra validation. True by default.
919
     * @return void
920
     * @throws DatabaseStatementException
921
     */
922
    final public function validateSQLQuery($query, $strict = true)
923
    {
924
        if (strpos($query, '\'--;') !== false) {
925
            throw (new DatabaseStatementException('Query contains SQL injection.'))->sql($query);
926
        } elseif ($strict && strpos($query, '--') !== false) {
927
            throw (new DatabaseStatementException('Query contains illegal characters: `--`.'))->sql($query);
928
        } elseif ($strict && strpos($query, '\'') !== false) {
929
            throw (new DatabaseStatementException('Query contains illegal character: `\'`.'))->sql($query);
930
        } elseif ($strict && strpos($query, ';') !== false) {
931
            throw (new DatabaseStatementException('Query contains illegal character: `;`.'))->sql($query);
932
        }
933
    }
934
935
    /**
936
     * Convenience function to allow you to execute multiple SQL queries at once
937
     * by providing a string with the queries delimited with a `;`
938
     *
939
     * @throws DatabaseException
940
     * @throws Exception
941
     * @param string $sql
942
     *  A string containing SQL queries delimited by `;`
943
     * @param boolean $force_engine
944
     *  @deprecated @since Symphony 3.0.0
945
     *  The default engine is now InnoDb.
946
     *  The import script should use InnoDb as well.
947
     *  The import script is also run in a transaction, resulting in a all or nothing import.
948
     *  Before 3.0.0:
949
     *  If set to true, this will set MySQL's default storage engine to MyISAM.
950
     *  Defaults to false, which will use MySQL's default storage engine when
951
     *  tables don't explicitly define which engine they should be created with
952
     * @return boolean
953
     *  If one of the queries fails, false will be returned and no further queries
954
     *  will be executed, otherwise true will be returned.
955
     */
956
    public function import($sql, $force_engine = false)
957
    {
958
        General::ensureType([
959
            'sql' => ['var' => $sql, 'type' => 'string'],
960
        ]);
961
        $queries = preg_split('/;[\\r\\n]+/', $sql, -1, PREG_SPLIT_NO_EMPTY);
962
963
        if (!is_array($queries) || empty($queries) || count($queries) <= 0) {
964
            throw new Exception('The SQL string contains no queries.');
965
        }
966
967
        return $this->transaction(function (Database $db) use ($queries) {
968
            foreach ($queries as $sql) {
969
                if (trim($sql) !== '') {
970
                    $stm = $db->statement();
971
                    $sql = $stm->replaceTablePrefix($sql);
972
                    $stm->unsafe()->unsafeAppendSQLPart('statement', $sql);
973
                    if (!$stm->execute()->success()) {
974
                        throw new DatabaseException('Failed to execute import statement');
975
                    }
976
                }
977
            }
978
        })->execute()->success();
979
    }
980
981
    /**
982
     * Given an Exception, or called when an error occurs, this function will
983
     * fire the `QueryExecutionError` delegate and then raise a `DatabaseException`
984
     *
985
     * @uses QueryExecutionError
986
     * @throws DatabaseException
987
     * @param Exception $ex
988
     *  The exception thrown while doing something with the Database
989
     */
990
    private function throwDatabaseError(Exception $ex = null)
991
    {
992
        if (isset($ex) && $ex) {
993
            $msg = $ex->getMessage();
994
            $errornum = (int)$ex->getCode();
995
        } else {
996
            $error = $this->conn->errorInfo();
997
            $msg = $error[2];
998
            $errornum = $error[0];
999
        }
1000
1001
        /**
1002
         * After a query execution has failed this delegate will provide the query,
1003
         * query hash, error message and the error number.
1004
         *
1005
         * Note that this function only starts logging once the `ExtensionManager`
1006
         * is available, which means it will not fire for the first couple of
1007
         * queries that set the character set.
1008
         *
1009
         * @since Symphony 2.3
1010
         * @delegate QueryExecutionError
1011
         * @param string $context
1012
         * '/frontend/' or '/backend/'
1013
         * @param string $query
1014
         *  The query that has just been executed
1015
         * @param string $query_hash
1016
         *  The hash used by Symphony to uniquely identify this query
1017
         * @param string $msg
1018
         *  The error message provided by MySQL which includes information on why the execution failed
1019
         * @param int $num
1020
         *  The error number that corresponds with the MySQL error message
1021
         * @param Exception $exception
1022
         *  @since Symphony 3.0.0
1023
         *  The raised exception, if any
1024
         */
1025
        if (Symphony::ExtensionManager() instanceof ExtensionManager) {
0 ignored issues
show
introduced by
Symphony::ExtensionManager() is always a sub-type of ExtensionManager.
Loading history...
1026
            Symphony::ExtensionManager()->notifyMembers(
1027
                'QueryExecutionError',
1028
                class_exists('Administration') ? '/backend/' : '/frontend/',
1029
                [
1030
                    'query' => $this->lastQuery,
1031
                    'query_hash' => $this->lastQueryHash,
1032
                    'msg' => $msg,
1033
                    'num' => $errornum,
1034
                    'exception' => $ex,
1035
                ]
1036
            );
1037
        }
1038
1039
        throw new DatabaseException(
1040
            __(
1041
                'Database Error (%1$s): %2$s in query:%4$s%3$s',
1042
                [$errornum, $msg, $this->lastQuery, PHP_EOL]
1043
            ),
1044
            [
1045
                'msg' => $msg,
1046
                'num' => $errornum,
1047
                'query' => $this->lastQuery
1048
            ],
1049
            $ex
1050
        );
1051
    }
1052
1053
    /**
1054
     * Function is called every time a query is executed to log it for
1055
     * basic profiling/debugging purposes
1056
     *
1057
     * @uses PostQueryExecution
1058
     * @param int $stop
1059
     */
1060
    private function logLastQuery($stop)
1061
    {
1062
        /**
1063
         * After a query has successfully executed, that is it was considered
1064
         * valid SQL, this delegate will provide the query, the query_hash and
1065
         * the execution time of the query.
1066
         *
1067
         * Note that this function only starts logging once the ExtensionManager
1068
         * is available, which means it will not fire for the first couple of
1069
         * queries that set the character set.
1070
         *
1071
         * @since Symphony 2.3
1072
         * @delegate PostQueryExecution
1073
         * @param string $context
1074
         * '/frontend/' or '/backend/'
1075
         * @param string $query
1076
         *  The query that has just been executed
1077
         * @param string $query_hash
1078
         *  The hash used by Symphony to uniquely identify this query
1079
         * @param array $query_values
1080
         *  @since Symphony 3.0.0
1081
         *  The values passed by Symphony to the database
1082
         * @param bool $query_safe
1083
         *  @since Symphony 3.0.0
1084
         *  If the query was using the unsafe mode
1085
         * @param float $execution_time
1086
         *  The time that it took to run `$query`
1087
         */
1088
        if (Symphony::ExtensionManager() instanceof ExtensionManager) {
0 ignored issues
show
introduced by
Symphony::ExtensionManager() is always a sub-type of ExtensionManager.
Loading history...
1089
            // TODO: Log unlogged queries
1090
            Symphony::ExtensionManager()->notifyMembers(
1091
                'PostQueryExecution',
1092
                class_exists('Administration', false) ? '/backend/' : '/frontend/',
1093
                [
1094
                    'query' => $this->lastQuery, // TODO: Format
1095
                    'query_hash' => $this->lastQueryHash,
1096
                    'query_values' => $this->lastQueryValues,
1097
                    'query_safe' => $this->lastQuerySafe,
1098
                    'execution_time' => $stop
1099
                ]
1100
            );
1101
        }
1102
1103
        // Keep internal log for easy debugging
1104
        $this->log[] = [
1105
            'query' => $this->lastQuery, // TODO: Format
1106
            'query_hash' => $this->lastQueryHash,
1107
            'query_values' => $this->lastQueryValues,
1108
            'query_safe' => $this->lastQuerySafe,
1109
            'execution_time' => $stop
1110
        ];
1111
    }
1112
1113
    /**
1114
     * Returns some basic statistics from the Database class about the
1115
     * number of queries, the time it took to query and any slow queries.
1116
     * A slow query is defined as one that took longer than 0.0999 seconds
1117
     * This function is used by the Profile devkit
1118
     *
1119
     * @return array
1120
     *  An associative array with the number of queries, an array of slow
1121
     *  queries and the total query time.
1122
     */
1123
    public function getStatistics()
1124
    {
1125
        $stats = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $stats is dead and can be removed.
Loading history...
1126
        $query_timer = 0.0;
1127
        $slow_queries = [];
1128
1129
        foreach ($this->log as $key => $val) {
1130
            $query_timer += $val['execution_time'];
1131
            if ($val['execution_time'] > 0.0999) {
1132
                $slow_queries[] = $val;
1133
            }
1134
        }
1135
1136
        return [
1137
            'queries' => $this->queryCount(),
1138
            'slow-queries' => $slow_queries,
1139
            'total-query-time' => number_format($query_timer, 5, '.', '')
1140
        ];
1141
    }
1142
1143
    //--------------------------------------------------------------------------
1144
    // COMPAT LAYER
1145
    // All the following methods are deprecated and are there for
1146
    // backward compatibility only.
1147
    //--------------------------------------------------------------------------
1148
1149
    /**
1150
     * Returns all the log entries by type. There are two valid types,
1151
     * error and debug. If no type is given, the entire log is returned,
1152
     * otherwise only log messages for that type are returned
1153
     *
1154
     * @deprecated @since Symphony 3.0.0
1155
     * @see Database::getLogs()
1156
     * @param null|string $type
1157
     * @return array
1158
     *  An array of associative array's. Log entries of the error type
1159
     *  return the query the error occurred on and the error number and
1160
     *  message from MySQL. Log entries of the debug type return the
1161
     *  the query and the start/stop time to indicate how long it took
1162
     *  to run
1163
     */
1164
    public function debug($type = null)
1165
    {
1166
        return $this->getLogs();
1167
    }
1168
1169
    /**
1170
     * This function will clean a string using the `PDO::quote` function
1171
     * taking into account the current database character encoding. Note that this
1172
     * function does not encode _ or %.
1173
     *
1174
     * @deprecated @since Symphony 3.0.0
1175
     * @see quote
1176
     * @param string $value
1177
     *  The string to be encoded into an escaped SQL string
1178
     * @return string
1179
     *  The escaped SQL string
1180
     */
1181
    public function cleanValue($value)
1182
    {
1183
        if (Symphony::Log()) {
1184
            Symphony::Log()->pushDeprecateWarningToLog('Database::cleanValue()', 'The new API');
1185
        }
1186
        return trim($this->quote($value), "'");
0 ignored issues
show
Deprecated Code introduced by
The function Database::quote() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1186
        return trim(/** @scrutinizer ignore-deprecated */ $this->quote($value), "'");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1187
    }
1188
1189
    /**
1190
     * This function will apply the `quote` function to an associative
1191
     * array of data, encoding only the value, not the key. This function
1192
     * can handle recursive arrays. This function manipulates the given
1193
     * parameter by reference.
1194
     *
1195
     * @deprecated @since Symphony 3.0.0
1196
     * @see quoteFields
1197
     * @param array $array
1198
     *  The associative array of data to encode, this parameter is manipulated
1199
     *  by reference.
1200
     */
1201
    public function cleanFields(array &$array)
1202
    {
1203
        $this->quoteFields($array);
0 ignored issues
show
Deprecated Code introduced by
The function Database::quoteFields() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1203
        /** @scrutinizer ignore-deprecated */ $this->quoteFields($array);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1204
    }
1205
1206
    /**
1207
     * Determines whether this query is a read operation, or if it is a write operation.
1208
     * A write operation is determined as any query that starts with CREATE, INSERT,
1209
     * REPLACE, ALTER, DELETE, UPDATE, OPTIMIZE, TRUNCATE, DROP, LOCK or UNLOCK. Anything else is
1210
     * considered to be a read operation which are subject to query caching.
1211
     *
1212
     * @deprecated @since Symphony 3.0.0
1213
     * @param string $query
1214
     * @return int
1215
     *  `self::__WRITE_OPERATION__` or `self::__READ_OPERATION__`
1216
     */
1217
    public function determineQueryType($query)
1218
    {
1219
        return preg_match(
1220
            '/^(create|insert|replace|alter|delete|update|optimize|truncate|drop|lock|unlock)/i',
1221
            $query
1222
        ) === 1 ? self::__WRITE_OPERATION__ : self::__READ_OPERATION__;
0 ignored issues
show
Deprecated Code introduced by
The constant Database::__READ_OPERATION__ has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1222
        ) === 1 ? self::__WRITE_OPERATION__ : /** @scrutinizer ignore-deprecated */ self::__READ_OPERATION__;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant Database::__WRITE_OPERATION__ has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1222
        ) === 1 ? /** @scrutinizer ignore-deprecated */ self::__WRITE_OPERATION__ : self::__READ_OPERATION__;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1223
    }
1224
1225
    /**
1226
     * Takes an SQL string and executes it. This function will apply query
1227
     * caching if it is a read operation and if query caching is set. Symphony
1228
     * will convert the `tbl_` prefix of tables to be the one set during installation.
1229
     * A type parameter is provided to specify whether `$this->_lastResult` will be an array
1230
     * of objects or an array of associative arrays. The default is objects. This
1231
     * function will return boolean, but set `$this->_lastResult` to the result.
1232
     *
1233
     * @deprecated @since Symphony 3.0.0
1234
     * @see select()
1235
     * @see insert()
1236
     * @see update()
1237
     * @see delete()
1238
     * @see create()
1239
     * @see alter()
1240
     * @see drop()
1241
     * @see truncate()
1242
     * @see optimize()
1243
     * @see set()
1244
     * @see autoConnect()
1245
     * @uses PostQueryExecution
1246
     * @param string $query
1247
     *  The full SQL query to execute.
1248
     * @param string $type
1249
     *  Whether to return the result as objects or associative array. Defaults
1250
     *  to OBJECT which will return objects. The other option is ASSOC. If $type
1251
     *  is not either of these, it will return objects.
1252
     * @throws DatabaseException
1253
     * @return boolean|Traversable
1254
     *  true if the query executed without errors, false otherwise
1255
     */
1256
    public function query($query, $type = "OBJECT")
1257
    {
1258
        General::ensureType([
1259
            'query' => ['var' => $query, 'type' => 'string'],
1260
            'type' => ['var' => $type, 'type' => 'string'],
1261
        ]);
1262
        $this->autoConnect();
1263
1264
        if ($this->isLoggingEnabled()) {
1265
            $start = precision_timer();
1266
        }
1267
1268
        $result = null;
1269
        // Format SQL because PDO does not seem to like it
1270
        $query = trim(str_replace(PHP_EOL, ' ', $query));
1271
        $query = trim(str_replace('\t', ' ', $query));
1272
        while (strpos($query, '  ') !== false) {
1273
            $query = str_replace('  ', ' ', $query);
1274
        }
1275
        if ($this->getPrefix() !== 'tbl_') {
1276
            $query = preg_replace('/tbl_(\S+?)([\s\.,]|$)/', $this->getPrefix().'\\1\\2', $query);
1277
        }
1278
1279
        // Cleanup from last time, set some logging parameters
1280
        $this->flush();
1281
        $this->lastQuery = $query;
1282
        $this->lastQueryHash = md5($query . $start);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $start does not seem to be defined for all execution paths leading up to this point.
Loading history...
1283
        $this->lastQueryValues = null;
1284
        $this->lastQuerySafe = false;
1285
        $query_type = $this->determineQueryType($query);
0 ignored issues
show
Deprecated Code introduced by
The function Database::determineQueryType() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1285
        $query_type = /** @scrutinizer ignore-deprecated */ $this->determineQueryType($query);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1286
1287
        // TYPE is deprecated since MySQL 4.0.18, ENGINE is preferred
1288
        if ($query_type == self::__WRITE_OPERATION__) {
0 ignored issues
show
Deprecated Code introduced by
The constant Database::__WRITE_OPERATION__ has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1288
        if ($query_type == /** @scrutinizer ignore-deprecated */ self::__WRITE_OPERATION__) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1289
            $query = preg_replace('/MyISAM/i', 'InnoDB', $query);
1290
            $query = preg_replace('/TYPE=InnoDB/i', 'ENGINE=InnoDB', $query);
1291
        } elseif ($query_type == self::__READ_OPERATION__) {
0 ignored issues
show
Deprecated Code introduced by
The constant Database::__READ_OPERATION__ has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1291
        } elseif ($query_type == /** @scrutinizer ignore-deprecated */ self::__READ_OPERATION__) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1292
            if (!preg_match('/^\s*SELECT\s+SQL(_NO)?_CACHE/i', $query)) {
1293
                if ($this->isCachingEnabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function Database::isCachingEnabled() has been deprecated: The query cache is deprecated as of MySQL 5.7.20, and is removed in MySQL 8.0. ( Ignorable by Annotation )

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

1293
                if (/** @scrutinizer ignore-deprecated */ $this->isCachingEnabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1294
                    $query = preg_replace('/^SELECT\s+/i', 'SELECT SQL_CACHE ', $query);
1295
                } else {
1296
                    $query = preg_replace('/^SELECT\s+/i', 'SELECT SQL_NO_CACHE ', $query);
1297
                }
1298
            }
1299
            $fetchType = $type == "OBJECT" ? PDO::FETCH_OBJ : PDO::FETCH_ASSOC;
1300
        }
1301
1302
        try {
1303
            $resultPdo = $this->conn->query($query);
1304
            // Execute it
1305
            if ($fetchType) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fetchType does not seem to be defined for all execution paths leading up to this point.
Loading history...
1306
                $result = $resultPdo->fetchAll($fetchType);
1307
            } else {
1308
                $result = $resultPdo->execute();
1309
            }
1310
            $resultPdo->closeCursor();
1311
            $this->queryCount++;
1312
            $this->_lastResult = $result;
0 ignored issues
show
Bug Best Practice introduced by
The property _lastResult does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1313
        } catch (PDOException $ex) {
1314
            $this->throwDatabaseError($ex);
1315
            return;
1316
        }
1317
1318
        // Check for errors
1319
        if ($this->conn->errorCode() !== PDO::ERR_NONE) {
1320
            $this->throwDatabaseError();
1321
            return;
1322
        }
1323
1324
        // Log the query
1325
        if ($this->isLoggingEnabled()) {
1326
            $this->logLastQuery(precision_timer('stop', $start));
1327
        }
1328
1329
        return $result !== false;
1330
    }
1331
1332
    /**
1333
     * A convenience method to insert data into the Database. This function
1334
     * takes an associative array of data to input, with the keys being the column
1335
     * names and the table. An optional parameter exposes MySQL's ON DUPLICATE
1336
     * KEY UPDATE functionality, which will update the values if a duplicate key
1337
     * is found.
1338
     *
1339
     * @deprecated @since Symphony 3.0.0
1340
     * @param array $fields
1341
     *  An associative array of data to input, with the key's mapping to the
1342
     *  column names. Alternatively, an array of associative array's can be
1343
     *  provided, which will perform multiple inserts
1344
     * @param string $table
1345
     *  The table name, including the tbl prefix which will be changed
1346
     *  to this Symphony's table prefix in the query function
1347
     * @param boolean $updateOnDuplicate
1348
     *  If set to true, data will updated if any key constraints are found that cause
1349
     *  conflicts. By default this is set to false, which will not update the data and
1350
     *  would return an SQL error
1351
     * @throws DatabaseException
1352
     * @return boolean
1353
     */
1354
    public function _insert(array $fields, $table, $updateOnDuplicate = false) // @codingStandardsIgnoreLine
1355
    {
1356
        $success = true;
1357
        // Multiple inserts
1358
        if (!is_array(current($fields))) {
1359
            $fields = [$fields];
1360
        }
1361
        foreach ($fields as $field) {
1362
            $stm = $this->insert($table)->values($field);
0 ignored issues
show
Deprecated Code introduced by
The function Database::insert() has been deprecated: Symphony 3.0.0 If $table is an array, it is treated as the fields values Use DatabaseInsert::values() ( Ignorable by Annotation )

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

1362
            $stm = /** @scrutinizer ignore-deprecated */ $this->insert($table)->values($field);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1363
            if ($updateOnDuplicate) {
1364
                $stm->updateOnDuplicateKey();
1365
            }
1366
            $success = $success && $stm->execute()->success();
1367
        }
1368
        return $success;
1369
    }
1370
1371
    /**
1372
     * A convenience method to update data that exists in the Database. This function
1373
     * takes an associative array of data to input, with the keys being the column
1374
     * names and the table. A WHERE statement can be provided to select the rows
1375
     * to update
1376
     *
1377
     * @deprecated @since Symphony 3.0.0
1378
     * @param array $fields
1379
     *  An associative array of data to input, with the key's mapping to the
1380
     *  column names.
1381
     * @param string $table
1382
     *  The table name, including the tbl prefix which will be changed
1383
     *  to this Symphony's table prefix in the query function
1384
     * @param string $where
1385
     *  A WHERE statement for this UPDATE statement, defaults to null
1386
     *  which will update all rows in the $table
1387
     * @throws DatabaseException
1388
     * @return boolean
1389
     */
1390
    public function _update(array $fields, $table, $where = null) // @codingStandardsIgnoreLine
1391
    {
1392
        $stm = $this->update($table)->set($fields);
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

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

1392
        $stm = /** @scrutinizer ignore-deprecated */ $this->update($table)->set($fields);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1393
        if ($where) {
1394
            $stm->unsafeAppendSQLPart('where', "WHERE $where");
1395
        }
1396
        return $stm->execute()->success();
1397
    }
1398
1399
    /**
1400
     * Returns an associative array that contains the results of the
1401
     * given `$query`. Optionally, the resulting array can be indexed
1402
     * by a particular column.
1403
     *
1404
     * @deprecated @since Symphony 3.0.0
1405
     * @param string $query
1406
     *  The full SQL query to execute. Defaults to null, which will
1407
     *  use the _lastResult
1408
     * @param string $index_by_column
1409
     *  The name of a column in the table to use it's value to index
1410
     *  the result by. If this is omitted (and it is by default), an
1411
     *  array of associative arrays is returned, with the key being the
1412
     *  column names
1413
     * @throws DatabaseException
1414
     * @return array
1415
     *  An associative array with the column names as the keys
1416
     */
1417
    public function fetch($query = null, $index_by_column = null)
1418
    {
1419
        if (!is_null($query)) {
1420
            $this->query($query, "ASSOC");
0 ignored issues
show
Deprecated Code introduced by
The function Database::query() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1420
            /** @scrutinizer ignore-deprecated */ $this->query($query, "ASSOC");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1421
        } elseif (is_null($this->_lastResult)) {
1422
            return array();
1423
        }
1424
1425
        $result = $this->_lastResult;
1426
1427
        if (!is_null($index_by_column) && isset($result[0][$index_by_column])) {
1428
            $n = array();
1429
1430
            foreach ($result as $ii) {
1431
                $n[$ii[$index_by_column]] = $ii;
1432
            }
1433
1434
            $result = $n;
1435
        }
1436
1437
        return $result;
1438
    }
1439
1440
    /**
1441
     * Returns the row at the specified index from the given query. If no
1442
     * query is given, it will use the `$this->_lastResult`. If no offset is provided,
1443
     * the function will return the first row. This function does not imply any
1444
     * LIMIT to the given `$query`, so for the more efficient use, it is recommended
1445
     * that the `$query` have a LIMIT set.
1446
     *
1447
     * @deprecated @since Symphony 3.0.0
1448
     * @param int $offset
1449
     *  The row to return from the SQL query. For instance, if the second
1450
     *  row from the result was required, the offset would be 1, because it
1451
     *  is zero based.
1452
     * @param string $query
1453
     *  The full SQL query to execute. Defaults to null, which will
1454
     *  use the `$this->_lastResult`
1455
     * @throws DatabaseException
1456
     * @return array
1457
     *  If there is no row at the specified `$offset`, an empty array will be returned
1458
     *  otherwise an associative array of that row will be returned.
1459
     */
1460
    public function fetchRow($offset = 0, $query = null)
1461
    {
1462
        $result = $this->fetch($query);
0 ignored issues
show
Deprecated Code introduced by
The function Database::fetch() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1462
        $result = /** @scrutinizer ignore-deprecated */ $this->fetch($query);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1463
        return (empty($result) ? array() : $result[$offset]);
1464
    }
1465
1466
    /**
1467
     * Returns an array of values for a specified column in a given query.
1468
     * If no query is given, it will use the `$this->_lastResult`.
1469
     *
1470
     * @deprecated @since Symphony 3.0.0
1471
     * @param string $column
1472
     *  The column name in the query to return the values for
1473
     * @param string $query
1474
     *  The full SQL query to execute. Defaults to null, which will
1475
     *  use the `$this->_lastResult`
1476
     * @throws DatabaseException
1477
     * @return array
1478
     *  If there is no results for the `$query`, an empty array will be returned
1479
     *  otherwise an array of values for that given `$column` will be returned
1480
     */
1481
    public function fetchCol($column, $query = null)
1482
    {
1483
        $result = $this->fetch($query);
0 ignored issues
show
Deprecated Code introduced by
The function Database::fetch() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1483
        $result = /** @scrutinizer ignore-deprecated */ $this->fetch($query);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1484
1485
        if (empty($result)) {
1486
            return array();
1487
        }
1488
1489
        $rows = array();
1490
        foreach ($result as $row) {
1491
            $rows[] = $row[$column];
1492
        }
1493
1494
        return $rows;
1495
    }
1496
1497
    /**
1498
     * Returns the value for a specified column at a specified offset. If no
1499
     * offset is provided, it will return the value for column of the first row.
1500
     * If no query is given, it will use the `$this->_lastResult`.
1501
     *
1502
     * @deprecated @since Symphony 3.0.0
1503
     * @see select
1504
     * @param string $column
1505
     *  The column name in the query to return the values for
1506
     * @param int $offset
1507
     *  The row to use to return the value for the given `$column` from the SQL
1508
     *  query. For instance, if `$column` form the second row was required, the
1509
     *  offset would be 1, because it is zero based.
1510
     * @param string $query
1511
     *  The full SQL query to execute. Defaults to null, which will
1512
     *  use the `$this->_lastResult`
1513
     * @throws DatabaseException
1514
     * @return string|null
1515
     *  Returns the value of the given column, if it doesn't exist, null will be
1516
     *  returned
1517
     */
1518
    public function fetchVar($column, $offset = 0, $query = null)
1519
    {
1520
        $result = $this->fetch($query);
0 ignored issues
show
Deprecated Code introduced by
The function Database::fetch() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1520
        $result = /** @scrutinizer ignore-deprecated */ $this->fetch($query);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1521
        return (empty($result) ? null : $result[$offset][$column]);
1522
    }
1523
}
1524
1525
/**
1526
 * Compat layer: Recreate the old MySQL class by extending our new Database class
1527
 */
1528
class MySQL extends Database
1529
{
1530
    public function cleanValue($value)
1531
    {
1532
        return (new Database())->cleanValue($value);
0 ignored issues
show
Deprecated Code introduced by
The function Database::cleanValue() has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1532
        return /** @scrutinizer ignore-deprecated */ (new Database())->cleanValue($value);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1533
    }
1534
}
1535