Passed
Push — master ( f13f78...5c1b24 )
by Ismayil
04:22
created

engine/classes/Elgg/BootService.php (1 issue)

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
3
namespace Elgg;
4
5
use Elgg\Database\SiteSecret;
6
use Stash\Driver\BlackHole;
7
use Stash\Driver\FileSystem;
8
use Stash\Driver\Memcache;
9
use Stash\Invalidation;
10
use Stash\Pool;
11
use Elgg\Di\ServiceProvider;
12
13
/**
14
 * Boots Elgg and manages a cache of data needed during boot
15
 *
16
 * @access private
17
 * @since 2.1
18
 */
19
class BootService {
20
	use Profilable;
21
22
	/**
23
	 * Under load, a short TTL gives nearly all of the benefits of a longer TTL, but it also ensures
24
	 * that, should cache invalidation fail for some reason, it'll be rebuilt quickly anyway.
25
	 *
26
	 * In 2.x we do not cache by default. This will likely change to 10 in 3.0.
27
	 */
28
	const DEFAULT_BOOT_CACHE_TTL = 0;
29
30
	/**
31
	 * Has the cache already been invalidated this request? Avoids thrashing
32
	 *
33
	 * @var bool
34
	 */
35
	private $was_cleared = false;
36
37
	/**
38
	 * Boots the engine
39
	 *
40
	 * @param ServiceProvider $services Services
41
	 * @return void
42
	 */
43
	public function boot(ServiceProvider $services) {
44
		$db = $services->db;
45
		$config = $services->config;
46
47
		// set cookie values for session and remember me
48
		$config->getCookieConfig();
49
50
		// defaults in case these aren't in config table
51
		if ($config->boot_cache_ttl === null) {
52
			$config->boot_cache_ttl = self::DEFAULT_BOOT_CACHE_TTL;
53
		}
54
		if ($config->simplecache_enabled === null) {
55
			$config->simplecache_enabled = 0;
56
		}
57
		if ($config->system_cache_enabled === null) {
58
			$config->system_cache_enabled = false;
59
		}
60
		if ($config->simplecache_lastupdate === null) {
61
			$config->simplecache_lastupdate = 0;
62
		}
63
64
		// we were using NOTICE temporarily so we can't just check for null
65
		if (!$config->hasInitialValue('debug') && !$config->debug) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $config->debug of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
66
			$config->debug = '';
67
		}
68
69
		// copy all table values into config
70
		$config->mergeValues($services->configTable->getAll());
71
72
		// needs to be set before [init, system] for links in html head
73
		$config->lastcache = (int) $config->simplecache_lastupdate;
74
75
		if (!$config->elgg_config_set_secret) {
76
			$site_secret = SiteSecret::fromConfig($config);
77
			if (!$site_secret) {
78
				// The 2.3 installer doesn't create a site key (it's created on-demand on the first request)
79
				// so for our Travis upgrade testing we need to check for this and create one on the spot.
80
				if (defined('UPGRADING')) {
81
					$site_secret = SiteSecret::regenerate($services->crypto, $services->configTable);
82
				}
83
			}
84
			if ($site_secret) {
85
				$services->setValue('siteSecret', $site_secret);
86
			} else {
87
				throw new \RuntimeException('The site secret is not set.');
88
			}
89
		}
90
91
		$installed = isset($config->installed);
92
93
		if ($this->timer) {
94
			$this->timer->begin([__CLASS__ . '::getBootData']);
95
		}
96
97
		// early config is done, now get the core boot data
98
		$data = $this->getBootData($config, $db, $installed);
99
100
		$site = $data->getSite();
101
		if (!$site) {
102
			// must be set in config
103
			$site = $config->site;
104
			if (!$site instanceof \ElggSite) {
105
				throw new \RuntimeException('Before installation, config->site must have an unsaved ElggSite.');
106
			}
107
		}
108
		$config->site = $site;
109
		$config->sitename = $site->name;
110
		$config->sitedescription = $site->description;
111
112
		$services->subtypeTable->setCachedValues($data->getSubtypeData());
113
114
		$services->plugins->setBootPlugins($data->getActivePlugins());
115
116
		$services->pluginSettingsCache->setCachedValues($data->getPluginSettings());
117
118
		$services->logger->setLevel($config->debug);
119
		if ($config->debug) {
120
			$services->logger->setDisplay(true);
121
		}
122
123
		// finish boot sequence
124
		_elgg_session_boot($services);
125
126
		if ($config->system_cache_enabled) {
127
			$config->system_cache_loaded = false;
128
129
			if ($services->views->configureFromCache($services->systemCache)) {
130
				$config->system_cache_loaded = true;
131
			}
132
		}
133
134
		// we don't store langs in boot data because it varies by user
135
		$services->translator->loadTranslations();
136
137
		// we always need site->email and user->icontime, so load them together
138
		$user_guid = $services->session->getLoggedInUserGuid();
139
		if ($user_guid) {
140
			$services->metadataCache->populateFromEntities([$user_guid]);
141
		}
142
143
		// invalidate on some actions just in case other invalidation triggers miss something
144
		$services->hooks->registerHandler('action', 'all', function ($action) {
145
			if (0 === strpos($action, 'admin/' || $action === 'plugins/settings/save')) {
146
				$this->invalidateCache();
147
			}
148
		}, 1);
149
	}
150
151
	/**
152
	 * Invalidate the cache item
153
	 *
154
	 * @return void
155
	 */
156 10
	public function invalidateCache() {
157 10
		if (!$this->was_cleared) {
158 8
			$this->getStashItem(_elgg_config())->clear();
159 8
			$this->was_cleared = true;
160
		}
161 10
	}
162
163
	/**
164
	 * Get the boot data
165
	 *
166
	 * @param Config   $config    Elgg config object
167
	 * @param Database $db        Elgg database
168
	 * @param bool     $installed Is the site installed?
169
	 *
170
	 * @return BootData
171
	 *
172
	 * @throws \InstallationException
173
	 */
174
	private function getBootData(Config $config, Database $db, $installed) {
175
		$config->_boot_cache_hit = false;
176
177
		if (!$config->boot_cache_ttl) {
178
			$data = new BootData();
179
			$data->populate($db, _elgg_services()->entityTable, _elgg_services()->plugins, $installed);
180
			return $data;
181
		}
182
183
		$item = $this->getStashItem($config);
184
		$item->setInvalidationMethod(Invalidation::NONE);
185
		$data = $item->get();
186
		if ($item->isMiss()) {
187
			$data = new BootData();
188
			$data->populate($db, _elgg_services()->entityTable, _elgg_services()->plugins, $installed);
189
			$item->set($data);
190
			$item->expiresAfter($config->boot_cache_ttl);
191
			$item->save();
192
		} else {
193
			$config->_boot_cache_hit = true;
194
		}
195
196
		return $data;
197
	}
198
199
	/**
200
	 * Get a Stash cache item
201
	 *
202
	 * @param Config $config Elgg config
203
	 *
204
	 * @return \Stash\Interfaces\ItemInterface
205
	 */
206 8
	private function getStashItem(Config $config) {
207 8
		if ($config->memcache && class_exists('Memcache')) {
208
			$options = [];
209
			if ($config->memcache_servers) {
210
				$options['servers'] = $config->memcache_servers;
211
			}
212
			$driver = new Memcache($options);
213
		} else {
214 8
			if (!$config->dataroot) {
215
				// we're in the installer
216
				$driver = new BlackHole();
217
			} else {
218 8
				$driver = new FileSystem([
219 8
					'path' => $config->dataroot,
220
				]);
221
			}
222
		}
223 8
		return (new Pool($driver))->getItem("boot_data");
224
	}
225
}
226