1 | <?php |
||||||
2 | |||||||
3 | use Elgg\Database; |
||||||
4 | use Elgg\Filesystem\Directory; |
||||||
0 ignored issues
–
show
|
|||||||
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) { |
|||||
0 ignored issues
–
show
|
|||||||
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)) { |
|||||
0 ignored issues
–
show
$path of type Elgg\Config is incompatible with the type string expected by parameter $filename of is_file() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
$path of type Elgg\Config is incompatible with the type string expected by parameter $filename of is_readable() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
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) { |
|||||
0 ignored issues
–
show
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
Loading history...
|
|||||||
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()); |
|||||
1 ignored issue
–
show
|
|||||||
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()); |
|||||
0 ignored issues
–
show
Elgg\Config::resolvePath() of type Elgg\Config is incompatible with the type string expected by parameter $path of Elgg\Config::fromFile() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
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())) { |
|||||
0 ignored issues
–
show
Elgg\Config::resolvePath() of type Elgg\Config is incompatible with the type string expected by parameter $filename of is_file() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
916 | 3 | return false; |
|||||
917 | } |
||||||
918 | |||||||
919 | if (!is_readable(Config::resolvePath())) { |
||||||
0 ignored issues
–
show
Elgg\Config::resolvePath() of type Elgg\Config is incompatible with the type string expected by parameter $filename of is_readable() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
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 |
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: