SwitchableSession::close()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
crap 6
1
<?php
2
/**
3
 * SwitchableSession.php
4
 *
5
 * @copyright      More in license.md
6
 * @license        http://www.ipublikuj.eu
7
 * @author         Adam Kadlec http://www.ipublikuj.eu
8
 * @package        iPublikuj:WebSocketSession!
9
 * @subpackage     Session
10
 * @since          1.0.0
11
 *
12
 * @date           21.02.17
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\WebSocketsSession\Session;
18
19
use Nette\Diagnostics\Debugger;
20
use Nette\Http;
21
22
use IPub;
23
use IPub\WebSocketsSession\Exceptions;
24
use IPub\WebSocketsSession\Serializers;
25
26
use IPub\WebSockets\Entities as WebSocketsEntities;
27
use IPub\WebSockets\Http as WebSocketsHttp;
28
29
/**
30
 * WebSocket session switcher
31
 *
32
 * @package        iPublikuj:WebSocketSession!
33
 * @subpackage     Session
34
 *
35
 * @author         Adam Kadlec <[email protected]>
36
 */
37 1
final class SwitchableSession extends Http\Session
38
{
39
	/**
40
	 * @var Http\Session
41
	 */
42
	private $systemSession;
43
44
	/**
45
	 * @var WebSocketsEntities\Clients\IClient|NULL
46
	 */
47
	private $client;
48
49
	/**
50
	 * Has been session started?
51
	 *
52
	 * @var bool
53
	 */
54
	private $started = FALSE;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
55
56
	/**
57
	 * @var array
58
	 */
59
	private $data = [];
60
61
	/**
62
	 * @var array
63
	 */
64
	private $sections = [];
65
66
	/**
67
	 * @var bool
68
	 */
69
	private $attached = FALSE;
70
71
	/**
72
	 * @var \SessionHandlerInterface|NULL
73
	 */
74
	private $handler;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
75
76
	/**
77
	 * @var NullHandler
78
	 */
79
	private $nullHandler;
80
81
	/**
82
	 * @var Serializers\ISessionSerializer
83
	 */
84
	private $serializer;
85
86
	/**
87
	 * @param Http\Session $session
88
	 * @param Serializers\ISessionSerializer $serializer
89
	 * @param \SessionHandlerInterface|NULL $handler
90
	 */
91
	public function __construct(
92
		Http\Session $session,
93
		Serializers\ISessionSerializer $serializer,
94
		\SessionHandlerInterface $handler = NULL
95
	) {
96 1
		$this->systemSession = $session;
97 1
		$this->handler = $handler;
98 1
		$this->nullHandler = new NullHandler;
99 1
		$this->serializer = $serializer;
100 1
	}
101
102
	/**
103
	 * @param WebSocketsEntities\Clients\IClient $client
104
	 * @param WebSocketsHttp\IRequest $httpRequest
105
	 *
106
	 * @return void
107
	 *
108
	 * @throws Exceptions\InvalidArgumentException
109
	 * @throws Exceptions\LogicException
110
	 */
111
	public function attach(WebSocketsEntities\Clients\IClient $client, WebSocketsHttp\IRequest $httpRequest)
112
	{
113
		if ($this->systemSession->isStarted()) {
114
			throw new Exceptions\LogicException('Session is already started, please close it first and then you can disabled it.');
115
		}
116
117
		$client->addParameter('sessionId', $httpRequest->getCookie($this->systemSession->getName()));
118
119
		$this->attached = TRUE;
120
		$this->started = FALSE;
121
122
		$this->client = $client;
123
	}
124
125
	/**
126
	 * @return void
127
	 */
128
	public function detach()
129
	{
130
		if ($this->attached) {
131
			$this->close();
132
133
			$this->attached = FALSE;
134
135
			$this->client = NULL;
136
		}
137
	}
138
139
	/**
140
	 * @return bool
141
	 */
142
	public function isAttached() : bool
143
	{
144
		return $this->attached;
145
	}
146
147
	/**
148
	 * {@inheritdoc}
149
	 */
150
	public function start()
151
	{
152
		if (!$this->attached) {
153
			$this->systemSession->start();
154
155
			return;
156
		}
157
158
		if ($this->started) {
159
			return;
160
		}
161
162
		$this->started = TRUE;
163
164
		if (($id = $this->client->getParameter('sessionId')) === NULL) {
165
			$handler = $this->nullHandler;
166
			$id = '';
167
168
		} else {
169
			$handler = $this->handler;
170
		}
171
172
		$handler->open(session_save_path(), $this->systemSession->getName());
173
174
		$rawData = $handler->read($id);
175
176
		$data = $this->serializer->unserialize($rawData);
177
178
		/* structure:
179
			__NF: Data, Meta, Time
180
				DATA: section->variable = data
181
				META: section->variable = Timestamp
182
		*/
183
		$nf = &$data['__NF'];
184
185
		if (!is_array($nf)) {
186
			$nf = [];
187
		}
188
189
		// regenerate empty session
190
		if (empty($nf['Time'])) {
191
			$nf['Time'] = time();
192
		}
193
194
		// process meta metadata
195
		if (isset($nf['META'])) {
196
			$now = time();
197
			// expire section variables
198
			foreach ($nf['META'] as $section => $metadata) {
199
				if (is_array($metadata)) {
200
					foreach ($metadata as $variable => $value) {
201
						if (!empty($value['T']) && $now > $value['T']) {
202
							if ($variable === '') { // expire whole section
203
								unset($nf['META'][$section], $nf['DATA'][$section]);
204
								continue 2;
205
							}
206
							unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]);
207
						}
208
					}
209
				}
210
			}
211
		}
212
213
		$this->data[$this->getConnectionId()] = $data;
214
	}
215
216
	/**
217
	 * {@inheritdoc}
218
	 */
219
	public function isStarted()
220
	{
221
		if (!$this->attached) {
222
			return $this->systemSession->isStarted();
223
		}
224
225
		return $this->started;
226
	}
227
228
	/**
229
	 * {@inheritdoc}
230
	 */
231
	public function close()
232
	{
233
		if (!$this->attached) {
234
			$this->systemSession->close();
235
236
		} else {
237
			$this->started = FALSE;
238
239
			$this->handler->close();
240
		}
241
	}
242
243
	/**
244
	 * {@inheritdoc}
245
	 */
246
	public function destroy()
247
	{
248
		if (!$this->attached) {
249
			$this->systemSession->destroy();
250
251
		} else {
252
			if (!$this->started) {
253
				throw new Exceptions\InvalidStateException('Session is not started.');
254
			}
255
256
			$this->started = FALSE;
257
258
			$this->data = [];
259
		}
260
	}
261
262
	/**
263
	 * {@inheritdoc}
264
	 */
265
	public function exists()
266
	{
267
		if (!$this->attached) {
268
			return $this->systemSession->exists();
269
		}
270
271
		return $this->started;
272
	}
273
274
	/**
275
	 * {@inheritdoc}
276
	 */
277
	public function regenerateId()
278
	{
279
		if (!$this->attached) {
280
			$this->systemSession->regenerateId();
281
		}
282
283
		// For WS session nothing to do
284
	}
285
286
	/**
287
	 * {@inheritdoc}
288
	 */
289
	public function getId()
290
	{
291
		if (!$this->attached) {
292
			return $this->systemSession->getId();
293
		}
294
295
		return $this->client->getParameter('sessionId');
296
	}
297
298
	/**
299
	 * {@inheritdoc}
300
	 */
301
	public function getSection($section, $class = 'Nette\Http\SessionSection')
302
	{
303
		if (!$this->attached) {
304
			return $this->systemSession->getSection($section, $class);
305
		}
306
307
		return new SessionSection($this, $section);
308
	}
309
310
	/**
311
	 * {@inheritdoc}
312
	 */
313
	public function hasSection($section)
314
	{
315
		if (!$this->attached) {
316
			return $this->systemSession->hasSection($section);
317
		}
318
319
		return isset($this->sections[$this->getConnectionId()][$section]);
320
	}
321
322
	/**
323
	 * {@inheritdoc}
324
	 */
325
	public function getIterator() : \ArrayIterator
326
	{
327
		if (!$this->attached) {
328
			return $this->systemSession->getIterator();
329
		}
330
331
		return new \ArrayIterator(array_keys($this->sections[$this->getConnectionId()]));
332
	}
333
334
	/**
335
	 * {@inheritdoc}
336
	 */
337
	public function clean()
338
	{
339
		if (!$this->attached) {
340
			$this->systemSession->clean();
341
342
		} else {
343
			if (!$this->started || empty($this->data[$this->getConnectionId()])) {
344
				return;
345
			}
346
347
			$nf = &$this->data[$this->getConnectionId()]['__NF'];
348
			if (isset($nf['META']) && is_array($nf['META'])) {
349
				foreach ($nf['META'] as $name => $foo) {
350
					if (empty($nf['META'][$name])) {
351
						unset($nf['META'][$name]);
352
					}
353
				}
354
			}
355
356
			if (empty($nf['META'])) {
357
				unset($nf['META']);
358
			}
359
360
			if (empty($nf['DATA'])) {
361
				unset($nf['DATA']);
362
			}
363
		}
364
	}
365
366
	/**
367
	 * {@inheritdoc}
368
	 */
369
	public function setName($name)
370
	{
371
		return $this->systemSession->setName($name);
372
	}
373
374
	/**
375
	 * {@inheritdoc}
376
	 */
377
	public function getName()
378
	{
379
		return $this->systemSession->getName();
380
	}
381
382
	/**
383
	 * {@inheritdoc}
384
	 */
385
	public function setOptions(array $options)
386
	{
387
		return $this->systemSession->setOptions($options);
388
	}
389
390
	/**
391
	 * {@inheritdoc}
392
	 */
393
	public function getOptions()
394
	{
395
		return $this->systemSession->getOptions();
396
	}
397
398
	/**
399
	 * {@inheritdoc}
400
	 */
401
	public function setExpiration($time)
402
	{
403
		return $this->systemSession->setExpiration($time);
404
	}
405
406
	/**
407
	 * {@inheritdoc}
408
	 */
409
	public function setCookieParameters($path, $domain = NULL, $secure = NULL)
410
	{
411
		return $this->systemSession->setCookieParameters($path, $domain, $secure);
412
	}
413
414
	/**
415
	 * {@inheritdoc}
416
	 */
417
	public function getCookieParameters()
418
	{
419
		return $this->systemSession->getCookieParameters();
420
	}
421
422
	/**
423
	 * {@inheritdoc}
424
	 */
425
	public function setSavePath($path)
426
	{
427
		return $this->systemSession->setSavePath($path);
428
	}
429
430
	/**
431
	 * {@inheritdoc}
432
	 */
433
	public function setHandler(\SessionHandlerInterface $handler)
434
	{
435
		return $this->systemSession->setHandler($handler);
436
	}
437
438
	/**
439
	 * @param string $section
440
	 *
441
	 * @return array
442
	 */
443
	public function getData(string $section) : array
444
	{
445
		if (!$this->attached) {
446
			return $_SESSION['DATA'][$section];
447
		}
448
449
		return isset($this->data[$this->getConnectionId()]['__NF']['DATA'][$section]) ? $this->data[$this->getConnectionId()]['__NF']['DATA'][$section] : [];
450
	}
451
452
	/**
453
	 * @return int
454
	 *
455
	 * @throws Exceptions\InvalidStateException
456
	 */
457
	private function getConnectionId() : int
458
	{
459
		return $this->client->getId();
460
	}
461
}
462