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.
Completed
Push — integration ( 137779...0212ae )
by Nicolas
10s
created

Database::transaction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
7
/**
8
 * The DatabaseException class extends a normal Exception to add in
9
 * debugging information when a SQL query fails such as the internal
10
 * database error code and message in additional to the usual
11
 * Exception information. It allows a DatabaseException to contain a human
12
 * readable error, as well more technical information for debugging.
13
 */
14
Class DatabaseException extends Exception{
15
16
    /**
17
     * An associative array with three keys, 'query', 'msg' and 'num'
18
     * @var array
19
     */
20
    private $_error = array();
21
22
    /**
23
     * Constructor takes a message and an associative array to set to
24
     * `$_error`. The message is passed to the default Exception constructor
25
     */
26
    public function __construct($message, array $error=NULL, Exception $ex = null)
27
    {
28
        parent::__construct($message, (int)$error['num'], $ex);
29
30
        $this->_error = $error;
31
    }
32
33
    /**
34
     * Accessor function for the original query that caused this Exception
35
     *
36
     * @return string
37
     */
38
    public function getQuery()
39
    {
40
        return $this->_error['query'];
41
    }
42
43
    /**
44
     * Accessor function for the Database error code for this type of error
45
     *
46
     * @return integer
47
     */
48
    public function getDatabaseErrorCode()
49
    {
50
        return $this->_error['num'];
51
    }
52
53
    /**
54
     * Accessor function for the Database message from this Exception
55
     *
56
     * @return string
57
     */
58
    public function getDatabaseErrorMessage()
59
    {
60
        return $this->_error['msg'];
61
    }
62
63
}
64
65
class DatabaseStatement
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
66
{
67
    /**
68
     * The database connection.
69
     *
70
     * @var Database
71
     */
72
    public $database;
73
74
    /**
75
     * The internal PDOStatement.
76
     *
77
     * @var PDOStatement
78
     */
79
    public $statement;
80
81
    /**
82
     * Wrap a PDOStatement object so that we can log its queries
83
     * and handle its errors.
84
     *
85
     * @param Database      $database
86
     * @param PDOStatement  $statement
87
     */
88
    public function __construct(Database $database, PDOStatement $statement)
89
    {
90
        $this->database = $database;
91
        $this->statement = $statement;
92
    }
93
94
    /**
95
     * Call a method on the internal PDOStatement object.
96
     *
97
     * @link    http://php.net/manual/en/class.pdostatement.php
98
     *  For a list of available methods.
99
     * @param   string $name
100
     * @param   array  $arguments
101
     * @return  mixed
102
     */
103
    public function __call($name, $arguments)
104
    {
105
        return $this->statement->{$name}(...$arguments);
106
    }
107
108
    /**
109
     * Get the value of a property on the internal PDOStatement.
110
     *
111
     * @link    http://php.net/manual/en/class.pdostatement.php
112
     *  For a list of available properties.
113
     * @param   string $name
114
     * @return  mixed
115
     *  The value of the property or null if the property does not exist.
116
     */
117
    public function __get($name)
118
    {
119
        return (
120
            isset($this->statement->{$name})
121
                ? $this->statement->{$name}
122
                : null
123
        );
124
    }
125
126
    /**
127
     * Set a property on the internal PDOStatement.
128
     *
129
     * @link    http://php.net/manual/en/class.pdostatement.php
130
     *  For a list of available properties.
131
     * @param   string $name
132
     * @param   mixed $value
133
     */
134
    public function __set($name, $value)
135
    {
136
        $this->statement->{$name} = $value;
137
    }
138
139
    /**
140
     * Executes the internal PDOStatement.
141
     *
142
     * @link    http://php.net/manual/en/pdostatement.execute.php
143
     *  For complete documentation.
144
     * @throws  DatabaseException
145
     * @return  boolean
146
     */
147
    public function execute(...$arguments)
148
    {
149
        $start = precision_timer();
150
151
        $this->database->flush();
152
153
        $query = $this->statement->queryString;
154
        $hash = md5($query . $start);
155
156
        try {
157
            $result = $this->statement->execute(...$arguments);
158
        }
159
160
        catch (PDOException $error) {
161
            $this->database->throwError($error, $query, $hash);
162
        }
163
164
        $this->database->logQuery($query, $hash, precision_timer('stop', $start));
165
166
        return $result;
167
    }
168
}
169
170
class DatabaseTransaction
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
171
{
172
    /**
173
     * The number of currently open transactions.
174
     *
175
     * @var integer
176
     */
177
    protected static $transactions = 0;
178
179
    /**
180
     * The database connection.
181
     *
182
     * @var PDO
183
     */
184
    protected $connection;
185
186
    /**
187
     * Has this transaction completed.
188
     *
189
     * @var boolean
190
     */
191
    protected $completed;
192
193
    /**
194
     * A nested database transaction manager.
195
     *
196
     * @param PDO   $connection
197
     */
198
    public function __construct(PDO $connection)
199
    {
200
        $this->completed = false;
201
        $this->connection = $connection;
202
203
        if (0 === self::$transactions) {
204
            $this->connection->beginTransaction();
205
        }
206
207
        else {
208
            $this->connection->exec('savepoint trans' . self::$transactions);
209
        }
210
211
        self::$transactions++;
212
    }
213
214 View Code Duplication
    public function commit()
215
    {
216
        if ($this->completed) return false;
217
218
        self::$transactions--;
219
        $this->completed = true;
220
221
        if (0 === self::$transactions) {
222
            return $this->connection->commit();
223
        }
224
225
        else {
226
            return $this->connection->exec('release savepoint trans' . self::$transactions);
227
        }
228
    }
229
230 View Code Duplication
    public function rollBack()
231
    {
232
        if ($this->completed) return false;
233
234
        self::$transactions--;
235
        $this->completed = true;
236
237
        if (0 === self::$transactions) {
238
            return $this->connection->rollBack();
239
        }
240
241
        else {
242
            return $this->connection->exec('rollback to savepoint trans' . self::$transactions);
243
        }
244
    }
245
}
246
247
Class Database {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
248
249
    /**
250
     * Constant to indicate whether the query is a write operation.
251
     *
252
     * @var integer
253
     */
254
    const __WRITE_OPERATION__ = 0;
255
256
    /**
257
     * Constant to indicate whether the query is a write operation
258
     *
259
     * @var integer
260
     */
261
    const __READ_OPERATION__ = 1;
262
263
    /**
264
     * An instance of the current PDO object
265
     * @var PDO
266
     */
267
    public $conn = null;
268
269
    /**
270
     * Sets the current `$_log` to be an empty array
271
     *
272
     * @var array
273
     */
274
    public $log = array();
275
276
    /**
277
     * The number of queries this class has executed, defaults to 0.
278
     *
279
     * @var integer
280
     */
281
    private $_query_count = 0;
282
283
    /**
284
     * The table prefix for this connection. Queries to be written using
285
     * a `tbl_table_name` syntax, where `tbl_` will be replaced by this
286
     * variable. By default it is `sym_` but it configured in the configuration
287
     *
288
     * @var string
289
     */
290
    private $_prefix = 'sym_';
291
292
    /**
293
     * Whether query caching is enabled or not. By default this set
294
     * to true which will use SQL_CACHE to cache the results of queries
295
     *
296
     * @var boolean
297
     */
298
    private $_cache = true;
299
300
    /**
301
     * Whether to log this query in the internal `$log`.
302
     * Defaults to true
303
     *
304
     * @var boolean
305
     */
306
    private $_logging = true;
307
308
    /**
309
     * Resets the result, `$this->_lastResult` and `$this->_lastQuery` to their empty
310
     * values. Called on each query and when the class is destroyed.
311
     */
312
    public function flush()
313
    {
314
        $this->_result = null;
0 ignored issues
show
Bug introduced by
The property _result does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
315
        $this->_lastResult = array();
0 ignored issues
show
Bug introduced by
The property _lastResult does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
316
        $this->_lastQuery = null;
0 ignored issues
show
Bug introduced by
The property _lastQuery does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
317
        $this->_lastQueryHash = null;
0 ignored issues
show
Bug introduced by
The property _lastQueryHash does not seem to exist. Did you mean _lastQuery?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
318
    }
319
320
    /**
321
     * Creates a new Database object given an associative array of configuration
322
     * parameters in `$config`. If `$config` contains a key, `pdo` then this
323
     * `Database` instance will use that PDO connection. Otherwise, `$config`
324
     * should include `driver`, `host`, `port`, `user`, `password` and an optional
325
     * array of PDO options in `options`.
326
     *
327
     * @param array $config
328
     * @return PDO
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
329
     */
330
    public function __construct(array $config = array())
331
    {
332
        // If we have an existing PDO object
333
        if(isset($config['pdo'])) {
334
            $this->conn = $config['pdo'];
335
        }
336
        // Otherwise create a PDO object from parameters
337
        else {
338
            $this->connect(sprintf('%s:dbname=%s;host=%s;port=%d;charset=%s', $config['driver'], $config['db'], $config['host'], $config['port'], $config['charset']),
339
                $config['user'],
340
                $config['password'],
341
                $config['options']
342
            );
343
        }
344
345
        return $this->conn;
346
    }
347
348
    /**
349
     * Magic function that will flush the MySQL log and close the MySQL
350
     * connection when the MySQL class is removed or destroyed.
351
     *
352
     * @link http://php.net/manual/en/language.oop5.decon.php
353
     */
354
    public function __destruct()
355
    {
356
        unset($this->conn);
357
        $this->flush();
358
    }
359
360
    /**
361
     * Creates a PDO connection to the desired database given the parameters.
362
     * This will also set the error mode to be exceptions (handled by this class)
363
     *
364
     * @link http://www.php.net/manual/en/pdo.drivers.php
365
     * @param string $dsn
366
     * @param string $username
367
     * @param string $password
368
     * @param array $options
369
     * @return boolean
370
     */
371
    public function connect($dsn = null, $username = null, $password = null, array $options = array())
372
    {
373
        try {
374
            $this->conn = new PDO($dsn, $username, $password, $options);
375
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
376
        }
377
        catch (PDOException $ex) {
378
            $this->error($ex);
379
380
            return false;
381
        }
382
383
        return true;
384
    }
385
386
    /**
387
     * Returns the number of queries that has been executed
388
     *
389
     * @return integer
390
     */
391
    public function queryCount()
392
    {
393
        return $this->_query_count;
394
    }
395
396
    /**
397
     * Sets query caching to true, this will prepend all READ_OPERATION
398
     * queries with SQL_CACHE. Symphony be default enables caching. It
399
     * can be turned off by setting the query_cache parameter to 'off' in the
400
     * Symphony config file.
401
     *
402
     * @link http://dev.mysql.com/doc/refman/5.1/en/query-cache.html
403
     */
404
    public function enableCaching()
405
    {
406
        $this->_cache = true;
407
    }
408
409
    /**
410
     * Sets query caching to false, this will prepend all READ_OPERATION
411
     * queries will SQL_NO_CACHE.
412
     */
413
    public function disableCaching()
414
    {
415
        $this->_cache = false;
416
    }
417
418
    /**
419
     * Returns boolean if query caching is enabled or not
420
     *
421
     * @return boolean
422
     */
423
    public function isCachingEnabled()
424
    {
425
        return $this->_cache;
426
    }
427
428
    /**
429
     * Enables the logging of queries
430
     */
431
    public function enableLogging()
432
    {
433
        $this->_logging = true;
434
    }
435
436
    /**
437
     * Sets logging to false
438
     */
439
    public function disableLogging()
440
    {
441
        $this->_logging = false;
442
    }
443
444
    /**
445
     * Returns boolean if logging is enabled or not
446
     *
447
     * @return boolean
448
     */
449
    public function isLoggingEnabled()
450
    {
451
        return $this->_logging;
452
    }
453
454
    /**
455
     * Symphony uses a prefix for all it's database tables so it can live peacefully
456
     * on the same database as other applications. By default this is sym_, but it
457
     * can be changed when Symphony is installed.
458
     *
459
     * @param string $prefix
460
     *  The table prefix for Symphony, by default this is sym_
461
     */
462
    public function setPrefix($prefix)
463
    {
464
        $this->_prefix = $prefix;
465
    }
466
467
    /**
468
     * Returns the prefix used by Symphony for this Database instance.
469
     *
470
     * @since Symphony 2.4
471
     * @return string
472
     */
473
    public function getPrefix()
474
    {
475
        return $this->_prefix;
476
    }
477
478
    /**
479
     * Given a string, replace the default table prefixes with the
480
     * table prefix for this database instance.
481
     *
482
     * @param string $query
483
     * @return string
484
     */
485
    public function replaceTablePrefix($query)
486
    {
487
        if($this->_prefix != 'tbl_'){
488
            $query = preg_replace('/tbl_(\S+?)([\s\.,]|$)/', $this->_prefix .'\\1\\2', $query);
489
        }
490
491
        return $query;
492
    }
493
494
    /**
495
     * Function looks over a query to determine if it's a READ or WRITE operation.
496
     * WRITE operations are any query that starts with: SET, CREATE, INSERT, REPLACE
497
     * ALTER, DELETE, UPDATE, OPTIMIZE, TRUNCATE or DROP. All other queries are considered
498
     * READ operations
499
     *
500
     * @param string $query
501
     * @return integer
502
     */
503
    public function determineQueryType($query)
504
    {
505
        return (preg_match('/^(set|create|insert|replace|alter|delete|update|optimize|truncate|drop)/i', $query)
506
            ? self::__WRITE_OPERATION__
507
            : self::__READ_OPERATION__);
508
    }
509
510
    /**
511
     * @param array $values
512
     * @return string
513
     */
514
    public static function addPlaceholders(array $values = array())
515
    {
516
        $placeholders = null;
517
        if(!empty($values)) {
518
            $placeholders = str_repeat('?,', count($values) - 1) . '?';
519
        }
520
521
        return $placeholders;
522
    }
523
524
    /**
525
     * Given a query that has been prepared and an array of values to subsitute
526
     * into the query, the function will return the result.
527
     *
528
     * @param string $query
529
     * @param array $values
530
     * @return PDOStatement
531
     */
532
    public function insert($query, array $values)
533
    {
534
        $result = $this->q($query, $values);
535
536
        return $result;
537
    }
538
539
    /**
540
     * Returns the last insert ID from the previous query. This is
541
     * the value from an auto_increment field.
542
     *
543
     * @return integer
544
     *  The last interested row's ID
545
     */
546
    public function getInsertID()
547
    {
548
        return $this->conn->lastInsertId();
549
    }
550
551
    /**
552
     * Given a query that has been prepared and an array of values to subsitute
553
     * into the query, the function will return the result.
554
     *
555
     * @param string $query
556
     * @param array $values
557
     * @return PDOStatement
558
     */
559
    public function update($query, array $values)
560
    {
561
        $result = $this->q($query, $values);
562
563
        return $result;
564
    }
565
566
    /**
567
     * Given a query that has been prepared and an array of values to subsitute
568
     * into the query, the function will return the result.
569
     *
570
     * @param string $query
571
     * @param array $values
572
     * @return PDOStatement
573
     */
574
    public function delete($query, array $values)
575
    {
576
        $result = $this->q($query, $values);
577
578
        return $result;
579
    }
580
581
    /**
582
     * Given a query that has been prepared and an array of optional
583
     * parameters, this function will return the results of a query
584
     * as an array.
585
     *
586
     * @param string $query
587
     * @param array $params
588
     *   - `fetch-type` = 'ASSOC'/'OBJECT'
589
     *          Return result as array or an object
590
     *   - `index` = 'column_name'
591
     *          The name of a column in the table to use it's value to index
592
     *          the result by. If this is omitted (and it is by default), an
593
     *          array of associative arrays is returned, with the key being the
594
     *          column names
595
     *      `offset` = `0`
596
     *          An integer representing the row to return
597
     * @return array
598
     */
599
    public function fetch($query = null, array $params = array(), array $values = array())
600
    {
601
        if(!is_null($query)) {
602
            $params['fetch-type'] = 'ASSOC';
603
            $this->query($query, $params, $values);
604
        }
605
        else if(is_null($this->_lastResult) || $this->_lastResult === false) {
606
            return array();
607
        }
608
609
        $result = $this->_lastResult;
610
611
        if(isset($params['index']) && isset($result[0][$params['index']])){
612
            $n = array();
613
614
            foreach($result as $ii) {
615
                $n[$ii[$params['index']]] = $ii;
616
            }
617
618
            $result = $n;
619
        }
620
621
        return $result;
622
    }
623
624
    /**
625
     * Takes an SQL string and creates a prepared statement.
626
     *
627
     * @link http://php.net/manual/en/pdo.prepare.php
628
     * @param string $query
629
     * @param array $driver_options
630
     *  This array holds one or more key=>value pairs to set attribute values
631
     *  for the DatabaseStatement object that this method returns.
632
     * @return DatabaseStatement
633
     */
634
    public function prepare($query, array $driver_options = array())
635
    {
636
        $query = $this->replaceTablePrefix($query);
637
638
        return new DatabaseStatement($this, $this->conn->prepare($query, $driver_options));
639
    }
640
641
    /**
642
     * Create a transaction.
643
     *
644
     * @return DatabaseTransaction
645
     */
646
    public function transaction()
647
    {
648
        return new DatabaseTransaction($this->conn);
649
    }
650
651
    /**
652
     * Given a query that has been prepared and an array of values to subsitute
653
     * into the query, the function will return the result. Unlike `insert` and
654
     * `update`, this function is a bit of a catch all and will be able to populate
655
     * `$this->_lastResult` with an array of data. This function is usually used
656
     * via `fetch()`.
657
     *
658
     * @see fetch()
659
     * @param string $query
660
     * @param array $params
661
     *  Supports `fetch-type` and `offset` parameters for the moment
662
     * @param array $values
663
     *  If the `$query` has placeholders, this parameter will include the data
664
     *  to subsitute into the placeholders
665
     * @return boolean
666
     */
667
    public function query($query, array $params = array(), array $values = array())
668
    {
669
        if(empty($query)) return false;
670
671
        $query_type = $this->determineQueryType(trim($query));
672
        $query_hash = md5($query.$start);
0 ignored issues
show
Bug introduced by
The variable $start does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Unused Code introduced by
$query_hash is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
673
674
        if($query_type == self::__READ_OPERATION__ && !preg_match('/^\s*SELECT\s+SQL(_NO)?_CACHE/i', $query)){
675
            if($this->isCachingEnabled()) {
676
                $query = preg_replace('/^\s*SELECT\s+/i', 'SELECT SQL_CACHE ', $query);
677
            }
678
            else {
679
                $query = preg_replace('/^\s*SELECT\s+/i', 'SELECT SQL_NO_CACHE ', $query);
680
            }
681
        }
682
683
        $this->q($query, $values, false);
684
685
        if($this->_result instanceof PDOStatement && $query_type == self::__READ_OPERATION__) {
686
            if($params['fetch-type'] == "ASSOC") {
687
                if(isset($params['offset'])) {
688
                    while ($row = $this->_result->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, $params['offset'])) {
689
                        $this->_lastResult = $row;
690
                    }
691
                }
692
                else {
693
                    while ($row = $this->_result->fetch(PDO::FETCH_ASSOC)) {
694
                        $this->_lastResult[] = $row;
695
                    }
696
                }
697
            }
698
            else if($params['fetch-type'] == 'OBJECT') {
699
                while ($row = $this->_result->fetchObject()) {
700
                    $this->_lastResult[] = $row;
701
                }
702
            }
703
        }
704
705
        return true;
706
    }
707
708
    /**
709
     * This function is actually responsible for subsituting the values into
710
     * the query and logging the query for basic profiling/debugging.
711
     *
712
     * @param string $query
713
     * @param array $values
714
     * @param boolean $close
715
     *  If true, once the query is executed, the cursor will be closed,
716
     *  otherwise it'll be left open for further manipulation (as done by
717
     *  `query()`). Defaults to `true`
718
     * @return PDOStatement
719
     */
720
    private function q($query, $values, $close = true)
721
    {
722
        if(empty($query)) return false;
723
724
        // Default query preparation
725
        $query = $this->replaceTablePrefix(trim($query));
726
727
        if($this->_logging) {
728
            $start = precision_timer();
729
        }
730
731
        // Cleanup from last time, set some logging parameters
732
        $this->flush();
733
        $this->_lastQuery = $query;
734
        $this->_lastQueryHash = md5($query.$start);
0 ignored issues
show
Bug introduced by
The property _lastQueryHash does not seem to exist. Did you mean _lastQuery?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Bug introduced by
The variable $start does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
735
736
        // Execute
737
        try {
738
            $this->_result = $this->conn->prepare($query);
739
            $result = $this->_result->execute($values);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
740
            $this->_query_count++;
741
        }
742
        catch (PDOException $ex) {
743
            $this->error($ex);
744
        }
745
746
        if($this->conn->errorCode() !== PDO::ERR_NONE) {
747
            $this->error();
748
749
            return false;
750
        }
751
        else if($this->_result instanceof PDOStatement) {
752
            $this->_lastQuery = $this->_result->queryString;
753
754
            if($close) {
755
                $this->_result->closeCursor();
756
            }
757
        }
758
759
        if($this->_logging) {
760
            $this->logQuery($query, $this->_lastQueryHash, precision_timer('stop', $start));
0 ignored issues
show
Bug introduced by
The property _lastQueryHash does not seem to exist. Did you mean _lastQuery?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
761
        }
762
763
        return $this->_result;
764
    }
765
766
    /**
767
     * Given an Exception, or called when an error occurs, this function will
768
     * fire the `QueryExecutionError` delegate and then raise a `DatabaseException`
769
     *
770
     * @uses QueryExecutionError
771
     * @throws DatabaseException
772
     * @param Exception $ex
773
     *  The exception thrown while doing something with the Database
774
     * @return void
775
     */
776
    public function error(Exception $ex = null)
777
    {
778
        if(isset($ex)) {
779
            $msg = $ex->getMessage();
780
            $errornum = $ex->getCode();
781
        }
782
        else {
783
            $error = $this->conn->errorInfo();
784
            $msg = $error[2];
785
            $errornum = $error[0];
786
        }
787
788
        /**
789
         * After a query execution has failed this delegate will provide the query,
790
         * query hash, error message and the error number.
791
         *
792
         * Note that this function only starts logging once the `ExtensionManager`
793
         * is available, which means it will not fire for the first couple of
794
         * queries that set the character set.
795
         *
796
         * @since Symphony 2.3
797
         * @delegate QueryExecutionError
798
         * @param string $context
799
         * '/frontend/' or '/backend/'
800
         * @param string $query
801
         *  The query that has just been executed
802
         * @param string $query_hash
803
         *  The hash used by Symphony to uniquely identify this query
804
         * @param string $msg
805
         *  The error message provided by MySQL which includes information on why the execution failed
806
         * @param integer $num
807
         *  The error number that corresponds with the MySQL error message
808
         */
809
        if(Symphony::ExtensionManager() instanceof ExtensionManager) {
810
            Symphony::ExtensionManager()->notifyMembers('QueryExecutionError', class_exists('Administration') ? '/backend/' : '/frontend/', array(
811
                'query' => $this->_lastQuery,
812
                'query_hash' => $this->_lastQueryHash,
0 ignored issues
show
Bug introduced by
The property _lastQueryHash does not seem to exist. Did you mean _lastQuery?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
813
                'msg' => $msg,
814
                'num' => $errornum
815
            ));
816
        }
817
818
        throw new DatabaseException(__('Database Error (%1$s): %2$s in query: %3$s', array($errornum, $msg, $this->_lastQuery)), array(
819
            'msg' => $msg,
820
            'num' => $errornum,
821
            'query' => $this->_lastQuery
822
        ), $ex);
823
    }
824
825
    /**
826
     * Throw a new DatabaseException when given an original exception and a query.
827
     *
828
     * @param Exception $error
829
     * @param string $query
830
     * @param string $query_hash
831
     */
832
    public function throwError(Exception $error, $query, $query_hash)
833
    {
834
        $this->_lastQuery = $query;
835
        $this->_lastQueryHash = $query_hash;
0 ignored issues
show
Bug introduced by
The property _lastQueryHash does not seem to exist. Did you mean _lastQuery?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
836
837
        $this->error($error);
838
    }
839
840
    /**
841
     * Function is called everytime a query is executed to log it for
842
     * basic profiling/debugging purposes
843
     *
844
     * @uses PostQueryExecution
845
     * @param string $query
846
     * @param string $query_hash
847
     * @param integer $stop
848
     */
849
    public function logQuery($query, $query_hash, $stop)
850
    {
851
        /**
852
         * After a query has successfully executed, that is it was considered
853
         * valid SQL, this delegate will provide the query, the query_hash and
854
         * the execution time of the query.
855
         *
856
         * Note that this function only starts logging once the ExtensionManager
857
         * is available, which means it will not fire for the first couple of
858
         * queries that set the character set.
859
         *
860
         * @since Symphony 2.3
861
         * @delegate PostQueryExecution
862
         * @param string $context
863
         * '/frontend/' or '/backend/'
864
         * @param string $query
865
         *  The query that has just been executed
866
         * @param string $query_hash
867
         *  The hash used by Symphony to uniquely identify this query
868
         * @param float $execution_time
869
         *  The time that it took to run `$query`
870
         */
871
        if(Symphony::ExtensionManager() instanceof ExtensionManager) {
872
            Symphony::ExtensionManager()->notifyMembers('PostQueryExecution', class_exists('Administration') ? '/backend/' : '/frontend/', array(
873
                'query' => $query,
874
                'query_hash' => $query_hash,
875
                'execution_time' => $stop
876
            ));
877
878
            // If the ExceptionHandler is enabled, then the user is authenticated
879
            // or we have a serious issue, so log the query.
880
            if(GenericExceptionHandler::$enabled) {
881
                $this->_log[$query_hash] = array(
0 ignored issues
show
Bug introduced by
The property _log does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
882
                    'query' => $query,
883
                    'query_hash' => $query_hash,
884
                    'execution_time' => $stop
885
                );
886
            }
887
        }
888
889
        // Symphony isn't ready yet. Log internally
890
        else {
891
            $this->_log[$query_hash] = array(
892
                'query' => $query,
893
                'query_hash' => $query_hash,
894
                'execution_time' => $stop
895
            );
896
        }
897
    }
898
899
    /**
900
     * Returns all the log entries by type. There are two valid types,
901
     * error and debug. If no type is given, the entire log is returned,
902
     * otherwise only log messages for that type are returned
903
     *
904
     * @return array
905
     *  An array of associative array's. Log entries of the error type
906
     *  return the query the error occurred on and the error number and
907
     *  message from MySQL. Log entries of the debug type return the
908
     *  the query and the start/stop time to indicate how long it took
909
     *  to run
910
     */
911
    public function debug($type = null)
912
    {
913
        if(!$type) return $this->_log;
914
915
        return ($type == 'error' ? $this->_log['error'] : $this->_log['query']);
916
    }
917
918
    /**
919
     * Returns some basic statistics from the MySQL class about the
920
     * number of queries, the time it took to query and any slow queries.
921
     * A slow query is defined as one that took longer than 0.0999 seconds
922
     * This function is used by the Profile devkit
923
     *
924
     * @return array
925
     *  An associative array with the number of queries, an array of slow
926
     *  queries and the total query time.
927
     */
928
    public function getStatistics()
929
    {
930
        $stats = array();
0 ignored issues
show
Unused Code introduced by
$stats is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
931
        $query_timer = 0.0;
932
        $slow_queries = array();
933
934
        foreach($this->_log as $key => $val) {
935
            $query_timer += $val['execution_time'];
936
            if($val['execution_time'] > 0.0999) $slow_queries[] = $val;
937
        }
938
939
        return array(
940
            'queries' => $this->queryCount(),
941
            'slow-queries' => $slow_queries,
942
            'total-query-time' => number_format($query_timer, 4, '.', '')
943
        );
944
    }
945
}
946