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

Failed Conditions
Pull Request — master (#1040)
by Dan
12:15
created

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