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
Pull Request — 2.6.x (#2509)
by Nicolas
04:59
created

Symphony::__construct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 2
b 0
f 0
nc 4
nop 0
dl 0
loc 23
rs 9.0856
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` and `$_POST` 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
    protected function __construct()
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
86
    {
87
        self::$Profiler = Profiler::instance();
88
89
        if (get_magic_quotes_gpc()) {
90
            General::cleanArray($_SERVER);
91
            General::cleanArray($_COOKIE);
92
            General::cleanArray($_GET);
93
            General::cleanArray($_POST);
94
        }
95
96
        // Initialize language management
97
        Lang::initialize();
98
        Lang::set(self::$Configuration->get('lang', 'symphony'));
0 ignored issues
show
Bug introduced by
It seems like self::$Configuration->get('lang', 'symphony') targeting Configuration::get() can also be of type array; however, Lang::set() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
99
100
        self::initialiseCookie();
0 ignored issues
show
Deprecated Code introduced by
The method Symphony::initialiseCookie() has been deprecated with message: 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

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

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

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

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
165
        define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->g...time_format', 'region') targeting Configuration::get() can also be of type array; however, define_safe() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
166
        define_safe('__SYM_DATETIME_FORMAT__', __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__);
167
        DateTimeObj::setSettings(self::Configuration()->get('region'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->get('region') targeting Configuration::get() can also be of type string; however, DateTimeObj::setSettings() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
168
    }
169
170
    /**
171
     * Accessor for the current `Configuration` instance. This contains
172
     * representation of the the Symphony config file.
173
     *
174
     * @return Configuration
175
     */
176
    public static function Configuration()
177
    {
178
        return self::$Configuration;
179
    }
180
181
    /**
182
     * Is XSRF enabled for this Symphony install?
183
     *
184
     * @since Symphony 2.4
185
     * @return boolean
186
     */
187
    public static function isXSRFEnabled()
188
    {
189
        return self::Configuration()->get('enable_xsrf', 'symphony') === 'yes';
190
    }
191
192
    /**
193
     * Accessor for the current `Profiler` instance.
194
     *
195
     * @since Symphony 2.3
196
     * @return Profiler
197
     */
198
    public static function Profiler()
199
    {
200
        return self::$Profiler;
201
    }
202
203
    /**
204
     * Setter for `$Log`. This function uses the configuration
205
     * settings in the 'log' group in the Configuration to create an instance. Date
206
     * formatting options are also retrieved from the configuration.
207
     *
208
     * @param string $filename (optional)
209
     *  The file to write the log to, if omitted this will default to `ACTIVITY_LOG`
210
     * @throws Exception
211
     * @return bool|void
212
     */
213
    public static function initialiseLog($filename = null)
214
    {
215
        if (self::$Log instanceof Log && self::$Log->getLogPath() == $filename) {
216
            return true;
217
        }
218
219
        if (is_null($filename)) {
220
            $filename = ACTIVITY_LOG;
221
        }
222
223
        self::$Log = new Log($filename);
224
        self::$Log->setArchive((self::Configuration()->get('archive', 'log') == '1' ? true : false));
225
        self::$Log->setMaxSize(intval(self::Configuration()->get('maxsize', 'log')));
226
        self::$Log->setDateTimeFormat(self::Configuration()->get('date_format', 'region') . ' ' . self::Configuration()->get('time_format', 'region'));
227
228
        if (self::$Log->open(Log::APPEND, self::Configuration()->get('write_mode', 'file')) == '1') {
0 ignored issues
show
Documentation introduced by
self::Configuration()->get('write_mode', 'file') is of type array|string, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
229
            self::$Log->initialise('Symphony Log');
230
        }
231
    }
232
233
    /**
234
     * Accessor for the current `Log` instance
235
     *
236
     * @since Symphony 2.3
237
     * @return Log
238
     */
239
    public static function Log()
240
    {
241
        return self::$Log;
242
    }
243
244
    /**
245
     * Setter for `$Cookie`. This will use PHP's parse_url
246
     * function on the current URL to set a cookie using the cookie_prefix
247
     * defined in the Symphony configuration. The cookie will last two
248
     * weeks.
249
     *
250
     * This function also defines two constants, `__SYM_COOKIE_PATH__`
251
     * and `__SYM_COOKIE_PREFIX__`.
252
     *
253
     * @deprecated Prior to Symphony 2.3.2, the constant `__SYM_COOKIE_PREFIX_`
254
     *  had a typo where it was missing the second underscore. Symphony will
255
     *  support both constants, `__SYM_COOKIE_PREFIX_` and `__SYM_COOKIE_PREFIX__`
256
     *  until Symphony 3.0
257
     */
258
    public static function initialiseCookie()
259
    {
260
        $cookie_path = @parse_url(URL, PHP_URL_PATH);
261
        $cookie_path = '/' . trim($cookie_path, '/');
262
263
        define_safe('__SYM_COOKIE_PATH__', $cookie_path);
264
        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') targeting Configuration::get() can also be of type array; however, define_safe() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
265
        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') targeting Configuration::get() can also be of type array; however, define_safe() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
266
267
        self::$Cookie = new Cookie(__SYM_COOKIE_PREFIX__, TWO_WEEKS, __SYM_COOKIE_PATH__);
268
    }
269
270
    /**
271
     * Accessor for the current `$Cookie` instance.
272
     *
273
     * @since Symphony 2.5.0
274
     * @return Cookie
275
     */
276
    public static function Cookie()
277
    {
278
        return self::$Cookie;
279
    }
280
281
    /**
282
     * Setter for `$ExtensionManager` using the current
283
     * Symphony instance as the parent. If for some reason this fails,
284
     * a Symphony Error page will be thrown
285
     * @param Boolean $force (optional)
286
     *  When set to true, this function will always create a new
287
     *  instance of ExtensionManager, replacing self::$ExtensionManager.
288
     */
289
    public static function initialiseExtensionManager($force=false)
290
    {
291
        if (!$force && self::$ExtensionManager instanceof ExtensionManager) {
292
            return true;
293
        }
294
295
        self::$ExtensionManager = new ExtensionManager;
296
297
        if (!(self::$ExtensionManager instanceof ExtensionManager)) {
298
            self::throwCustomError(__('Error creating Symphony extension manager.'));
299
        }
300
    }
301
302
    /**
303
     * Accessor for the current `$ExtensionManager` instance.
304
     *
305
     * @since Symphony 2.2
306
     * @return ExtensionManager
307
     */
308
    public static function ExtensionManager()
309
    {
310
        return self::$ExtensionManager;
311
    }
312
313
    /**
314
     * Setter for `$Database`, accepts a Database object. If `$database`
315
     * is omitted, this function will set `$Database` to be of the `MySQL`
316
     * class.
317
     *
318
     * @since Symphony 2.3
319
     * @param StdClass $database (optional)
320
     *  The class to handle all Database operations, if omitted this function
321
     *  will set `self::$Database` to be an instance of the `MySQL` class.
322
     * @return boolean
323
     *  This function will always return true
324
     */
325
    public static function setDatabase(StdClass $database = null)
326
    {
327
        if (self::Database()) {
328
            return true;
329
        }
330
331
        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 object<stdClass>. However, the property $Database is declared as type object<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...
332
333
        return true;
334
    }
335
336
    /**
337
     * Accessor for the current `$Database` instance.
338
     *
339
     * @return MySQL
340
     */
341
    public static function Database()
342
    {
343
        return self::$Database;
344
    }
345
346
    /**
347
     * This will initialise the Database class and attempt to create a connection
348
     * using the connection details provided in the Symphony configuration. If any
349
     * errors occur whilst doing so, a Symphony Error Page is displayed.
350
     *
351
     * @throws SymphonyErrorPage
352
     * @return boolean
353
     *  This function will return true if the `$Database` was
354
     *  initialised successfully.
355
     */
356
    public static function initialiseDatabase()
357
    {
358
        self::setDatabase();
359
        $details = self::Configuration()->get('database');
360
361
        try {
362
            if (!self::Database()->connect($details['host'], $details['user'], $details['password'], $details['port'], $details['db'])) {
363
                return false;
364
            }
365
366
            if (!self::Database()->isConnected()) {
367
                return false;
368
            }
369
370
            self::Database()->setPrefix($details['tbl_prefix']);
371
            self::Database()->setCharacterEncoding();
372
            self::Database()->setCharacterSet();
373
            self::Database()->setTimeZone(self::Configuration()->get('timezone', 'region'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->get('timezone', 'region') targeting Configuration::get() can also be of type array; however, MySQL::setTimeZone() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
374
375
            if (isset($details['query_caching'])) {
376
                if ($details['query_caching'] == 'off') {
377
                    self::Database()->disableCaching();
378
                } elseif ($details['query_caching'] == 'on') {
379
                    self::Database()->enableCaching();
380
                }
381
            }
382
383
            if (isset($details['query_logging'])) {
384
                if ($details['query_logging'] == 'off') {
385
                    self::Database()->disableLogging();
386
                } elseif ($details['query_logging'] == 'on') {
387
                    self::Database()->enableLogging();
388
                }
389
            }
390
        } catch (DatabaseException $e) {
391
            self::throwCustomError(
392
                $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(),
393
                __('Symphony Database Error'),
394
                Page::HTTP_STATUS_ERROR,
395
                'database',
396
                array(
397
                    'error' => $e,
398
                    'message' => __('There was a problem whilst attempting to establish a database connection. Please check all connection information is correct.') . ' ' . __('The following error was returned:')
399
                )
400
            );
401
        }
402
403
        return true;
404
    }
405
406
    /**
407
     * Accessor for the current `$Author` instance.
408
     *
409
     * @since Symphony 2.5.0
410
     * @return Author
411
     */
412
    public static function Author()
413
    {
414
        return self::$Author;
415
    }
416
417
    /**
418
     * Attempts to log an Author in given a username and password.
419
     * If the password is not hashed, it will be hashed using the sha1
420
     * algorithm. The username and password will be sanitized before
421
     * being used to query the Database. If an Author is found, they
422
     * will be logged in and the sanitized username and password (also hashed)
423
     * will be saved as values in the `$Cookie`.
424
     *
425
     * @see toolkit.Cryptography#hash()
426
     * @throws DatabaseException
427
     * @param string $username
428
     *  The Author's username. This will be sanitized before use.
429
     * @param string $password
430
     *  The Author's password. This will be sanitized and then hashed before use
431
     * @param boolean $isHash
432
     *  If the password provided is already hashed, setting this parameter to
433
     *  true will stop it becoming rehashed. By default it is false.
434
     * @return boolean
435
     *  True if the Author was logged in, false otherwise
436
     */
437
    public static function login($username, $password, $isHash = false)
438
    {
439
        $username = trim(self::Database()->cleanValue($username));
440
        $password = trim(self::Database()->cleanValue($password));
441
442
        if (strlen($username) > 0 && strlen($password) > 0) {
443
            $author = AuthorManager::fetch('id', 'ASC', 1, null, sprintf(
444
                "`username` = '%s'",
445
                $username
446
            ));
447
448
            if (!empty($author) && Cryptography::compare($password, current($author)->get('password'), $isHash)) {
449
                self::$Author = current($author);
450
451
                // Only migrate hashes if there is no update available as the update might change the tbl_authors table.
452
                if (self::isUpgradeAvailable() === false && Cryptography::requiresMigration(self::$Author->get('password'))) {
453
                    self::$Author->set('password', Cryptography::hash($password));
454
455
                    self::Database()->update(array('password' => self::$Author->get('password')), 'tbl_authors', sprintf(
456
                        " `id` = %d", self::$Author->get('id')
457
                    ));
458
                }
459
460
                self::$Cookie->set('username', $username);
461
                self::$Cookie->set('pass', self::$Author->get('password'));
462
463
                self::Database()->update(array(
464
                    'last_seen' => DateTimeObj::get('Y-m-d H:i:s')),
465
                    'tbl_authors',
466
                    sprintf(" `id` = %d", self::$Author->get('id'))
467
                );
468
469
                // Only set custom author language in the backend
470
                if (class_exists('Administration', false)) {
471
                    Lang::set(self::$Author->get('language'));
472
                }
473
474
                return true;
475
            }
476
        }
477
478
        return false;
479
    }
480
481
    /**
482
     * Symphony allows Authors to login via the use of tokens instead of
483
     * a username and password. A token is derived from concatenating the
484
     * Author's username and password and applying the sha1 hash to
485
     * it, from this, a portion of the hash is used as the token. This is a useful
486
     * feature often used when setting up other Authors accounts or if an
487
     * Author forgets their password.
488
     *
489
     * @param string $token
490
     *  The Author token, which is a portion of the hashed string concatenation
491
     *  of the Author's username and password
492
     * @throws DatabaseException
493
     * @return boolean
494
     *  True if the Author is logged in, false otherwise
495
     */
496
    public static function loginFromToken($token)
497
    {
498
        $token = self::Database()->cleanValue($token);
499
500
        if (strlen(trim($token)) == 0) {
501
            return false;
502
        }
503
504
        if (strlen($token) == 6 || strlen($token) == 16) {
505
            $row = self::Database()->fetchRow(0, sprintf(
506
                "SELECT `a`.`id`, `a`.`username`, `a`.`password`
507
                FROM `tbl_authors` AS `a`, `tbl_forgotpass` AS `f`
508
                WHERE `a`.`id` = `f`.`author_id`
509
                AND `f`.`expiry` > '%s'
510
                AND `f`.`token` = '%s'
511
                LIMIT 1",
512
                DateTimeObj::getGMT('c'),
513
                $token
514
            ));
515
516
            self::Database()->delete('tbl_forgotpass', sprintf(" `token` = '%s' ", $token));
517
        } else {
518
            $row = self::Database()->fetchRow(0, sprintf(
519
                "SELECT `id`, `username`, `password`
520
                FROM `tbl_authors`
521
                WHERE SUBSTR(%s(CONCAT(`username`, `password`)), 1, 8) = '%s'
522
                AND `auth_token_active` = 'yes'
523
                LIMIT 1",
524
                'SHA1',
525
                $token
526
            ));
527
        }
528
529
        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...
530
            self::$Author = AuthorManager::fetchByID($row['id']);
531
            self::$Cookie->set('username', $row['username']);
532
            self::$Cookie->set('pass', $row['password']);
533
            self::Database()->update(array('last_seen' => DateTimeObj::getGMT('Y-m-d H:i:s')), 'tbl_authors', sprintf("
534
                `id` = %d", $row['id']
535
            ));
536
537
            return true;
538
        }
539
540
        return false;
541
    }
542
543
    /**
544
     * This function will destroy the currently logged in `$Author`
545
     * session, essentially logging them out.
546
     *
547
     * @see core.Cookie#expire()
548
     */
549
    public static function logout()
550
    {
551
        self::$Cookie->expire();
552
    }
553
554
    /**
555
     * This function determines whether an there is a currently logged in
556
     * Author for Symphony by using the `$Cookie`'s username
557
     * and password. If the instance is not found, they will be logged
558
     * in using the cookied credentials.
559
     *
560
     * @see login()
561
     * @return boolean
562
     */
563
    public static function isLoggedIn()
564
    {
565
        // Check to see if we already have an Author instance.
566
        if (self::$Author) {
567
            return true;
568
        }
569
570
        // No author instance found, attempt to log in with the cookied credentials
571
        return self::login(self::$Cookie->get('username'), self::$Cookie->get('pass'), true);
572
    }
573
574
    /**
575
     * Returns the most recent version found in the `/install/migrations` folder.
576
     * Returns a version string to be used in `version_compare()` if an updater
577
     * has been found. Returns `FALSE` otherwise.
578
     *
579
     * @since Symphony 2.3.1
580
     * @return string|boolean
581
     */
582
    public static function getMigrationVersion()
583
    {
584
        if (self::isInstallerAvailable()) {
585
            $migrations = scandir(DOCROOT . '/install/migrations');
586
            $migration_file = end($migrations);
587
            $migration_class = 'migration_' . str_replace('.', '', substr($migration_file, 0, -4));
588
            return call_user_func(array($migration_class, 'getVersion'));
589
        }
590
591
        return false;
592
    }
593
594
    /**
595
     * Checks if an update is available and applicable for the current installation.
596
     *
597
     * @since Symphony 2.3.1
598
     * @return boolean
599
     */
600
    public static function isUpgradeAvailable()
601
    {
602
        if (self::isInstallerAvailable()) {
603
            $migration_version = self::getMigrationVersion();
604
            $current_version = Symphony::Configuration()->get('version', 'symphony');
605
606
            return version_compare($current_version, $migration_version, '<');
607
        }
608
609
        return false;
610
    }
611
612
    /**
613
     * Checks if the installer/upgrader is available.
614
     *
615
     * @since Symphony 2.3.1
616
     * @return boolean
617
     */
618
    public static function isInstallerAvailable()
619
    {
620
        return file_exists(DOCROOT . '/install/index.php');
621
    }
622
623
    /**
624
     * A wrapper for throwing a new Symphony Error page.
625
     *
626
     * @see core.SymphonyErrorPage
627
     * @param string|XMLElement $message
628
     *  A description for this error, which can be provided as a string
629
     *  or as an XMLElement.
630
     * @param string $heading
631
     *  A heading for the error page
632
     * @param integer $status
633
     *  Properly sets the HTTP status code for the response. Defaults to
634
     *  `Page::HTTP_STATUS_ERROR`. Use `Page::HTTP_STATUS_XXX` to set this value.
635
     * @param string $template
636
     *  A string for the error page template to use, defaults to 'generic'. This
637
     *  can be the name of any template file in the `TEMPLATES` directory.
638
     *  A template using the naming convention of `tpl.*.php`.
639
     * @param array $additional
640
     *  Allows custom information to be passed to the Symphony Error Page
641
     *  that the template may want to expose, such as custom Headers etc.
642
     * @throws SymphonyErrorPage
643
     */
644
    public static function throwCustomError($message, $heading = 'Symphony Fatal Error', $status = Page::HTTP_STATUS_ERROR, $template = 'generic', array $additional = array())
645
    {
646
        throw new SymphonyErrorPage($message, $heading, $template, $additional, $status);
647
    }
648
649
    /**
650
     * Setter accepts a previous Exception. Useful for determining the context
651
     * of a current exception (ie. detecting recursion).
652
     *
653
     * @since Symphony 2.3.2
654
     * @param Exception $ex
655
     */
656
    public static function setException(Exception $ex)
657
    {
658
        self::$exception = $ex;
659
    }
660
661
    /**
662
     * Accessor for `self::$exception`.
663
     *
664
     * @since Symphony 2.3.2
665
     * @return Exception|null
666
     */
667
    public static function getException()
668
    {
669
        return self::$exception;
670
    }
671
672
    /**
673
     * Returns the page namespace based on the current URL.
674
     * A few examples:
675
     *
676
     * /login
677
     * /publish
678
     * /blueprints/datasources
679
     * [...]
680
     * /extension/$extension_name/$page_name
681
     *
682
     * This method is especially useful in couple with the translation function.
683
     *
684
     * @see toolkit#__()
685
     * @return string
686
     *  The page namespace, without any action string (e.g. "new", "saved") or
687
     *  any value that depends upon the single setup (e.g. the section handle in
688
     *  /publish/$handle)
689
     */
690
    public static function getPageNamespace()
691
    {
692
        if (self::$namespace !== false) {
693
            return self::$namespace;
694
        }
695
696
        $page = getCurrentPage();
697
698
        if (!is_null($page)) {
699
            $page = trim($page, '/');
700
        }
701
702
        if (substr($page, 0, 7) == 'publish') {
703
            self::$namespace = '/publish';
704
        } elseif (empty($page) && isset($_REQUEST['mode'])) {
705
            self::$namespace = '/login';
706
        } elseif (empty($page)) {
707
            self::$namespace = null;
708
        } else {
709
            $bits = explode('/', $page);
710
711
            if ($bits[0] == 'extension') {
712
                self::$namespace = sprintf('/%s/%s/%s', $bits[0], $bits[1], $bits[2]);
713
            } else {
714
                self::$namespace =  sprintf('/%s/%s', $bits[0], isset($bits[1]) ? $bits[1] : '');
715
            }
716
        }
717
718
        return self::$namespace;
719
    }
720
}
721
722
/**
723
 * The `SymphonyErrorPageHandler` extends the `GenericExceptionHandler`
724
 * to allow the template for the exception to be provided from the `TEMPLATES`
725
 * directory
726
 */
727
class SymphonyErrorPageHandler extends GenericExceptionHandler
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...
728
{
729
    /**
730
     * The render function will take a `SymphonyErrorPage` exception and
731
     * output a HTML page. This function first checks to see if the `GenericExceptionHandler`
732
     * is enabled and pass control to it if not. After that, the method checks if there is a custom
733
     * template for this exception otherwise it reverts to using the default
734
     * `usererror.generic.php`. If the template is not found, it will call
735
     * `GenericExceptionHandler::render()`.
736
     *
737
     * @param Exception $e
738
     *  The Exception object
739
     * @return string
740
     *  An HTML string
741
     */
742
    public static function render(Exception $e)
743
    {
744
        if (!GenericExceptionHandler::$enabled) {
745
            return GenericExceptionHandler::render($e);
746
        }
747
        else if ($e->getTemplate() === false) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getTemplate() does only exist in the following sub-classes of Exception: SymphonyErrorPage. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

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

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
961
            !self::$enabled ? '' : $e->getQuery(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getQuery() does only exist in the following sub-classes of Exception: DatabaseException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
962
            !self::$enabled ? '' : $trace,
963
            !self::$enabled ? '' : $queries
964
        );
965
966
        $html = str_replace('{ASSETS_URL}', ASSETS_URL, $html);
967
        $html = str_replace('{SYMPHONY_URL}', SYMPHONY_URL, $html);
968
        $html = str_replace('{URL}', URL, $html);
969
970
        return $html;
971
    }
972
}
973