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

Session::getRequestVarIntArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 4
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
		$sn = Request::get('sn', '');
87
		if (!USING_AJAX && !empty($sn) && !empty($this->var[$sn])) {
88
			$var = $this->var[$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
	 * Returns the Game ID associated with the session.
156
	 */
157
	public function getGameID() : int {
158
		return $this->gameID;
159
	}
160
161
	/**
162
	 * Returns true if the session is inside a game, false otherwise.
163
	 */
164
	public function hasGame() : bool {
165
		return $this->gameID != 0;
166
	}
167
168
	public function hasAccount() : bool {
169
		return $this->accountID > 0;
170
	}
171
172
	public function getAccountID() : int {
173
		return $this->accountID;
174
	}
175
176
	public function getAccount() : AbstractSmrAccount {
177
		return SmrAccount::getAccount($this->accountID);
178
	}
179
180
	public function getPlayer(bool $forceUpdate = false) : AbstractSmrPlayer {
181
		return SmrPlayer::getPlayer($this->accountID, $this->gameID, $forceUpdate);
182
	}
183
184
	/**
185
	 * Sets the `accountID` attribute of this session.
186
	 */
187
	public function setAccount(AbstractSmrAccount $account) : void {
188
		$this->accountID = $account->getAccountID();
189
	}
190
191
	/**
192
	 * Updates the `gameID` attribute of the session and deletes any other
193
	 * active sessions in this game for this account.
194
	 */
195
	public function updateGame(int $gameID) : void {
196
		if ($this->gameID == $gameID) {
197
			return;
198
		}
199
		$this->gameID = $gameID;
200
		$this->db->query('DELETE FROM active_session WHERE account_id = ' . $this->db->escapeNumber($this->accountID) . ' AND game_id = ' . $this->gameID);
201
		$this->db->query('UPDATE active_session SET game_id=' . $this->db->escapeNumber($this->gameID) . ' WHERE session_id=' . $this->db->escapeString($this->sessionID));
202
	}
203
204
	/**
205
	 * Returns true if the current SN is different than the previous SN.
206
	 */
207
	public function hasChangedSN() : bool {
208
		return $this->SN != $this->lastSN;
209
	}
210
211
	private function updateSN() : void {
212
		if (!USING_AJAX) {
213
			$this->db->query('UPDATE active_session SET last_sn=' . $this->db->escapeString($this->SN) .
214
				' WHERE session_id=' . $this->db->escapeString($this->sessionID) . ' LIMIT 1');
215
		}
216
	}
217
218
	public function destroy() : void {
219
		$this->db->query('DELETE FROM active_session WHERE session_id = ' . $this->db->escapeString($this->sessionID));
220
		unset($this->sessionID);
221
		unset($this->accountID);
222
		unset($this->gameID);
223
	}
224
225
	public function getLastAccessed() : int {
226
		return $this->lastAccessed;
227
	}
228
229
	/**
230
	 * Check if the session has a var associated with the given $sn.
231
	 * If $sn is not specified, use the current SN (i.e. $this->SN).
232
	 */
233
	public function findCurrentVar(string $sn = null) : bool {
234
		if (is_null($sn)) {
235
			$sn = $this->SN;
236
		}
237
		if (empty($this->var[$sn])) {
238
			return false;
239
		}
240
		$this->SN = $sn;
241
		$this->updateSN();
242
		if (isset($this->var[$sn]['body']) && isset($this->var[$sn]['CommonID'])) {
243
//			if(preg_match('/processing/',$this->var[$sn]['body']))
244
			unset($this->commonIDs[$this->var[$sn]['CommonID']]); //Do not store common id for current page
245
			unset($this->var[$sn]['CommonID']);
246
		}
247
248
		$this->var[$sn]['RemainingPageLoads'] += 1; // Allow refreshing
249
250
		return true;
251
	}
252
253
	/**
254
	 * Returns the session var associated with the current SN.
255
	 * Must be called after Session::findCurrentVar sets the current SN.
256
	 */
257
	public function getCurrentVar() : Page {
258
		return $this->var[$this->SN];
259
	}
260
261
	/**
262
	 * Gets a var from $var, $_REQUEST, or $default. Then stores it in the
263
	 * session so that it can still be retrieved when the page auto-refreshes.
264
	 * This is the recommended way to get $_REQUEST data for display pages.
265
	 * For processing pages, see the Request class.
266
	 */
267
	public function getRequestVar(string $varName, string $default = null) : string {
268
		$result = Request::getVar($varName, $default);
269
		$this->updateVar($varName, $result);
270
		return $result;
271
	}
272
273
	public function getRequestVarInt(string $varName, int $default = null) : int {
274
		$result = Request::getVarInt($varName, $default);
275
		$this->updateVar($varName, $result);
276
		return $result;
277
	}
278
279
	public function getRequestVarIntArray(string $varName, array $default = null) : array {
280
		$result = Request::getVarIntArray($varName, $default);
281
		$this->updateVar($varName, $result);
282
		return $result;
283
	}
284
285
	/**
286
	 * Replace the global $var with the given $container.
287
	 */
288
	public function setCurrentVar(Page $container, bool $allowUpdate = true) : void {
289
		$var = $this->getCurrentVar();
290
291
		//Do not allow sharing SN, useful for forwarding.
292
		global $lock;
293
		if (isset($var['CommonID'])) {
294
			unset($this->commonIDs[$var['CommonID']]); //Do not store common id for reset page, to allow refreshing to always give the same page in response
295
		}
296
		if (!isset($container['RemainingPageLoads'])) {
297
			$container['RemainingPageLoads'] = 1; // Allow refreshing
298
		}
299
		if (!isset($container['PreviousRequestTime'])) {
300
			if (isset($var['PreviousRequestTime'])) {
301
				$container['PreviousRequestTime'] = $var['PreviousRequestTime']; // Copy across the previous request time if not explicitly set.
302
			}
303
		}
304
305
		$var->exchangeArray($container);
306
		if ($allowUpdate && !$lock && !USING_AJAX) {
307
			$this->update();
308
		}
309
	}
310
311
	public function updateVar(string $key, mixed $value) : void {
312
		$var = $this->getCurrentVar();
313
		if ($value === null) {
314
			if (isset($var[$key])) {
315
				unset($var[$key]);
316
			}
317
		} else {
318
			$var[$key] = $value;
319
		}
320
	}
321
322
	public function clearLinks() : void {
323
		$this->var = array($this->SN => $this->var[$this->SN]);
324
		$this->commonIDs = array();
325
	}
326
327
	public function addLink(Page $container) : string {
328
		$sn = $this->generateSN($container);
329
		$this->var[$sn] = $container;
330
		return $sn;
331
	}
332
333
	protected function generateSN(Page $container) : string {
334
		if (isset($this->commonIDs[$container['CommonID']])) {
335
			$sn = $this->commonIDs[$container['CommonID']];
336
			$container['PreviousRequestTime'] = isset($this->var[$sn]) ? $this->var[$sn]['PreviousRequestTime'] : Epoch::microtime();
337
		} else {
338
			do {
339
				$sn = random_alphabetic_string(6);
340
			} while (isset($this->var[$sn]));
341
			$container['PreviousRequestTime'] = Epoch::microtime();
342
		}
343
		$this->commonIDs[$container['CommonID']] = $sn;
344
		return $sn;
345
	}
346
347
	public function addAjaxReturns(string $element, string $contents) : bool {
348
		$this->ajaxReturns[$element] = $contents;
349
		return isset($this->previousAjaxReturns[$element]) && $this->previousAjaxReturns[$element] == $contents;
350
	}
351
352
	public function saveAjaxReturns() : void {
353
		if (empty($this->ajaxReturns)) {
354
			return;
355
		}
356
		$this->db->query('UPDATE active_session SET ajax_returns=' . $this->db->escapeObject($this->ajaxReturns, true) .
357
				' WHERE session_id=' . $this->db->escapeString($this->sessionID) . ' LIMIT 1');
358
	}
359
}
360