GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

Symphony::ExtensionManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * @package 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 Database
36
     */
37
    private static $Database = null;
38
39
    /**
40
     * An instance of the `ExtensionManager` class
41
     * @var ExtensionManager
42
     */
43
    private static $ExtensionManager = null;
44
45
    /**
46
     * An instance of the `Log` class
47
     * @var Log
48
     */
49
    private static $Log = null;
50
51
    /**
52
     * The current page namespace, used for translations
53
     * @since Symphony 2.3
54
     * @var string
55
     */
56
    private static $namespace = false;
57
58
    /**
59
     * An instance of the Cookie class
60
     * @var Cookie
61
     */
62
    public static $Cookie = null;
63
64
    /**
65
     * An instance of the currently logged in Author
66
     * @var Author
67
     */
68
    public static $Author = null;
69
70
    /**
71
     * A previous exception that has been fired. Defaults to null.
72
     * @since Symphony 2.3.2
73
     * @var Exception
74
     */
75
    private static $exception = null;
76
77
    /**
78
     * The Symphony constructor initialises the class variables of Symphony. At present
79
     * constructor has a couple of responsibilities:
80
     * - Start a profiler instance
81
     * - If magic quotes are enabled, clean `$_SERVER`, `$_COOKIE`, `$_GET`, `$_POST` and the `$_REQUEST` arrays.
82
     * - Initialise the correct Language for the currently logged in Author.
83
     * - Start the session and adjust the error handling if the user is logged in
84
     *
85
     * The `$_REQUEST` array has been added in 2.7.0
86
     */
87
    protected function __construct()
88
    {
89
        self::$Profiler = Profiler::instance();
90
91
        if (get_magic_quotes_gpc()) {
92
            General::cleanArray($_SERVER);
93
            General::cleanArray($_COOKIE);
94
            General::cleanArray($_GET);
95
            General::cleanArray($_POST);
96
            General::cleanArray($_REQUEST);
97
        }
98
99
        // Initialize language management
100
        Lang::initialize();
101
        Lang::set(self::$Configuration->get('lang', 'symphony'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration->get('lang', 'symphony') can also be of type array; however, parameter $code of Lang::set() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

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

322
    public static function setDatabase(/** @scrutinizer ignore-unused */ StdClass $database = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
323
    {
324
        return true;
325
    }
326
327
    /**
328
     * Accessor for the current `$Database` instance.
329
     *
330
     * @return Database
331
     */
332
    public static function Database()
333
    {
334
        return self::$Database;
335
    }
336
337
    /**
338
     * This will initialise the Database class and attempt to create a connection
339
     * using the connection details provided in the Symphony configuration. If any
340
     * errors occur whilst doing so, a Symphony Error Page is displayed.
341
     *
342
     * @throws SymphonyErrorPage
343
     * @return boolean
344
     *  This function will return true if the `$Database` was
345
     *  initialised successfully.
346
     */
347
    public static function initialiseDatabase()
348
    {
349
        $details = self::Configuration()->get('database');
350
        self::$Database = new Database($details);
0 ignored issues
show
Bug introduced by
It seems like $details can also be of type integer and string and boolean and double; however, parameter $config of Database::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

350
        self::$Database = new Database(/** @scrutinizer ignore-type */ $details);
Loading history...
351
352
        try {
353
            self::Database()->connect();
354
355
            if (!self::Database()->isConnected()) {
356
                return false;
357
            }
358
359
            self::Database()->setTimeZone(self::Configuration()->get('timezone', 'region'));
0 ignored issues
show
Bug introduced by
It seems like self::Configuration()->get('timezone', 'region') can also be of type array; however, parameter $timezone of Database::setTimeZone() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

359
            self::Database()->setTimeZone(/** @scrutinizer ignore-type */ self::Configuration()->get('timezone', 'region'));
Loading history...
360
        } catch (DatabaseException $e) {
361
            self::throwCustomError(
362
                $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(),
363
                __('Symphony Database Error'),
364
                Page::HTTP_STATUS_ERROR,
365
                'database',
366
                array(
367
                    'error' => $e,
368
                    'message' => __('There was a problem whilst attempting to establish a database connection. Please check all connection information is correct.') . ' ' . __('The following error was returned:')
369
                )
370
            );
371
        }
372
373
        return true;
374
    }
375
376
    /**
377
     * Accessor for the current `$Author` instance.
378
     *
379
     * @since Symphony 2.5.0
380
     * @return Author
381
     */
382
    public static function Author()
383
    {
384
        return self::$Author;
385
    }
386
387
    /**
388
     * Attempts to log an Author in given a username and password.
389
     * If the password is not hashed, it will be hashed using the sha1
390
     * algorithm. The username and password will be sanitized before
391
     * being used to query the Database. If an Author is found, they
392
     * will be logged in and the sanitized username and password (also hashed)
393
     * will be saved as values in the `$Cookie`.
394
     *
395
     * @see toolkit.Cryptography#hash()
396
     * @throws DatabaseException
397
     * @param string $username
398
     *  The Author's username. This will be sanitized before use.
399
     * @param string $password
400
     *  The Author's password. This will be sanitized and then hashed before use
401
     * @param boolean $isHash
402
     *  If the password provided is already hashed, setting this parameter to
403
     *  true will stop it becoming rehashed. By default it is false.
404
     * @return boolean
405
     *  true if the Author was logged in, false otherwise
406
     */
407
    public static function login($username, $password, $isHash = false)
408
    {
409
        $username = trim($username);
410
        $password = trim($password);
411
412
        if (strlen($username) > 0 && strlen($password) > 0) {
413
            $author = (new AuthorManager)
414
                ->select()
415
                ->username($username)
416
                ->limit(1)
417
                ->execute()
418
                ->next();
419
420
            if ($author && Cryptography::compare($password, $author->get('password'), $isHash)) {
421
                self::$Author = $author;
422
423
                // Only migrate hashes if there is no update available as the update might change the tbl_authors table.
424
                // Also, only upgrade if the password is clear text.
425
                if (!self::isUpgradeAvailable() && !$isHash && Cryptography::requiresMigration(self::$Author->get('password'))) {
426
                    self::$Author->set('password', Cryptography::hash($password));
427
428
                    self::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

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

428
                    /** @scrutinizer ignore-deprecated */ self::Database()

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

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

Loading history...
429
                        ->update('tbl_authors')
430
                        ->set(['password' => self::$Author->get('password')])
431
                        ->where(['id' => self::$Author->get('id')])
432
                        ->execute();
433
                }
434
435
                self::$Cookie->set('username', $username);
436
                self::$Cookie->set('pass', self::$Author->get('password'));
437
438
                self::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

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

438
                /** @scrutinizer ignore-deprecated */ self::Database()

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

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

Loading history...
439
                    ->update('tbl_authors')
440
                    ->set(['last_seen' => DateTimeObj::get('Y-m-d H:i:s')])
441
                    ->where(['id' => self::$Author->get('id')])
442
                    ->execute();
443
444
                // Only set custom author language in the backend
445
                if (class_exists('Administration', false)) {
446
                    Lang::set(self::$Author->get('language'));
447
                }
448
449
                return true;
450
            }
451
        }
452
453
        return false;
454
    }
455
456
    /**
457
     * Symphony allows Authors to login via the use of tokens instead of
458
     * a username and password.
459
     * A token is a random string of characters.
460
     * This is a useful feature often used when setting up other Authors accounts or
461
     * if an Author forgets their password.
462
     *
463
     * @param string $token
464
     *  The Author token
465
     * @throws DatabaseException
466
     * @return boolean
467
     *  true if the Author is logged in, false otherwise
468
     */
469
    public static function loginFromToken($token)
470
    {
471
        $token = trim($token);
472
473
        if (strlen($token) === 0) {
474
            return false;
475
        }
476
477
        $am = new AuthorManager;
478
        // Try with the password reset
479
        $rowByResetPass = $am->fetchByPasswordResetToken($token);
480
        if ($rowByResetPass) {
0 ignored issues
show
introduced by
$rowByResetPass is of type Author, thus it always evaluated to true.
Loading history...
481
            $row = $rowByResetPass;
482
            // consume the token
483
            self::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::delete() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseDelete::where() ( Ignorable by Annotation )

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

483
            /** @scrutinizer ignore-deprecated */ self::Database()

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

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

Loading history...
484
                ->delete('tbl_forgotpass')
485
                ->where(['token' => $token])
486
                ->execute();
487
        } else {
488
            // Fallback to auth token
489
            $row = $am->fetchByAuthToken($token);
490
        }
491
492
        if ($row) {
0 ignored issues
show
introduced by
$row is of type Author, thus it always evaluated to true.
Loading history...
493
            self::$Author = $row;
494
            self::$Cookie->set('username', $row['username']);
495
            self::$Cookie->set('pass', $row['password']);
496
            return self::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::update() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseUpdate::where() ( Ignorable by Annotation )

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

496
            return /** @scrutinizer ignore-deprecated */ self::Database()

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

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

Loading history...
497
                ->update('tbl_authors')
498
                ->set(['last_seen' => DateTimeObj::get('Y-m-d H:i:s')])
499
                ->where(['id' => $row['id']])
500
                ->execute()
501
                ->success();
502
        }
503
504
        return false;
505
    }
506
507
    /**
508
     * This function will destroy the currently logged in `$Author`
509
     * session, essentially logging them out.
510
     *
511
     * @see core.Cookie#expire()
512
     */
513
    public static function logout()
514
    {
515
        self::$Cookie->expire();
516
    }
517
518
    /**
519
     * This function determines whether an there is a currently logged in
520
     * Author for Symphony by using the `$Cookie`'s username
521
     * and password. If the instance is not found, they will be logged
522
     * in using the cookied credentials.
523
     *
524
     * @see login()
525
     * @return boolean
526
     */
527
    public static function isLoggedIn()
528
    {
529
        // Check to see if we already have an Author instance.
530
        if (self::$Author) {
531
            return true;
532
        }
533
534
        // No author instance found, attempt to log in with the cookied credentials
535
        return self::login(self::$Cookie->get('username'), self::$Cookie->get('pass'), true);
536
    }
537
538
    /**
539
     * Returns the most recent version found in the `/install/migrations` folder.
540
     * Returns a semver version string if an updater
541
     * has been found.
542
     * Returns `false` otherwise.
543
     *
544
     * @since Symphony 2.3.1
545
     * @return string|boolean
546
     */
547
    public static function getMigrationVersion()
548
    {
549
        if (self::isInstallerAvailable() && class_exists('Updater')) {
550
            $migrations = Updater::getAvailableMigrations();
551
            $m = end($migrations);
552
            if (!$m) {
553
                return false;
554
            }
555
            return $m->getVersion();
556
        }
557
558
        return false;
559
    }
560
561
    /**
562
     * Checks if an update is available and applicable for the current installation.
563
     *
564
     * @since Symphony 2.3.1
565
     * @return boolean
566
     */
567
    public static function isUpgradeAvailable()
568
    {
569
        if (self::isInstallerAvailable()) {
570
            $migration_version = self::getMigrationVersion();
571
            $current_version = Symphony::Configuration()->get('version', 'symphony');
572
573
            return \Composer\Semver\Comparator::lessThan($current_version, $migration_version);
0 ignored issues
show
Bug introduced by
It seems like $current_version can also be of type array; however, parameter $version1 of Composer\Semver\Comparator::lessThan() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

573
            return \Composer\Semver\Comparator::lessThan(/** @scrutinizer ignore-type */ $current_version, $migration_version);
Loading history...
574
        }
575
576
        return false;
577
    }
578
579
    /**
580
     * Checks if the installer/updater is available.
581
     *
582
     * @since Symphony 2.3.1
583
     * @return boolean
584
     */
585
    public static function isInstallerAvailable()
586
    {
587
        return file_exists(DOCROOT . '/install/index.php');
0 ignored issues
show
Bug introduced by
The constant DOCROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
588
    }
589
590
    /**
591
     * A wrapper for throwing a new Symphony Error page.
592
     *
593
     * @see core.SymphonyErrorPage
594
     * @param string|XMLElement $message
595
     *  A description for this error, which can be provided as a string
596
     *  or as an XMLElement.
597
     * @param string $heading
598
     *  A heading for the error page
599
     * @param integer $status
600
     *  Properly sets the HTTP status code for the response. Defaults to
601
     *  `Page::HTTP_STATUS_ERROR`. Use `Page::HTTP_STATUS_XXX` to set this value.
602
     * @param string $template
603
     *  A string for the error page template to use, defaults to 'generic'. This
604
     *  can be the name of any template file in the `TEMPLATES` directory.
605
     *  A template using the naming convention of `tpl.*.php`.
606
     * @param array $additional
607
     *  Allows custom information to be passed to the Symphony Error Page
608
     *  that the template may want to expose, such as custom Headers etc.
609
     * @throws SymphonyErrorPage
610
     */
611
    public static function throwCustomError($message, $heading = 'Symphony Fatal Error', $status = Page::HTTP_STATUS_ERROR, $template = 'generic', array $additional = array())
612
    {
613
        throw new SymphonyErrorPage($message, $heading, $template, $additional, $status);
614
    }
615
616
    /**
617
     * Setter accepts a previous Throwable. Useful for determining the context
618
     * of a current Throwable (ie. detecting recursion).
619
     *
620
     * @since Symphony 2.3.2
621
     *
622
     * @since Symphony 2.7.0
623
     *  This function works with both Exception and Throwable
624
     *  Supporting both PHP 5.6 and 7 forces use to not qualify the $e parameter
625
     *
626
     * @param Throwable $ex
627
     */
628
    public static function setException($ex)
629
    {
630
        self::$exception = $ex;
0 ignored issues
show
Documentation Bug introduced by
$ex is of type Throwable, but the property $exception was declared to be of type Exception. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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

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

class Alien {}

class Dalek extends Alien {}

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

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
631
    }
632
633
    /**
634
     * Accessor for `self::$exception`.
635
     *
636
     * @since Symphony 2.3.2
637
     * @return Throwable|null
638
     */
639
    public static function getException()
640
    {
641
        return self::$exception;
642
    }
643
644
    /**
645
     * Returns the page namespace based on the current URL.
646
     * A few examples:
647
     *
648
     * /login
649
     * /publish
650
     * /blueprints/datasources
651
     * [...]
652
     * /extension/$extension_name/$page_name
653
     *
654
     * This method is especially useful in couple with the translation function.
655
     *
656
     * @see toolkit#__()
657
     * @return string
658
     *  The page namespace, without any action string (e.g. "new", "saved") or
659
     *  any value that depends upon the single setup (e.g. the section handle in
660
     *  /publish/$handle)
661
     */
662
    public static function getPageNamespace()
663
    {
664
        if (self::$namespace !== false) {
0 ignored issues
show
introduced by
The condition self::namespace !== false is always true. If self::namespace !== false can have other possible types, add them to symphony/lib/core/class.symphony.php:54
Loading history...
665
            return self::$namespace;
666
        }
667
668
        $page = getCurrentPage();
669
670
        if (!is_null($page)) {
671
            $page = trim($page, '/');
672
        }
673
674
        if (substr($page, 0, 7) == 'publish') {
675
            self::$namespace = '/publish';
676
        } elseif (empty($page) && isset($_REQUEST['mode'])) {
677
            self::$namespace = '/login';
678
        } elseif (empty($page)) {
679
            self::$namespace = null;
680
        } else {
681
            $bits = explode('/', $page);
682
683
            if ($bits[0] == 'extension') {
684
                self::$namespace = sprintf('/%s/%s/%s', $bits[0], $bits[1], $bits[2]);
685
            } else {
686
                self::$namespace =  sprintf('/%s/%s', $bits[0], isset($bits[1]) ? $bits[1] : '');
687
            }
688
        }
689
690
        return self::$namespace;
691
    }
692
}
693
694
/**
695
 * The `SymphonyErrorPageHandler` extends the `GenericExceptionHandler`
696
 * to allow the template for the exception to be provided from the `TEMPLATES`
697
 * directory
698
 */
699
class SymphonyErrorPageHandler extends GenericExceptionHandler
700
{
701
    /**
702
     * The render function will take a `SymphonyErrorPage` exception and
703
     * output a HTML page. This function first checks to see if the `GenericExceptionHandler`
704
     * is enabled and pass control to it if not. After that, the method checks if there is a custom
705
     * template for this exception otherwise it reverts to using the default
706
     * `usererror.generic.php`. If the template is not found, it will call
707
     * `GenericExceptionHandler::render()`.
708
     *
709
     * @param Throwable $e
710
     *  The Throwable object
711
     * @return string
712
     *  An HTML string
713
     */
714
    public static function render($e)
715
    {
716
        // Validate the type, resolve to a 404 if not valid
717
        if (!static::isValidThrowable($e)) {
718
            $e = new FrontendPageNotFoundException();
719
        }
720
721
        if (!GenericExceptionHandler::$enabled) {
722
            return GenericExceptionHandler::render($e);
723
        }
724
        else if ($e->getTemplate() === false) {
0 ignored issues
show
Bug introduced by
The method getTemplate() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

941
            file_get_contents(/** @scrutinizer ignore-type */ self::getTemplate('fatalerror.database')),
Loading history...
942
            !self::$enabled ? 'Database error' : $e->getDatabaseErrorMessage(),
0 ignored issues
show
Bug introduced by
The method getDatabaseErrorMessage() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as DatabaseException. ( Ignorable by Annotation )

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

942
            !self::$enabled ? 'Database error' : $e->/** @scrutinizer ignore-call */ getDatabaseErrorMessage(),
Loading history...
Bug introduced by
The method getDatabaseErrorMessage() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

942
            !self::$enabled ? 'Database error' : $e->/** @scrutinizer ignore-call */ getDatabaseErrorMessage(),

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

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

Loading history...
943
            !self::$enabled ? '' : $e->getQuery(),
0 ignored issues
show
Bug introduced by
The method getQuery() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as DatabaseException. ( Ignorable by Annotation )

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

943
            !self::$enabled ? '' : $e->/** @scrutinizer ignore-call */ getQuery(),
Loading history...
Bug introduced by
The method getQuery() does not exist on FrontendPageNotFoundException. ( Ignorable by Annotation )

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

943
            !self::$enabled ? '' : $e->/** @scrutinizer ignore-call */ getQuery(),

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

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

Loading history...
944
            !self::$enabled ? '' : $trace,
945
            !self::$enabled ? '' : $queries
946
        );
947
948
        $html = str_replace('{ASSETS_URL}', ASSETS_URL, $html);
0 ignored issues
show
Bug introduced by
The constant ASSETS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
949
        $html = str_replace('{SYMPHONY_URL}', SYMPHONY_URL, $html);
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
950
        $html = str_replace('{URL}', URL, $html);
0 ignored issues
show
Bug introduced by
The constant URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
951
        $html = str_replace('{PHP}', PHP_VERSION, $html);
952
        $html = str_replace('{MYSQL}', Symphony::Database()->getVersion(), $html);
953
954
        return $html;
955
    }
956
}
957