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
04:24
created

Installer::initialiseLang()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
rs 9.4285
1
<?php
2
3
    use SymphonyCms\Installer\Lib\Requirements;
4
    use SymphonyCms\Installer\Steps;
5
6
    class Installer extends Administration
7
    {
8
        protected static $requirements;
9
10
        /**
11
         * Override the default Symphony constructor to initialise the Log, Config
12
         * and Database objects for installation/update. This allows us to use the
13
         * normal accessors.
14
         */
15
        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...
16
        {
17
            self::$Profiler = Profiler::instance();
18
            self::$Profiler->sample('Engine Initialisation');
19
20
            if (get_magic_quotes_gpc()) {
21
                General::cleanArray($_SERVER);
22
                General::cleanArray($_COOKIE);
23
                General::cleanArray($_GET);
24
                General::cleanArray($_POST);
25
            }
26
27
            // Include the default Config for installation.
28
            include(INSTALL . '/includes/config_default.php');
29
            static::initialiseConfiguration($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...
30
31
            // Initialize date/time
32
            define_safe('__SYM_DATE_FORMAT__', self::Configuration()->get('date_format', 'region'));
33
            define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region'));
34
            define_safe('__SYM_DATETIME_FORMAT__',
35
                __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__);
36
            DateTimeObj::setSettings(self::Configuration()->get('region'));
37
38
            // Initialize Language, Logs and Database
39
            static::initialiseLang();
40
            static::initialiseLog(INSTALL_LOGS . '/install');
41
            static::initialiseDatabase();
42
43
            // Initialize error handlers
44
            GenericExceptionHandler::initialise(Symphony::Log());
45
            GenericErrorHandler::initialise(Symphony::Log());
46
47
            self::$requirements = new Requirements();
48
        }
49
50
        /**
51
         * Initialises the language by looking at the `lang` key,
52
         * passed via GET or POST
53
         */
54
        public static function initialiseLang()
55
        {
56
            $lang = !empty($_REQUEST['lang']) ? preg_replace('/[^a-zA-Z\-]/', null, $_REQUEST['lang']) : 'en';
57
            Lang::initialize();
58
            Lang::set($lang, false);
59
        }
60
61
        /**
62
         * Overrides the default `initialiseLog()` method and writes
63
         * logs to manifest/logs/install
64
         *
65
         * @param null $filename
66
         * @return boolean|void
67
         * @throws Exception
68
         */
69 View Code Duplication
        public static function initialiseLog($filename = null)
70
        {
71
            if (is_dir(INSTALL_LOGS) || General::realiseDirectory(INSTALL_LOGS,
72
                self::Configuration()->get('write_mode', 'directory'))
73
            ) {
74
                return parent::initialiseLog($filename);
75
            }
76
77
            return;
78
        }
79
80
        /**
81
         * Overrides the default `initialiseDatabase()` method
82
         * This allows us to still use the normal accessor
83
         */
84
        public static function initialiseDatabase()
85
        {
86
            self::setDatabase();
87
        }
88
89
        /**
90
         * This function returns an instance of the Installer
91
         * class. It is the only way to create a new Installer, as
92
         * it implements the Singleton interface
93
         *
94
         * @return Installer
95
         */
96
        public static function instance()
97
        {
98
            if (!(self::$_instance instanceof Installer)) {
99
                self::$_instance = new Installer;
100
            }
101
102
            return self::$_instance;
103
        }
104
105
        public function run()
106
        {
107
            // Make sure a log file is available
108
            if (is_null(Symphony::Log())) {
109
                self::__render(new InstallerPage('missing-log'));
110
            }
111
112
            // Check essential server requirements
113
            $errors = self::__checkRequirements();
114 View Code Duplication
            if (!empty($errors)) {
115
                Symphony::Log()->error('Installer - Missing requirements.');
116
117
                foreach ($errors as $err) {
118
                    Symphony::Log()->error(
119
                        sprintf('Requirement - %s', $err['msg'])
120
                    );
121
                }
122
123
                self::__render(new InstallerPage('requirements', array(
124
                    'errors' => $errors
125
                )));
126
            }
127
128
            // If language is not set and there is language packs available, show language selection pages
129
            if (!isset($_POST['lang']) && count(Lang::getAvailableLanguages(false)) > 1) {
130
                self::__render(new InstallerPage('languages'));
131
            }
132
133
            // Check for configuration errors and, if there are no errors, install Symphony!
134
            if (isset($_POST['fields'])) {
135
                $fields = $_POST['fields'];
136
                $errors = self::checkConfiguration($fields);
137 View Code Duplication
                if (!empty($errors)) {
138
                    Symphony::Log()->error('Installer - Wrong configuration.');
139
140
                    foreach ($errors as $err) {
141
                        Symphony::Log()->error(sprintf('Configuration - %s', $err['msg']));
142
                    }
143
                } else {
144
                    $disabled_extensions = self::install($fields);
145
146
                    self::__render(new InstallerPage('success', array(
147
                        'disabled-extensions' => $disabled_extensions
148
                    )));
149
                }
150
            }
151
152
            // Display the Installation page
153
            self::__render(new InstallerPage('configuration', array(
154
                'errors' => $errors,
155
                'default-config' => Symphony::Configuration()->get()
156
            )));
157
        }
158
159
        protected static function __render(InstallerPage $page)
160
        {
161
            $output = $page->generate();
162
163
            header('Content-Type: text/html; charset=utf-8');
164
            echo $output;
165
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method __render() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
166
        }
167
168
        /**
169
         * This function checks the server can support a Symphony installation.
170
         * It checks that PHP is 5.2+, MySQL, Zlib, LibXML, XSLT modules are enabled
171
         * and a `install.sql` file exists.
172
         * If any of these requirements fail the installation will not proceed.
173
         *
174
         * @return array
175
         *  An associative array of errors, with `msg` and `details` keys
176
         */
177
        private static function __checkRequirements()
178
        {
179
            $errors = array();
180
181
            // Make sure the install.sql file exists
182
            if (!file_exists(INSTALL . '/includes/install.sql') || !is_readable(INSTALL . '/includes/install.sql')) {
183
                $errors[] = array(
184
                    'msg' => __('Missing install.sql file'),
185
                    'details' => __('It appears that %s is either missing or not readable. This is required to populate the database and must be uploaded before installation can commence. Ensure that PHP has read permissions.',
186
                        array('<code>install.sql</code>'))
187
                );
188
            }
189
190
            $errors = array_merge($errors, self::$requirements->check());
191
192
            return $errors;
193
        }
194
195
        /**
196
         * This function checks the current Configuration (which is the values entered
197
         * by the user on the installation form) to ensure that `/symphony` and `/workspace`
198
         * folders exist and are writable and that the Database credentials are correct.
199
         * Once those initial checks pass, the rest of the form values are validated.
200
         *
201
         * @param array $fields
202
         * @return array An associative array of errors if something went wrong, otherwise an empty array.
203
         */
204
        public static function checkConfiguration(array $fields)
205
        {
206
            $errors = array();
207
208
            // Testing the database connection
209
            try {
210
                Symphony::Database()->connect(
211
                    $fields['database']['host'],
212
                    $fields['database']['user'],
213
                    $fields['database']['password'],
214
                    $fields['database']['port'],
215
                    $fields['database']['db']
216
                );
217
            } catch (DatabaseException $e) {
218
                // Invalid credentials
219
                // @link http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
220
                if ($e->getDatabaseErrorCode() === 1044 || $e->getDatabaseErrorCode() === 1045) {
221
                    $errors['database-invalid-credentials'] = array(
222
                        'msg' => 'Database credentials were denied',
223
                        'details' => __('Symphony was unable to access the database with these credentials.')
224
                    );
225
                } // Connection related
226
                else {
227
                    $errors['no-database-connection'] = array(
228
                        'msg' => 'Could not establish database connection.',
229
                        'details' => __('Symphony was unable to establish a valid database connection. You may need to modify host or port settings.')
230
                    );
231
                }
232
            }
233
234
            try {
235
                // Check the database table prefix is legal. #1815
236
                if (!preg_match('/^[0-9a-zA-Z\$_]*$/', $fields['database']['tbl_prefix'])) {
237
                    $errors['database-table-prefix'] = array(
238
                        'msg' => 'Invalid database table prefix: ‘' . $fields['database']['tbl_prefix'] . '’',
239
                        'details' => __('The table prefix %s is invalid. The table prefix must only contain numbers, letters or underscore characters.',
240
                            array('<code>' . $fields['database']['tbl_prefix'] . '</code>'))
241
                    );
242
                } // Check the database credentials
243
                elseif (Symphony::Database()->isConnected()) {
244
                    // Incorrect MySQL version
245
                    $version = Symphony::Database()->fetchVar('version', 0, "SELECT VERSION() AS `version`;");
246
                    if (version_compare($version, '5.5', '<')) {
247
                        $errors['database-incorrect-version'] = array(
248
                            'msg' => 'MySQL Version is not correct. ' . $version . ' detected.',
249
                            'details' => __('Symphony requires %1$s or greater to work, however version %2$s was detected. This requirement must be met before installation can proceed.',
250
                                array('<code>MySQL 5.5</code>', '<code>' . $version . '</code>'))
251
                        );
252
                    } else {
253
                        // Existing table prefix
254
                        if (Symphony::Database()->tableExists($fields['database']['tbl_prefix'] . '%')) {
255
                            $errors['database-table-prefix'] = array(
256
                                'msg' => 'Database table prefix clash with ‘' . $fields['database']['db'] . '’',
257
                                'details' => __('The table prefix %s is already in use. Please choose a different prefix to use with Symphony.',
258
                                    array(
259
260
                                        '<code>' . $fields['database']['tbl_prefix'] . '</code>'
261
                                    ))
262
                            );
263
                        }
264
                    }
265
                }
266
            } catch (DatabaseException $e) {
267
                $errors['unknown-database'] = array(
268
                    'msg' => 'Database ‘' . $fields['database']['db'] . '’ not found.',
269
                    'details' => __('Symphony was unable to connect to the specified database.')
270
                );
271
            }
272
273
            // Website name not entered
274
            if (trim($fields['general']['sitename']) === '') {
275
                $errors['general-no-sitename'] = array(
276
                    'msg' => 'No sitename entered.',
277
                    'details' => __('You must enter a Site name. This will be shown at the top of your backend.')
278
                );
279
            }
280
281
            // Username Not Entered
282
            if (trim($fields['user']['username']) === '') {
283
                $errors['user-no-username'] = array(
284
                    'msg' => 'No username entered.',
285
                    'details' => __('You must enter a Username. This will be your Symphony login information.')
286
                );
287
            }
288
289
            // Password Not Entered
290
            if (trim($fields['user']['password']) === '') {
291
                $errors['user-no-password'] = array(
292
                    'msg' => 'No password entered.',
293
                    'details' => __('You must enter a Password. This will be your Symphony login information.')
294
                );
295
            } // Password mismatch
296
            elseif ($fields['user']['password'] !== $fields['user']['confirm-password']) {
297
                $errors['user-password-mismatch'] = array(
298
                    'msg' => 'Passwords did not match.',
299
                    'details' => __('The password and confirmation did not match. Please retype your password.')
300
                );
301
            }
302
303
            // No Name entered
304
            if (trim($fields['user']['firstname']) === '' || trim($fields['user']['lastname']) === '') {
305
                $errors['user-no-name'] = array(
306
                    'msg' => 'Did not enter First and Last names.',
307
                    'details' => __('You must enter your name.')
308
                );
309
            }
310
311
            // Invalid Email
312
            if (!preg_match('/^\w(?:\.?[\w%+-]+)*@\w(?:[\w-]*\.)+?[a-z]{2,}$/i', $fields['user']['email'])) {
313
                $errors['user-invalid-email'] = array(
314
                    'msg' => 'Invalid email address supplied.',
315
                    'details' => __('This is not a valid email address. You must provide an email address since you will need it if you forget your password.')
316
                );
317
            }
318
319
            // Admin path not entered
320
            if (trim($fields['symphony']['admin-path']) === '') {
321
                $errors['no-symphony-path'] = array(
322
                    'msg' => 'No Symphony path entered.',
323
                    'details' => __('You must enter a path for accessing Symphony, or leave the default. This will be used to access Symphony\'s backend.')
324
                );
325
            }
326
327
            return $errors;
328
        }
329
330
        /**
331
         * @param array $data
332
         * @return array
333
         */
334
        public static function install(array $data)
335
        {
336
            $start = microtime(true);
337
            Symphony::Log()->info('INSTALLATION PROCESS STARTED (' . DateTimeObj::get('c') . ')');
338
339
            // Configuration: Populating array
340
            $conf = Symphony::Configuration()->get();
341
342
            foreach ($conf as $group => $settings) {
343
                foreach ($settings as $key => $value) {
344
                    // This ensures on data the configuration cares about is populated,
345
                    // anything else will be ignored and accessible in `$data`.
346
                    if (isset($data[$group]) && isset($data[$group][$key])) {
347
                        $conf[$group][$key] = $data[$group][$key];
348
                    }
349
                }
350
            }
351
352
            Symphony::Configuration()->setArray($conf);
353
354
            $steps = [
355
                // Create database
356
                Steps\CreateDatabase::class,
357
                // Create manifest folder structure
358
                Steps\CreateManifest::class,
359
                // Write .htaccess
360
                Steps\CreateHtaccess::class,
361
                // Create or import the workspace
362
                Steps\Workspace::class,
363
                // Enable extensions
364
                Steps\EnableExtensions::class,
365
                // Enable language
366
                Steps\EnableLanguage::class
367
            ];
368
369
            try {
370
                foreach ($steps as $step) {
371
                    (new $step(Symphony::Log()->getLog()))->handle(Symphony::Configuration(), $data);
372
                }
373
            } catch (Exception $ex) {
374
                self::__abort($ex->getMessage(), $start);
375
            }
376
377
            // Writing configuration file
378
            Symphony::Log()->info('WRITING: Configuration File');
379
            if (!Symphony::Configuration()->write(CONFIG, Symphony::Configuration()->get('write_mode', 'file'))) {
380
                self::__abort(
381
                    'Could not create config file ‘' . CONFIG . '’. Check permission on /manifest.',
382
                    $start
383
                );
384
            }
385
386
            // Installation completed. Woo-hoo!
387
            Symphony::Log()->info(sprintf('INSTALLATION COMPLETED: Execution Time - %d sec (%s)',
388
                max(1, time() - $start),
389
                date('d.m.y H:i:s')
390
            ));
391
392
            return [];
393
        }
394
395
        /**
396
         * If something went wrong, the `__abort` function will write an entry to the Log
397
         * file and display the failure page to the user.
398
         *
399
         * @todo: Resume installation after an error has been fixed.
400
         * @param string $message
401
         * @param integer $start
402
         */
403
        protected static function __abort($message, $start)
404
        {
405
            Symphony::Log()->error($message);
406
            Symphony::Log()->error(sprintf('INSTALLATION ABORTED: Execution Time - %f sec (%s)',
407
                microtime(true) - $start,
408
                date('d.m.y H:i:s')
409
            ));
410
411
            self::__render(new InstallerPage('failure'));
412
        }
413
    }
414