Test Failed
Push — master ( c2873c...a077d1 )
by Jeroen
01:35
created

engine/classes/Elgg/Config.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Elgg;
3
4
use Elgg\Config\DatarootSettingMigrator;
5
use Elgg\Config\SettingsMigrator;
6
use Elgg\Config\WwwrootSettingMigrator;
7
use Elgg\Filesystem\Directory;
8
use Elgg\Database\ConfigTable;
9
use ConfigurationException;
10
use Elgg\Project\Paths;
11
12
/**
13
 * Access to configuration values
14
 *
15
 * @since 1.10.0
16
 *
17
 * @property int           $action_time_limit
18
 * @property int           $action_token_timeout
19
 * @property bool          $allow_registration
20
 * @property string        $allow_user_default_access
21
 * @property bool          $auto_disable_plugins
22
 * @property int           $batch_run_time_in_secs
23
 * @property bool          $boot_complete
24
 * @property int           $boot_cache_ttl
25
 * @property array         $breadcrumbs
26
 * @property string        $cacheroot         Path of cache storage with trailing "/"
27
 * @property string        $dataroot          Path of data storage with trailing "/"
28
 * @property bool          $data_dir_override
29
 * @property array         $db
30
 * @property string        $dbencoding
31
 * @property string        $dbname
32
 * @property string        $dbuser
33
 * @property string        $dbhost
34
 * @property string        $dbpass
35
 * @property string        $dbprefix
36
 * @property string        $debug
37
 * @property int           $default_access
38
 * @property int           $default_limit
39
 * @property array         $default_widget_info
40
 * @property bool          $elgg_config_locks The application will lock some settings (default true)
41
 * @property string[]      $elgg_cron_periods
42
 * @property array         $elgg_lazy_hover_menus
43 196
 * @property bool          $elgg_load_sync_code
44 196
 * @property bool          $elgg_maintenance_mode
45
 * @property string        $elgg_settings_file
46
 * @property bool          $elgg_config_set_secret
47 196
 * @property bool          $enable_profiling
48 196
 * @property mixed         $embed_tab
49
 * @property string        $exception_include
50 196
 * @property string[]      $group
51
 * @property array         $group_tool_options
52
 * @property bool          $i18n_loaded_from_cache
53
 * @property array         $icon_sizes
54
 * @property string        $installed
55
 * @property bool          $installer_running
56
 * @property string        $language     Site language code
57
 * @property int           $lastcache
58
 * @property \ElggLogCache $log_cache
59
 * @property array         $libraries
60
 * @property bool          $memcache
61
 * @property array         $memcache_servers
62
 * @property array         $menus
63
 * @property int           $min_password_length
64 196
 * @property string[]      $pages
65 196
 * @property-read string   $path         Path of composer install with trailing "/"
66
 * @property-read string   $pluginspath  Alias of plugins_path
67 196
 * @property-read string   $plugins_path Path of project "mod/" directory
68
 * @property array         $profile_custom_fields
69
 * @property array         $profile_fields
70
 * @property string        $profiling_minimum_percentage
71
 * @property bool          $profiling_sql
72 461
 * @property array         $processed_upgrades
73 461
 * @property string[]      $registered_entities
74
 * @property bool          $security_disable_password_autocomplete
75
 * @property bool          $security_email_require_password
76
 * @property bool          $security_notify_admins
77
 * @property bool          $security_notify_user_admin
78
 * @property bool          $security_notify_user_ban
79
 * @property bool          $security_protect_cron
80
 * @property bool          $security_protect_upgrade
81
 * @property int           $simplecache_enabled
82
 * @property int           $simplecache_lastupdate
83
 * @property bool          $simplecache_minify_css
84
 * @property bool          $simplecache_minify_js
85
 * @property \ElggSite     $site
86
 * @property string        $sitedescription
87
 * @property string        $sitename
88 353
 * @property string[]      $site_custom_menu_items
89 353
 * @property string[]      $site_featured_menu_names
90
 * @property-read int      $site_guid
91 353
 * @property bool          $system_cache_enabled
92 157
 * @property bool          $system_cache_loaded
93
 * @property string        $url          Alias of "wwwroot"
94
 * @property int           $version
95 196
 * @property string        $view         Default viewtype (usually not set)
96
 * @property bool          $walled_garden
97
 * @property string        $wwwroot      Site URL
98 196
 * @property string        $x_sendfile_type
99
 * @property string        $x_accel_mapping
100
 * @property bool          $_boot_cache_hit
101 196
 * @property bool          $_elgg_autofeed
102
 */
103
class Config {
104 196
	use Loggable;
105 196
106 196
	/**
107 196
	 * @var array Configuration storage
108
	 */
109
	private $values;
110 196
111 196
	/**
112 196
	 * @var array
113
	 */
114 196
	private $initial_values;
115
116 196
	/**
117
	 * @var bool
118
	 */
119
	private $cookies_configured = false;
120
121
	/**
122 18
	 * @var ConfigTable Do not use directly. Use getConfigTable().
123 18
	 */
124 18
	private $config_table;
125
126
	/**
127
	 * @var array
128
	 */
129
	private $locked = [];
130 2
131 2
	/**
132 2
	 * Constructor
133
	 *
134
	 * @param array $values Initial config values from Env/settings file
135
	 * @internal Do not use
136
	 * @access private
137
	 */
138 409
	public function __construct(array $values = []) {
139 409
		$this->values = $values;
140
141 409
		// Don't keep copies of these in case config gets dumped
142 309
		$sensitive_props = [
143
			'__site_secret__',
144
			'db',
145 255
			'dbhost',
146 255
			'dbuser',
147
			'dbpass',
148
			'dbname',
149
			'profiler_secret_get_var'
150
		];
151
		foreach ($sensitive_props as $name) {
152
			unset($values[$name]);
153
		}
154
		$this->initial_values = $values;
155
	}
156
157
	/**
158
	 * Build a config from default settings locations
159
	 *
160
	 * @param string $settings_path Path of settings file
161
	 * @param bool   $try_env       If path not given, try $_ENV['ELGG_SETTINGS_FILE']
162
	 * @return Config
163 211
	 *
164 211
	 * @throws ConfigurationException
165
	 */
166
	public static function factory($settings_path = '', $try_env = true) {
0 ignored issues
show
factory uses the super-global variable $_ENV which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
167
		$reason1 = '';
168
		$reason2 = '';
169
170 270
		if ($try_env && !empty($_ENV['ELGG_SETTINGS_FILE'])) {
171 270
			$settings_path = $_ENV['ELGG_SETTINGS_FILE'];
172 270
		}
173 270
174
		if ($settings_path) {
175
			$config = self::fromFile($settings_path, $reason1);
176
		} else {
177
			$config = self::fromFile(Paths::settingsFile(Paths::SETTINGS_PHP), $reason1);
178
		}
179
180
		if (!$config) {
181
			$msg = __METHOD__ . ": Reading configs failed: $reason1 $reason2";
182
			throw new ConfigurationException($msg);
183
		}
184
185
		return $config;
186
	}
187
188
	/**
189
	 * Build a config from a file
190
	 *
191
	 * @param string $path   Path of settings.php
192
	 * @param string $reason Returned reason for failure
193
	 *
194
	 * @return bool|Config false on failure
195
	 */
196
	public static function fromFile($path, &$reason = '') {
0 ignored issues
show
fromFile uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
197
		if (!is_file($path)) {
198
			$reason = "File $path not present.";
199
			return false;
200
		}
201
202
		if (!is_readable($path)) {
203
			$reason = "File $path not readable.";
204
			return false;
205
		}
206
207
		// legacy loading. If $CONFIG doesn't exist, remove it after the
208
		// settings file is read.
209
		$global_is_set = isset($GLOBALS['CONFIG']);
210
211
		Includer::requireFile($path);
212
213
		$get_db = function() {
214
			// try to migrate settings to the file
215
			$db_conf = new \Elgg\Database\DbConfig($GLOBALS['CONFIG']);
216
			return new Database($db_conf);
217
		};
218
219
		if (empty($GLOBALS['CONFIG']->dataroot)) {
220
			$dataroot = (new DatarootSettingMigrator($get_db(), $path))->migrate();
221
			if (!isset($dataroot)) {
222
				$reason = 'The Elgg settings file is missing $CONFIG->dataroot.';
223 216
				return false;
224 216
			}
225 216
226
			$GLOBALS['CONFIG']->dataroot = $dataroot;
227
228 196
			// just try this one time to migrate wwwroot
229 196
			if (!isset($GLOBALS['CONFIG']->wwwroot)) {
230 196
				$wwwroot = (new WwwrootSettingMigrator($get_db(), $path))->migrate();
231 196
				if (isset($wwwroot)) {
232
					$GLOBALS['CONFIG']->wwwroot = $wwwroot;
233
				}
234
			}
235
		}
236
237
		$config = new self(get_object_vars($GLOBALS['CONFIG']));
238
239
		if (!$global_is_set) {
240
			unset($GLOBALS['CONFIG']);
241
		}
242
243
		if ($config->{'X-Sendfile-Type'}) {
244
			$config->{'x_sendfile_type'} = $config->{'X-Sendfile-Type'};
245
			unset($config->{'X-Sendfile-Type'});
246
		}
247
		if ($config->{'X-Accel-Mapping'}) {
248
			$config->{'x_accel_mapping'} = $config->{'X-Accel-Mapping'};
249
			unset($config->{'X-Accel-Mapping'});
250
		}
251
252
		$config->elgg_settings_file = $path;
253
		$config->lock('elgg_settings_file');
254
255
		return $config;
256
	}
257
258
	/**
259
	 * Set an array of values
260
	 *
261
	 * @param array $values Values
262
	 * @return void
263
	 */
264
	public function mergeValues(array $values) {
265
		foreach ($values as $name => $value) {
266
			$this->__set($name, $value);
267
		}
268
	}
269
270
	/**
271
	 * Get all values
272
	 *
273
	 * @return array
274
	 */
275
	public function getValues() {
276
		return $this->values;
277
	}
278
279
	/**
280
	 * Set up and return the cookie configuration array resolved from settings
281
	 *
282
	 * @return array
283
	 */
284
	public function getCookieConfig() {
285
		if ($this->cookies_configured) {
286
			return $this->cookies;
0 ignored issues
show
The property cookies does not seem to exist. Did you mean cookies_configured?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
287
		}
288
289
		$cookies = $this->cookies;
0 ignored issues
show
The property cookies does not seem to exist. Did you mean cookies_configured?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
290
		if (!is_array($cookies)) {
291
			$cookies = [];
292
		}
293
294
		if (!isset($cookies['session'])) {
295
			$cookies['session'] = [];
296
		}
297
		$session_defaults = session_get_cookie_params();
298
		$session_defaults['name'] = 'Elgg';
299
		$cookies['session'] = array_merge($session_defaults, $cookies['session']);
300
		if (!isset($cookies['remember_me'])) {
301
			$cookies['remember_me'] = [];
302 200
		}
303 200
		$session_defaults['name'] = 'elggperm';
304
		$session_defaults['expire'] = strtotime("+30 days");
305
		$cookies['remember_me'] = array_merge($session_defaults, $cookies['remember_me']);
306
307
		$this->cookies = $cookies;
0 ignored issues
show
The property cookies does not seem to exist. Did you mean cookies_configured?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
308
		$this->cookies_configured = true;
309
310
		return $cookies;
311
	}
312
313
	/**
314
	 * Get an Elgg configuration value if it's been set or loaded during the boot process.
315
	 *
316
	 * Before \Elgg\BootService::boot, values from the database will not be present.
317 196
	 *
318 196
	 * @param string $name Name
319 196
	 *
320
	 * @return mixed null if does not exist
321
	 */
322
	public function __get($name) {
323
		if (isset($this->values[$name])) {
324
			return $this->values[$name];
325
		}
326
327
		return null;
328
	}
329
330
	/**
331
	 * Get a value set at construction time
332
	 *
333
	 * @param string $name Name
334
	 * @return mixed null = not set
335
	 */
336
	public function getInitialValue($name) {
337
		return isset($this->initial_values[$name]) ? $this->initial_values[$name] : null;
338
	}
339
340
	/**
341
	 * Was a value available at construction time? (From settings.php)
342
	 *
343
	 * @param string $name Name
344
	 *
345
	 * @return bool
346
	 */
347
	public function hasInitialValue($name) {
348
		return isset($this->initial_values[$name]);
349
	}
350
351
	/**
352
	 * Make a value read-only
353
	 *
354
	 * @param string $name Name
355
	 * @return void
356
	 */
357
	public function lock($name) {
358
		$this->locked[$name] = true;
359
	}
360
361
	/**
362
	 * Is this value locked?
363
	 *
364
	 * @param string $name Name
365
	 *
366
	 * @return bool
367
	 */
368
	public function isLocked($name) {
369
		return isset($this->locked[$name]);
370
	}
371
372
	/**
373
	 * Set an Elgg configuration value
374
	 *
375
	 * @warning This does not persist the configuration setting. Use elgg_save_config()
376
	 *
377
	 * @param string $name  Name
378
	 * @param mixed  $value Value
379
	 * @return void
380
	 */
381
	public function __set($name, $value) {
382
		if ($this->wasWarnedLocked($name)) {
383
			return;
384
		}
385
386
		$this->values[$name] = $value;
387
	}
388
389
	/**
390
	 * Handle isset()
391
	 *
392
	 * @param string $name Name
393
	 * @return bool
394
	 */
395
	public function __isset($name) {
396
		return $this->__get($name) !== null;
397
	}
398
399
	/**
400
	 * Handle unset()
401
	 *
402
	 * @param string $name Name
403
	 * @return void
404
	 */
405
	public function __unset($name) {
406
		if ($this->wasWarnedLocked($name)) {
407
			return;
408
		}
409
410
		unset($this->values[$name]);
411
	}
412
413
	/**
414
	 * Save a configuration setting to the database
415
	 *
416
	 * @param string $name  Name (cannot be greater than 255 characters)
417
	 * @param mixed  $value Value
418
	 *
419
	 * @return bool
420
	 */
421
	public function save($name, $value) {
422
		if ($this->wasWarnedLocked($name)) {
423
			return false;
424
		}
425
426
		if (strlen($name) > 255) {
427
			if ($this->logger) {
428
				$this->logger->error("The name length for configuration variables cannot be greater than 255");
429
			}
430
			return false;
431
		}
432
433
		$result = $this->getConfigTable()->set($name, $value);
434
435
		$this->__set($name, $value);
436
	
437
		return $result;
438
	}
439
440
	/**
441
	 * Removes a configuration setting from the database
442
	 *
443
	 * @param string $name Configuration name
444
	 *
445
	 * @return bool
446
	 */
447
	public function remove($name) {
448
		if ($this->wasWarnedLocked($name)) {
449
			return false;
450
		}
451
452
		$result = $this->getConfigTable()->remove($name);
453
454
		unset($this->values[$name]);
455
	
456
		return $result;
457
	}
458
459
	/**
460
	 * Log a read-only warning if the name is read-only
461
	 *
462
	 * @param string $name Name
463
	 * @return bool
464
	 */
465
	private function wasWarnedLocked($name) {
466
		if (!isset($this->locked[$name])) {
467
			return false;
468
		}
469
470
		if ($this->logger) {
471
			$this->logger->warn("The property $name is read-only.");
472
		}
473
		return true;
474
	}
475
476
	/**
477
	 * Set the config table service (must be set)
478
	 *
479
	 * This is a necessary evil until we refactor so that the service provider has no dependencies.
480
	 *
481
	 * @param ConfigTable $table
482
	 * @return void
483
	 *
484
	 * @access private
485
	 * @internal
486
	 */
487
	public function setConfigTable(ConfigTable $table) {
488
		$this->config_table = $table;
489
	}
490
491
	/**
492
	 * Get the core entity types
493
	 *
494
	 * @return string[]
495
	 */
496
	public static function getEntityTypes() {
497
		return ['group', 'object', 'site', 'user'];
498
	}
499
500
	/**
501
	 * Get the config table API
502
	 *
503
	 * @return ConfigTable
504
	 */
505
	private function getConfigTable() {
506
		if (!$this->config_table) {
507
			if (!function_exists('_elgg_services')) {
508
				throw new \RuntimeException('setConfigTable() must be called before using API that' .
509
					' uses the database.');
510
			}
511
512
			$this->config_table = _elgg_services()->configTable;
513
		}
514
515
		return $this->config_table;
516
	}
517
}
518