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 (#1034)
by Dan
04:53
created

SmrSession::getLastAccessed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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