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.
Passed
Pull Request — master (#2837)
by
unknown
05:48
created

Symphony   F

Complexity

Total Complexity 77

Size/Duplication

Total Lines 720
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 17

Importance

Changes 0
Metric Value
dl 0
loc 720
rs 1.7391
c 0
b 0
f 0
wmc 77
lcom 3
cbo 17

28 Methods

Rating   Name   Duplication   Size   Complexity  
A isInstallerAvailable() 0 3 1
A initialiseErrorHandler() 0 6 1
D getPageNamespace() 0 29 9
A throwCustomError() 0 4 1
A isXSRFEnabled() 0 3 1
A Configuration() 0 3 1
A setException() 0 3 1
A getException() 0 3 1
C login() 0 42 8
A Author() 0 3 1
A isUpgradeAvailable() 0 10 2
A Profiler() 0 3 1
A Engine() 0 8 3
B initialiseLog() 0 18 6
A isLoggedIn() 0 9 3
B loginFromToken() 0 45 5
A Log() 0 3 1
A Cookie() 0 3 1
B __construct() 0 25 4
A initialiseCookie() 0 7 2
C initialiseDatabase() 0 48 10
A logout() 0 3 1
A initialiseConfiguration() 0 19 2
A Database() 0 3 1
A initialiseExtensionManager() 0 10 4
A getMigrationVersion() 0 10 2
A setDatabase() 0 9 3
A ExtensionManager() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Symphony often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Symphony, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package core
5
 */
6
/**
7
 * The Symphony class is an abstract class that implements the
8
 * Singleton interface. It provides the glue that forms the Symphony
9
 * CMS and initialises the toolkit classes. Symphony is extended by
10
 * the Frontend and Administration classes
11
 */
12
13
abstract class Symphony implements Singleton
14
{
15
    /**
16
     * An instance of the Symphony class, either `Administration` or `Frontend`.
17
     * @var Symphony
18
     */
19
    protected static $_instance = null;
20
21
    /**
22
     * An instance of the Profiler class
23
     * @var Profiler
24
     */
25
    protected static $Profiler = null;
26
27
    /**
28
     * An instance of the `Configuration` class
29
     * @var Configuration
30
     */
31
    private static $Configuration = null;
32
33
    /**
34
     * An instance of the `Database` class
35
     * @var MySQL
36
     */
37
    private static $Database = null;
38
39
    /**
40
     * An instance of the `ExtensionManager` class
41
     * @var ExtensionManager
42
     */
43
    private static $ExtensionManager = null;
44
45
    /**
46
     * An instance of the `Log` class
47
     * @var Log
48
     */
49
    private static $Log = null;
50
51
    /**
52
     * The current page namespace, used for translations
53
     * @since Symphony 2.3
54
     * @var string
55
     */
56
    private static $namespace = false;
57
58
    /**
59
     * An instance of the Cookie class
60
     * @var Cookie
61
     */
62
    public static $Cookie = null;
63
64
    /**
65
     * An instance of the currently logged in Author
66
     * @var Author
67
     */
68
    public static $Author = null;
69
70
    /**
71
     * A previous exception that has been fired. Defaults to null.
72
     * @since Symphony 2.3.2
73
     * @var Exception
74
     */
75
    private static $exception = null;
76
77
    /**
78
     * The Symphony constructor initialises the class variables of Symphony. At present
79
     * constructor has a couple of responsibilities:
80
     * - Start a profiler instance
81
     * - If magic quotes are enabled, clean `$_SERVER`, `$_COOKIE`, `$_GET`, `$_POST` and the `$_REQUEST` arrays.
82
     * - Initialise the correct Language for the currently logged in Author.
83
     * - Start the session and adjust the error handling if the user is logged in
84
     *
85
     * The `$_REQUEST` array has been added in 2.7.0
86
     */
87
    protected function __construct()
88
    {
89
        self::$Profiler = Profiler::instance();
90
91
        if (get_magic_quotes_gpc()) {
92
            General::cleanArray($_SERVER);
93
            General::cleanArray($_COOKIE);
94
            General::cleanArray($_GET);
95
            General::cleanArray($_POST);
96
            General::cleanArray($_REQUEST);
97
        }
98
99
        // Initialize language management
100
        Lang::initialize();
101
        Lang::set(self::$Configuration->get('lang', 'symphony'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration->get('lang', 'symphony') can also be of type array; however, parameter $code of Lang::set() does only seem to accept string, 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

101
        Lang::set(/** @scrutinizer ignore-type */ self::$Configuration->get('lang', 'symphony'));
Loading history...
102
103
        self::initialiseCookie();
0 ignored issues
show
Deprecated Code introduced by
The function Symphony::initialiseCookie() has been deprecated: Prior to Symphony 2.3.2, the constant `__SYM_COOKIE_PREFIX_` had a typo where it was missing the second underscore. Symphony will support both constants, `__SYM_COOKIE_PREFIX_` and `__SYM_COOKIE_PREFIX__` until Symphony 3.0 ( Ignorable by Annotation )

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

103
        /** @scrutinizer ignore-deprecated */ self::initialiseCookie();

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...
104
105
        // If the user is not a logged in Author, turn off the verbose error messages.
106
        if (!self::isLoggedIn() && is_null(self::$Author)) {
107
            GenericExceptionHandler::$enabled = false;
108
        }
109
110
        // Engine is ready.
111
        self::$Profiler->sample('Engine Initialisation');
112
    }
113
114
    /**
115
     * Setter for the Symphony Log and Error Handling system
116
     *
117
     * @since Symphony 2.6.0
118
     */
119
    public static function initialiseErrorHandler()
120
    {
121
        // Initialise logging
122
        self::initialiseLog();
123
        GenericExceptionHandler::initialise(self::Log());
124
        GenericErrorHandler::initialise(self::Log());
125
    }
126
127
    /**
128
     * Accessor for the Symphony instance, whether it be Frontend
129
     * or Administration
130
     *
131
     * @since Symphony 2.2
132
     * @throws Exception
133
     * @return Symphony
134
     */
135
    public static function Engine()
136
    {
137
        if (class_exists('Administration', false)) {
138
            return Administration::instance();
139
        } elseif (class_exists('Frontend', false)) {
140
            return Frontend::instance();
141
        } else {
142
            throw new Exception(__('No suitable engine object found'));
143
        }
144
    }
145
146
    /**
147
     * Setter for `$Configuration`. This function initialise the configuration
148
     * object and populate its properties based on the given `$array`. Since
149
     * Symphony 2.6.5, it will also set Symphony's date constants.
150
     *
151
     * @since Symphony 2.3
152
     * @param array $data
153
     *  An array of settings to be stored into the Configuration object
154
     */
155
    public static function initialiseConfiguration(array $data = array())
156
    {
157
        if (empty($data)) {
158
            // Includes the existing CONFIG file and initialises the Configuration
159
            // by setting the values with the setArray function.
160
            include CONFIG;
0 ignored issues
show
Bug introduced by
The constant CONFIG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
161
162
            $data = $settings;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $settings seems to be never defined.
Loading history...
163
        }
164
165
        self::$Configuration = new Configuration(true);
166
        self::$Configuration->setArray($data);
167
168
        // Set date format throughout the system
169
        $region = self::Configuration()->get('region');
170
        define_safe('__SYM_DATE_FORMAT__', $region['date_format']);
171
        define_safe('__SYM_TIME_FORMAT__', $region['time_format']);
172
        define_safe('__SYM_DATETIME_FORMAT__', __SYM_DATE_FORMAT__ . $region['datetime_separator'] . __SYM_TIME_FORMAT__);
0 ignored issues
show
Bug introduced by
The constant __SYM_DATE_FORMAT__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant __SYM_TIME_FORMAT__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
173
        DateTimeObj::setSettings($region);
0 ignored issues
show
Bug introduced by
It seems like $region can also be of type integer and string and boolean and double; however, parameter $settings of DateTimeObj::setSettings() does only seem to accept array, 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

173
        DateTimeObj::setSettings(/** @scrutinizer ignore-type */ $region);
Loading history...
174
    }
175
176
    /**
177
     * Accessor for the current `Configuration` instance. This contains
178
     * representation of the the Symphony config file.
179
     *
180
     * @return Configuration
181
     */
182
    public static function Configuration()
183
    {
184
        return self::$Configuration;
185
    }
186
187
    /**
188
     * Is XSRF enabled for this Symphony install?
189
     *
190
     * @since Symphony 2.4
191
     * @return boolean
192
     */
193
    public static function isXSRFEnabled()
194
    {
195
        return self::Configuration()->get('enable_xsrf', 'symphony') === 'yes';
196
    }
197
198
    /**
199
     * Accessor for the current `Profiler` instance.
200
     *
201
     * @since Symphony 2.3
202
     * @return Profiler
203
     */
204
    public static function Profiler()
205
    {
206
        return self::$Profiler;
207
    }
208
209
    /**
210
     * Setter for `$Log`. This function uses the configuration
211
     * settings in the 'log' group in the Configuration to create an instance. Date
212
     * formatting options are also retrieved from the configuration.
213
     *
214
     * @param string $filename (optional)
215
     *  The file to write the log to, if omitted this will default to `ACTIVITY_LOG`
216
     * @throws Exception
217
     * @return bool|void
218
     */
219
    public static function initialiseLog($filename = null)
220
    {
221
        if (self::$Log instanceof Log && self::$Log->getLogPath() == $filename) {
222
            return true;
223
        }
224
225
        if (is_null($filename)) {
226
            $filename = ACTIVITY_LOG;
0 ignored issues
show
Bug introduced by
The constant ACTIVITY_LOG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
227
        }
228
229
        self::$Log = new Log($filename);
230
        self::$Log->setArchive((self::Configuration()->get('archive', 'log') == '1' ? true : false));
231
        self::$Log->setMaxSize(self::Configuration()->get('maxsize', 'log'));
232
        self::$Log->setFilter(self::Configuration()->get('filter', 'log'));
233
        self::$Log->setDateTimeFormat(__SYM_DATETIME_FORMAT__);
0 ignored issues
show
Bug introduced by
The constant __SYM_DATETIME_FORMAT__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
234
235
        if (self::$Log->open(Log::APPEND, self::Configuration()->get('write_mode', 'file')) == '1') {
236
            self::$Log->initialise('Symphony Log');
237
        }
238
    }
239
240
    /**
241
     * Accessor for the current `Log` instance
242
     *
243
     * @since Symphony 2.3
244
     * @return Log
245
     */
246
    public static function Log()
247
    {
248
        return self::$Log;
249
    }
250
251
    /**
252
     * Setter for `$Cookie`. This will use PHP's parse_url
253
     * function on the current URL to set a cookie using the cookie_prefix
254
     * defined in the Symphony configuration. The cookie will last two
255
     * weeks.
256
     *
257
     * This function also defines two constants, `__SYM_COOKIE_PATH__`
258
     * and `__SYM_COOKIE_PREFIX__`.
259
     *
260
     * @deprecated Prior to Symphony 2.3.2, the constant `__SYM_COOKIE_PREFIX_`
261
     *  had a typo where it was missing the second underscore. Symphony will
262
     *  support both constants, `__SYM_COOKIE_PREFIX_` and `__SYM_COOKIE_PREFIX__`
263
     *  until Symphony 3.0
264
     */
265
    public static function initialiseCookie()
266
    {
267
        define_safe('__SYM_COOKIE_PATH__', DIRROOT === '' ? '/' : DIRROOT);
0 ignored issues
show
Bug introduced by
The constant DIRROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
268
        define_safe('__SYM_COOKIE_PREFIX_', self::Configuration()->get('cookie_prefix', 'symphony'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->g...ie_prefix', 'symphony') can also be of type array; however, parameter $value of define_safe() does only seem to accept integer|string|boolean, 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

268
        define_safe('__SYM_COOKIE_PREFIX_', /** @scrutinizer ignore-type */ self::Configuration()->get('cookie_prefix', 'symphony'));
Loading history...
269
        define_safe('__SYM_COOKIE_PREFIX__', self::Configuration()->get('cookie_prefix', 'symphony'));
270
271
        self::$Cookie = new Cookie(__SYM_COOKIE_PREFIX__, TWO_WEEKS, __SYM_COOKIE_PATH__);
0 ignored issues
show
Bug introduced by
The constant __SYM_COOKIE_PATH__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant __SYM_COOKIE_PREFIX__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant TWO_WEEKS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
272
    }
273
274
    /**
275
     * Accessor for the current `$Cookie` instance.
276
     *
277
     * @since Symphony 2.5.0
278
     * @return Cookie
279
     */
280
    public static function Cookie()
281
    {
282
        return self::$Cookie;
283
    }
284
285
    /**
286
     * Setter for `$ExtensionManager` using the current
287
     * Symphony instance as the parent. If for some reason this fails,
288
     * a Symphony Error page will be thrown
289
     * @param Boolean $force (optional)
290
     *  When set to true, this function will always create a new
291
     *  instance of ExtensionManager, replacing self::$ExtensionManager.
292
     */
293
    public static function initialiseExtensionManager($force=false)
294
    {
295
        if (!$force && self::$ExtensionManager instanceof ExtensionManager) {
296
            return true;
297
        }
298
299
        self::$ExtensionManager = new ExtensionManager;
300
301
        if (!(self::$ExtensionManager instanceof ExtensionManager)) {
0 ignored issues
show
introduced by
self::ExtensionManager is always a sub-type of ExtensionManager. If self::ExtensionManager can have other possible types, add them to symphony/lib/core/class.symphony.php:40.
Loading history...
302
            self::throwCustomError(__('Error creating Symphony extension manager.'));
303
        }
304
    }
305
306
    /**
307
     * Accessor for the current `$ExtensionManager` instance.
308
     *
309
     * @since Symphony 2.2
310
     * @return ExtensionManager
311
     */
312
    public static function ExtensionManager()
313
    {
314
        return self::$ExtensionManager;
315
    }
316
317
    /**
318
     * Setter for `$Database`, accepts a Database object. If `$database`
319
     * is omitted, this function will set `$Database` to be of the `MySQL`
320
     * class.
321
     *
322
     * @since Symphony 2.3
323
     * @param StdClass $database (optional)
324
     *  The class to handle all Database operations, if omitted this function
325
     *  will set `self::$Database` to be an instance of the `MySQL` class.
326
     * @return boolean
327
     *  This function will always return true
328
     */
329
    public static function setDatabase(StdClass $database = null)
330
    {
331
        if (self::Database()) {
332
            return true;
333
        }
334
335
        self::$Database = !is_null($database) ? $database : new MySQL;
0 ignored issues
show
Documentation Bug introduced by
It seems like ! is_null($database) ? $database : new MySQL() can also be of type StdClass. However, the property $Database is declared as type MySQL. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
336
337
        return true;
338
    }
339
340
    /**
341
     * Accessor for the current `$Database` instance.
342
     *
343
     * @return MySQL
344
     */
345
    public static function Database()
346
    {
347
        return self::$Database;
348
    }
349
350
    /**
351
     * This will initialise the Database class and attempt to create a connection
352
     * using the connection details provided in the Symphony configuration. If any
353
     * errors occur whilst doing so, a Symphony Error Page is displayed.
354
     *
355
     * @throws SymphonyErrorPage
356
     * @return boolean
357
     *  This function will return true if the `$Database` was
358
     *  initialised successfully.
359
     */
360
    public static function initialiseDatabase()
361
    {
362
        self::setDatabase();
363
        $details = self::Configuration()->get('database');
364
365
        try {
366
            if (!self::Database()->connect($details['host'], $details['user'], $details['password'], $details['port'], $details['db'])) {
367
                return false;
368
            }
369
370
            if (!self::Database()->isConnected()) {
371
                return false;
372
            }
373
374
            self::Database()->setPrefix($details['tbl_prefix']);
375
            self::Database()->setCharacterEncoding();
376
            self::Database()->setCharacterSet();
377
            self::Database()->setTimeZone(self::Configuration()->get('timezone', 'region'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->get('timezone', 'region') can also be of type array; however, parameter $timezone of MySQL::setTimeZone() does only seem to accept string, 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

377
            self::Database()->setTimeZone(/** @scrutinizer ignore-type */ self::Configuration()->get('timezone', 'region'));
Loading history...
378
379
            if (isset($details['query_caching'])) {
380
                if ($details['query_caching'] == 'off') {
381
                    self::Database()->disableCaching();
382
                } elseif ($details['query_caching'] == 'on') {
383
                    self::Database()->enableCaching();
384
                }
385
            }
386
387
            if (isset($details['query_logging'])) {
388
                if ($details['query_logging'] == 'off') {
389
                    self::Database()->disableLogging();
390
                } elseif ($details['query_logging'] == 'on') {
391
                    self::Database()->enableLogging();
392
                }
393
            }
394
        } catch (DatabaseException $e) {
395
            self::throwCustomError(
396
                $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(),
397
                __('Symphony Database Error'),
398
                Page::HTTP_STATUS_ERROR,
399
                'database',
400
                array(
401
                    'error' => $e,
402
                    'message' => __('There was a problem whilst attempting to establish a database connection. Please check all connection information is correct.') . ' ' . __('The following error was returned:')
403
                )
404
            );
405
        }
406
407
        return true;
408
    }
409
410
    /**
411
     * Accessor for the current `$Author` instance.
412
     *
413
     * @since Symphony 2.5.0
414
     * @return Author
415
     */
416
    public static function Author()
417
    {
418
        return self::$Author;
419
    }
420
421
    /**
422
     * Attempts to log an Author in given a username and password.
423
     * If the password is not hashed, it will be hashed using the sha1
424
     * algorithm. The username and password will be sanitized before
425
     * being used to query the Database. If an Author is found, they
426
     * will be logged in and the sanitized username and password (also hashed)
427
     * will be saved as values in the `$Cookie`.
428
     *
429
     * @see toolkit.Cryptography#hash()
430
     * @throws DatabaseException
431
     * @param string $username
432
     *  The Author's username. This will be sanitized before use.
433
     * @param string $password
434
     *  The Author's password. This will be sanitized and then hashed before use
435
     * @param boolean $isHash
436
     *  If the password provided is already hashed, setting this parameter to
437
     *  true will stop it becoming rehashed. By default it is false.
438
     * @return boolean
439
     *  true if the Author was logged in, false otherwise
440
     */
441
    public static function login($username, $password, $isHash = false)
442
    {
443
        $username = trim(self::Database()->cleanValue($username));
444
        $password = trim(self::Database()->cleanValue($password));
445
446
        if (strlen($username) > 0 && strlen($password) > 0) {
447
            $author = AuthorManager::fetch('id', 'ASC', 1, null, sprintf(
448
                "`username` = '%s'",
449
                $username
450
            ));
451
452
            if (!empty($author) && Cryptography::compare($password, current($author)->get('password'), $isHash)) {
453
                self::$Author = current($author);
454
455
                // Only migrate hashes if there is no update available as the update might change the tbl_authors table.
456
                if (self::isUpgradeAvailable() === false && Cryptography::requiresMigration(self::$Author->get('password'))) {
457
                    self::$Author->set('password', Cryptography::hash($password));
458
459
                    self::Database()->update(array('password' => self::$Author->get('password')), 'tbl_authors', sprintf(
460
                        " `id` = %d", self::$Author->get('id')
461
                    ));
462
                }
463
464
                self::$Cookie->set('username', $username);
465
                self::$Cookie->set('pass', self::$Author->get('password'));
466
467
                self::Database()->update(array(
468
                    'last_seen' => DateTimeObj::get('Y-m-d H:i:s')),
469
                    'tbl_authors',
470
                    sprintf(" `id` = %d", self::$Author->get('id'))
471
                );
472
473
                // Only set custom author language in the backend
474
                if (class_exists('Administration', false)) {
475
                    Lang::set(self::$Author->get('language'));
476
                }
477
478
                return true;
479
            }
480
        }
481
482
        return false;
483
    }
484
485
    /**
486
     * Symphony allows Authors to login via the use of tokens instead of
487
     * a username and password. A token is derived from concatenating the
488
     * Author's username and password and applying the sha1 hash to
489
     * it, from this, a portion of the hash is used as the token. This is a useful
490
     * feature often used when setting up other Authors accounts or if an
491
     * Author forgets their password.
492
     *
493
     * @param string $token
494
     *  The Author token, which is a portion of the hashed string concatenation
495
     *  of the Author's username and password
496
     * @throws DatabaseException
497
     * @return boolean
498
     *  true if the Author is logged in, false otherwise
499
     */
500
    public static function loginFromToken($token)
501
    {
502
        $token = self::Database()->cleanValue($token);
503
504
        if (strlen(trim($token)) == 0) {
505
            return false;
506
        }
507
508
        if (strlen($token) == 6 || strlen($token) == 16) {
509
            $row = self::Database()->fetchRow(0, sprintf(
510
                "SELECT `a`.`id`, `a`.`username`, `a`.`password`
511
                FROM `tbl_authors` AS `a`, `tbl_forgotpass` AS `f`
512
                WHERE `a`.`id` = `f`.`author_id`
513
                AND `f`.`expiry` > '%s'
514
                AND `f`.`token` = '%s'
515
                LIMIT 1",
516
                DateTimeObj::getGMT('c'),
517
                $token
518
            ));
519
520
            self::Database()->delete('tbl_forgotpass', sprintf(" `token` = '%s' ", $token));
521
        } else {
522
            $row = self::Database()->fetchRow(0, sprintf(
523
                "SELECT `id`, `username`, `password`
524
                FROM `tbl_authors`
525
                WHERE SUBSTR(%s(CONCAT(`username`, `password`)), 1, 8) = '%s'
526
                AND `auth_token_active` = 'yes'
527
                LIMIT 1",
528
                'SHA1',
529
                $token
530
            ));
531
        }
532
533
        if ($row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
534
            self::$Author = AuthorManager::fetchByID($row['id']);
0 ignored issues
show
Documentation Bug introduced by
It seems like AuthorManager::fetchByID($row['id']) can also be of type array or array. However, the property $Author is declared as type Author. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
535
            self::$Cookie->set('username', $row['username']);
536
            self::$Cookie->set('pass', $row['password']);
537
            self::Database()->update(array('last_seen' => DateTimeObj::getGMT('Y-m-d H:i:s')), 'tbl_authors', sprintf("
538
                `id` = %d", $row['id']
539
            ));
540
541
            return true;
542
        }
543
544
        return false;
545
    }
546
547
    /**
548
     * This function will destroy the currently logged in `$Author`
549
     * session, essentially logging them out.
550
     *
551
     * @see core.Cookie#expire()
552
     */
553
    public static function logout()
554
    {
555
        self::$Cookie->expire();
556
    }
557
558
    /**
559
     * This function determines whether an there is a currently logged in
560
     * Author for Symphony by using the `$Cookie`'s username
561
     * and password. If the instance is not found, they will be logged
562
     * in using the cookied credentials.
563
     *
564
     * @see login()
565
     * @return boolean
566
     */
567
    public static function isLoggedIn()
568
    {
569
        // Check to see if Symphony exists, or if we already have an Author instance.
570
        if (is_null(self::$_instance) || self::$Author) {
571
            return true;
572
        }
573
574
        // No author instance found, attempt to log in with the cookied credentials
575
        return self::login(self::$Cookie->get('username'), self::$Cookie->get('pass'), true);
576
    }
577
578
    /**
579
     * Returns the most recent version found in the `/install/migrations` folder.
580
     * Returns a version string to be used in `version_compare()` if an updater
581
     * has been found. Returns `FALSE` otherwise.
582
     *
583
     * @since Symphony 2.3.1
584
     * @return string|boolean
585
     */
586
    public static function getMigrationVersion()
587
    {
588
        if (self::isInstallerAvailable()) {
589
            $migrations = scandir(DOCROOT . '/install/migrations');
0 ignored issues
show
Bug introduced by
The constant DOCROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
590
            $migration_file = end($migrations);
591
            $migration_class = 'migration_' . str_replace('.', '', substr($migration_file, 0, -4));
592
            return call_user_func(array($migration_class, 'getVersion'));
593
        }
594
595
        return false;
596
    }
597
598
    /**
599
     * Checks if an update is available and applicable for the current installation.
600
     *
601
     * @since Symphony 2.3.1
602
     * @return boolean
603
     */
604
    public static function isUpgradeAvailable()
605
    {
606
        if (self::isInstallerAvailable()) {
607
            $migration_version = self::getMigrationVersion();
608
            $current_version = Symphony::Configuration()->get('version', 'symphony');
609
610
            return version_compare($current_version, $migration_version, '<');
0 ignored issues
show
Bug introduced by
It seems like $current_version can also be of type array; however, parameter $version1 of version_compare() does only seem to accept string, 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

610
            return version_compare(/** @scrutinizer ignore-type */ $current_version, $migration_version, '<');
Loading history...
611
        }
612
613
        return false;
614
    }
615
616
    /**
617
     * Checks if the installer/upgrader is available.
618
     *
619
     * @since Symphony 2.3.1
620
     * @return boolean
621
     */
622
    public static function isInstallerAvailable()
623
    {
624
        return file_exists(DOCROOT . '/install/index.php');
0 ignored issues
show
Bug introduced by
The constant DOCROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
625
    }
626
627
    /**
628
     * A wrapper for throwing a new Symphony Error page.
629
     *
630
     * This methods sets the `GenericExceptionHandler::$enabled` value to `true`.
631
     *
632
     * @see core.SymphonyErrorPage
633
     * @param string|XMLElement $message
634
     *  A description for this error, which can be provided as a string
635
     *  or as an XMLElement.
636
     * @param string $heading
637
     *  A heading for the error page
638
     * @param integer $status
639
     *  Properly sets the HTTP status code for the response. Defaults to
640
     *  `Page::HTTP_STATUS_ERROR`. Use `Page::HTTP_STATUS_XXX` to set this value.
641
     * @param string $template
642
     *  A string for the error page template to use, defaults to 'generic'. This
643
     *  can be the name of any template file in the `TEMPLATES` directory.
644
     *  A template using the naming convention of `tpl.*.php`.
645
     * @param array $additional
646
     *  Allows custom information to be passed to the Symphony Error Page
647
     *  that the template may want to expose, such as custom Headers etc.
648
     * @throws SymphonyErrorPage
649
     */
650
    public static function throwCustomError($message, $heading = 'Symphony Fatal Error', $status = Page::HTTP_STATUS_ERROR, $template = 'generic', array $additional = array())
651
    {
652
        GenericExceptionHandler::$enabled = true;
653
        throw new SymphonyErrorPage($message, $heading, $template, $additional, $status);
654
    }
655
656
    /**
657
     * Setter accepts a previous Throwable. Useful for determining the context
658
     * of a current Throwable (ie. detecting recursion).
659
     *
660
     * @since Symphony 2.3.2
661
     *
662
     * @since Symphony 2.7.0
663
     *  This function works with both Exception and Throwable
664
     *  Supporting both PHP 5.6 and 7 forces use to not qualify the $e parameter
665
     *
666
     * @param Throwable $ex
667
     */
668
    public static function setException($ex)
669
    {
670
        self::$exception = $ex;
0 ignored issues
show
Documentation Bug introduced by
$ex is of type Throwable, but the property $exception was declared to be of type Exception. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
671
    }
672
673
    /**
674
     * Accessor for `self::$exception`.
675
     *
676
     * @since Symphony 2.3.2
677
     * @return Throwable|null
678
     */
679
    public static function getException()
680
    {
681
        return self::$exception;
682
    }
683
684
    /**
685
     * Returns the page namespace based on the current URL.
686
     * A few examples:
687
     *
688
     * /login
689
     * /publish
690
     * /blueprints/datasources
691
     * [...]
692
     * /extension/$extension_name/$page_name
693
     *
694
     * This method is especially useful in couple with the translation function.
695
     *
696
     * @see toolkit#__()
697
     * @return string
698
     *  The page namespace, without any action string (e.g. "new", "saved") or
699
     *  any value that depends upon the single setup (e.g. the section handle in
700
     *  /publish/$handle)
701
     */
702
    public static function getPageNamespace()
703
    {
704
        if (self::$namespace !== false) {
0 ignored issues
show
introduced by
The condition self::namespace !== false is always true. If self::namespace !== false can have other possible types, add them to symphony/lib/core/class.symphony.php:54
Loading history...
705
            return self::$namespace;
706
        }
707
708
        $page = getCurrentPage();
709
710
        if (!is_null($page)) {
711
            $page = trim($page, '/');
712
        }
713
714
        if (substr($page, 0, 7) == 'publish') {
715
            self::$namespace = '/publish';
716
        } elseif (empty($page) && isset($_REQUEST['mode'])) {
717
            self::$namespace = '/login';
718
        } elseif (empty($page)) {
719
            self::$namespace = null;
720
        } else {
721
            $bits = explode('/', $page);
722
723
            if ($bits[0] == 'extension') {
724
                self::$namespace = sprintf('/%s/%s/%s', $bits[0], $bits[1], $bits[2]);
725
            } else {
726
                self::$namespace =  sprintf('/%s/%s', $bits[0], isset($bits[1]) ? $bits[1] : '');
727
            }
728
        }
729
730
        return self::$namespace;
731
    }
732
}
733
734
/**
735
 * The `SymphonyErrorPageHandler` extends the `GenericExceptionHandler`
736
 * to allow the template for the exception to be provided from the `TEMPLATES`
737
 * directory
738
 */
739
class SymphonyErrorPageHandler extends GenericExceptionHandler
740
{
741
    /**
742
     * The render function will take a `SymphonyErrorPage` exception and
743
     * output a HTML page. This function first checks to see if their is a custom
744
     * template for this exception otherwise it reverts to using the default
745
     * `usererror.generic.php`
746
     *
747
     * @param Throwable $e
748
     *  The Throwable object
749
     * @return string
750
     *  An HTML string
751
     */
752
    public static function render($e)
753
    {
754
        // Validate the type, resolve to a 404 if not valid
755
        if (!static::isValidThrowable($e)) {
756
            $e = new FrontendPageNotFoundException();
757
        }
758
759
        if ($e->getTemplate() === false) {
0 ignored issues
show
Bug introduced by
The method getTemplate() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as SymphonyErrorPage. ( Ignorable by Annotation )

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

759
        if ($e->/** @scrutinizer ignore-call */ getTemplate() === false) {
Loading history...
Bug introduced by
The method getTemplate() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

759
        if ($e->/** @scrutinizer ignore-call */ getTemplate() === false) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
760
            Page::renderStatusCode($e->getHttpStatusCode());
0 ignored issues
show
Bug introduced by
The method getHttpStatusCode() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

760
            Page::renderStatusCode($e->/** @scrutinizer ignore-call */ getHttpStatusCode());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getHttpStatusCode() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as SymphonyErrorPage. ( Ignorable by Annotation )

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

760
            Page::renderStatusCode($e->/** @scrutinizer ignore-call */ getHttpStatusCode());
Loading history...
761
762
            if (isset($e->getAdditional()->header)) {
0 ignored issues
show
Bug introduced by
The method getAdditional() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

762
            if (isset($e->/** @scrutinizer ignore-call */ getAdditional()->header)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAdditional() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as SymphonyErrorPage. ( Ignorable by Annotation )

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

762
            if (isset($e->/** @scrutinizer ignore-call */ getAdditional()->header)) {
Loading history...
763
                header($e->getAdditional()->header);
764
            }
765
766
            echo '<h1>Symphony Fatal Error</h1><p>'.$e->getMessage().'</p>';
767
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
768
        }
769
770
        include $e->getTemplate();
771
    }
772
}
773
774
/**
775
 * `SymphonyErrorPage` extends the default `Exception` class. All
776
 * of these exceptions will halt execution immediately and return the
777
 * exception as a HTML page. By default the HTML template is `usererror.generic.php`
778
 * from the `TEMPLATES` directory.
779
 */
780
781
class SymphonyErrorPage extends Exception
782
{
783
    /**
784
     * A heading for the error page, this will be prepended to
785
     * "Symphony Fatal Error".
786
     * @return string
787
     */
788
    private $_heading;
789
790
    /**
791
     * A string for the error page template to use, defaults to 'generic'. This
792
     * can be the name of any template file in the `TEMPLATES` directory.
793
     * A template using the naming convention of `usererror.*.php`.
794
     * @var string
795
     */
796
    private $_template = 'generic';
797
798
    /**
799
     * If the message as provided as an `XMLElement`, it will be saved to
800
     * this parameter
801
     * @var XMLElement
802
     */
803
    private $_messageObject = null;
804
805
    /**
806
     * An object of an additional information for this error page. Note that
807
     * this is provided as an array and then typecast to an object
808
     * @var StdClass
809
     */
810
    private $_additional = null;
811
812
    /**
813
     * A simple container for the response status code.
814
     * Full value is setted usign `$Page->setHttpStatus()`
815
     * in the template.
816
     */
817
    private $_status = Page::HTTP_STATUS_ERROR;
818
819
    /**
820
     * Constructor for SymphonyErrorPage sets it's class variables
821
     *
822
     * @param string|XMLElement $message
823
     *  A description for this error, which can be provided as a string
824
     *  or as an XMLElement.
825
     * @param string $heading
826
     *  A heading for the error page, by default this is "Symphony Fatal Error"
827
     * @param string $template
828
     *  A string for the error page template to use, defaults to 'generic'. This
829
     *  can be the name of any template file in the `TEMPLATES` directory.
830
     *  A template using the naming convention of `tpl.*.php`.
831
     * @param array $additional
832
     *  Allows custom information to be passed to the Symphony Error Page
833
     *  that the template may want to expose, such as custom Headers etc.
834
     * @param integer $status
835
     *  Properly sets the HTTP status code for the response. Defaults to
836
     *  `Page::HTTP_STATUS_ERROR`
837
     */
838
    public function __construct($message, $heading = 'Symphony Fatal Error', $template = 'generic', array $additional = array(), $status = Page::HTTP_STATUS_ERROR)
839
    {
840
        if ($message instanceof XMLElement) {
841
            $this->_messageObject = $message;
842
            $message = $this->_messageObject->generate();
843
        }
844
845
        parent::__construct($message);
846
847
        $this->_heading = $heading;
848
        $this->_template = $template;
849
        $this->_additional = (object)$additional;
850
        $this->_status = $status;
851
    }
852
853
    /**
854
     * Accessor for the `$_heading` of the error page
855
     *
856
     * @return string
857
     */
858
    public function getHeading()
859
    {
860
        return $this->_heading;
861
    }
862
863
    /**
864
     * Accessor for `$_messageObject`
865
     *
866
     * @return XMLElement
867
     */
868
    public function getMessageObject()
869
    {
870
        return $this->_messageObject;
871
    }
872
873
    /**
874
     * Accessor for `$_additional`
875
     *
876
     * @return StdClass
877
     */
878
    public function getAdditional()
879
    {
880
        return $this->_additional;
881
    }
882
883
    /**
884
     * Accessor for `$_status`
885
     *
886
     * @since Symphony 2.3.2
887
     * @return integer
888
     */
889
    public function getHttpStatusCode()
890
    {
891
        return $this->_status;
892
    }
893
894
    /**
895
     * Returns the path to the current template by looking at the
896
     * `WORKSPACE/template/` directory, then at the `TEMPLATES`
897
     * directory for the convention `usererror.*.php`. If the template
898
     * is not found, `false` is returned
899
     *
900
     * @since Symphony 2.3
901
     * @return string|false
902
     *  String, which is the path to the template if the template is found,
903
     *  false otherwise
904
     */
905
    public function getTemplate()
906
    {
907
        $format = '%s/usererror.%s.php';
908
909
        if (file_exists($template = sprintf($format, WORKSPACE . '/template', $this->_template))) {
0 ignored issues
show
Bug introduced by
The constant WORKSPACE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
910
            return $template;
911
        } elseif (file_exists($template = sprintf($format, TEMPLATE, $this->_template))) {
0 ignored issues
show
Bug introduced by
The constant TEMPLATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
912
            return $template;
913
        } else {
914
            return false;
915
        }
916
    }
917
918
    /**
919
     * A simple getter to the template name in order to be able
920
     * to identify which type of exception this is.
921
     *
922
     * @since Symphony 2.3.2
923
     * @return string
924
     */
925
    public function getTemplateName()
926
    {
927
        return $this->_template;
928
    }
929
}
930
931
/**
932
 * The `DatabaseExceptionHandler` provides a render function to provide
933
 * customised output for database exceptions. It displays the exception
934
 * message as provided by the Database.
935
 */
936
class DatabaseExceptionHandler extends GenericExceptionHandler
937
{
938
    /**
939
     * The render function will take a `DatabaseException` and output a
940
     * HTML page.
941
     *
942
     * @param Throwable $e
943
     *  The Throwable object
944
     * @return string
945
     *  An HTML string
946
     */
947
    public static function render($e)
948
    {
949
        // Validate the type, resolve to a 404 if not valid
950
        if (!static::isValidThrowable($e)) {
951
            $e = new FrontendPageNotFoundException();
952
        }
953
954
        $trace = $queries = null;
955
956
        foreach ($e->getTrace() as $t) {
957
            $trace .= sprintf(
958
                '<li><code><em>[%s:%d]</em></code></li><li><code>&#160;&#160;&#160;&#160;%s%s%s();</code></li>',
959
                $t['file'],
960
                $t['line'],
961
                (isset($t['class']) ? $t['class'] : null),
962
                (isset($t['type']) ? $t['type'] : null),
963
                $t['function']
964
            );
965
        }
966
967
        if (is_object(Symphony::Database())) {
968
            $debug = Symphony::Database()->debug();
969
970
            if (!empty($debug)) {
971
                foreach ($debug as $query) {
972
                    $queries .= sprintf(
973
                        '<li><em>[%01.4f]</em><code> %s;</code> </li>',
974
                        (isset($query['execution_time']) ? $query['execution_time'] : null),
975
                        htmlspecialchars($query['query'])
976
                    );
977
                }
978
            }
979
        }
980
981
        $html = sprintf(
982
            file_get_contents(self::getTemplate('fatalerror.database')),
0 ignored issues
show
Bug introduced by
It seems like self::getTemplate('fatalerror.database') can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, 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

982
            file_get_contents(/** @scrutinizer ignore-type */ self::getTemplate('fatalerror.database')),
Loading history...
983
            $e->getDatabaseErrorMessage(),
0 ignored issues
show
Bug introduced by
The method getDatabaseErrorMessage() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

983
            $e->/** @scrutinizer ignore-call */ 
984
                getDatabaseErrorMessage(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getDatabaseErrorMessage() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as DatabaseException. ( Ignorable by Annotation )

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

983
            $e->/** @scrutinizer ignore-call */ 
984
                getDatabaseErrorMessage(),
Loading history...
984
            $e->getQuery(),
0 ignored issues
show
Bug introduced by
The method getQuery() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

984
            $e->/** @scrutinizer ignore-call */ 
985
                getQuery(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getQuery() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as DatabaseException. ( Ignorable by Annotation )

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

984
            $e->/** @scrutinizer ignore-call */ 
985
                getQuery(),
Loading history...
985
            $trace,
986
            $queries
987
        );
988
989
        $html = str_replace('{ASSETS_URL}', ASSETS_URL, $html);
0 ignored issues
show
Bug introduced by
The constant ASSETS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
990
        $html = str_replace('{SYMPHONY_URL}', SYMPHONY_URL, $html);
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
991
        $html = str_replace('{URL}', URL, $html);
0 ignored issues
show
Bug introduced by
The constant URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
992
993
        return $html;
994
    }
995
}
996