Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#1031)
by Dan
05:27
created

SmrSession::getCommonID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
if (!defined('USING_AJAX')) {
4
	define('USING_AJAX', false);
5
}
6
7
class SmrSession {
8
9
	const TIME_BEFORE_EXPIRY = 3600;
10
11
	private const URL_LOAD_DELAY = array(
12
		'configure_hardware.php' => .4,
13
		'forces_drop.php' => .4,
14
		'forces_drop_processing.php' => .5,
15
		'forces_refresh_processing.php' => .4,
16
		'sector_jump_processing.php' => .4,
17
		'sector_move_processing.php' => .4,
18
		'sector_scan.php' => .4,
19
		'shop_goods_processing.php' => .4,
20
		'trader_attack_processing.php' => .75,
21
		'trader_examine.php' => .75
22
	);
23
24
	protected static MySqlDatabase $db;
25
26
	private static ?string $session_id;
27
	private static int $game_id;
28
	private static array $var;
29
	private static array $commonIDs;
30
	private static bool $generate;
31
	private static string $SN = '';
32
	private static string $lastSN;
33
	private static int $account_id;
34
	public static int $last_accessed;
35
	private static Time $pageRequestTime;
36
37
	protected static array $previousAjaxReturns;
38
	protected static array $ajaxReturns = array();
39
40
	/**
41
	 * Returns the time (in seconds) associated with this page request.
42
	 */
43
	public static function getTime() : int {
44
		return self::$pageRequestTime->getTime();
45
	}
46
47
	/**
48
	 * Returns the time (in seconds, with microsecond-level precision)
49
	 * associated with this page request.
50
	 */
51
	public static function getMicroTime() : float {
52
		return self::$pageRequestTime->getMicroTime();
53
	}
54
55
	/**
56
	 * Update the time associated with this page request.
57
	 *
58
	 * NOTE: This should never be called by normal page requests, and should
59
	 * only be used by the CLI programs that run continuously.
60
	 */
61
	public static function updateTime() : void {
62
		if (!defined('NPC_SCRIPT')) {
63
			throw new Exception('Only call this function from CLI programs!');
64
		}
65
		self::$pageRequestTime = new Time();
66
	}
67
68
	public static function init() : void {
69
		// Return immediately if the SmrSession is already initialized
70
		if (isset(self::$session_id)) {
71
			return;
72
		}
73
74
		// Initialize the page request time
75
		self::$pageRequestTime = new Time();
76
77
		// Initialize the db connector here, since `init` is always called
78
		self::$db = MySqlDatabase::getInstance();
79
80
		// now try the cookie
81
		if (isset($_COOKIE['session_id']) && strlen($_COOKIE['session_id']) === 32) {
82
			self::$session_id = $_COOKIE['session_id'];
83
		} else {
84
			// create a new session id
85
			do {
86
				self::$session_id = md5(uniqid(strval(rand())));
87
				self::$db->query('SELECT 1 FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
88
			} while (self::$db->nextRecord()); //Make sure we haven't somehow clashed with someone else's session.
89
90
			// This is a minor hack to make sure that setcookie is not called
91
			// for CLI programs and tests (to avoid "headers already sent").
92
			if (headers_sent() === false) {
93
				setcookie('session_id', self::$session_id);
94
			}
95
		}
96
97
		// try to get current session
98
		self::$db->query('DELETE FROM active_session WHERE last_accessed < ' . self::$db->escapeNumber(time() - self::TIME_BEFORE_EXPIRY));
99
		self::fetchVarInfo();
100
101
		$sn = Request::get('sn', '');
102
		if (!USING_AJAX && !empty($sn) && !empty(self::$var[$sn])) {
103
			$var = self::$var[$sn];
104
			$currentPage = $var['url'] == 'skeleton.php' ? $var['body'] : $var['url'];
105
			$loadDelay = self::URL_LOAD_DELAY[$currentPage] ?? 0;
106
			$initialTimeBetweenLoads = microtime(true) - $var['PreviousRequestTime'];
107
			while (($timeBetweenLoads = microtime(true) - $var['PreviousRequestTime']) < $loadDelay) {
108
				$sleepTime = IRound(($loadDelay - $timeBetweenLoads) * 1000000);
109
			//	echo 'Sleeping for: ' . $sleepTime . 'us';
110
				usleep($sleepTime);
111
			}
112
			if (ENABLE_DEBUG) {
113
				self::$db->query('INSERT INTO debug VALUES (' . self::$db->escapeString('Delay: ' . $currentPage) . ',' . self::$db->escapeNumber(self::$account_id) . ',' . self::$db->escapeNumber($initialTimeBetweenLoads) . ',' . self::$db->escapeNumber($timeBetweenLoads) . ')');
114
			}
115
		}
116
	}
117
118
	public static function fetchVarInfo() : void {
119
		self::$db->query('SELECT * FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
120
		if (self::$db->nextRecord()) {
121
			self::$generate = false;
122
			self::$session_id = self::$db->getField('session_id');
123
			self::$account_id = self::$db->getInt('account_id');
124
			self::$game_id = self::$db->getInt('game_id');
125
			self::$last_accessed = self::$db->getInt('last_accessed');
126
			self::$commonIDs = array();
127
			self::$lastSN = self::$db->getField('last_sn');
128
			// We may not have ajax_returns if ajax was disabled
129
			$ajaxReturns = self::$db->getField('ajax_returns');
130
			if (!empty($ajaxReturns)) {
131
				self::$previousAjaxReturns = unserialize(gzuncompress($ajaxReturns));
132
			}
133
134
			$var = self::$db->getField('session_var');
135
			if ($var === '') {
136
				self::$account_id = 0;
137
				self::$game_id = 0;
138
				self::$var = array();
139
			} else {
140
				self::$var = unserialize(gzuncompress($var));
141
			}
142
143
			foreach (self::$var as $key => $value) {
144
				if ($value['Expires'] > 0 && $value['Expires'] <= self::getTime()) { // Use 0 for infinity
145
					//This link is no longer valid
146
					unset(self::$var[$key]);
147
				} elseif ($value['RemainingPageLoads'] < 0) {
148
					//This link is no longer valid
149
					unset(self::$var[$key]);
150
				} else {
151
					--self::$var[$key]['RemainingPageLoads'];
152
					if (isset($value['CommonID'])) {
153
						self::$commonIDs[$value['CommonID']] = $key;
154
					}
155
				}
156
			}
157
		} else {
158
			self::$generate = true;
159
			self::$account_id = 0;
160
			self::$game_id = 0;
161
			self::$var = array();
162
			self::$commonIDs = array();
163
		}
164
	}
165
166
	public static function update() : void {
167
		foreach (self::$var as $key => $value) {
168
			if ($value['RemainingPageLoads'] <= 0) {
169
				//This link was valid this load but will not be in the future, removing it now saves database space and data transfer.
170
				unset(self::$var[$key]);
171
			}
172
		}
173
		$compressed = gzcompress(serialize(self::$var));
174
		if (!self::$generate) {
175
			self::$db->query('UPDATE active_session SET account_id=' . self::$db->escapeNumber(self::$account_id) . ',game_id=' . self::$db->escapeNumber(self::$game_id) . (!USING_AJAX ? ',last_accessed=' . self::$db->escapeNumber(self::getTime()) : '') . ',session_var=' . self::$db->escapeBinary($compressed) .
176
					',last_sn=' . self::$db->escapeString(self::$SN) .
177
					' WHERE session_id=' . self::$db->escapeString(self::$session_id) . (USING_AJAX ? ' AND last_sn=' . self::$db->escapeString(self::$lastSN) : '') . ' LIMIT 1');
178
		} else {
179
			self::$db->query('DELETE FROM active_session WHERE account_id = ' . self::$db->escapeNumber(self::$account_id) . ' AND game_id = ' . self::$db->escapeNumber(self::$game_id));
180
			self::$db->query('INSERT INTO active_session (session_id, account_id, game_id, last_accessed, session_var) VALUES(' . self::$db->escapeString(self::$session_id) . ',' . self::$db->escapeNumber(self::$account_id) . ',' . self::$db->escapeNumber(self::$game_id) . ',' . self::$db->escapeNumber(self::getTime()) . ',' . self::$db->escapeBinary($compressed) . ')');
181
			self::$generate = false;
182
		}
183
	}
184
185
	/**
186
	 * Returns the Game ID associated with the session.
187
	 */
188
	public static function getGameID() : int {
189
		return self::$game_id;
190
	}
191
192
	/**
193
	 * Returns true if the session is inside a game, false otherwise.
194
	 */
195
	public static function hasGame() : bool {
196
		return self::$game_id != 0;
197
	}
198
199
	public static function hasAccount() : bool {
200
		return self::$account_id > 0;
201
	}
202
203
	public static function getAccountID() : int {
204
		return self::$account_id;
205
	}
206
207
	public static function getAccount() : SmrAccount {
208
		return SmrAccount::getAccount(self::$account_id);
209
	}
210
211
	/**
212
	 * Sets the `account_id` attribute of this session.
213
	 */
214
	public static function setAccount(AbstractSmrAccount $account) : void {
215
		self::$account_id = $account->getAccountID();
216
	}
217
218
	/**
219
	 * Updates the `game_id` attribute of the session and deletes any other
220
	 * active sessions in this game for this account.
221
	 */
222
	public static function updateGame(int $gameID) : void {
223
		if (self::$game_id == $gameID) {
224
			return;
225
		}
226
		self::$game_id = $gameID;
227
		self::$db->query('DELETE FROM active_session WHERE account_id = ' . self::$db->escapeNumber(self::$account_id) . ' AND game_id = ' . self::$game_id);
228
		self::$db->query('UPDATE active_session SET game_id=' . self::$db->escapeNumber(self::$game_id) . ' WHERE session_id=' . self::$db->escapeString(self::$session_id));
229
	}
230
231
	/**
232
	 * Returns true if the current SN is different than the previous SN.
233
	 */
234
	public static function hasChangedSN() : bool {
235
		return self::$SN != self::$lastSN;
236
	}
237
238
	private static function updateSN() : void {
239
		if (!USING_AJAX) {
240
			self::$db->query('UPDATE active_session SET last_sn=' . self::$db->escapeString(self::$SN) .
241
				' WHERE session_id=' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
242
		}
243
	}
244
245
	public static function destroy() : void {
246
		self::$db->query('DELETE FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id));
247
		self::$session_id = null;
248
		self::$account_id = 0;
249
		self::$game_id = 0;
250
	}
251
252
	/**
253
	 * Retrieve the session var for the page given by $sn.
254
	 * If $sn is not specified, use the current page (i.e. self::$SN).
255
	 */
256
	public static function retrieveVar(string $sn = null) : Page|false {
257
		if (is_null($sn)) {
258
			$sn = self::$SN;
259
		}
260
		if (empty(self::$var[$sn])) {
261
			return false;
262
		}
263
		self::$SN = $sn;
264
		SmrSession::updateSN();
265
		if (isset(self::$var[$sn]['body']) && isset(self::$var[$sn]['CommonID'])) {
266
//			if(preg_match('/processing/',self::$var[$sn]['body']))
267
			unset(self::$commonIDs[self::$var[$sn]['CommonID']]); //Do not store common id for current page
268
			unset(self::$var[$sn]['CommonID']);
269
		}
270
271
		self::$var[$sn]['RemainingPageLoads'] += 1; // Allow refreshing
272
		self::$var[$sn]['Expires'] = 0; // Allow refreshing forever
273
		return self::$var[$sn];
274
	}
275
276
	/**
277
	 * Gets a var from $var, $_REQUEST, or $default. Then stores it in the
278
	 * session so that it can still be retrieved when the page auto-refreshes.
279
	 * This is the recommended way to get $_REQUEST data for display pages.
280
	 * For processing pages, see the Request class.
281
	 */
282
	public static function getRequestVar(string $varName, string $default = null) : string {
283
		$result = Request::getVar($varName, $default);
284
		self::updateVar($varName, $result);
285
		return $result;
286
	}
287
288
	public static function getRequestVarInt(string $varName, int $default = null) : int {
289
		$result = Request::getVarInt($varName, $default);
290
		self::updateVar($varName, $result);
291
		return $result;
292
	}
293
294
	public static function getRequestVarIntArray(string $varName, array $default = null) : array {
295
		$result = Request::getVarIntArray($varName, $default);
296
		self::updateVar($varName, $result);
297
		return $result;
298
	}
299
300
	public static function resetLink(Page $container, string $sn) : string {
301
		//Do not allow sharing SN, useful for forwarding.
302
		global $lock;
303
		if (isset(self::$var[$sn]['CommonID'])) {
304
			unset(self::$commonIDs[self::$var[$sn]['CommonID']]); //Do not store common id for reset page, to allow refreshing to always give the same page in response
305
		}
306
		self::$SN = $sn;
307
		if (!isset($container['Expires'])) {
308
			$container['Expires'] = 0; // Lasts forever
309
		}
310
		if (!isset($container['RemainingPageLoads'])) {
311
			$container['RemainingPageLoads'] = 1; // Allow refreshing
312
		}
313
		if (!isset($container['PreviousRequestTime'])) {
314
			if (isset(self::$var[$sn]['PreviousRequestTime'])) {
315
				$container['PreviousRequestTime'] = self::$var[$sn]['PreviousRequestTime']; // Copy across the previous request time if not explicitly set.
316
			}
317
		}
318
319
		self::$var[$sn] = $container;
320
		if (!$lock && !USING_AJAX) {
321
			self::update();
322
		}
323
		return $sn;
324
	}
325
326
	public static function updateVar(string $key, mixed $value) : void {
327
		global $var;
328
		if ($value === null) {
329
			if (isset($var[$key])) {
330
				unset($var[$key]);
331
			}
332
			if (isset($var[self::$SN][$key])) {
333
				unset(self::$var[self::$SN][$key]);
334
			}
335
		} else {
336
			$var[$key] = $value;
337
			self::$var[self::$SN][$key] = $value;
338
		}
339
	}
340
341
	public static function clearLinks() : void {
342
		self::$var = array(self::$SN => self::$var[self::$SN]);
343
		self::$commonIDs = array();
344
	}
345
346
	public static function addLink(Page $container) : string {
347
		$sn = self::generateSN($container);
348
		self::$var[$sn] = $container;
349
		return $sn;
350
	}
351
352
	protected static function generateSN(Page $container) : string {
353
		if (isset(self::$commonIDs[$container['CommonID']])) {
354
			$sn = self::$commonIDs[$container['CommonID']];
355
			$container['PreviousRequestTime'] = isset(self::$var[$sn]) ? self::$var[$sn]['PreviousRequestTime'] : self::getMicroTime();
356
		} else {
357
			do {
358
				$sn = random_alphabetic_string(6);
359
			} while (isset(self::$var[$sn]));
360
			$container['PreviousRequestTime'] = self::getMicroTime();
361
		}
362
		self::$commonIDs[$container['CommonID']] = $sn;
363
		return $sn;
364
	}
365
366
	public static function addAjaxReturns(string $element, string $contents) : bool {
367
		self::$ajaxReturns[$element] = $contents;
368
		return isset(self::$previousAjaxReturns[$element]) && self::$previousAjaxReturns[$element] == $contents;
369
	}
370
371
	public static function saveAjaxReturns() : void {
372
		if (empty(self::$ajaxReturns)) {
373
			return;
374
		}
375
		$compressed = gzcompress(serialize(self::$ajaxReturns));
376
		self::$db->query('UPDATE active_session SET ajax_returns=' . self::$db->escapeBinary($compressed) .
377
				' WHERE session_id=' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
378
	}
379
}
380
381
SmrSession::init();
382