1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
use Elgg\Database; |
4
|
|
|
use Elgg\Filesystem\Directory; |
|
|
|
|
5
|
|
|
use Elgg\Application; |
6
|
|
|
use Elgg\Config; |
7
|
|
|
use Elgg\Database\DbConfig; |
8
|
|
|
use Elgg\Project\Paths; |
9
|
|
|
use Elgg\Di\ServiceProvider; |
10
|
|
|
use Elgg\Http\Request; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Elgg Installer. |
14
|
|
|
* Controller for installing Elgg. Supports both web-based on CLI installation. |
15
|
|
|
* |
16
|
|
|
* This controller steps the user through the install process. The method for |
17
|
|
|
* each step handles both the GET and POST requests. There is no XSS/CSRF protection |
18
|
|
|
* on the POST processing since the installer is only run once by the administrator. |
19
|
|
|
* |
20
|
|
|
* The installation process can be resumed by hitting the first page. The installer |
21
|
|
|
* will try to figure out where to pick up again. |
22
|
|
|
* |
23
|
|
|
* All the logic for the installation process is in this class, but it depends on |
24
|
|
|
* the core libraries. To do this, we selectively load a subset of the core libraries |
25
|
|
|
* for the first few steps and then load the entire engine once the database and |
26
|
|
|
* site settings are configured. In addition, this controller does its own session |
27
|
|
|
* handling until the database is setup. |
28
|
|
|
* |
29
|
|
|
* There is an aborted attempt in the code at creating the data directory for |
30
|
|
|
* users as a subdirectory of Elgg's root. The idea was to protect this directory |
31
|
|
|
* through a .htaccess file. The problem is that a malicious user can upload a |
32
|
|
|
* .htaccess of his own that overrides the protection for his user directory. The |
33
|
|
|
* best solution is server level configuration that turns off AllowOverride for the |
34
|
|
|
* data directory. See ticket #3453 for discussion on this. |
35
|
|
|
*/ |
36
|
|
|
class ElggInstaller { |
37
|
|
|
|
38
|
|
|
private $steps = [ |
39
|
|
|
'welcome', |
40
|
|
|
'requirements', |
41
|
|
|
'database', |
42
|
|
|
'settings', |
43
|
|
|
'admin', |
44
|
|
|
'complete', |
45
|
|
|
]; |
46
|
|
|
|
47
|
|
|
private $has_completed = [ |
48
|
|
|
'config' => false, |
49
|
|
|
'database' => false, |
50
|
|
|
'settings' => false, |
51
|
|
|
'admin' => false, |
52
|
|
|
]; |
53
|
|
|
|
54
|
|
|
private $is_action = false; |
55
|
|
|
|
56
|
|
|
private $autoLogin = true; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var Application |
60
|
|
|
*/ |
61
|
|
|
private $app; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Dispatches a request to one of the step controllers |
65
|
|
|
* |
66
|
|
|
* @return \Elgg\Http\ResponseBuilder |
67
|
|
|
* @throws InstallationException |
68
|
|
|
*/ |
69
|
8 |
|
public function run() { |
70
|
8 |
|
$app = $this->getApp(); |
71
|
|
|
|
72
|
8 |
|
$this->is_action = $app->_services->request->getMethod() === 'POST'; |
73
|
|
|
|
74
|
8 |
|
$step = get_input('step', 'welcome'); |
75
|
|
|
|
76
|
8 |
|
if (!in_array($step, $this->getSteps())) { |
77
|
|
|
$step = 'welcome'; |
78
|
|
|
} |
79
|
|
|
|
80
|
8 |
|
$this->determineInstallStatus(); |
81
|
|
|
|
82
|
8 |
|
$response = $this->checkInstallCompletion($step); |
83
|
8 |
|
if ($response) { |
84
|
|
|
return $response; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
// check if this is an install being resumed |
88
|
8 |
|
$response = $this->resumeInstall($step); |
89
|
8 |
|
if ($response) { |
90
|
|
|
return $response; |
91
|
|
|
} |
92
|
|
|
|
93
|
8 |
|
$this->finishBootstrapping($step); |
94
|
|
|
|
95
|
8 |
|
$params = $app->_services->request->request->all(); |
96
|
|
|
|
97
|
8 |
|
$method = "run" . ucwords($step); |
98
|
8 |
|
return $this->$method($params); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Build the application needed by the installer |
103
|
|
|
* |
104
|
|
|
* @return Application |
105
|
|
|
* @throws InstallationException |
106
|
|
|
*/ |
107
|
|
|
protected function getApp() { |
108
|
|
|
if ($this->app) { |
109
|
|
|
return $this->app; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
try { |
113
|
|
|
$config = new Config(); |
114
|
|
|
$config->elgg_config_locks = false; |
115
|
|
|
$config->installer_running = true; |
116
|
|
|
$config->dbencoding = 'utf8mb4'; |
117
|
|
|
|
118
|
|
|
$services = new ServiceProvider($config); |
119
|
|
|
|
120
|
|
|
$app = Application::factory([ |
121
|
|
|
'service_provider' => $services, |
122
|
|
|
'handle_exceptions' => false, |
123
|
|
|
'handle_shutdown' => false, |
124
|
|
|
]); |
125
|
|
|
|
126
|
|
|
// Don't set global $CONFIG, because loading the settings file may require it to write to |
127
|
|
|
// it, and it can have array sets (e.g. cookie config) that fail when using a proxy for |
128
|
|
|
// the config service. |
129
|
|
|
//$app->setGlobalConfig(); |
130
|
|
|
|
131
|
|
|
Application::setInstance($app); |
132
|
|
|
$app->loadCore(); |
133
|
|
|
$this->app = $app; |
134
|
|
|
|
135
|
|
|
$app->_services->setValue('session', \ElggSession::getMock()); |
136
|
|
|
$app->_services->views->setViewtype('installation'); |
137
|
|
|
$app->_services->views->registerViewtypeFallback('installation'); |
138
|
|
|
$app->_services->views->registerPluginViews(Paths::elgg()); |
139
|
|
|
$app->_services->translator->registerTranslations(Paths::elgg() . "install/languages/", true); |
140
|
|
|
|
141
|
|
|
return $this->app; |
142
|
|
|
} catch (ConfigurationException $ex) { |
143
|
|
|
throw new InstallationException($ex->getMessage()); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Set the auto login flag |
149
|
|
|
* |
150
|
|
|
* @param bool $flag Auto login |
151
|
|
|
* |
152
|
|
|
* @return void |
153
|
|
|
*/ |
154
|
|
|
public function setAutoLogin($flag) { |
155
|
|
|
$this->autoLogin = (bool) $flag; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* A batch install of Elgg |
160
|
|
|
* |
161
|
|
|
* All required parameters must be passed in as an associative array. See |
162
|
|
|
* $requiredParams for a list of them. This creates the necessary files, |
163
|
|
|
* loads the database, configures the site settings, and creates the admin |
164
|
|
|
* account. If it fails, an exception is thrown. It does not check any of |
165
|
|
|
* the requirements as the multiple step web installer does. |
166
|
|
|
* |
167
|
|
|
* @param array $params Array of key value pairs |
168
|
|
|
* @param bool $create_htaccess Should .htaccess be created |
169
|
|
|
* |
170
|
|
|
* @return void |
171
|
|
|
* @throws InstallationException |
172
|
|
|
*/ |
173
|
1 |
|
public function batchInstall(array $params, $create_htaccess = false) { |
174
|
1 |
|
$app = $this->getApp(); |
175
|
|
|
|
176
|
|
|
$defaults = [ |
177
|
1 |
|
'dbhost' => 'localhost', |
178
|
|
|
'dbprefix' => 'elgg_', |
179
|
|
|
'language' => 'en', |
180
|
|
|
'siteaccess' => ACCESS_PUBLIC, |
181
|
|
|
]; |
182
|
1 |
|
$params = array_merge($defaults, $params); |
183
|
|
|
|
184
|
|
|
$required_params = [ |
185
|
1 |
|
'dbuser', |
186
|
|
|
'dbpassword', |
187
|
|
|
'dbname', |
188
|
|
|
'sitename', |
189
|
|
|
'wwwroot', |
190
|
|
|
'dataroot', |
191
|
|
|
'displayname', |
192
|
|
|
'email', |
193
|
|
|
'username', |
194
|
|
|
'password', |
195
|
|
|
]; |
196
|
1 |
|
foreach ($required_params as $key) { |
197
|
1 |
|
if (empty($params[$key])) { |
198
|
|
|
$msg = elgg_echo('install:error:requiredfield', [$key]); |
199
|
1 |
|
throw new InstallationException($msg); |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
// password is passed in once |
204
|
1 |
|
$params['password1'] = $params['password2'] = $params['password']; |
205
|
|
|
|
206
|
1 |
|
if ($create_htaccess) { |
207
|
|
|
$rewrite_tester = new ElggRewriteTester(); |
208
|
|
|
if (!$rewrite_tester->createHtaccess($params['wwwroot'])) { |
209
|
|
|
throw new InstallationException(elgg_echo('install:error:htaccess')); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
213
|
1 |
|
if (!empty($params['wwwroot']) && !_elgg_sane_validate_url($params['wwwroot'])) { |
214
|
|
|
throw new InstallationException(elgg_echo('install:error:wwwroot', [$params['wwwroot']])); |
215
|
|
|
} |
216
|
|
|
|
217
|
1 |
|
$this->determineInstallStatus(); |
218
|
|
|
|
219
|
1 |
|
if (!$this->has_completed['config']) { |
220
|
1 |
|
if (!$this->createSettingsFile($params)) { |
221
|
|
|
throw new InstallationException(elgg_echo('install:error:settings')); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
1 |
|
$this->loadSettingsFile(); |
226
|
|
|
|
227
|
|
|
// Make sure settings file matches parameters |
228
|
1 |
|
$config = $app->_services->config; |
229
|
|
|
$config_keys = [ |
230
|
|
|
// param key => config key |
231
|
1 |
|
'dbhost' => 'dbhost', |
232
|
|
|
'dbuser' => 'dbuser', |
233
|
|
|
'dbpassword' => 'dbpass', |
234
|
|
|
'dbname' => 'dbname', |
235
|
|
|
'dataroot' => 'dataroot', |
236
|
|
|
'dbprefix' => 'dbprefix', |
237
|
|
|
]; |
238
|
1 |
|
foreach ($config_keys as $params_key => $config_key) { |
239
|
1 |
|
if ($params[$params_key] !== $config->$config_key) { |
240
|
1 |
|
throw new InstallationException(elgg_echo('install:error:settings_mismatch', [$config_key])); |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
244
|
1 |
|
if (!$this->connectToDatabase()) { |
245
|
|
|
throw new InstallationException(elgg_echo('install:error:databasesettings')); |
246
|
|
|
} |
247
|
|
|
|
248
|
1 |
|
if (!$this->has_completed['database']) { |
249
|
1 |
|
if (!$this->installDatabase()) { |
250
|
|
|
throw new InstallationException(elgg_echo('install:error:cannotloadtables')); |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
// load remaining core libraries |
255
|
1 |
|
$this->finishBootstrapping('settings'); |
256
|
|
|
|
257
|
1 |
|
if (!$this->saveSiteSettings($params)) { |
258
|
|
|
throw new InstallationException(elgg_echo('install:error:savesitesettings')); |
259
|
|
|
} |
260
|
|
|
|
261
|
1 |
|
if (!$this->createAdminAccount($params)) { |
262
|
|
|
throw new InstallationException(elgg_echo('install:admin:cannot_create')); |
263
|
|
|
} |
264
|
1 |
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Renders the data passed by a controller |
268
|
|
|
* |
269
|
|
|
* @param string $step The current step |
270
|
|
|
* @param array $vars Array of vars to pass to the view |
271
|
|
|
* |
272
|
|
|
* @return \Elgg\Http\OkResponse |
273
|
|
|
*/ |
274
|
5 |
|
protected function render($step, $vars = []) { |
275
|
5 |
|
$vars['next_step'] = $this->getNextStep($step); |
276
|
|
|
|
277
|
5 |
|
$title = elgg_echo("install:$step"); |
278
|
5 |
|
$body = elgg_view("install/pages/$step", $vars); |
279
|
|
|
|
280
|
5 |
|
$output = elgg_view_page( |
281
|
5 |
|
$title, |
282
|
5 |
|
$body, |
283
|
5 |
|
'default', |
284
|
|
|
[ |
285
|
5 |
|
'step' => $step, |
286
|
5 |
|
'steps' => $this->getSteps(), |
287
|
|
|
] |
288
|
|
|
); |
289
|
|
|
|
290
|
5 |
|
return new \Elgg\Http\OkResponse($output); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Step controllers |
295
|
|
|
*/ |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Welcome controller |
299
|
|
|
* |
300
|
|
|
* @param array $vars Not used |
301
|
|
|
* |
302
|
|
|
* @return \Elgg\Http\ResponseBuilder |
303
|
|
|
*/ |
304
|
1 |
|
protected function runWelcome($vars) { |
305
|
1 |
|
return $this->render('welcome'); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Requirements controller |
310
|
|
|
* |
311
|
|
|
* Checks version of php, libraries, permissions, and rewrite rules |
312
|
|
|
* |
313
|
|
|
* @param array $vars Vars |
314
|
|
|
* |
315
|
|
|
* @return \Elgg\Http\ResponseBuilder |
316
|
|
|
* @throws InstallationException |
317
|
|
|
*/ |
318
|
1 |
|
protected function runRequirements($vars) { |
319
|
|
|
|
320
|
1 |
|
$report = []; |
321
|
|
|
|
322
|
|
|
// check PHP parameters and libraries |
323
|
1 |
|
$this->checkPHP($report); |
324
|
|
|
|
325
|
|
|
// check URL rewriting |
326
|
1 |
|
$this->checkRewriteRules($report); |
327
|
|
|
|
328
|
|
|
// check for existence of settings file |
329
|
1 |
|
if ($this->checkSettingsFile($report) != true) { |
|
|
|
|
330
|
|
|
// no file, so check permissions on engine directory |
331
|
1 |
|
$this->isInstallDirWritable($report); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
// check the database later |
335
|
1 |
|
$report['database'] = [ |
336
|
|
|
[ |
337
|
1 |
|
'severity' => 'info', |
338
|
1 |
|
'message' => elgg_echo('install:check:database') |
339
|
|
|
] |
340
|
|
|
]; |
341
|
|
|
|
342
|
|
|
// any failures? |
343
|
1 |
|
$numFailures = $this->countNumConditions($report, 'failure'); |
344
|
|
|
|
345
|
|
|
// any warnings |
346
|
1 |
|
$numWarnings = $this->countNumConditions($report, 'warning'); |
347
|
|
|
|
348
|
|
|
|
349
|
|
|
$params = [ |
350
|
1 |
|
'report' => $report, |
351
|
1 |
|
'num_failures' => $numFailures, |
352
|
1 |
|
'num_warnings' => $numWarnings, |
353
|
|
|
]; |
354
|
|
|
|
355
|
1 |
|
return $this->render('requirements', $params); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Database set up controller |
360
|
|
|
* |
361
|
|
|
* Creates the settings.php file and creates the database tables |
362
|
|
|
* |
363
|
|
|
* @param array $submissionVars Submitted form variables |
364
|
|
|
* |
365
|
|
|
* @return \Elgg\Http\ResponseBuilder |
366
|
|
|
* @throws ConfigurationException |
367
|
|
|
*/ |
368
|
2 |
|
protected function runDatabase($submissionVars) { |
369
|
|
|
|
370
|
2 |
|
$app = $this->getApp(); |
371
|
|
|
|
372
|
|
|
$formVars = [ |
373
|
2 |
|
'dbuser' => [ |
374
|
|
|
'type' => 'text', |
375
|
|
|
'value' => '', |
376
|
|
|
'required' => true, |
377
|
|
|
], |
378
|
|
|
'dbpassword' => [ |
379
|
|
|
'type' => 'password', |
380
|
|
|
'value' => '', |
381
|
|
|
'required' => false, |
382
|
|
|
], |
383
|
|
|
'dbname' => [ |
384
|
|
|
'type' => 'text', |
385
|
|
|
'value' => '', |
386
|
|
|
'required' => true, |
387
|
|
|
], |
388
|
|
|
'dbhost' => [ |
389
|
|
|
'type' => 'text', |
390
|
|
|
'value' => 'localhost', |
391
|
|
|
'required' => true, |
392
|
|
|
], |
393
|
|
|
'dbprefix' => [ |
394
|
|
|
'type' => 'text', |
395
|
|
|
'value' => 'elgg_', |
396
|
|
|
'required' => true, |
397
|
|
|
], |
398
|
|
|
'dataroot' => [ |
399
|
|
|
'type' => 'text', |
400
|
|
|
'value' => '', |
401
|
|
|
'required' => true, |
402
|
|
|
], |
403
|
|
|
'wwwroot' => [ |
404
|
2 |
|
'type' => 'url', |
405
|
2 |
|
'value' => $app->_services->config->wwwroot, |
406
|
|
|
'required' => true, |
407
|
|
|
], |
408
|
|
|
'timezone' => [ |
409
|
2 |
|
'type' => 'dropdown', |
410
|
2 |
|
'value' => 'UTC', |
411
|
2 |
|
'options' => \DateTimeZone::listIdentifiers(), |
412
|
|
|
'required' => true |
413
|
|
|
] |
414
|
|
|
]; |
415
|
|
|
|
416
|
2 |
|
if ($this->checkSettingsFile()) { |
417
|
|
|
// user manually created settings file so we fake out action test |
418
|
|
|
$this->is_action = true; |
419
|
|
|
} |
420
|
|
|
|
421
|
2 |
|
if ($this->is_action) { |
422
|
1 |
|
$getResponse = function () use ($submissionVars, $formVars) { |
423
|
|
|
// only create settings file if it doesn't exist |
424
|
1 |
|
if (!$this->checkSettingsFile()) { |
425
|
1 |
|
if (!$this->validateDatabaseVars($submissionVars, $formVars)) { |
426
|
|
|
// error so we break out of action and serve same page |
427
|
|
|
return; |
428
|
|
|
} |
429
|
|
|
|
430
|
1 |
|
if (!$this->createSettingsFile($submissionVars)) { |
431
|
|
|
return; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
// check db version and connect |
436
|
1 |
|
if (!$this->connectToDatabase()) { |
437
|
|
|
return; |
438
|
|
|
} |
439
|
|
|
|
440
|
1 |
|
if (!$this->installDatabase()) { |
441
|
|
|
return; |
442
|
|
|
} |
443
|
|
|
|
444
|
1 |
|
system_message(elgg_echo('install:success:database')); |
445
|
|
|
|
446
|
1 |
|
return $this->continueToNextStep('database'); |
447
|
1 |
|
}; |
448
|
|
|
|
449
|
1 |
|
$response = $getResponse(); |
450
|
1 |
|
if ($response) { |
451
|
1 |
|
return $response; |
452
|
|
|
} |
453
|
|
|
} |
454
|
|
|
|
455
|
1 |
|
$formVars = $this->makeFormSticky($formVars, $submissionVars); |
456
|
|
|
|
457
|
1 |
|
$params = ['variables' => $formVars,]; |
458
|
|
|
|
459
|
1 |
|
if ($this->checkSettingsFile()) { |
460
|
|
|
// settings file exists and we're here so failed to create database |
461
|
|
|
$params['failure'] = true; |
462
|
|
|
} |
463
|
|
|
|
464
|
1 |
|
return $this->render('database', $params); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* Site settings controller |
469
|
|
|
* |
470
|
|
|
* Sets the site name, URL, data directory, etc. |
471
|
|
|
* |
472
|
|
|
* @param array $submissionVars Submitted vars |
473
|
|
|
* |
474
|
|
|
* @return \Elgg\Http\ResponseBuilder |
475
|
|
|
*/ |
476
|
2 |
|
protected function runSettings($submissionVars) { |
477
|
|
|
$formVars = [ |
478
|
2 |
|
'sitename' => [ |
479
|
|
|
'type' => 'text', |
480
|
|
|
'value' => 'My New Community', |
481
|
|
|
'required' => true, |
482
|
|
|
], |
483
|
|
|
'siteemail' => [ |
484
|
|
|
'type' => 'email', |
485
|
|
|
'value' => '', |
486
|
|
|
'required' => false, |
487
|
|
|
], |
488
|
|
|
'siteaccess' => [ |
489
|
|
|
'type' => 'access', |
490
|
|
|
'value' => ACCESS_PUBLIC, |
491
|
|
|
'required' => true, |
492
|
|
|
], |
493
|
|
|
]; |
494
|
|
|
|
495
|
2 |
|
if ($this->is_action) { |
496
|
1 |
|
$getResponse = function () use ($submissionVars, $formVars) { |
497
|
|
|
|
498
|
1 |
|
if (!$this->validateSettingsVars($submissionVars, $formVars)) { |
499
|
|
|
return; |
500
|
|
|
} |
501
|
|
|
|
502
|
1 |
|
if (!$this->saveSiteSettings($submissionVars)) { |
503
|
|
|
return; |
504
|
|
|
} |
505
|
|
|
|
506
|
1 |
|
system_message(elgg_echo('install:success:settings')); |
507
|
|
|
|
508
|
1 |
|
return $this->continueToNextStep('settings'); |
509
|
1 |
|
}; |
510
|
|
|
|
511
|
1 |
|
$response = $getResponse(); |
512
|
1 |
|
if ($response) { |
513
|
1 |
|
return $response; |
514
|
|
|
} |
515
|
|
|
} |
516
|
|
|
|
517
|
1 |
|
$formVars = $this->makeFormSticky($formVars, $submissionVars); |
518
|
|
|
|
519
|
1 |
|
return $this->render('settings', ['variables' => $formVars]); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Admin account controller |
524
|
|
|
* |
525
|
|
|
* Creates an admin user account |
526
|
|
|
* |
527
|
|
|
* @param array $submissionVars Submitted vars |
528
|
|
|
* |
529
|
|
|
* @return \Elgg\Http\ResponseBuilder |
530
|
|
|
* @throws InstallationException |
531
|
|
|
*/ |
532
|
2 |
|
protected function runAdmin($submissionVars) { |
533
|
|
|
$formVars = [ |
534
|
2 |
|
'displayname' => [ |
535
|
|
|
'type' => 'text', |
536
|
|
|
'value' => '', |
537
|
|
|
'required' => true, |
538
|
|
|
], |
539
|
|
|
'email' => [ |
540
|
|
|
'type' => 'email', |
541
|
|
|
'value' => '', |
542
|
|
|
'required' => true, |
543
|
|
|
], |
544
|
|
|
'username' => [ |
545
|
|
|
'type' => 'text', |
546
|
|
|
'value' => '', |
547
|
|
|
'required' => true, |
548
|
|
|
], |
549
|
|
|
'password1' => [ |
550
|
|
|
'type' => 'password', |
551
|
|
|
'value' => '', |
552
|
|
|
'required' => true, |
553
|
|
|
'pattern' => '.{6,}', |
554
|
|
|
], |
555
|
|
|
'password2' => [ |
556
|
|
|
'type' => 'password', |
557
|
|
|
'value' => '', |
558
|
|
|
'required' => true, |
559
|
|
|
], |
560
|
|
|
]; |
561
|
|
|
|
562
|
2 |
|
if ($this->is_action) { |
563
|
1 |
|
$getResponse = function () use ($submissionVars, $formVars) { |
564
|
1 |
|
if (!$this->validateAdminVars($submissionVars, $formVars)) { |
565
|
|
|
return; |
566
|
|
|
} |
567
|
|
|
|
568
|
1 |
|
if (!$this->createAdminAccount($submissionVars, $this->autoLogin)) { |
569
|
|
|
return; |
570
|
|
|
} |
571
|
|
|
|
572
|
1 |
|
system_message(elgg_echo('install:success:admin')); |
573
|
|
|
|
574
|
1 |
|
return $this->continueToNextStep('admin'); |
575
|
1 |
|
}; |
576
|
|
|
|
577
|
1 |
|
$response = $getResponse(); |
578
|
1 |
|
if ($response) { |
579
|
1 |
|
return $response; |
580
|
|
|
} |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
// Bit of a hack to get the password help to show right number of characters |
584
|
|
|
// We burn the value into the stored translation. |
585
|
1 |
|
$app = $this->getApp(); |
586
|
1 |
|
$lang = $app->_services->translator->getCurrentLanguage(); |
587
|
1 |
|
$translations = $app->_services->translator->getLoadedTranslations(); |
588
|
1 |
|
$app->_services->translator->addTranslation($lang, [ |
589
|
1 |
|
'install:admin:help:password1' => sprintf( |
590
|
1 |
|
$translations[$lang]['install:admin:help:password1'], |
591
|
1 |
|
$app->_services->config->min_password_length |
592
|
|
|
), |
593
|
|
|
]); |
594
|
|
|
|
595
|
1 |
|
$formVars = $this->makeFormSticky($formVars, $submissionVars); |
596
|
|
|
|
597
|
1 |
|
return $this->render('admin', ['variables' => $formVars]); |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* Controller for last step |
602
|
|
|
* |
603
|
|
|
* @return \Elgg\Http\ResponseBuilder |
604
|
|
|
*/ |
605
|
|
|
protected function runComplete() { |
606
|
|
|
|
607
|
|
|
// nudge to check out settings |
608
|
|
|
$link = elgg_format_element([ |
609
|
|
|
'#tag_name' => 'a', |
610
|
|
|
'#text' => elgg_echo('install:complete:admin_notice:link_text'), |
611
|
|
|
'href' => elgg_normalize_url('admin/settings/basic'), |
612
|
|
|
]); |
613
|
|
|
$notice = elgg_echo('install:complete:admin_notice', [$link]); |
614
|
|
|
elgg_add_admin_notice('fresh_install', $notice); |
615
|
|
|
|
616
|
|
|
return $this->render('complete'); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* Step management |
621
|
|
|
*/ |
622
|
|
|
|
623
|
|
|
/** |
624
|
|
|
* Get an array of steps |
625
|
|
|
* |
626
|
|
|
* @return array |
627
|
|
|
*/ |
628
|
9 |
|
protected function getSteps() { |
629
|
9 |
|
return $this->steps; |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Forwards the browser to the next step |
634
|
|
|
* |
635
|
|
|
* @param string $currentStep Current installation step |
636
|
|
|
* |
637
|
|
|
* @return \Elgg\Http\RedirectResponse |
638
|
|
|
* @throws InstallationException |
639
|
|
|
*/ |
640
|
3 |
|
protected function continueToNextStep($currentStep) { |
641
|
3 |
|
$this->is_action = false; |
642
|
|
|
|
643
|
3 |
|
return new \Elgg\Http\RedirectResponse($this->getNextStepUrl($currentStep)); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Get the next step as a string |
648
|
|
|
* |
649
|
|
|
* @param string $currentStep Current installation step |
650
|
|
|
* |
651
|
|
|
* @return string |
652
|
|
|
*/ |
653
|
8 |
|
protected function getNextStep($currentStep) { |
654
|
8 |
|
$index = 1 + array_search($currentStep, $this->steps); |
655
|
8 |
|
if (isset($this->steps[$index])) { |
656
|
8 |
|
return $this->steps[$index]; |
657
|
|
|
} else { |
658
|
|
|
return null; |
659
|
|
|
} |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
/** |
663
|
|
|
* Get the URL of the next step |
664
|
|
|
* |
665
|
|
|
* @param string $currentStep Current installation step |
666
|
|
|
* |
667
|
|
|
* @return string |
668
|
|
|
* @throws InstallationException |
669
|
|
|
*/ |
670
|
3 |
|
protected function getNextStepUrl($currentStep) { |
671
|
3 |
|
$app = $this->getApp(); |
672
|
3 |
|
$nextStep = $this->getNextStep($currentStep); |
673
|
|
|
|
674
|
3 |
|
return $app->_services->config->wwwroot . "install.php?step=$nextStep"; |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
/** |
678
|
|
|
* Updates $this->has_completed according to the current installation |
679
|
|
|
* |
680
|
|
|
* @return void |
681
|
|
|
* @throws InstallationException |
682
|
|
|
*/ |
683
|
9 |
|
protected function determineInstallStatus() { |
684
|
9 |
|
$app = $this->getApp(); |
685
|
|
|
|
686
|
9 |
|
$path = Config::resolvePath(); |
687
|
9 |
|
if (!is_file($path) || !is_readable($path)) { |
|
|
|
|
688
|
5 |
|
return; |
689
|
|
|
} |
690
|
|
|
|
691
|
4 |
|
$this->loadSettingsFile(); |
692
|
|
|
|
693
|
4 |
|
$this->has_completed['config'] = true; |
694
|
|
|
|
695
|
|
|
// must be able to connect to database to jump install steps |
696
|
4 |
|
$dbSettingsPass = $this->checkDatabaseSettings( |
697
|
4 |
|
$app->_services->config->dbuser, |
698
|
4 |
|
$app->_services->config->dbpass, |
699
|
4 |
|
$app->_services->config->dbname, |
700
|
4 |
|
$app->_services->config->dbhost |
701
|
|
|
); |
702
|
|
|
|
703
|
4 |
|
if (!$dbSettingsPass) { |
704
|
|
|
return; |
705
|
|
|
} |
706
|
|
|
|
707
|
4 |
|
$db = $app->_services->db; |
708
|
|
|
|
709
|
|
|
try { |
710
|
|
|
// check that the config table has been created |
711
|
4 |
|
$result = $db->getData("SHOW TABLES"); |
712
|
4 |
|
if (!$result) { |
|
|
|
|
713
|
|
|
return; |
714
|
|
|
} |
715
|
4 |
|
foreach ($result as $table) { |
716
|
4 |
|
$table = (array) $table; |
717
|
4 |
|
if (in_array("{$db->prefix}config", $table)) { |
718
|
4 |
|
$this->has_completed['database'] = true; |
719
|
|
|
} |
720
|
|
|
} |
721
|
4 |
|
if ($this->has_completed['database'] == false) { |
722
|
4 |
|
return; |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
// check that the config table has entries |
726
|
|
|
$qb = \Elgg\Database\Select::fromTable('config'); |
727
|
|
|
$qb->select('COUNT(*) AS total'); |
728
|
|
|
|
729
|
|
|
$result = $db->getData($qb); |
730
|
|
|
if ($result && $result[0]->total > 0) { |
731
|
|
|
$this->has_completed['settings'] = true; |
732
|
|
|
} else { |
733
|
|
|
return; |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
// check that the users entity table has an entry |
737
|
|
|
$qb = \Elgg\Database\Select::fromTable('entities'); |
738
|
|
|
$qb->select('COUNT(*) AS total') |
739
|
|
|
->where($qb->compare('type', '=', 'user', ELGG_VALUE_STRING)); |
740
|
|
|
|
741
|
|
|
$result = $db->getData($qb); |
742
|
|
|
if ($result && $result[0]->total > 0) { |
743
|
|
|
$this->has_completed['admin'] = true; |
744
|
|
|
} else { |
745
|
|
|
return; |
746
|
|
|
} |
747
|
|
|
} catch (DatabaseException $ex) { |
748
|
|
|
throw new InstallationException('Elgg can not connect to the database: ' . $ex->getMessage()); |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
return; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* Security check to ensure the installer cannot be run after installation |
756
|
|
|
* has finished. If this is detected, the viewer is sent to the front page. |
757
|
|
|
* |
758
|
|
|
* @param string $step Installation step to check against |
759
|
|
|
* |
760
|
|
|
* @return \Elgg\Http\RedirectResponse|null |
761
|
|
|
*/ |
762
|
8 |
|
protected function checkInstallCompletion($step) { |
763
|
8 |
|
if ($step != 'complete') { |
764
|
8 |
|
if (!in_array(false, $this->has_completed)) { |
765
|
|
|
// install complete but someone is trying to view an install page |
766
|
|
|
return new \Elgg\Http\RedirectResponse('/'); |
767
|
|
|
} |
768
|
|
|
} |
769
|
8 |
|
} |
770
|
|
|
|
771
|
|
|
/** |
772
|
|
|
* Check if this is a case of a install being resumed and figure |
773
|
|
|
* out where to continue from. Returns the best guess on the step. |
774
|
|
|
* |
775
|
|
|
* @param string $step Installation step to resume from |
776
|
|
|
* |
777
|
|
|
* @return \Elgg\Http\RedirectResponse|null |
778
|
|
|
*/ |
779
|
8 |
|
protected function resumeInstall($step) { |
780
|
|
|
// only do a resume from the first step |
781
|
8 |
|
if ($step !== 'welcome') { |
782
|
7 |
|
return null; |
783
|
|
|
} |
784
|
|
|
|
785
|
1 |
|
if ($this->has_completed['database'] == false) { |
786
|
1 |
|
return null; |
787
|
|
|
} |
788
|
|
|
|
789
|
|
|
if ($this->has_completed['settings'] == false) { |
790
|
|
|
return new \Elgg\Http\RedirectResponse("install.php?step=settings"); |
791
|
|
|
} |
792
|
|
|
|
793
|
|
|
if ($this->has_completed['admin'] == false) { |
794
|
|
|
return new \Elgg\Http\RedirectResponse("install.php?step=admin"); |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
// everything appears to be set up |
798
|
|
|
return new \Elgg\Http\RedirectResponse("install.php?step=complete"); |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* Bootstrapping |
803
|
|
|
*/ |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* Load remaining engine libraries and complete bootstrapping |
807
|
|
|
* |
808
|
|
|
* @param string $step Which step to boot strap for. Required because |
809
|
|
|
* boot strapping is different until the DB is populated. |
810
|
|
|
* |
811
|
|
|
* @return void |
812
|
|
|
* @throws InstallationException |
813
|
|
|
*/ |
814
|
9 |
|
protected function finishBootstrapping($step) { |
815
|
|
|
|
816
|
9 |
|
$app = $this->getApp(); |
817
|
|
|
|
818
|
9 |
|
$index_db = array_search('database', $this->getSteps()); |
819
|
9 |
|
$index_settings = array_search('settings', $this->getSteps()); |
|
|
|
|
820
|
9 |
|
$index_admin = array_search('admin', $this->getSteps()); |
821
|
9 |
|
$index_complete = array_search('complete', $this->getSteps()); |
822
|
9 |
|
$index_step = array_search($step, $this->getSteps()); |
823
|
|
|
|
824
|
|
|
// To log in the user, we need to use the Elgg core session handling. |
825
|
|
|
// Otherwise, use default php session handling |
826
|
9 |
|
$use_elgg_session = ($index_step == $index_admin && $this->is_action) || ($index_step == $index_complete); |
827
|
9 |
|
if (!$use_elgg_session) { |
828
|
8 |
|
$this->createSessionFromFile(); |
829
|
|
|
} |
830
|
|
|
|
831
|
9 |
|
if ($index_step > $index_db) { |
832
|
|
|
// once the database has been created, load rest of engine |
833
|
|
|
|
834
|
|
|
// dummy site needed to boot |
835
|
5 |
|
$app->_services->config->site = new ElggSite(); |
836
|
|
|
|
837
|
5 |
|
$app->bootCore(); |
838
|
|
|
} |
839
|
9 |
|
} |
840
|
|
|
|
841
|
|
|
/** |
842
|
|
|
* Load settings |
843
|
|
|
* |
844
|
|
|
* @return void |
845
|
|
|
* @throws InstallationException |
846
|
|
|
*/ |
847
|
5 |
|
protected function loadSettingsFile() { |
848
|
|
|
try { |
849
|
5 |
|
$app = $this->getApp(); |
850
|
|
|
|
851
|
5 |
|
$config = Config::fromFile(Config::resolvePath()); |
|
|
|
|
852
|
5 |
|
$app->_services->setValue('config', $config); |
853
|
|
|
|
854
|
|
|
// in case the DB instance is already captured in services, we re-inject its settings. |
855
|
5 |
|
$app->_services->db->resetConnections(DbConfig::fromElggConfig($config)); |
856
|
|
|
} catch (\Exception $e) { |
857
|
|
|
$msg = elgg_echo('InstallationException:CannotLoadSettings'); |
858
|
|
|
throw new InstallationException($msg, 0, $e); |
859
|
|
|
} |
860
|
5 |
|
} |
861
|
|
|
|
862
|
|
|
/** |
863
|
|
|
* Action handling methods |
864
|
|
|
*/ |
865
|
|
|
|
866
|
|
|
/** |
867
|
|
|
* If form is reshown, remember previously submitted variables |
868
|
|
|
* |
869
|
|
|
* @param array $formVars Vars int he form |
870
|
|
|
* @param array $submissionVars Submitted vars |
871
|
|
|
* |
872
|
|
|
* @return array |
873
|
|
|
*/ |
874
|
3 |
|
protected function makeFormSticky($formVars, $submissionVars) { |
875
|
3 |
|
foreach ($submissionVars as $field => $value) { |
876
|
|
|
$formVars[$field]['value'] = $value; |
877
|
|
|
} |
878
|
|
|
|
879
|
3 |
|
return $formVars; |
880
|
|
|
} |
881
|
|
|
|
882
|
|
|
/* Requirement checks support methods */ |
883
|
|
|
|
884
|
|
|
/** |
885
|
|
|
* Indicates whether the webserver can add settings.php on its own or not. |
886
|
|
|
* |
887
|
|
|
* @param array $report The requirements report object |
888
|
|
|
* |
889
|
|
|
* @return bool |
890
|
|
|
*/ |
891
|
1 |
|
protected function isInstallDirWritable(&$report) { |
892
|
1 |
|
if (!is_writable(Paths::projectConfig())) { |
893
|
|
|
$msg = elgg_echo('install:check:installdir', [Paths::PATH_TO_CONFIG]); |
894
|
|
|
$report['settings'] = [ |
895
|
|
|
[ |
896
|
|
|
'severity' => 'failure', |
897
|
|
|
'message' => $msg, |
898
|
|
|
] |
899
|
|
|
]; |
900
|
|
|
|
901
|
|
|
return false; |
902
|
|
|
} |
903
|
|
|
|
904
|
1 |
|
return true; |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
/** |
908
|
|
|
* Check that the settings file exists |
909
|
|
|
* |
910
|
|
|
* @param array $report The requirements report array |
911
|
|
|
* |
912
|
|
|
* @return bool |
913
|
|
|
*/ |
914
|
3 |
|
protected function checkSettingsFile(&$report = []) { |
915
|
3 |
|
if (!is_file(Config::resolvePath())) { |
|
|
|
|
916
|
3 |
|
return false; |
917
|
|
|
} |
918
|
|
|
|
919
|
|
|
if (!is_readable(Config::resolvePath())) { |
|
|
|
|
920
|
|
|
$report['settings'] = [ |
921
|
|
|
[ |
922
|
|
|
'severity' => 'failure', |
923
|
|
|
'message' => elgg_echo('install:check:readsettings'), |
924
|
|
|
] |
925
|
|
|
]; |
926
|
|
|
} |
927
|
|
|
|
928
|
|
|
return true; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
/** |
932
|
|
|
* Check version of PHP, extensions, and variables |
933
|
|
|
* |
934
|
|
|
* @param array $report The requirements report array |
935
|
|
|
* |
936
|
|
|
* @return void |
937
|
|
|
*/ |
938
|
1 |
|
protected function checkPHP(&$report) { |
939
|
1 |
|
$phpReport = []; |
940
|
|
|
|
941
|
1 |
|
$min_php_version = '7.0.0'; |
942
|
1 |
|
if (version_compare(PHP_VERSION, $min_php_version, '<')) { |
943
|
|
|
$phpReport[] = [ |
944
|
|
|
'severity' => 'failure', |
945
|
|
|
'message' => elgg_echo('install:check:php:version', [$min_php_version, PHP_VERSION]) |
946
|
|
|
]; |
947
|
|
|
} |
948
|
|
|
|
949
|
1 |
|
$this->checkPhpExtensions($phpReport); |
950
|
|
|
|
951
|
1 |
|
$this->checkPhpDirectives($phpReport); |
952
|
|
|
|
953
|
1 |
|
if (count($phpReport) == 0) { |
954
|
1 |
|
$phpReport[] = [ |
955
|
1 |
|
'severity' => 'pass', |
956
|
1 |
|
'message' => elgg_echo('install:check:php:success') |
957
|
|
|
]; |
958
|
|
|
} |
959
|
|
|
|
960
|
1 |
|
$report['php'] = $phpReport; |
961
|
1 |
|
} |
962
|
|
|
|
963
|
|
|
/** |
964
|
|
|
* Check the server's PHP extensions |
965
|
|
|
* |
966
|
|
|
* @param array $phpReport The PHP requirements report array |
967
|
|
|
* |
968
|
|
|
* @return void |
969
|
|
|
*/ |
970
|
1 |
|
protected function checkPhpExtensions(&$phpReport) { |
971
|
1 |
|
$extensions = get_loaded_extensions(); |
972
|
|
|
$requiredExtensions = [ |
973
|
1 |
|
'pdo_mysql', |
974
|
|
|
'json', |
975
|
|
|
'xml', |
976
|
|
|
'gd', |
977
|
|
|
]; |
978
|
1 |
|
foreach ($requiredExtensions as $extension) { |
979
|
1 |
|
if (!in_array($extension, $extensions)) { |
980
|
|
|
$phpReport[] = [ |
981
|
|
|
'severity' => 'failure', |
982
|
1 |
|
'message' => elgg_echo('install:check:php:extension', [$extension]) |
983
|
|
|
]; |
984
|
|
|
} |
985
|
|
|
} |
986
|
|
|
|
987
|
|
|
$recommendedExtensions = [ |
988
|
1 |
|
'mbstring', |
989
|
|
|
]; |
990
|
1 |
|
foreach ($recommendedExtensions as $extension) { |
991
|
1 |
|
if (!in_array($extension, $extensions)) { |
992
|
|
|
$phpReport[] = [ |
993
|
|
|
'severity' => 'warning', |
994
|
1 |
|
'message' => elgg_echo('install:check:php:extension:recommend', [$extension]) |
995
|
|
|
]; |
996
|
|
|
} |
997
|
|
|
} |
998
|
1 |
|
} |
999
|
|
|
|
1000
|
|
|
/** |
1001
|
|
|
* Check PHP parameters |
1002
|
|
|
* |
1003
|
|
|
* @param array $phpReport The PHP requirements report array |
1004
|
|
|
* |
1005
|
|
|
* @return void |
1006
|
|
|
*/ |
1007
|
1 |
|
protected function checkPhpDirectives(&$phpReport) { |
1008
|
1 |
|
if (ini_get('open_basedir')) { |
1009
|
|
|
$phpReport[] = [ |
1010
|
|
|
'severity' => 'warning', |
1011
|
|
|
'message' => elgg_echo("install:check:php:open_basedir") |
1012
|
|
|
]; |
1013
|
|
|
} |
1014
|
|
|
|
1015
|
1 |
|
if (ini_get('safe_mode')) { |
1016
|
|
|
$phpReport[] = [ |
1017
|
|
|
'severity' => 'warning', |
1018
|
|
|
'message' => elgg_echo("install:check:php:safe_mode") |
1019
|
|
|
]; |
1020
|
|
|
} |
1021
|
|
|
|
1022
|
1 |
|
if (ini_get('arg_separator.output') !== '&') { |
1023
|
|
|
$separator = htmlspecialchars(ini_get('arg_separator.output')); |
1024
|
|
|
$msg = elgg_echo("install:check:php:arg_separator", [$separator]); |
1025
|
|
|
$phpReport[] = [ |
1026
|
|
|
'severity' => 'failure', |
1027
|
|
|
'message' => $msg, |
1028
|
|
|
]; |
1029
|
|
|
} |
1030
|
|
|
|
1031
|
1 |
|
if (ini_get('register_globals')) { |
1032
|
|
|
$phpReport[] = [ |
1033
|
|
|
'severity' => 'failure', |
1034
|
|
|
'message' => elgg_echo("install:check:php:register_globals") |
1035
|
|
|
]; |
1036
|
|
|
} |
1037
|
|
|
|
1038
|
1 |
|
if (ini_get('session.auto_start')) { |
1039
|
|
|
$phpReport[] = [ |
1040
|
|
|
'severity' => 'failure', |
1041
|
|
|
'message' => elgg_echo("install:check:php:session.auto_start") |
1042
|
|
|
]; |
1043
|
|
|
} |
1044
|
1 |
|
} |
1045
|
|
|
|
1046
|
|
|
/** |
1047
|
|
|
* Confirm that the rewrite rules are firing |
1048
|
|
|
* |
1049
|
|
|
* @param array $report The requirements report array |
1050
|
|
|
* |
1051
|
|
|
* @return void |
1052
|
|
|
* @throws InstallationException |
1053
|
|
|
*/ |
1054
|
|
|
protected function checkRewriteRules(&$report) { |
1055
|
|
|
$app = $this->getApp(); |
1056
|
|
|
|
1057
|
|
|
$tester = new ElggRewriteTester(); |
1058
|
|
|
$url = $app->_services->config->wwwroot; |
1059
|
|
|
$url .= Request::REWRITE_TEST_TOKEN . '?' . http_build_query([ |
1060
|
|
|
Request::REWRITE_TEST_TOKEN => '1', |
1061
|
|
|
]); |
1062
|
|
|
$report['rewrite'] = [$tester->run($url, Paths::project())]; |
1063
|
|
|
} |
1064
|
|
|
|
1065
|
|
|
/** |
1066
|
|
|
* Count the number of failures in the requirements report |
1067
|
|
|
* |
1068
|
|
|
* @param array $report The requirements report array |
1069
|
|
|
* @param string $condition 'failure' or 'warning' |
1070
|
|
|
* |
1071
|
|
|
* @return int |
1072
|
|
|
*/ |
1073
|
1 |
|
protected function countNumConditions($report, $condition) { |
1074
|
1 |
|
$count = 0; |
1075
|
1 |
|
foreach ($report as $category => $checks) { |
1076
|
1 |
|
foreach ($checks as $check) { |
1077
|
1 |
|
if ($check['severity'] === $condition) { |
1078
|
1 |
|
$count++; |
1079
|
|
|
} |
1080
|
|
|
} |
1081
|
|
|
} |
1082
|
|
|
|
1083
|
1 |
|
return $count; |
1084
|
|
|
} |
1085
|
|
|
|
1086
|
|
|
|
1087
|
|
|
/** |
1088
|
|
|
* Database support methods |
1089
|
|
|
*/ |
1090
|
|
|
|
1091
|
|
|
/** |
1092
|
|
|
* Validate the variables for the database step |
1093
|
|
|
* |
1094
|
|
|
* @param array $submissionVars Submitted vars |
1095
|
|
|
* @param array $formVars Vars in the form |
1096
|
|
|
* |
1097
|
|
|
* @return bool |
1098
|
|
|
* @throws InstallationException |
1099
|
|
|
*/ |
1100
|
1 |
|
protected function validateDatabaseVars($submissionVars, $formVars) { |
1101
|
|
|
|
1102
|
1 |
|
$app = $this->getApp(); |
1103
|
|
|
|
1104
|
1 |
|
foreach ($formVars as $field => $info) { |
1105
|
1 |
|
if ($info['required'] == true && !$submissionVars[$field]) { |
1106
|
|
|
$name = elgg_echo("install:database:label:$field"); |
1107
|
|
|
register_error(elgg_echo('install:error:requiredfield', [$name])); |
1108
|
|
|
|
1109
|
1 |
|
return false; |
1110
|
|
|
} |
1111
|
|
|
} |
1112
|
|
|
|
1113
|
1 |
|
if (!empty($submissionVars['wwwroot']) && !_elgg_sane_validate_url($submissionVars['wwwroot'])) { |
1114
|
|
|
register_error(elgg_echo('install:error:wwwroot', [$submissionVars['wwwroot']])); |
1115
|
|
|
|
1116
|
|
|
return false; |
1117
|
|
|
} |
1118
|
|
|
|
1119
|
|
|
// check that data root is absolute path |
1120
|
1 |
|
if (stripos(PHP_OS, 'win') === 0) { |
1121
|
|
|
if (strpos($submissionVars['dataroot'], ':') !== 1) { |
1122
|
|
|
$msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]); |
1123
|
|
|
register_error($msg); |
1124
|
|
|
|
1125
|
|
|
return false; |
1126
|
|
|
} |
1127
|
|
|
} else { |
1128
|
1 |
|
if (strpos($submissionVars['dataroot'], '/') !== 0) { |
1129
|
|
|
$msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]); |
1130
|
|
|
register_error($msg); |
1131
|
|
|
|
1132
|
|
|
return false; |
1133
|
|
|
} |
1134
|
|
|
} |
1135
|
|
|
|
1136
|
|
|
// check that data root exists |
1137
|
1 |
|
if (!is_dir($submissionVars['dataroot'])) { |
1138
|
|
|
$msg = elgg_echo('install:error:datadirectoryexists', [$submissionVars['dataroot']]); |
1139
|
|
|
register_error($msg); |
1140
|
|
|
|
1141
|
|
|
return false; |
1142
|
|
|
} |
1143
|
|
|
|
1144
|
|
|
// check that data root is writable |
1145
|
1 |
|
if (!is_writable($submissionVars['dataroot'])) { |
1146
|
|
|
$msg = elgg_echo('install:error:writedatadirectory', [$submissionVars['dataroot']]); |
1147
|
|
|
register_error($msg); |
1148
|
|
|
|
1149
|
|
|
return false; |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
1 |
|
if (!$app->_services->config->data_dir_override) { |
1153
|
|
|
// check that data root is not subdirectory of Elgg root |
1154
|
1 |
|
if (stripos($submissionVars['dataroot'], $app->_services->config->path) === 0) { |
1155
|
|
|
$msg = elgg_echo('install:error:locationdatadirectory', [$submissionVars['dataroot']]); |
1156
|
|
|
register_error($msg); |
1157
|
|
|
|
1158
|
|
|
return false; |
1159
|
|
|
} |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
// according to postgres documentation: SQL identifiers and key words must |
1163
|
|
|
// begin with a letter (a-z, but also letters with diacritical marks and |
1164
|
|
|
// non-Latin letters) or an underscore (_). Subsequent characters in an |
1165
|
|
|
// identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($). |
1166
|
|
|
// Refs #4994 |
1167
|
1 |
|
if (!preg_match("/^[a-zA-Z_][\w]*$/", $submissionVars['dbprefix'])) { |
1168
|
|
|
register_error(elgg_echo('install:error:database_prefix')); |
1169
|
|
|
|
1170
|
|
|
return false; |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
1 |
|
return $this->checkDatabaseSettings( |
1174
|
1 |
|
$submissionVars['dbuser'], |
1175
|
1 |
|
$submissionVars['dbpassword'], |
1176
|
1 |
|
$submissionVars['dbname'], |
1177
|
1 |
|
$submissionVars['dbhost'] |
1178
|
|
|
); |
1179
|
|
|
} |
1180
|
|
|
|
1181
|
|
|
/** |
1182
|
|
|
* Confirm the settings for the database |
1183
|
|
|
* |
1184
|
|
|
* @param string $user Username |
1185
|
|
|
* @param string $password Password |
1186
|
|
|
* @param string $dbname Database name |
1187
|
|
|
* @param string $host Host |
1188
|
|
|
* |
1189
|
|
|
* @return bool |
1190
|
|
|
*/ |
1191
|
5 |
|
protected function checkDatabaseSettings($user, $password, $dbname, $host) { |
1192
|
5 |
|
$config = new DbConfig((object) [ |
1193
|
5 |
|
'dbhost' => $host, |
1194
|
5 |
|
'dbuser' => $user, |
1195
|
5 |
|
'dbpass' => $password, |
1196
|
5 |
|
'dbname' => $dbname, |
1197
|
5 |
|
'dbencoding' => 'utf8mb4', |
1198
|
|
|
]); |
1199
|
5 |
|
$db = new Database($config); |
1200
|
|
|
|
1201
|
|
|
try { |
1202
|
5 |
|
$db->getDataRow("SELECT 1"); |
1203
|
|
|
} catch (DatabaseException $e) { |
1204
|
|
|
if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) { |
1205
|
|
|
register_error(elgg_echo('install:error:databasesettings')); |
1206
|
|
|
} else { |
1207
|
|
|
register_error(elgg_echo('install:error:nodatabase', [$dbname])); |
1208
|
|
|
} |
1209
|
|
|
|
1210
|
|
|
return false; |
1211
|
|
|
} |
1212
|
|
|
|
1213
|
|
|
// check MySQL version |
1214
|
5 |
|
$version = $db->getServerVersion(DbConfig::READ_WRITE); |
1215
|
5 |
|
if (version_compare($version, '5.5.3', '<')) { |
1216
|
|
|
register_error(elgg_echo('install:error:oldmysql2', [$version])); |
1217
|
|
|
|
1218
|
|
|
return false; |
1219
|
|
|
} |
1220
|
|
|
|
1221
|
5 |
|
return true; |
1222
|
|
|
} |
1223
|
|
|
|
1224
|
|
|
/** |
1225
|
|
|
* Writes the settings file to the engine directory |
1226
|
|
|
* |
1227
|
|
|
* @param array $params Array of inputted params from the user |
1228
|
|
|
* |
1229
|
|
|
* @return bool |
1230
|
|
|
* @throws InstallationException |
1231
|
|
|
*/ |
1232
|
2 |
|
protected function createSettingsFile($params) { |
1233
|
2 |
|
$template = Application::elggDir()->getContents("elgg-config/settings.example.php"); |
1234
|
2 |
|
if (!$template) { |
1235
|
|
|
register_error(elgg_echo('install:error:readsettingsphp')); |
1236
|
|
|
|
1237
|
|
|
return false; |
1238
|
|
|
} |
1239
|
|
|
|
1240
|
2 |
|
foreach ($params as $k => $v) { |
1241
|
2 |
|
$template = str_replace("{{" . $k . "}}", $v, $template); |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
2 |
|
$result = file_put_contents(Config::resolvePath(), $template); |
|
|
|
|
1245
|
2 |
|
if ($result === false) { |
1246
|
|
|
register_error(elgg_echo('install:error:writesettingphp')); |
1247
|
|
|
|
1248
|
|
|
return false; |
1249
|
|
|
} |
1250
|
|
|
|
1251
|
|
|
$config = (object) [ |
1252
|
2 |
|
'dbhost' => elgg_extract('dbhost', $params, 'localhost'), |
1253
|
2 |
|
'dbuser' => elgg_extract('dbuser', $params), |
1254
|
2 |
|
'dbpass' => elgg_extract('dbpassword', $params), |
1255
|
2 |
|
'dbname' => elgg_extract('dbname', $params), |
1256
|
2 |
|
'dbencoding' => elgg_extract('dbencoding', $params, 'utf8mb4'), |
1257
|
2 |
|
'dbprefix' => elgg_extract('dbprefix', $params, 'elgg_'), |
1258
|
|
|
]; |
1259
|
|
|
|
1260
|
2 |
|
$dbConfig = new DbConfig($config); |
1261
|
2 |
|
$this->getApp()->_services->setValue('dbConfig', $dbConfig); |
1262
|
|
|
|
1263
|
2 |
|
return true; |
1264
|
|
|
} |
1265
|
|
|
|
1266
|
|
|
/** |
1267
|
|
|
* Bootstrap database connection before entire engine is available |
1268
|
|
|
* |
1269
|
|
|
* @return bool |
1270
|
|
|
* @throws InstallationException |
1271
|
|
|
*/ |
1272
|
2 |
|
protected function connectToDatabase() { |
1273
|
|
|
try { |
1274
|
2 |
|
$app = $this->getApp(); |
1275
|
2 |
|
$app->_services->db->setupConnections(); |
1276
|
|
|
} catch (DatabaseException $e) { |
1277
|
|
|
register_error($e->getMessage()); |
1278
|
|
|
|
1279
|
|
|
return false; |
1280
|
|
|
} |
1281
|
|
|
|
1282
|
2 |
|
return true; |
1283
|
|
|
} |
1284
|
|
|
|
1285
|
|
|
/** |
1286
|
|
|
* Create the database tables |
1287
|
|
|
* |
1288
|
|
|
* @return bool |
1289
|
|
|
*/ |
1290
|
2 |
|
protected function installDatabase() { |
1291
|
|
|
try { |
1292
|
2 |
|
$ret = $this->getApp()->migrate(); |
1293
|
2 |
|
if ($ret) { |
1294
|
2 |
|
init_site_secret(); |
1295
|
|
|
} |
1296
|
|
|
|
1297
|
2 |
|
return $ret; |
1298
|
|
|
} catch (\Exception $e) { |
1299
|
|
|
return false; |
1300
|
|
|
} |
1301
|
|
|
} |
1302
|
|
|
|
1303
|
|
|
/** |
1304
|
|
|
* Site settings support methods |
1305
|
|
|
*/ |
1306
|
|
|
|
1307
|
|
|
/** |
1308
|
|
|
* Create the data directory if requested |
1309
|
|
|
* |
1310
|
|
|
* @param array $submissionVars Submitted vars |
1311
|
|
|
* @param array $formVars Variables in the form |
1312
|
|
|
* |
1313
|
|
|
* @return bool |
1314
|
|
|
*/ |
1315
|
|
|
protected function createDataDirectory(&$submissionVars, $formVars) { |
1316
|
|
|
// did the user have option of Elgg creating the data directory |
1317
|
|
|
if ($formVars['dataroot']['type'] != 'combo') { |
1318
|
|
|
return true; |
1319
|
|
|
} |
1320
|
|
|
|
1321
|
|
|
// did the user select the option |
1322
|
|
|
if ($submissionVars['dataroot'] != 'dataroot-checkbox') { |
1323
|
|
|
return true; |
1324
|
|
|
} |
1325
|
|
|
|
1326
|
|
|
$dir = \Elgg\Project\Paths::sanitize($submissionVars['path']) . 'data'; |
1327
|
|
|
if (file_exists($dir) || mkdir($dir, 0700)) { |
1328
|
|
|
$submissionVars['dataroot'] = $dir; |
1329
|
|
|
if (!file_exists("$dir/.htaccess")) { |
1330
|
|
|
$htaccess = "Order Deny,Allow\nDeny from All\n"; |
1331
|
|
|
if (!file_put_contents("$dir/.htaccess", $htaccess)) { |
1332
|
|
|
return false; |
1333
|
|
|
} |
1334
|
|
|
} |
1335
|
|
|
|
1336
|
|
|
return true; |
1337
|
|
|
} |
1338
|
|
|
|
1339
|
|
|
return false; |
1340
|
|
|
} |
1341
|
|
|
|
1342
|
|
|
/** |
1343
|
|
|
* Validate the site settings form variables |
1344
|
|
|
* |
1345
|
|
|
* @param array $submissionVars Submitted vars |
1346
|
|
|
* @param array $formVars Vars in the form |
1347
|
|
|
* |
1348
|
|
|
* @return bool |
1349
|
|
|
*/ |
1350
|
1 |
|
protected function validateSettingsVars($submissionVars, $formVars) { |
1351
|
1 |
|
foreach ($formVars as $field => $info) { |
1352
|
1 |
|
$submissionVars[$field] = trim($submissionVars[$field]); |
1353
|
1 |
|
if ($info['required'] == true && $submissionVars[$field] === '') { |
1354
|
|
|
$name = elgg_echo("install:settings:label:$field"); |
1355
|
|
|
register_error(elgg_echo('install:error:requiredfield', [$name])); |
1356
|
|
|
|
1357
|
1 |
|
return false; |
1358
|
|
|
} |
1359
|
|
|
} |
1360
|
|
|
|
1361
|
|
|
// check that email address is email address |
1362
|
1 |
|
if ($submissionVars['siteemail'] && !is_email_address($submissionVars['siteemail'])) { |
1363
|
|
|
$msg = elgg_echo('install:error:emailaddress', [$submissionVars['siteemail']]); |
1364
|
|
|
register_error($msg); |
1365
|
|
|
|
1366
|
|
|
return false; |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
1 |
|
return true; |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
/** |
1373
|
|
|
* Initialize the site including site entity, plugins, and configuration |
1374
|
|
|
* |
1375
|
|
|
* @param array $submissionVars Submitted vars |
1376
|
|
|
* |
1377
|
|
|
* @return bool |
1378
|
|
|
* @throws InstallationException |
1379
|
|
|
*/ |
1380
|
2 |
|
protected function saveSiteSettings($submissionVars) { |
1381
|
2 |
|
$app = $this->getApp(); |
1382
|
|
|
|
1383
|
2 |
|
$site = new ElggSite(); |
1384
|
2 |
|
$site->name = strip_tags($submissionVars['sitename']); |
1385
|
2 |
|
$site->access_id = ACCESS_PUBLIC; |
1386
|
2 |
|
$site->email = $submissionVars['siteemail']; |
1387
|
2 |
|
$guid = $site->save(); |
1388
|
|
|
|
1389
|
2 |
|
if ($guid !== 1) { |
1390
|
|
|
register_error(elgg_echo('install:error:createsite')); |
1391
|
|
|
|
1392
|
|
|
return false; |
1393
|
|
|
} |
1394
|
|
|
|
1395
|
2 |
|
$app->_services->config->site = $site; |
1396
|
|
|
|
1397
|
|
|
// new installations have run all the upgrades |
1398
|
2 |
|
$upgrades = elgg_get_upgrade_files(Paths::elgg() . "engine/lib/upgrades/"); |
1399
|
|
|
|
1400
|
|
|
$sets = [ |
1401
|
2 |
|
'installed' => time(), |
1402
|
2 |
|
'version' => elgg_get_version(), |
1403
|
2 |
|
'simplecache_enabled' => 1, |
1404
|
2 |
|
'system_cache_enabled' => 1, |
1405
|
2 |
|
'simplecache_lastupdate' => time(), |
1406
|
2 |
|
'processed_upgrades' => $upgrades, |
1407
|
2 |
|
'language' => 'en', |
1408
|
2 |
|
'default_access' => $submissionVars['siteaccess'], |
1409
|
|
|
'allow_registration' => false, |
1410
|
|
|
'walled_garden' => false, |
1411
|
2 |
|
'allow_user_default_access' => '', |
1412
|
2 |
|
'default_limit' => 10, |
1413
|
|
|
'security_protect_upgrade' => true, |
1414
|
|
|
'security_notify_admins' => true, |
1415
|
|
|
'security_notify_user_password' => true, |
1416
|
|
|
'security_email_require_password' => true, |
1417
|
|
|
]; |
1418
|
2 |
|
foreach ($sets as $key => $value) { |
1419
|
2 |
|
elgg_save_config($key, $value); |
1420
|
|
|
} |
1421
|
|
|
|
1422
|
|
|
// Enable a set of default plugins |
1423
|
2 |
|
_elgg_generate_plugin_entities(); |
1424
|
|
|
|
1425
|
2 |
|
foreach (elgg_get_plugins('any') as $plugin) { |
1426
|
|
|
if ($plugin->getManifest()) { |
1427
|
|
|
if ($plugin->getManifest()->getActivateOnInstall()) { |
1428
|
|
|
$plugin->activate(); |
1429
|
|
|
} |
1430
|
|
|
if (in_array('theme', $plugin->getManifest()->getCategories())) { |
1431
|
|
|
$plugin->setPriority('last'); |
1432
|
|
|
} |
1433
|
|
|
} |
1434
|
|
|
} |
1435
|
|
|
|
1436
|
2 |
|
return true; |
1437
|
|
|
} |
1438
|
|
|
|
1439
|
|
|
/** |
1440
|
|
|
* Validate account form variables |
1441
|
|
|
* |
1442
|
|
|
* @param array $submissionVars Submitted vars |
1443
|
|
|
* @param array $formVars Form vars |
1444
|
|
|
* |
1445
|
|
|
* @return bool |
1446
|
|
|
* @throws InstallationException |
1447
|
|
|
*/ |
1448
|
1 |
|
protected function validateAdminVars($submissionVars, $formVars) { |
1449
|
|
|
|
1450
|
1 |
|
$app = $this->getApp(); |
1451
|
|
|
|
1452
|
1 |
|
foreach ($formVars as $field => $info) { |
1453
|
1 |
|
if ($info['required'] == true && !$submissionVars[$field]) { |
1454
|
|
|
$name = elgg_echo("install:admin:label:$field"); |
1455
|
|
|
register_error(elgg_echo('install:error:requiredfield', [$name])); |
1456
|
|
|
|
1457
|
1 |
|
return false; |
1458
|
|
|
} |
1459
|
|
|
} |
1460
|
|
|
|
1461
|
1 |
|
if ($submissionVars['password1'] !== $submissionVars['password2']) { |
1462
|
|
|
register_error(elgg_echo('install:admin:password:mismatch')); |
1463
|
|
|
|
1464
|
|
|
return false; |
1465
|
|
|
} |
1466
|
|
|
|
1467
|
1 |
|
if (trim($submissionVars['password1']) == "") { |
1468
|
|
|
register_error(elgg_echo('install:admin:password:empty')); |
1469
|
|
|
|
1470
|
|
|
return false; |
1471
|
|
|
} |
1472
|
|
|
|
1473
|
1 |
|
$minLength = $app->_services->configTable->get('min_password_length'); |
|
|
|
|
1474
|
1 |
|
if (strlen($submissionVars['password1']) < $minLength) { |
1475
|
|
|
register_error(elgg_echo('install:admin:password:tooshort')); |
1476
|
|
|
|
1477
|
|
|
return false; |
1478
|
|
|
} |
1479
|
|
|
|
1480
|
|
|
// check that email address is email address |
1481
|
1 |
|
if ($submissionVars['email'] && !is_email_address($submissionVars['email'])) { |
1482
|
|
|
$msg = elgg_echo('install:error:emailaddress', [$submissionVars['email']]); |
1483
|
|
|
register_error($msg); |
1484
|
|
|
|
1485
|
|
|
return false; |
1486
|
|
|
} |
1487
|
|
|
|
1488
|
1 |
|
return true; |
1489
|
|
|
} |
1490
|
|
|
|
1491
|
|
|
/** |
1492
|
|
|
* Create a user account for the admin |
1493
|
|
|
* |
1494
|
|
|
* @param array $submissionVars Submitted vars |
1495
|
|
|
* @param bool $login Login in the admin user? |
1496
|
|
|
* |
1497
|
|
|
* @return bool |
1498
|
|
|
* @throws InstallationException |
1499
|
|
|
*/ |
1500
|
2 |
|
protected function createAdminAccount($submissionVars, $login = false) { |
1501
|
|
|
try { |
1502
|
2 |
|
$guid = register_user( |
1503
|
2 |
|
$submissionVars['username'], |
1504
|
2 |
|
$submissionVars['password1'], |
1505
|
2 |
|
$submissionVars['displayname'], |
1506
|
2 |
|
$submissionVars['email'] |
1507
|
|
|
); |
1508
|
|
|
} catch (RegistrationException $e) { |
1509
|
|
|
register_error($e->getMessage()); |
1510
|
|
|
|
1511
|
|
|
return false; |
1512
|
|
|
} |
1513
|
|
|
|
1514
|
2 |
|
if (!$guid) { |
1515
|
|
|
register_error(elgg_echo('install:admin:cannot_create')); |
1516
|
|
|
|
1517
|
|
|
return false; |
1518
|
|
|
} |
1519
|
|
|
|
1520
|
2 |
|
$user = get_entity($guid); |
1521
|
|
|
|
1522
|
2 |
|
if (!$user instanceof ElggUser) { |
1523
|
|
|
register_error(elgg_echo('install:error:loadadmin')); |
1524
|
|
|
|
1525
|
|
|
return false; |
1526
|
|
|
} |
1527
|
|
|
|
1528
|
2 |
|
$app = $this->getApp(); |
1529
|
|
|
|
1530
|
2 |
|
$ia = elgg_set_ignore_access(true); |
1531
|
2 |
|
if ($user->makeAdmin() == false) { |
|
|
|
|
1532
|
|
|
register_error(elgg_echo('install:error:adminaccess')); |
1533
|
|
|
} else { |
1534
|
2 |
|
$app->_services->configTable->set('admin_registered', 1); |
1535
|
|
|
} |
1536
|
2 |
|
elgg_set_ignore_access($ia); |
1537
|
|
|
|
1538
|
|
|
// add validation data to satisfy user validation plugins |
1539
|
2 |
|
$user->validated = 1; |
|
|
|
|
1540
|
2 |
|
$user->validated_method = 'admin_user'; |
|
|
|
|
1541
|
|
|
|
1542
|
2 |
|
if (!$login) { |
1543
|
1 |
|
return true; |
1544
|
|
|
} |
1545
|
|
|
|
1546
|
1 |
|
$this->createSessionFromDatabase(); |
1547
|
|
|
try { |
1548
|
1 |
|
if (login($user) == false) { |
|
|
|
|
1549
|
1 |
|
register_error(elgg_echo('install:error:adminlogin')); |
1550
|
|
|
} |
1551
|
|
|
} catch (LoginException $ex) { |
1552
|
|
|
return false; |
1553
|
|
|
} |
1554
|
|
|
|
1555
|
1 |
|
return true; |
1556
|
|
|
} |
1557
|
|
|
|
1558
|
|
|
/** |
1559
|
|
|
* Setup session |
1560
|
|
|
* |
1561
|
|
|
* @return void |
1562
|
|
|
* @throws InstallationException |
1563
|
|
|
*/ |
1564
|
|
|
protected function createSessionFromFile() { |
1565
|
|
|
$app = $this->getApp(); |
1566
|
|
|
$session = ElggSession::fromFiles($app->_services->config); |
1567
|
|
|
$session->setName('Elgg_install'); |
1568
|
|
|
$app->_services->setValue('session', $session); |
1569
|
|
|
} |
1570
|
|
|
|
1571
|
|
|
/** |
1572
|
|
|
* Setup session |
1573
|
|
|
* |
1574
|
|
|
* @return void |
1575
|
|
|
* @throws InstallationException |
1576
|
|
|
*/ |
1577
|
|
|
protected function createSessionFromDatabase() { |
1578
|
|
|
$app = $this->getApp(); |
1579
|
|
|
$session = ElggSession::fromDatabase($app->_services->config, $app->_services->db); |
1580
|
|
|
$session->start(); |
1581
|
|
|
$app->_services->setValue('session', $session); |
1582
|
|
|
} |
1583
|
|
|
} |
1584
|
|
|
|
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/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 beforeOtherDir/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: