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 — integration (#2604)
by Brendan
05:01
created

Symphony   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 860
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 22

Importance

Changes 12
Bugs 1 Features 1
Metric Value
c 12
b 1
f 1
dl 0
loc 860
rs 1.263
wmc 88
lcom 3
cbo 22

31 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 28 4
B initialiseLog() 0 44 5
A Configuration() 0 4 1
A Log() 0 4 1
C initialiseDatabase() 0 49 10
A setDatabase() 0 10 3
A Database() 0 4 1
A throwCustomError() 0 10 1
A initialiseExtensionManager() 0 12 4
B initialiseSessionAndCookies() 0 53 5
B getSessionTimeout() 0 16 6
A Session() 0 4 1
A Cookies() 0 4 1
A isLoggedIn() 0 10 3
C login() 0 45 8
A isUpgradeAvailable() 0 11 2
A isInstallerAvailable() 0 4 1
A getMigrationVersion() 0 16 3
A initialiseErrorHandler() 0 7 1
A Engine() 0 10 3
A initialiseConfiguration() 0 20 2
A isXSRFEnabled() 0 4 1
A Profiler() 0 4 1
A Flash() 0 4 1
A ExtensionManager() 0 4 1
A Author() 0 4 1
B loginFromToken() 0 51 5
A logout() 0 4 1
A getException() 0 4 1
A setException() 0 4 1
D getPageNamespace() 0 30 9

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
    use Monolog\Logger;
13
14
    abstract class Symphony implements Singleton
15
    {
16
        /**
17
         * An instance of the Cookies class
18
         *
19
         * @var Cookies
20
         */
21
        public static $Cookies = null;
22
        /**
23
         * An instance of the Session class
24
         *
25
         * @var Session
26
         */
27
        public static $Session = null;
28
        /**
29
         * An instance of the SessionFlash class
30
         *
31
         * @var Session
32
         */
33
        public static $Flash = null;
34
        /**
35
         * An instance of the currently logged in Author
36
         *
37
         * @var Author
38
         */
39
        public static $Author = null;
40
        /**
41
         * An instance of the Symphony class, either `Administration` or `Frontend`.
42
         *
43
         * @var Symphony
44
         */
45
        protected static $_instance = null;
46
        /**
47
         * An instance of the Profiler class
48
         *
49
         * @var Profiler
50
         */
51
        protected static $Profiler = null;
52
        /**
53
         * An instance of the `Configuration` class
54
         *
55
         * @var Configuration
56
         */
57
        private static $Configuration = null;
58
        /**
59
         * An instance of the `Database` class
60
         *
61
         * @var MySQL
62
         */
63
        private static $Database = null;
64
        /**
65
         * An instance of the `ExtensionManager` class
66
         *
67
         * @var ExtensionManager
68
         */
69
        private static $ExtensionManager = null;
70
        /**
71
         * An instance of the `Log` class
72
         *
73
         * @var Log
74
         */
75
        private static $Log = null;
76
        /**
77
         * The current page namespace, used for translations
78
         *
79
         * @since Symphony 2.3
80
         * @var string
81
         */
82
        private static $namespace = false;
83
        /**
84
         * A previous exception that has been fired. Defaults to null.
85
         *
86
         * @since Symphony 2.3.2
87
         * @var Exception
88
         */
89
        private static $exception = null;
90
        /**
91
         * The version of the available Symphony upgrade, if available.
92
         * @var string
93
         */
94
        private static $migrationVersion = null;
95
96
        /**
97
         * The Symphony constructor initialises the class variables of Symphony. At present
98
         * constructor has a couple of responsibilities:
99
         * - Start a profiler instance
100
         * - If magic quotes are enabled, clean `$_SERVER`, `$_COOKIE`, `$_GET` and `$_POST` arrays
101
         * - Initialise the correct Language for the currently logged in Author.
102
         * - Start the session and adjust the error handling if the user is logged in
103
         */
104
        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...
105
        {
106
            self::$Profiler = Profiler::instance();
107
108
            if (get_magic_quotes_gpc()) {
109
                General::cleanArray($_SERVER);
110
                General::cleanArray($_COOKIE);
111
                General::cleanArray($_GET);
112
                General::cleanArray($_POST);
113
            }
114
115
            // Initialize language management
116
            Lang::initialize();
117
            Lang::set(self::$Configuration->get('lang', 'symphony'));
118
119
            $this->initialiseErrorHandler();
120
            $this->initialiseDatabase();
121
            $this->initialiseExtensionManager();
122
            $this->initialiseSessionAndCookies();
123
124
            // If the user is not a logged in Author, turn off the verbose error messages.
125
            if (!self::isLoggedIn() && is_null(self::$Author)) {
126
                GenericExceptionHandler::$enabled = false;
127
            }
128
129
            // Engine is ready.
130
            self::$Profiler->sample('Engine Initialisation');
131
        }
132
133
        /**
134
         * Setter for `$Log`. This function uses the configuration
135
         * settings in the 'log' group in the Configuration to create an instance. Date
136
         * formatting options are also retrieved from the configuration.
137
         *
138
         * @param string $filename (optional)
139
         *  The file to write the log to, if omitted this will default to `ACTIVITY_LOG`
140
         * @throws Exception
141
         * @return bool|void
142
         */
143
        public static function initialiseLog($filename = null)
144
        {
145
            if (self::$Log instanceof Log) {
146
                return true;
147
            }
148
149
            if (is_null($filename)) {
150
                $filename = ACTIVITY_LOG;
151
            }
152
153
            // Get the Handler from the Configuration
154
            $handler = self::Configuration()->get('handler', 'log');
155
            $context = array_merge(
156
                array(
157
                    'vars' => array(
158
                        'filename' => $filename
159
                    )
160
                ),
161
                self::Configuration()->get()
162
            );
163
164
            // Create the base handler
165
            if (is_array($handler['args'])) {
166
                array_walk($handler['args'], 'General::replacePlaceholdersWithContext', $context);
167
                $reflection = new ReflectionClass($handler['class']);
168
                $handler = $reflection->newInstanceArgs($handler['args']);
169
            } else {
170
                $handler = new \Monolog\Handler\StreamHandler($filename);
171
            }
172
173
            // Create the base formatter
174
            if ($format = self::Configuration()->get('formatter', 'log')) {
175
                array_walk($format['args'], 'General::replacePlaceholdersWithContext', $context);
176
                $reflection = new ReflectionClass($format['class']);
177
                $formatter = $reflection->newInstanceArgs($format['args']);
178
                $handler->setFormatter($formatter);
179
            }
180
181
            // Create the log object
182
            $logger = new Logger(basename($filename));
183
            $logger->pushHandler($handler);
184
185
            self::$Log = new Log($logger);
186
        }
187
188
        /**
189
         * Accessor for the current `Configuration` instance. This contains
190
         * representation of the the Symphony config file.
191
         *
192
         * @return Configuration
193
         */
194
        public static function Configuration()
195
        {
196
            return self::$Configuration;
197
        }
198
199
        /**
200
         * Accessor for the current `Log` instance
201
         *
202
         * @since Symphony 2.3
203
         * @return Log
204
         */
205
        public static function Log()
206
        {
207
            return self::$Log;
208
        }
209
210
        /**
211
         * This will initialise the Database class and attempt to create a connection
212
         * using the connection details provided in the Symphony configuration. If any
213
         * errors occur whilst doing so, a Symphony Error Page is displayed.
214
         *
215
         * @throws SymphonyErrorPage
216
         * @return boolean
217
         *  This function will return true if the `$Database` was
218
         *  initialised successfully.
219
         */
220
        public static function initialiseDatabase()
221
        {
222
            self::setDatabase();
223
            $details = self::Configuration()->get('database');
224
225
            try {
226
                if (!self::Database()->connect($details['host'], $details['user'], $details['password'],
227
                    $details['port'], $details['db'])
228
                ) {
229
                    return false;
230
                }
231
232
                if (!self::Database()->isConnected()) {
233
                    return false;
234
                }
235
236
                self::Database()->setPrefix($details['tbl_prefix']);
237
                self::Database()->setTimeZone(self::Configuration()->get('timezone', 'region'));
238
239
                if (isset($details['query_caching'])) {
240
                    if ($details['query_caching'] === 'off') {
241
                        self::Database()->disableCaching();
242
                    } elseif ($details['query_caching'] === 'on') {
243
                        self::Database()->enableCaching();
244
                    }
245
                }
246
247
                if (isset($details['query_logging'])) {
248
                    if ($details['query_logging'] === 'off') {
249
                        self::Database()->disableLogging();
250
                    } elseif ($details['query_logging'] === 'on') {
251
                        self::Database()->enableLogging();
252
                    }
253
                }
254
            } catch (DatabaseException $e) {
255
                self::throwCustomError(
256
                    $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(),
257
                    __('Symphony Database Error'),
258
                    Page::HTTP_STATUS_ERROR,
259
                    'database',
260
                    array(
261
                        'error' => $e,
262
                        'message' => __('There was a problem whilst attempting to establish a database connection. Please check all connection information is correct.') . ' ' . __('The following error was returned:')
263
                    )
264
                );
265
            }
266
267
            return true;
268
        }
269
270
        /**
271
         * Setter for `$Database`, accepts a Database object. If `$database`
272
         * is omitted, this function will set `$Database` to be of the `MySQL`
273
         * class.
274
         *
275
         * @since Symphony 2.3
276
         * @param stdClass $database (optional)
277
         *  The class to handle all Database operations, if omitted this function
278
         *  will set `self::$Database` to be an instance of the `MySQL` class.
279
         * @return boolean
280
         *  This function will always return true
281
         */
282
        public static function setDatabase(stdClass $database = null)
283
        {
284
            if (self::Database()) {
285
                return true;
286
            }
287
288
            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...
289
290
            return true;
291
        }
292
293
        /**
294
         * Accessor for the current `$Database` instance.
295
         *
296
         * @return MySQL
297
         */
298
        public static function Database()
299
        {
300
            return self::$Database;
301
        }
302
303
        /**
304
         * A wrapper for throwing a new Symphony Error page.
305
         *
306
         * This methods sets the `GenericExceptionHandler::$enabled` value to `true`.
307
         *
308
         * @see core.SymphonyErrorPage
309
         * @param string|XMLElement $message
310
         *  A description for this error, which can be provided as a string
311
         *  or as an XMLElement.
312
         * @param string $heading
313
         *  A heading for the error page
314
         * @param integer $status
315
         *  Properly sets the HTTP status code for the response. Defaults to
316
         *  `Page::HTTP_STATUS_ERROR`. Use `Page::HTTP_STATUS_XXX` to set this value.
317
         * @param string $template
318
         *  A string for the error page template to use, defaults to 'generic'. This
319
         *  can be the name of any template file in the `TEMPLATES` directory.
320
         *  A template using the naming convention of `tpl.*.php`.
321
         * @param array $additional
322
         *  Allows custom information to be passed to the Symphony Error Page
323
         *  that the template may want to expose, such as custom Headers etc.
324
         * @throws SymphonyErrorPage
325
         */
326
        public static function throwCustomError(
327
            $message,
328
            $heading = 'Symphony Fatal Error',
329
            $status = Page::HTTP_STATUS_ERROR,
330
            $template = 'generic',
331
            array $additional = array()
332
        ) {
333
            GenericExceptionHandler::$enabled = true;
334
            throw new SymphonyErrorPage($message, $heading, $template, $additional, $status);
335
        }
336
337
        /**
338
         * Setter for `$ExtensionManager` using the current
339
         * Symphony instance as the parent. If for some reason this fails,
340
         * a Symphony Error page will be thrown
341
         *
342
         * @param boolean $force (optional)
343
         *  When set to true, this function will always create a new
344
         *  instance of ExtensionManager, replacing self::$ExtensionManager.
345
         * @return void
346
         */
347
        public static function initialiseExtensionManager($force = false)
348
        {
349
            if (!$force && self::$ExtensionManager instanceof ExtensionManager) {
350
                return;
351
            }
352
353
            self::$ExtensionManager = new ExtensionManager;
354
355
            if (!(self::$ExtensionManager instanceof ExtensionManager)) {
356
                self::throwCustomError(__('Error creating Symphony extension manager.'));
357
            }
358
        }
359
360
        /**
361
         * Setter for `$Session`. This will use PHP's parse_url
362
         * function on the current URL to set a session using the `session_name`
363
         * defined in the Symphony configuration. The is either admin or public.
364
         * The session will last for the time defined in configuration.
365
         *
366
         * @since Symphony 3.0.0
367
         */
368
        public function initialiseSessionAndCookies()
369
        {
370
            $cookie_path = @parse_url(URL, PHP_URL_PATH);
371
            $cookie_path = '/' . trim($cookie_path, '/');
372
373
            $timeout = $this->getSessionTimeout();
374
375
            $name = null;
0 ignored issues
show
Unused Code introduced by
$name is not used, you could remove the assignment.

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

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

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

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

Loading history...
376
            if (class_exists('Administration', false)) {
377
                $name = self::Configuration()->get('admin_session_name', 'session');
378
            } else {
379
                $name = self::Configuration()->get('public_session_name', 'session');
380
            }
381
382
            if (is_null($name)) {
383
                $name = 'symphony';
384
            }
385
386
            // The handler accepts a database in a move towards dependency injection
387
            $handler = new DatabaseSessionHandler(self::Database(), array(
388
                'session_lifetime' => $timeout
389
            ), $name);
390
391
            // The session accepts a handler in a move towards dependency injection
392
            self::$Session = new Session($handler, array(
393
                'session_gc_probability' => self::Configuration()->get('session_gc_probability', 'session'),
394
                'session_gc_divisor' => self::Configuration()->get('session_gc_divisor', 'session'),
395
                'session_gc_maxlifetime' => $timeout,
396
                'session_cookie_lifetime' => $timeout,
397
                'session_cookie_path' => $cookie_path,
398
                'session_cookie_domain' => null,
399
                'session_cookie_secure' => (defined(__SECURE__) ? true : false),
400
                'session_cookie_httponly' => true
401
            ), $name);
402
403
            // Initialise the cookie handler
404
            self::$Cookies = new Cookies(array(
405
                'domain' => self::Session()->getDomain(),
406
                'path' => $cookie_path,
407
                'expires' => time() + $timeout,
408
                'secure' => (defined(__SECURE__) ? true : false),
409
                'httponly' => true
410
            ));
411
412
            // Start the session
413
            self::Session()->start();
414
415
            // The flash accepts a session in a move towards dependency injection
416
            self::$Flash = new SessionFlash(self::Session());
0 ignored issues
show
Documentation Bug introduced by
It seems like new \SessionFlash(self::Session()) of type object<SessionFlash> is incompatible with the declared type object<Session> of property $Flash.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
417
418
            // Fetch the current cookies from the header
419
            self::Cookies()->fetch();
420
        }
421
422
        /**
423
         * Gets the configuerd session timeout as seconds, based on the environment instance
424
         *
425
         * @return int
426
         *  The seconds
427
         */
428
        private function getSessionTimeout()
429
        {
430
            if (class_exists('Administration', false)) {
431
                $time = (self::Configuration()->get('admin_session_expires',
432
                    'symphony') ? self::Configuration()->get('admin_session_expires', 'symphony') : '2 weeks');
433
            } else {
434
                $time = (self::Configuration()->get('public_session_expires',
435
                    'symphony') ? self::Configuration()->get('public_session_expires', 'symphony') : '2 weeks');
436
            }
437
438
            if (is_string($time) && !is_numeric($time)) {
439
                $time = DateTimeObj::stringToSeconds($time);
440
            }
441
442
            return $time;
443
        }
444
445
        /**
446
         * Accessor for the current `$Session` instance.
447
         *
448
         * @since 3.0.0
449
         * @return Session
450
         */
451
        public static function Session()
452
        {
453
            return self::$Session;
454
        }
455
456
        /**
457
         * Accessor for the current `$Cookies` instance.
458
         *
459
         * @since 2.0.0
460
         * @return Cookies
461
         */
462
        public static function Cookies()
463
        {
464
            return self::$Cookies;
465
        }
466
467
        /**
468
         * This function determines whether an there is a currently logged in
469
         * Author for Symphony by using the `$Session`'s username
470
         * and password. If an Author is found, they will be logged in, otherwise
471
         * the `$Session` will be destroyed.
472
         *
473
         * @see login()
474
         * @return boolean
475
         */
476
        public static function isLoggedIn()
477
        {
478
            // Check to see if Symphony exists, or if we already have an Author instance.
479
            if (is_null(self::$_instance) || self::$Author) {
480
                return true;
481
            }
482
483
            // No author instance found, attempt to log in with the cookied credentials
484
            return self::login(self::Session()->get('username'), self::Session()->get('pass'), true);
485
        }
486
487
        /**
488
         * Attempts to log an Author in given a username and password.
489
         * If the password is not hashed, it will be hashed using the sha1
490
         * algorithm. The username and password will be sanitized before
491
         * being used to query the Database. If an Author is found, they
492
         * will be logged in and the sanitized username and password (also hashed)
493
         * will be saved as values in the `$Session`.
494
         *
495
         * @see toolkit.Cryptography#hash()
496
         * @throws DatabaseException
497
         * @param string $username
498
         *  The Author's username. This will be sanitized before use.
499
         * @param string $password
500
         *  The Author's password. This will be sanitized and then hashed before use
501
         * @param boolean $isHash
502
         *  If the password provided is already hashed, setting this parameter to
503
         *  true will stop it becoming rehashed. By default it is false.
504
         * @return boolean
505
         *  True if the Author was logged in, false otherwise
506
         */
507
        public static function login($username, $password, $isHash = false)
508
        {
509
            $username = trim(self::Database()->cleanValue($username));
510
            $password = trim(self::Database()->cleanValue($password));
511
512
            if (strlen($username) > 0 && strlen($password) > 0) {
513
                $author = AuthorManager::fetch('id', 'ASC', 1, null, sprintf(
514
                    "`username` = '%s'",
515
                    $username
516
                ));
517
518
                if (!empty($author) && Cryptography::compare($password, current($author)->get('password'), $isHash)) {
519
                    self::$Author = current($author);
520
521
                    // Only migrate hashes if there is no update available as the update might change the tbl_authors table.
522
                    if (self::isUpgradeAvailable() === false && Cryptography::requiresMigration(self::$Author->get('password'))) {
523
                        self::$Author->set('password', Cryptography::hash($password));
524
525
                        self::Database()->update(array('password' => self::$Author->get('password')), 'tbl_authors',
526
                            " `id` = ?", array(self::$Author->get('id'))
527
                        );
528
                    }
529
530
                    self::Session()->set('username', $username);
531
                    self::Session()->set('pass', self::$Author->get('password'));
532
533
                    self::Database()->update(array(
534
                        'last_seen' => DateTimeObj::get('Y-m-d H:i:s')
535
                    ),
536
                        'tbl_authors',
537
                        " `id` = ?",
538
                        array(self::$Author->get('id'))
539
                    );
540
541
                    // Only set custom author language in the backend
542
                    if (class_exists('Administration', false)) {
543
                        Lang::set(self::$Author->get('language'));
544
                    }
545
546
                    return true;
547
                }
548
            }
549
550
            return false;
551
        }
552
553
        /**
554
         * Checks if an update is available and applicable for the current installation.
555
         *
556
         * @since Symphony 2.3.1
557
         * @return boolean
558
         */
559
        public static function isUpgradeAvailable()
560
        {
561
            if (self::isInstallerAvailable()) {
562
                $migration_version = self::getMigrationVersion();
563
                $current_version = Symphony::Configuration()->get('version', 'symphony');
564
565
                return version_compare($current_version, $migration_version, '<');
566
            }
567
568
            return false;
569
        }
570
571
        /**
572
         * Checks if the installer/upgrader is available.
573
         *
574
         * @since Symphony 2.3.1
575
         * @return boolean
576
         */
577
        public static function isInstallerAvailable()
578
        {
579
            return file_exists(DOCROOT . '/install/index.php');
580
        }
581
582
        /**
583
         * Returns the most recent version found in the `/install/migrations` folder.
584
         * Returns a version string to be used in `version_compare()` if an updater
585
         * has been found. Returns `FALSE` otherwise.
586
         *
587
         * @since Symphony 2.3.1
588
         * @return string|boolean
589
         */
590
        public static function getMigrationVersion()
591
        {
592
            if (null === self::$migrationVersion && self::isInstallerAvailable()) {
593
                $migrations = scandir(DOCROOT . '/install/migrations');
594
                $migration_file = end($migrations);
595
596
                include_once DOCROOT . '/install/migrations/' . $migration_file;
597
598
                $migration_class = 'SymphonyCms\\Installer\\Migrations\\migration_' . str_replace('.', '', substr($migration_file, 0, -4));
599
                self::$migrationVersion = call_user_func(array($migration_class, 'getVersion'));
600
            } else {
601
                self::$migrationVersion = false;
0 ignored issues
show
Documentation Bug introduced by
The property $migrationVersion was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
602
            }
603
604
            return self::$migrationVersion;
605
        }
606
607
        /**
608
         * Setter for the Symphony Log and Error Handling system
609
         *
610
         * @since Symphony 2.6.0
611
         */
612
        public static function initialiseErrorHandler()
613
        {
614
            // Initialise logging
615
            self::initialiseLog();
616
            GenericExceptionHandler::initialise(self::Log());
617
            GenericErrorHandler::initialise(self::Log());
618
        }
619
620
        /**
621
         * Accessor for the Symphony instance, whether it be Frontend
622
         * or Administration
623
         *
624
         * @since Symphony 2.2
625
         * @throws Exception
626
         * @return Symphony
627
         */
628
        public static function Engine()
629
        {
630
            if (class_exists('Administration', false)) {
631
                return Administration::instance();
632
            } elseif (class_exists('Frontend', false)) {
633
                return Frontend::instance();
634
            } else {
635
                throw new Exception(__('No suitable engine object found'));
636
            }
637
        }
638
639
        /**
640
         * Setter for `$Configuration`. This function initialise the configuration
641
         * object and populate its properties based on the given `$array`. Since
642
         * Symphony 2.6.5, it will also set Symphony's date constants.
643
         *
644
         * @since Symphony 2.3
645
         * @param array $data
646
         *  An array of settings to be stored into the Configuration object
647
         */
648
        public static function initialiseConfiguration(array $data = array())
649
        {
650
            if (empty($data)) {
651
                // Includes the existing CONFIG file and initialises the Configuration
652
                // by setting the values with the setArray function.
653
                include CONFIG;
654
655
                $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...
656
            }
657
658
            self::$Configuration = new Configuration(true);
659
            self::$Configuration->setArray($data);
660
661
            // Set date format throughout the system
662
            define_safe('__SYM_DATE_FORMAT__', self::Configuration()->get('date_format', 'region'));
663
            define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region'));
664
            define_safe('__SYM_DATETIME_FORMAT__',
665
                __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__);
666
            DateTimeObj::setSettings(self::Configuration()->get('region'));
667
        }
668
669
        /**
670
         * Is XSRF enabled for this Symphony install?
671
         *
672
         * @since Symphony 2.4
673
         * @return boolean
674
         */
675
        public static function isXSRFEnabled()
676
        {
677
            return self::Configuration()->get('enable_xsrf', 'symphony') === 'yes';
678
        }
679
680
        /**
681
         * Accessor for the current `Profiler` instance.
682
         *
683
         * @since Symphony 2.3
684
         * @return Profiler
685
         */
686
        public static function Profiler()
687
        {
688
            return self::$Profiler;
689
        }
690
691
        /**
692
         * Accessor for the current `$Flash` instance.
693
         *
694
         * @since 3.0.0
695
         * @return SessionFlash
696
         */
697
        public static function Flash()
698
        {
699
            return self::$Flash;
700
        }
701
702
        /**
703
         * Accessor for the current `$ExtensionManager` instance.
704
         *
705
         * @since Symphony 2.2
706
         * @return ExtensionManager
707
         */
708
        public static function ExtensionManager()
709
        {
710
            return self::$ExtensionManager;
711
        }
712
713
        /**
714
         * Accessor for the current `$Author` instance.
715
         *
716
         * @since Symphony 2.5.0
717
         * @return Author
718
         */
719
        public static function Author()
720
        {
721
            return self::$Author;
722
        }
723
724
        /**
725
         * Symphony allows Authors to login via the use of tokens instead of
726
         * a username and password. A token is derived from concatenating the
727
         * Author's username and password and applying the sha1 hash to
728
         * it, from this, a portion of the hash is used as the token. This is a useful
729
         * feature often used when setting up other Authors accounts or if an
730
         * Author forgets their password.
731
         *
732
         * @param string $token
733
         *  The Author token, which is a portion of the hashed string concatenation
734
         *  of the Author's username and password
735
         * @throws DatabaseException
736
         * @return boolean
737
         *  True if the Author is logged in, false otherwise
738
         */
739
        public static function loginFromToken($token)
740
        {
741
            $token = self::Database()->cleanValue($token);
742
            $tokenLength = strlen(trim($token));
743
744
            if ($tokenLength === 0) {
745
                return false;
746
            }
747
748
            if ($tokenLength === 6 || $tokenLength === 16) {
749
                $row = self::Database()->fetchRow(0, "
750
                SELECT `a`.`id`, `a`.`username`, `a`.`password`
751
                FROM `tbl_authors` AS `a`, `tbl_forgotpass` AS `f`
752
                WHERE `a`.`id` = `f`.`author_id`
753
                AND `f`.`expiry` > ?
754
                AND `f`.`token` = ?
755
                LIMIT 1",
756
                    array(
757
                        DateTimeObj::getGMT('c'),
758
                        $token
759
                    )
760
                );
761
762
                self::Database()->delete('tbl_forgotpass', " `token` = ? ", array($token));
763
            } else {
764
                $row = self::Database()->fetchRow(0, sprintf(
765
                    "SELECT `id`, `username`, `password`
766
                FROM `tbl_authors`
767
                WHERE SUBSTR(%s(CONCAT(`username`, `password`)), 1, 8) = ?
768
                AND `auth_token_active` = 'yes'
769
                LIMIT 1",
770
                    'SHA1'
771
                ),
772
                    array($token)
773
                );
774
            }
775
776
            if ($row) {
777
                self::$Author = AuthorManager::fetchByID($row['id']);
778
                self::Session()->set('username', $row['username']);
779
                self::Session()->set('pass', $row['password']);
780
                self::Database()->update(array('last_seen' => DateTimeObj::getGMT('Y-m-d H:i:s')), 'tbl_authors',
781
                    "`id` = ?", array(
782
                        $row['id']
783
                    ));
784
785
                return true;
786
            }
787
788
            return false;
789
        }
790
791
        /**
792
         * This function will destroy the currently logged in `$Author`
793
         * session, essentially logging them out.
794
         *
795
         * @see core.Session#expire()
796
         */
797
        public static function logout()
798
        {
799
            self::Session()->expire();
800
        }
801
802
        /**
803
         * Accessor for `self::$exception`.
804
         *
805
         * @since Symphony 2.3.2
806
         * @return Exception|null
807
         */
808
        public static function getException()
809
        {
810
            return self::$exception;
811
        }
812
813
        /**
814
         * Setter accepts a previous Exception. Useful for determining the context
815
         * of a current exception (ie. detecting recursion).
816
         *
817
         * @since Symphony 2.3.2
818
         * @param Exception $ex
819
         */
820
        public static function setException(Exception $ex)
821
        {
822
            self::$exception = $ex;
823
        }
824
825
        /**
826
         * Returns the page namespace based on the current URL.
827
         * A few examples:
828
         *
829
         * /login
830
         * /publish
831
         * /blueprints/datasources
832
         * [...]
833
         * /extension/$extension_name/$page_name
834
         *
835
         * This method is especially useful in couple with the translation function.
836
         *
837
         * @see toolkit#__()
838
         * @return string
839
         *  The page namespace, without any action string (e.g. "new", "saved") or
840
         *  any value that depends upon the single setup (e.g. the section handle in
841
         *  /publish/$handle)
842
         */
843
        public static function getPageNamespace()
844
        {
845
            if (self::$namespace !== false) {
846
                return self::$namespace;
847
            }
848
849
            $page = getCurrentPage();
850
851
            if (!is_null($page)) {
852
                $page = trim($page, '/');
853
            }
854
855
            if (substr($page, 0, 7) === 'publish') {
856
                self::$namespace = '/publish';
857
            } elseif (empty($page) && isset($_REQUEST['mode'])) {
858
                self::$namespace = '/login';
859
            } elseif (empty($page)) {
860
                self::$namespace = null;
861
            } else {
862
                $bits = explode('/', $page);
863
864
                if ($bits[0] === 'extension') {
865
                    self::$namespace = sprintf('/%s/%s/%s', $bits[0], $bits[1], $bits[2]);
866
                } else {
867
                    self::$namespace = sprintf('/%s/%s', $bits[0], isset($bits[1]) ? $bits[1] : '');
868
                }
869
            }
870
871
            return self::$namespace;
872
        }
873
    }
874