Test Failed
Push — master ( 8c47c2...3acf9f )
by Steve
12:37
created

ElggInstaller::runRequirements()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 16
nc 2
nop 1
dl 0
loc 37
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
use Elgg\Filesystem\Directory;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Directory.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
4
use Elgg\Application;
5
use Elgg\Config;
6
use Elgg\Database\DbConfig;
7
use Elgg\Project\Paths;
8
use Elgg\Di\ServiceProvider;
9
use Elgg\Http\Request;
10
11
/**
12
 * Elgg Installer.
13
 * Controller for installing Elgg. Supports both web-based on CLI installation.
14
 *
15
 * This controller steps the user through the install process. The method for
16
 * each step handles both the GET and POST requests. There is no XSS/CSRF protection
17
 * on the POST processing since the installer is only run once by the administrator.
18
 *
19
 * The installation process can be resumed by hitting the first page. The installer
20
 * will try to figure out where to pick up again.
21
 *
22
 * All the logic for the installation process is in this class, but it depends on
23
 * the core libraries. To do this, we selectively load a subset of the core libraries
24
 * for the first few steps and then load the entire engine once the database and
25
 * site settings are configured. In addition, this controller does its own session
26
 * handling until the database is setup.
27
 *
28
 * There is an aborted attempt in the code at creating the data directory for
29
 * users as a subdirectory of Elgg's root. The idea was to protect this directory
30
 * through a .htaccess file. The problem is that a malicious user can upload a
31
 * .htaccess of his own that overrides the protection for his user directory. The
32
 * best solution is server level configuration that turns off AllowOverride for the
33
 * data directory. See ticket #3453 for discussion on this.
34
 */
35
class ElggInstaller {
36
	
37
	private $steps = [
38
		'welcome',
39
		'requirements',
40
		'database',
41
		'settings',
42
		'admin',
43
		'complete',
44
		];
45
46
	private $has_completed = [
47
		'config' => false,
48
		'database' => false,
49
		'settings' => false,
50
		'admin' => false,
51
	];
52
53
	private $is_action = false;
54
55
	private $autoLogin = true;
56
57
	/**
58
	 * @var ServiceProvider
59
	 */
60
	private $services;
61
62
	/**
63
	 * @var Config
64
	 */
65
	private $config;
66
67
	/**
68
	 * @var Application
69
	 */
70
	private $app;
71
72
	/**
73
	 * Dispatches a request to one of the step controllers
74
	 *
75
	 * @return void
76
	 * @throws InstallationException
77
	 */
78
	public function run() {
79
		$this->getApp();
80
81
		$this->is_action = $this->services->request->getMethod() === 'POST';
82
83
		$step = get_input('step', 'welcome');
84
85
		if (!in_array($step, $this->getSteps())) {
86
			$msg = elgg_echo('InstallationException:UnknownStep', [$step]);
87
			throw new InstallationException($msg);
88
		}
89
90
		$this->determineInstallStatus();
91
	
92
		$this->checkInstallCompletion($step);
93
94
		// check if this is an install being resumed
95
		$this->resumeInstall($step);
96
97
		$this->finishBootstrapping($step);
98
99
		$params = $this->services->request->request->all();
100
101
		$method = "run" . ucwords($step);
102
		$this->$method($params);
103
	}
104
105
	/**
106
	 * Build the application needed by the installer
107
	 *
108
	 * @return Application
109
	 * @throws InstallationException
110
	 */
111
	protected function getApp() {
112
		if ($this->app) {
113
			return $this->app;
114
		}
115
116
		$config = new Config();
117
		$config->elgg_config_locks = false;
118
		$config->installer_running = true;
119
		$config->dbencoding = 'utf8mb4';
120
121
		$this->config = $config;
122
		$this->services = new ServiceProvider($config);
123
124
		$app = Application::factory([
125
			'service_provider' => $this->services,
126
			'handle_exceptions' => false,
127
			'handle_shutdown' => false,
128
129
			// allows settings.php to be loaded, which might try to write to global $CONFIG
130
			// which is only a problem due to the config values that deep-write to arrays,
131
			// like cookies.
132
			'overwrite_global_config' => false,
133
		]);
134
		$app->loadCore();
135
		$this->app = $app;
136
137
		$this->services->setValue('session', \ElggSession::getMock());
138
		$this->services->views->setViewtype('installation');
139
		$this->services->views->registerPluginViews(Paths::elgg());
140
		$this->services->translator->registerTranslations(Paths::elgg() . "install/languages/", true);
141
142
		return $this->app;
143
	}
144
145
	/**
146
	 * Set the auto login flag
147
	 *
148
	 * @param bool $flag Auto login
149
	 *
150
	 * @return void
151
	 */
152
	public function setAutoLogin($flag) {
153
		$this->autoLogin = (bool) $flag;
154
	}
155
156
	/**
157
	 * A batch install of Elgg
158
	 *
159
	 * All required parameters must be passed in as an associative array. See
160
	 * $requiredParams for a list of them. This creates the necessary files,
161
	 * loads the database, configures the site settings, and creates the admin
162
	 * account. If it fails, an exception is thrown. It does not check any of
163
	 * the requirements as the multiple step web installer does.
164
	 *
165
	 * @param array $params          Array of key value pairs
166
	 * @param bool  $create_htaccess Should .htaccess be created
167
	 *
168
	 * @return void
169
	 * @throws InstallationException
170
	 */
171
	public function batchInstall(array $params, $create_htaccess = false) {
172
		$this->getApp();
173
174
		$defaults = [
175
			'dbhost' => 'localhost',
176
			'dbprefix' => 'elgg_',
177
			'language' => 'en',
178
			'siteaccess' => ACCESS_PUBLIC,
179
		];
180
		$params = array_merge($defaults, $params);
181
182
		$required_params = [
183
			'dbuser',
184
			'dbpassword',
185
			'dbname',
186
			'sitename',
187
			'wwwroot',
188
			'dataroot',
189
			'displayname',
190
			'email',
191
			'username',
192
			'password',
193
		];
194
		foreach ($required_params as $key) {
195
			if (empty($params[$key])) {
196
				$msg = elgg_echo('install:error:requiredfield', [$key]);
197
				throw new InstallationException($msg);
198
			}
199
		}
200
201
		// password is passed in once
202
		$params['password1'] = $params['password2'] = $params['password'];
203
204
		if ($create_htaccess) {
205
			$rewrite_tester = new ElggRewriteTester();
206
			if (!$rewrite_tester->createHtaccess($params['wwwroot'])) {
207
				throw new InstallationException(elgg_echo('install:error:htaccess'));
208
			}
209
		}
210
211
		$this->determineInstallStatus();
212
213
		if (!$this->has_completed['config']) {
214
			if (!$this->createSettingsFile($params)) {
215
				throw new InstallationException(elgg_echo('install:error:settings'));
216
			}
217
		}
218
219
		$this->loadSettingsFile();
220
221
		// Make sure settings file matches parameters
222
		$config = $this->config;
223
		$config_keys = [
224
			// param key => config key
225
			'dbhost' => 'dbhost',
226
			'dbuser' => 'dbuser',
227
			'dbpassword' => 'dbpass',
228
			'dbname' => 'dbname',
229
			'dataroot' => 'dataroot',
230
			'dbprefix' => 'dbprefix',
231
		];
232
		foreach ($config_keys as $params_key => $config_key) {
233
			if ($params[$params_key] !== $config->$config_key) {
234
				throw new InstallationException(elgg_echo('install:error:settings_mismatch', [$config_key]));
235
			}
236
		}
237
238
		if (!$this->connectToDatabase()) {
239
			throw new InstallationException(elgg_echo('install:error:databasesettings'));
240
		}
241
242
		if (!$this->has_completed['database']) {
243
			if (!$this->installDatabase()) {
244
				throw new InstallationException(elgg_echo('install:error:cannotloadtables'));
245
			}
246
		}
247
248
		// load remaining core libraries
249
		$this->finishBootstrapping('settings');
250
251
		if (!$this->saveSiteSettings($params)) {
252
			throw new InstallationException(elgg_echo('install:error:savesitesettings'));
253
		}
254
255
		if (!$this->createAdminAccount($params)) {
256
			throw new InstallationException(elgg_echo('install:admin:cannot_create'));
257
		}
258
	}
259
260
	/**
261
	 * Renders the data passed by a controller
262
	 *
263
	 * @param string $step The current step
264
	 * @param array  $vars Array of vars to pass to the view
265
	 *
266
	 * @return void
267
	 */
268
	protected function render($step, $vars = []) {
269
		$vars['next_step'] = $this->getNextStep($step);
270
271
		$title = elgg_echo("install:$step");
272
		$body = elgg_view("install/pages/$step", $vars);
273
				
274
		echo elgg_view_page(
275
				$title,
276
				$body,
277
				'default',
278
				[
279
					'step' => $step,
280
					'steps' => $this->getSteps(),
281
					]
282
				);
283
		exit;
284
	}
285
286
	/**
287
	 * Step controllers
288
	 */
289
290
	/**
291
	 * Welcome controller
292
	 *
293
	 * @param array $vars Not used
294
	 *
295
	 * @return void
296
	 */
297
	protected function runWelcome($vars) {
0 ignored issues
show
Unused Code introduced by
The parameter $vars is not used and could be removed.

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

Loading history...
298
		$this->render('welcome');
299
	}
300
301
	/**
302
	 * Requirements controller
303
	 *
304
	 * Checks version of php, libraries, permissions, and rewrite rules
305
	 *
306
	 * @param array $vars Vars
307
	 *
308
	 * @return void
309
	 */
310
	protected function runRequirements($vars) {
0 ignored issues
show
Unused Code introduced by
The parameter $vars is not used and could be removed.

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

Loading history...
311
312
		$report = [];
313
314
		// check PHP parameters and libraries
315
		$this->checkPHP($report);
316
317
		// check URL rewriting
318
		$this->checkRewriteRules($report);
319
320
		// check for existence of settings file
321
		if ($this->checkSettingsFile($report) != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
322
			// no file, so check permissions on engine directory
323
			$this->isInstallDirWritable($report);
324
		}
325
326
		// check the database later
327
		$report['database'] = [[
328
			'severity' => 'info',
329
			'message' => elgg_echo('install:check:database')
330
		]];
331
332
		// any failures?
333
		$numFailures = $this->countNumConditions($report, 'failure');
334
335
		// any warnings
336
		$numWarnings = $this->countNumConditions($report, 'warning');
337
338
339
		$params = [
340
			'report' => $report,
341
			'num_failures' => $numFailures,
342
			'num_warnings' => $numWarnings,
343
		];
344
345
		$this->render('requirements', $params);
346
	}
347
348
	/**
349
	 * Database set up controller
350
	 *
351
	 * Creates the settings.php file and creates the database tables
352
	 *
353
	 * @param array $submissionVars Submitted form variables
354
	 *
355
	 * @return void
356
	 */
357
	protected function runDatabase($submissionVars) {
358
359
		$formVars = [
360
			'dbuser' => [
361
				'type' => 'text',
362
				'value' => '',
363
				'required' => true,
364
				],
365
			'dbpassword' => [
366
				'type' => 'password',
367
				'value' => '',
368
				'required' => false,
369
				],
370
			'dbname' => [
371
				'type' => 'text',
372
				'value' => '',
373
				'required' => true,
374
				],
375
			'dbhost' => [
376
				'type' => 'text',
377
				'value' => 'localhost',
378
				'required' => true,
379
				],
380
			'dbprefix' => [
381
				'type' => 'text',
382
				'value' => 'elgg_',
383
				'required' => true,
384
				],
385
			'dataroot' => [
386
				'type' => 'text',
387
				'value' => '',
388
				'required' => true,
389
			],
390
			'wwwroot' => [
391
				'type' => 'url',
392
				'value' => _elgg_services()->config->wwwroot,
393
				'required' => true,
394
			],
395
			'timezone' => [
396
				'type' => 'dropdown',
397
				'value' => 'UTC',
398
				'options' => \DateTimeZone::listIdentifiers(),
399
				'required' => true
400
			]
401
		];
402
403
		if ($this->checkSettingsFile()) {
404
			// user manually created settings file so we fake out action test
405
			$this->is_action = true;
406
		}
407
408
		if ($this->is_action) {
409
			call_user_func(function () use ($submissionVars, $formVars) {
410
				// only create settings file if it doesn't exist
411
				if (!$this->checkSettingsFile()) {
412
					if (!$this->validateDatabaseVars($submissionVars, $formVars)) {
413
						// error so we break out of action and serve same page
414
						return;
415
					}
416
417
					if (!$this->createSettingsFile($submissionVars)) {
418
						return;
419
					}
420
				}
421
422
				// check db version and connect
423
				if (!$this->connectToDatabase()) {
424
					return;
425
				}
426
427
				if (!$this->installDatabase()) {
428
					return;
429
				}
430
431
				system_message(elgg_echo('install:success:database'));
432
433
				$this->continueToNextStep('database');
434
			});
435
		}
436
437
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
438
439
		$params = ['variables' => $formVars,];
440
441
		if ($this->checkSettingsFile()) {
442
			// settings file exists and we're here so failed to create database
443
			$params['failure'] = true;
444
		}
445
446
		$this->render('database', $params);
447
	}
448
449
	/**
450
	 * Site settings controller
451
	 *
452
	 * Sets the site name, URL, data directory, etc.
453
	 *
454
	 * @param array $submissionVars Submitted vars
455
	 *
456
	 * @return void
457
	 */
458
	protected function runSettings($submissionVars) {
459
		$formVars = [
460
			'sitename' => [
461
				'type' => 'text',
462
				'value' => 'My New Community',
463
				'required' => true,
464
				],
465
			'siteemail' => [
466
				'type' => 'email',
467
				'value' => '',
468
				'required' => false,
469
				],
470
			'siteaccess' => [
471
				'type' => 'access',
472
				'value' => ACCESS_PUBLIC,
473
				'required' => true,
474
				],
475
		];
476
477 View Code Duplication
		if ($this->is_action) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
			call_user_func(function () use ($submissionVars, $formVars) {
479
				if (!$this->validateSettingsVars($submissionVars, $formVars)) {
480
					return;
481
				}
482
483
				if (!$this->saveSiteSettings($submissionVars)) {
484
					return;
485
				}
486
487
				system_message(elgg_echo('install:success:settings'));
488
489
				$this->continueToNextStep('settings');
490
			});
491
		}
492
493
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
494
495
		$this->render('settings', ['variables' => $formVars]);
496
	}
497
498
	/**
499
	 * Admin account controller
500
	 *
501
	 * Creates an admin user account
502
	 *
503
	 * @param array $submissionVars Submitted vars
504
	 *
505
	 * @return void
506
	 */
507
	protected function runAdmin($submissionVars) {
508
		$formVars = [
509
			'displayname' => [
510
				'type' => 'text',
511
				'value' => '',
512
				'required' => true,
513
				],
514
			'email' => [
515
				'type' => 'email',
516
				'value' => '',
517
				'required' => true,
518
				],
519
			'username' => [
520
				'type' => 'text',
521
				'value' => '',
522
				'required' => true,
523
				],
524
			'password1' => [
525
				'type' => 'password',
526
				'value' => '',
527
				'required' => true,
528
				'pattern' => '.{6,}',
529
				],
530
			'password2' => [
531
				'type' => 'password',
532
				'value' => '',
533
				'required' => true,
534
				],
535
		];
536
537 View Code Duplication
		if ($this->is_action) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
538
			call_user_func(function () use ($submissionVars, $formVars) {
539
				if (!$this->validateAdminVars($submissionVars, $formVars)) {
540
					return;
541
				}
542
543
				if (!$this->createAdminAccount($submissionVars, $this->autoLogin)) {
544
					return;
545
				}
546
547
				system_message(elgg_echo('install:success:admin'));
548
549
				$this->continueToNextStep('admin');
550
			});
551
		}
552
553
		// Bit of a hack to get the password help to show right number of characters
554
		// We burn the value into the stored translation.
555
		$lang = $this->services->translator->getCurrentLanguage();
556
		$translations = $this->services->translator->getLoadedTranslations();
557
		$this->services->translator->addTranslation($lang, [
558
			'install:admin:help:password1' => sprintf(
559
				$translations[$lang]['install:admin:help:password1'],
560
				$this->config->min_password_length
561
			),
562
		]);
563
564
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
565
566
		$this->render('admin', ['variables' => $formVars]);
567
	}
568
569
	/**
570
	 * Controller for last step
571
	 *
572
	 * @return void
573
	 */
574
	protected function runComplete() {
575
576
		// nudge to check out settings
577
		$link = elgg_format_element([
578
			'#tag_name' => 'a',
579
			'#text' => elgg_echo('install:complete:admin_notice:link_text'),
580
			'href' => elgg_normalize_url('admin/settings/basic'),
581
		]);
582
		$notice = elgg_echo('install:complete:admin_notice', [$link]);
583
		elgg_add_admin_notice('fresh_install', $notice);
584
585
		$this->render('complete');
586
	}
587
588
	/**
589
	 * Step management
590
	 */
591
592
	/**
593
	 * Get an array of steps
594
	 *
595
	 * @return array
596
	 */
597
	protected function getSteps() {
598
		return $this->steps;
599
	}
600
601
	/**
602
	 * Forwards the browser to the next step
603
	 *
604
	 * @param string $currentStep Current installation step
605
	 *
606
	 * @return void
607
	 */
608
	protected function continueToNextStep($currentStep) {
609
		$this->is_action = false;
610
		forward($this->getNextStepUrl($currentStep));
611
	}
612
613
	/**
614
	 * Get the next step as a string
615
	 *
616
	 * @param string $currentStep Current installation step
617
	 *
618
	 * @return string
619
	 */
620
	protected function getNextStep($currentStep) {
621
		$index = 1 + array_search($currentStep, $this->steps);
622
		if (isset($this->steps[$index])) {
623
			return $this->steps[$index];
624
		} else {
625
			return null;
626
		}
627
	}
628
629
	/**
630
	 * Get the URL of the next step
631
	 *
632
	 * @param string $currentStep Current installation step
633
	 *
634
	 * @return string
635
	 */
636
	protected function getNextStepUrl($currentStep) {
637
		$nextStep = $this->getNextStep($currentStep);
638
		return $this->config->wwwroot . "install.php?step=$nextStep";
639
	}
640
641
	/**
642
	 * Updates $this->has_completed according to the current installation
643
	 *
644
	 * @return void
645
	 * @throws InstallationException
646
	 */
647
	protected function determineInstallStatus() {
648
		$path = Paths::settingsFile();
649
		if (!is_file($path) || !is_readable($path)) {
650
			return;
651
		}
652
653
		$this->loadSettingsFile();
654
655
		$this->has_completed['config'] = true;
656
657
		// must be able to connect to database to jump install steps
658
		$dbSettingsPass = $this->checkDatabaseSettings(
659
			$this->config->dbuser,
660
			$this->config->dbpass,
661
			$this->config->dbname,
662
			$this->config->dbhost
663
		);
664
665
		if (!$dbSettingsPass) {
666
			return;
667
		}
668
669
		$db = $this->services->db;
670
671
		// check that the config table has been created
672
		$result = $db->getData("SHOW TABLES");
673
		if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
674
			foreach ($result as $table) {
675
				$table = (array) $table;
676
				if (in_array("{$db->prefix}config", $table)) {
677
					$this->has_completed['database'] = true;
678
				}
679
			}
680
			if ($this->has_completed['database'] == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
681
				return;
682
			}
683
		} else {
684
			// no tables
685
			return;
686
		}
687
688
		// check that the config table has entries
689
		$query = "SELECT COUNT(*) AS total FROM {$db->prefix}config";
690
		$result = $db->getData($query);
691
		if ($result && $result[0]->total > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
692
			$this->has_completed['settings'] = true;
693
		} else {
694
			return;
695
		}
696
697
		// check that the users entity table has an entry
698
		$query = "SELECT COUNT(*) AS total FROM {$db->prefix}users_entity";
699
		$result = $db->getData($query);
700
		if ($result && $result[0]->total > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
701
			$this->has_completed['admin'] = true;
702
		} else {
703
			return;
704
		}
705
	}
706
707
	/**
708
	 * Security check to ensure the installer cannot be run after installation
709
	 * has finished. If this is detected, the viewer is sent to the front page.
710
	 *
711
	 * @param string $step Installation step to check against
712
	 *
713
	 * @return void
714
	 */
715
	protected function checkInstallCompletion($step) {
716
		if ($step != 'complete') {
717
			if (!in_array(false, $this->has_completed)) {
718
				// install complete but someone is trying to view an install page
719
				forward();
720
			}
721
		}
722
	}
723
724
	/**
725
	 * Check if this is a case of a install being resumed and figure
726
	 * out where to continue from. Returns the best guess on the step.
727
	 *
728
	 * @param string $step Installation step to resume from
729
	 *
730
	 * @return string
731
	 */
732
	protected function resumeInstall($step) {
733
		// only do a resume from the first step
734
		if ($step !== 'welcome') {
735
			return;
736
		}
737
738
		if ($this->has_completed['database'] == false) {
739
			return;
740
		}
741
742
		if ($this->has_completed['settings'] == false) {
743
			forward("install.php?step=settings");
744
		}
745
746
		if ($this->has_completed['admin'] == false) {
747
			forward("install.php?step=admin");
748
		}
749
750
		// everything appears to be set up
751
		forward("install.php?step=complete");
752
	}
753
754
	/**
755
	 * Bootstrapping
756
	 */
757
758
	/**
759
	 * Load remaining engine libraries and complete bootstrapping
760
	 *
761
	 * @param string $step Which step to boot strap for. Required because
762
	 *                     boot strapping is different until the DB is populated.
763
	 *
764
	 * @return void
765
	 * @throws InstallationException
766
	 */
767
	protected function finishBootstrapping($step) {
768
769
		$index_db = array_search('database', $this->getSteps());
770
		$index_settings = array_search('settings', $this->getSteps());
0 ignored issues
show
Unused Code introduced by
$index_settings 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...
771
		$index_admin = array_search('admin', $this->getSteps());
772
		$index_complete = array_search('complete', $this->getSteps());
773
		$index_step = array_search($step, $this->getSteps());
774
775
		// To log in the user, we need to use the Elgg core session handling.
776
		// Otherwise, use default php session handling
777
		$use_elgg_session = ($index_step == $index_admin && $this->is_action) || ($index_step == $index_complete);
778
		if (!$use_elgg_session) {
779
			$session = ElggSession::fromFiles($this->config);
780
			$session->setName('Elgg_install');
781
			$this->services->setValue('session', $session);
782
		}
783
784
		if ($index_step > $index_db) {
785
			// once the database has been created, load rest of engine
786
787
			// dummy site needed to boot
788
			$this->config->site = new ElggSite();
789
790
			$this->app->bootCore();
791
		}
792
	}
793
794
	/**
795
	 * Load settings
796
	 *
797
	 * @return void
798
	 * @throws InstallationException
799
	 */
800
	protected function loadSettingsFile() {
801
		try {
802
			$config = Config::fromFile(Paths::settingsFile());
803
			$this->config->mergeValues($config->getValues());
804
805
			// in case the DB instance is already captured in services, we re-inject its settings.
806
			$this->services->db->resetConnections(DbConfig::fromElggConfig($this->config));
807
		} catch (\Exception $e) {
808
			$msg = elgg_echo('InstallationException:CannotLoadSettings');
809
			throw new InstallationException($msg, 0, $e);
810
		}
811
	}
812
813
	/**
814
	 * Action handling methods
815
	 */
816
817
	/**
818
	 * If form is reshown, remember previously submitted variables
819
	 *
820
	 * @param array $formVars       Vars int he form
821
	 * @param array $submissionVars Submitted vars
822
	 *
823
	 * @return array
824
	 */
825
	protected function makeFormSticky($formVars, $submissionVars) {
826
		foreach ($submissionVars as $field => $value) {
827
			$formVars[$field]['value'] = $value;
828
		}
829
		return $formVars;
830
	}
831
832
	/* Requirement checks support methods */
833
834
	/**
835
	 * Indicates whether the webserver can add settings.php on its own or not.
836
	 *
837
	 * @param array $report The requirements report object
838
	 *
839
	 * @return bool
840
	 */
841
	protected function isInstallDirWritable(&$report) {
842
		if (!is_writable(Paths::projectConfig())) {
843
			$msg = elgg_echo('install:check:installdir', [Paths::PATH_TO_CONFIG]);
844
			$report['settings'] = [
845
				[
846
					'severity' => 'failure',
847
					'message' => $msg,
848
				]
849
			];
850
			return false;
851
		}
852
853
		return true;
854
	}
855
856
	/**
857
	 * Check that the settings file exists
858
	 *
859
	 * @param array $report The requirements report array
860
	 *
861
	 * @return bool
862
	 */
863
	protected function checkSettingsFile(&$report = []) {
864
		if (!is_file(Paths::settingsFile())) {
865
			return false;
866
		}
867
868
		if (!is_readable(Paths::settingsFile())) {
869
			$report['settings'] = [
870
				[
871
					'severity' => 'failure',
872
					'message' => elgg_echo('install:check:readsettings'),
873
				]
874
			];
875
		}
876
		
877
		return true;
878
	}
879
	
880
	/**
881
	 * Check version of PHP, extensions, and variables
882
	 *
883
	 * @param array $report The requirements report array
884
	 *
885
	 * @return void
886
	 */
887
	protected function checkPHP(&$report) {
888
		$phpReport = [];
889
890
		$min_php_version = '5.6.0';
891
		if (version_compare(PHP_VERSION, $min_php_version, '<')) {
892
			$phpReport[] = [
893
				'severity' => 'failure',
894
				'message' => elgg_echo('install:check:php:version', [$min_php_version, PHP_VERSION])
895
			];
896
		}
897
898
		$this->checkPhpExtensions($phpReport);
899
900
		$this->checkPhpDirectives($phpReport);
901
902
		if (count($phpReport) == 0) {
903
			$phpReport[] = [
904
				'severity' => 'pass',
905
				'message' => elgg_echo('install:check:php:success')
906
			];
907
		}
908
909
		$report['php'] = $phpReport;
910
	}
911
912
	/**
913
	 * Check the server's PHP extensions
914
	 *
915
	 * @param array $phpReport The PHP requirements report array
916
	 *
917
	 * @return void
918
	 */
919
	protected function checkPhpExtensions(&$phpReport) {
920
		$extensions = get_loaded_extensions();
921
		$requiredExtensions = [
922
			'pdo_mysql',
923
			'json',
924
			'xml',
925
			'gd',
926
		];
927 View Code Duplication
		foreach ($requiredExtensions as $extension) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
928
			if (!in_array($extension, $extensions)) {
929
				$phpReport[] = [
930
					'severity' => 'failure',
931
					'message' => elgg_echo('install:check:php:extension', [$extension])
932
				];
933
			}
934
		}
935
936
		$recommendedExtensions = [
937
			'mbstring',
938
		];
939 View Code Duplication
		foreach ($recommendedExtensions as $extension) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
940
			if (!in_array($extension, $extensions)) {
941
				$phpReport[] = [
942
					'severity' => 'warning',
943
					'message' => elgg_echo('install:check:php:extension:recommend', [$extension])
944
				];
945
			}
946
		}
947
	}
948
949
	/**
950
	 * Check PHP parameters
951
	 *
952
	 * @param array $phpReport The PHP requirements report array
953
	 *
954
	 * @return void
955
	 */
956
	protected function checkPhpDirectives(&$phpReport) {
957
		if (ini_get('open_basedir')) {
958
			$phpReport[] = [
959
				'severity' => 'warning',
960
				'message' => elgg_echo("install:check:php:open_basedir")
961
			];
962
		}
963
964
		if (ini_get('safe_mode')) {
965
			$phpReport[] = [
966
				'severity' => 'warning',
967
				'message' => elgg_echo("install:check:php:safe_mode")
968
			];
969
		}
970
971
		if (ini_get('arg_separator.output') !== '&') {
972
			$separator = htmlspecialchars(ini_get('arg_separator.output'));
973
			$msg = elgg_echo("install:check:php:arg_separator", [$separator]);
974
			$phpReport[] = [
975
				'severity' => 'failure',
976
				'message' => $msg,
977
			];
978
		}
979
980
		if (ini_get('register_globals')) {
981
			$phpReport[] = [
982
				'severity' => 'failure',
983
				'message' => elgg_echo("install:check:php:register_globals")
984
			];
985
		}
986
987
		if (ini_get('session.auto_start')) {
988
			$phpReport[] = [
989
				'severity' => 'failure',
990
				'message' => elgg_echo("install:check:php:session.auto_start")
991
			];
992
		}
993
	}
994
995
	/**
996
	 * Confirm that the rewrite rules are firing
997
	 *
998
	 * @param array $report The requirements report array
999
	 *
1000
	 * @return void
1001
	 */
1002
	protected function checkRewriteRules(&$report) {
1003
		$tester = new ElggRewriteTester();
1004
		$url = $this->config->wwwroot;
1005
		$url .= Request::REWRITE_TEST_TOKEN . '?' . http_build_query([
1006
				Request::REWRITE_TEST_TOKEN => '1',
1007
		]);
1008
		$report['rewrite'] = [$tester->run($url, Paths::project())];
1009
	}
1010
1011
	/**
1012
	 * Count the number of failures in the requirements report
1013
	 *
1014
	 * @param array  $report    The requirements report array
1015
	 * @param string $condition 'failure' or 'warning'
1016
	 *
1017
	 * @return int
1018
	 */
1019
	protected function countNumConditions($report, $condition) {
1020
		$count = 0;
1021
		foreach ($report as $category => $checks) {
1022
			foreach ($checks as $check) {
1023
				if ($check['severity'] === $condition) {
1024
					$count++;
1025
				}
1026
			}
1027
		}
1028
1029
		return $count;
1030
	}
1031
1032
1033
	/**
1034
	 * Database support methods
1035
	 */
1036
1037
	/**
1038
	 * Validate the variables for the database step
1039
	 *
1040
	 * @param array $submissionVars Submitted vars
1041
	 * @param array $formVars       Vars in the form
1042
	 *
1043
	 * @return bool
1044
	 */
1045
	protected function validateDatabaseVars($submissionVars, $formVars) {
1046
1047 View Code Duplication
		foreach ($formVars as $field => $info) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1048
			if ($info['required'] == true && !$submissionVars[$field]) {
1049
				$name = elgg_echo("install:database:label:$field");
1050
				register_error(elgg_echo('install:error:requiredfield', [$name]));
1051
				return false;
1052
			}
1053
		}
1054
1055
		// check that data root is absolute path
1056
		if (stripos(PHP_OS, 'win') === 0) {
1057
			if (strpos($submissionVars['dataroot'], ':') !== 1) {
1058
				$msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]);
1059
				register_error($msg);
1060
				return false;
1061
			}
1062 View Code Duplication
		} else {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1063
			if (strpos($submissionVars['dataroot'], '/') !== 0) {
1064
				$msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]);
1065
				register_error($msg);
1066
				return false;
1067
			}
1068
		}
1069
1070
		// check that data root exists
1071 View Code Duplication
		if (!is_dir($submissionVars['dataroot'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1072
			$msg = elgg_echo('install:error:datadirectoryexists', [$submissionVars['dataroot']]);
1073
			register_error($msg);
1074
			return false;
1075
		}
1076
1077
		// check that data root is writable
1078 View Code Duplication
		if (!is_writable($submissionVars['dataroot'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1079
			$msg = elgg_echo('install:error:writedatadirectory', [$submissionVars['dataroot']]);
1080
			register_error($msg);
1081
			return false;
1082
		}
1083
1084
		if (!$this->config->data_dir_override) {
1085
			// check that data root is not subdirectory of Elgg root
1086
			if (stripos($submissionVars['dataroot'], $this->config->path) === 0) {
1087
				$msg = elgg_echo('install:error:locationdatadirectory', [$submissionVars['dataroot']]);
1088
				register_error($msg);
1089
				return false;
1090
			}
1091
		}
1092
1093
		// according to postgres documentation: SQL identifiers and key words must
1094
		// begin with a letter (a-z, but also letters with diacritical marks and
1095
		// non-Latin letters) or an underscore (_). Subsequent characters in an
1096
		// identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
1097
		// Refs #4994
1098
		if (!preg_match("/^[a-zA-Z_][\w]*$/", $submissionVars['dbprefix'])) {
1099
			register_error(elgg_echo('install:error:database_prefix'));
1100
			return false;
1101
		}
1102
1103
		return $this->checkDatabaseSettings(
1104
			$submissionVars['dbuser'],
1105
			$submissionVars['dbpassword'],
1106
			$submissionVars['dbname'],
1107
			$submissionVars['dbhost']
1108
		);
1109
	}
1110
1111
	/**
1112
	 * Confirm the settings for the database
1113
	 *
1114
	 * @param string $user     Username
1115
	 * @param string $password Password
1116
	 * @param string $dbname   Database name
1117
	 * @param string $host     Host
1118
	 *
1119
	 * @return bool
1120
	 */
1121
	protected function checkDatabaseSettings($user, $password, $dbname, $host) {
1122
		$config = new DbConfig((object) [
1123
			'dbhost' => $host,
1124
			'dbuser' => $user,
1125
			'dbpass' => $password,
1126
			'dbname' => $dbname,
1127
			'dbencoding' => 'utf8mb4',
1128
		]);
1129
		$db = new \Elgg\Database($config);
1130
1131
		try {
1132
			$db->getDataRow("SELECT 1");
1133
		} catch (DatabaseException $e) {
1134
			if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) {
1135
				register_error(elgg_echo('install:error:databasesettings'));
1136
			} else {
1137
				register_error(elgg_echo('install:error:nodatabase', [$dbname]));
1138
			}
1139
			return false;
1140
		}
1141
1142
		// check MySQL version
1143
		$version = $db->getServerVersion(DbConfig::READ_WRITE);
1144
		if (version_compare($version, '5.5.3', '<')) {
1145
			register_error(elgg_echo('install:error:oldmysql2', [$version]));
1146
			return false;
1147
		}
1148
1149
		return true;
1150
	}
1151
1152
	/**
1153
	 * Writes the settings file to the engine directory
1154
	 *
1155
	 * @param array $params Array of inputted params from the user
1156
	 *
1157
	 * @return bool
1158
	 */
1159
	protected function createSettingsFile($params) {
1160
		$template = Application::elggDir()->getContents("elgg-config/settings.example.php");
1161
		if (!$template) {
1162
			register_error(elgg_echo('install:error:readsettingsphp'));
1163
			return false;
1164
		}
1165
1166
		foreach ($params as $k => $v) {
1167
			$template = str_replace("{{" . $k . "}}", $v, $template);
1168
		}
1169
1170
		$result = file_put_contents(Paths::settingsFile(), $template);
1171
		if (!$result) {
1172
			register_error(elgg_echo('install:error:writesettingphp'));
1173
			return false;
1174
		}
1175
1176
		return true;
1177
	}
1178
1179
	/**
1180
	 * Bootstrap database connection before entire engine is available
1181
	 *
1182
	 * @return bool
1183
	 */
1184
	protected function connectToDatabase() {
1185
		try {
1186
			$this->services->db->setupConnections();
1187
		} catch (DatabaseException $e) {
1188
			register_error($e->getMessage());
1189
			return false;
1190
		}
1191
1192
		return true;
1193
	}
1194
1195
	/**
1196
	 * Create the database tables
1197
	 *
1198
	 * @return bool
1199
	 */
1200
	protected function installDatabase() {
1201
		try {
1202
			$this->services->db->runSqlScript(Paths::elgg() . "engine/schema/mysql.sql");
1203
			init_site_secret();
1204
		} catch (Exception $e) {
1205
			$msg = $e->getMessage();
1206
			if (strpos($msg, 'already exists')) {
1207
				$msg = elgg_echo('install:error:tables_exist');
1208
			}
1209
			register_error($msg);
1210
			return false;
1211
		}
1212
1213
		return true;
1214
	}
1215
1216
	/**
1217
	 * Site settings support methods
1218
	 */
1219
1220
	/**
1221
	 * Create the data directory if requested
1222
	 *
1223
	 * @param array $submissionVars Submitted vars
1224
	 * @param array $formVars       Variables in the form
1225
	 *
1226
	 * @return bool
1227
	 */
1228
	protected function createDataDirectory(&$submissionVars, $formVars) {
1229
		// did the user have option of Elgg creating the data directory
1230
		if ($formVars['dataroot']['type'] != 'combo') {
1231
			return true;
1232
		}
1233
1234
		// did the user select the option
1235
		if ($submissionVars['dataroot'] != 'dataroot-checkbox') {
1236
			return true;
1237
		}
1238
1239
		$dir = sanitise_filepath($submissionVars['path']) . 'data';
1240
		if (file_exists($dir) || mkdir($dir, 0700)) {
1241
			$submissionVars['dataroot'] = $dir;
1242
			if (!file_exists("$dir/.htaccess")) {
1243
				$htaccess = "Order Deny,Allow\nDeny from All\n";
1244
				if (!file_put_contents("$dir/.htaccess", $htaccess)) {
1245
					return false;
1246
				}
1247
			}
1248
			return true;
1249
		}
1250
1251
		return false;
1252
	}
1253
1254
	/**
1255
	 * Validate the site settings form variables
1256
	 *
1257
	 * @param array $submissionVars Submitted vars
1258
	 * @param array $formVars       Vars in the form
1259
	 *
1260
	 * @return bool
1261
	 */
1262
	protected function validateSettingsVars($submissionVars, $formVars) {
1263
		foreach ($formVars as $field => $info) {
1264
			$submissionVars[$field] = trim($submissionVars[$field]);
1265
			if ($info['required'] == true && $submissionVars[$field] === '') {
1266
				$name = elgg_echo("install:settings:label:$field");
1267
				register_error(elgg_echo('install:error:requiredfield', [$name]));
1268
				return false;
1269
			}
1270
		}
1271
1272
		// check that email address is email address
1273 View Code Duplication
		if ($submissionVars['siteemail'] && !is_email_address($submissionVars['siteemail'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1274
			$msg = elgg_echo('install:error:emailaddress', [$submissionVars['siteemail']]);
1275
			register_error($msg);
1276
			return false;
1277
		}
1278
1279
		// @todo check that url is a url
1280
		// @note filter_var cannot be used because it doesn't work on international urls
1281
1282
		return true;
1283
	}
1284
1285
	/**
1286
	 * Initialize the site including site entity, plugins, and configuration
1287
	 *
1288
	 * @param array $submissionVars Submitted vars
1289
	 *
1290
	 * @return bool
1291
	 */
1292
	protected function saveSiteSettings($submissionVars) {
1293
		$site = new ElggSite();
1294
		$site->name = strip_tags($submissionVars['sitename']);
1295
		$site->access_id = ACCESS_PUBLIC;
1296
		$site->email = $submissionVars['siteemail'];
1297
1298
		$guid = $site->save();
1299
		if ($guid !== 1) {
1300
			register_error(elgg_echo('install:error:createsite'));
1301
			return false;
1302
		}
1303
1304
		$this->config->site = $site;
1305
1306
		// new installations have run all the upgrades
1307
		$upgrades = elgg_get_upgrade_files(Paths::elgg() . "engine/lib/upgrades/");
1308
1309
		$sets = [
1310
			'installed' => time(),
1311
			'version' => elgg_get_version(),
1312
			'simplecache_enabled' => 1,
1313
			'system_cache_enabled' => 1,
1314
			'simplecache_lastupdate' => time(),
1315
			'processed_upgrades' => $upgrades,
1316
			'language' => 'en',
1317
			'default_access' => $submissionVars['siteaccess'],
1318
			'allow_registration' => false,
1319
			'walled_garden' => false,
1320
			'allow_user_default_access' => '',
1321
			'default_limit' => 10,
1322
			'security_protect_upgrade' => true,
1323
			'security_notify_admins' => true,
1324
			'security_notify_user_password' => true,
1325
			'security_email_require_password' => true,
1326
		];
1327
		foreach ($sets as $key => $value) {
1328
			elgg_save_config($key, $value);
1329
		}
1330
1331
		// Enable a set of default plugins
1332
		_elgg_generate_plugin_entities();
1333
1334
		foreach (elgg_get_plugins('any') as $plugin) {
1335
			if ($plugin->getManifest()) {
1336
				if ($plugin->getManifest()->getActivateOnInstall()) {
1337
					$plugin->activate();
1338
				}
1339
				if (in_array('theme', $plugin->getManifest()->getCategories())) {
1340
					$plugin->setPriority('last');
1341
				}
1342
			}
1343
		}
1344
1345
		return true;
1346
	}
1347
1348
	/**
1349
	 * Admin account support methods
1350
	 */
1351
1352
	/**
1353
	 * Validate account form variables
1354
	 *
1355
	 * @param array $submissionVars Submitted vars
1356
	 * @param array $formVars       Form vars
1357
	 *
1358
	 * @return bool
1359
	 */
1360
	protected function validateAdminVars($submissionVars, $formVars) {
1361
1362 View Code Duplication
		foreach ($formVars as $field => $info) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1363
			if ($info['required'] == true && !$submissionVars[$field]) {
1364
				$name = elgg_echo("install:admin:label:$field");
1365
				register_error(elgg_echo('install:error:requiredfield', [$name]));
1366
				return false;
1367
			}
1368
		}
1369
1370
		if ($submissionVars['password1'] !== $submissionVars['password2']) {
1371
			register_error(elgg_echo('install:admin:password:mismatch'));
1372
			return false;
1373
		}
1374
1375
		if (trim($submissionVars['password1']) == "") {
1376
			register_error(elgg_echo('install:admin:password:empty'));
1377
			return false;
1378
		}
1379
1380
		$minLength = $this->services->configTable->get('min_password_length');
1381
		if (strlen($submissionVars['password1']) < $minLength) {
1382
			register_error(elgg_echo('install:admin:password:tooshort'));
1383
			return false;
1384
		}
1385
1386
		// check that email address is email address
1387 View Code Duplication
		if ($submissionVars['email'] && !is_email_address($submissionVars['email'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1388
			$msg = elgg_echo('install:error:emailaddress', [$submissionVars['email']]);
1389
			register_error($msg);
1390
			return false;
1391
		}
1392
1393
		return true;
1394
	}
1395
1396
	/**
1397
	 * Create a user account for the admin
1398
	 *
1399
	 * @param array $submissionVars Submitted vars
1400
	 * @param bool  $login          Login in the admin user?
1401
	 *
1402
	 * @return bool
1403
	 */
1404
	protected function createAdminAccount($submissionVars, $login = false) {
1405
		try {
1406
			$guid = register_user(
1407
				$submissionVars['username'],
1408
				$submissionVars['password1'],
1409
				$submissionVars['displayname'],
1410
				$submissionVars['email']
1411
			);
1412
		} catch (Exception $e) {
1413
			register_error($e->getMessage());
1414
			return false;
1415
		}
1416
1417
		if (!$guid) {
1418
			register_error(elgg_echo('install:admin:cannot_create'));
1419
			return false;
1420
		}
1421
1422
		$user = get_entity($guid);
1423
		if (!$user instanceof ElggUser) {
1424
			register_error(elgg_echo('install:error:loadadmin'));
1425
			return false;
1426
		}
1427
1428
		elgg_set_ignore_access(true);
1429
		if ($user->makeAdmin() == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1430
			register_error(elgg_echo('install:error:adminaccess'));
1431
		} else {
1432
			$this->services->configTable->set('admin_registered', 1);
1433
		}
1434
		elgg_set_ignore_access(false);
1435
1436
		// add validation data to satisfy user validation plugins
1437
		$user->validated = 1;
1 ignored issue
show
Documentation introduced by
The property validated does not exist on object<ElggUser>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1438
		$user->validated_method = 'admin_user';
1 ignored issue
show
Documentation introduced by
The property validated_method does not exist on object<ElggUser>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1439
1440
		if (!$login) {
1441
			return true;
1442
		}
1443
1444
		$session = ElggSession::fromDatabase($this->config, $this->services->db);
1445
		$session->start();
1446
		$this->services->setValue('session', $session);
1447
		if (login($user) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1448
			register_error(elgg_echo('install:error:adminlogin'));
1449
		}
1450
1451
		return true;
1452
	}
1453
}
1454