Completed
Branch 2.x (8ca90a)
by Julián
08:08
created

Session   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 401
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 4
dl 0
loc 401
rs 8.3673
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 2
A getManager() 0 4 1
A start() 0 18 3
A regenerateId() 0 12 2
A reset() 0 12 2
A abort() 0 12 2
A close() 0 12 2
A destroy() 0 14 2
A isActive() 0 4 1
A isDestroyed() 0 4 1
A getId() 0 4 1
A setId() 0 8 3
A has() 0 4 1
A get() 0 4 2
A set() 0 8 1
A verifyScalarValue() 0 12 4
A remove() 0 8 2
A clear() 0 11 2
A manageTimeout() 0 15 3
A getCookieString() 0 16 2
B getCookieParameters() 0 30 5
A getConfiguration() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Session often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Session, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * sessionware (https://github.com/juliangut/sessionware).
5
 * PSR7 compatible session management.
6
 *
7
 * @license BSD-3-Clause
8
 * @link https://github.com/juliangut/sessionware
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Jgut\Sessionware;
15
16
use Jgut\Sessionware\Manager\Manager;
17
use League\Event\EmitterAwareInterface;
18
use League\Event\EmitterTrait;
19
use League\Event\Event;
20
21
/**
22
 * Session helper.
23
 */
24
class Session implements EmitterAwareInterface
25
{
26
    use EmitterTrait;
27
28
    /**
29
     * Session manager.
30
     *
31
     * @var Manager
32
     */
33
    protected $sessionManager;
34
35
    /**
36
     * Session initial data.
37
     *
38
     * @var array
39
     */
40
    protected $originalData;
41
42
    /**
43
     * Session data.
44
     *
45
     * @var array
46
     */
47
    protected $data;
48
49
    /**
50
     * Session constructor.
51
     *
52
     * @param Manager $sessionManager
53
     * @param array   $initialData
54
     *
55
     * @throws \InvalidArgumentException
56
     */
57
    public function __construct(Manager $sessionManager, array $initialData = [])
58
    {
59
        $this->sessionManager = $sessionManager;
60
        $this->data = [];
61
62
        foreach ($initialData as $key => $value) {
63
            $this->set($key, $value);
64
        }
65
66
        $this->originalData = $this->data;
67
68
        register_shutdown_function([$this, 'close']);
69
    }
70
71
    /**
72
     * Get session manager.
73
     *
74
     * @return Manager
75
     */
76
    public function getManager()
77
    {
78
        return $this->sessionManager;
79
    }
80
81
    /**
82
     * Start session.
83
     *
84
     * @throws \RuntimeException
85
     */
86
    public function start()
87
    {
88
        if ($this->isActive()) {
89
            return;
90
        }
91
92
        $this->emit(Event::named('pre.session_start'), $this);
93
94
        $this->originalData = $this->data = array_merge($this->data, $this->sessionManager->sessionStart());
95
96
        if ($this->sessionManager->shouldRegenerateId()) {
97
            $this->regenerateId();
98
        }
99
100
        $this->emit(Event::named('post.session_start'), $this);
101
102
        $this->manageTimeout();
103
    }
104
105
    /**
106
     * Regenerate session identifier keeping parameters.
107
     *
108
     * @throws \RuntimeException
109
     */
110
    public function regenerateId()
111
    {
112
        if (!$this->isActive()) {
113
            throw new \RuntimeException('Cannot regenerate a not started session');
114
        }
115
116
        $this->emit(Event::named('pre.session_regenerate_id'), $this);
117
118
        $this->sessionManager->sessionRegenerateId();
119
120
        $this->emit(Event::named('pre.session_regenerate_id'), $this);
121
    }
122
123
    /**
124
     * Revert session to its original data.
125
     */
126
    public function reset()
127
    {
128
        if (!$this->isActive()) {
129
            return;
130
        }
131
132
        $this->emit(Event::named('pre.session_reset'), $this);
133
134
        $this->data = $this->originalData;
135
136
        $this->emit(Event::named('pre.session_reset'), $this);
137
    }
138
139
    /**
140
     * Close session keeping original session data.
141
     */
142
    public function abort()
143
    {
144
        if (!$this->isActive()) {
145
            return;
146
        }
147
148
        $this->emit(Event::named('pre.session_abort'), $this);
149
150
        $this->sessionManager->sessionEnd($this->originalData);
151
152
        $this->emit(Event::named('pre.session_abort'), $this);
153
    }
154
155
    /**
156
     * Close session.
157
     */
158
    public function close()
159
    {
160
        if (!$this->isActive()) {
161
            return;
162
        }
163
164
        $this->emit(Event::named('pre.session_close'), $this);
165
166
        $this->sessionManager->sessionEnd($this->data);
167
168
        $this->emit(Event::named('pre.session_close'), $this);
169
    }
170
171
    /**
172
     * Destroy session.
173
     *
174
     * @throws \RuntimeException
175
     */
176
    public function destroy()
177
    {
178
        if (!$this->isActive()) {
179
            throw new \RuntimeException('Cannot destroy a not started session');
180
        }
181
182
        $this->emit(Event::named('pre.session_destroy'), $this);
183
184
        $this->sessionManager->sessionDestroy();
185
186
        $this->emit(Event::named('pre.session_destroy'), $this);
187
188
        $this->originalData = $this->data = [];
189
    }
190
191
    /**
192
     * Has session been started.
193
     *
194
     * @return bool
195
     */
196
    public function isActive() : bool
197
    {
198
        return $this->sessionManager->isSessionStarted();
199
    }
200
201
    /**
202
     * Has session been destroyed.
203
     *
204
     * @return bool
205
     */
206
    public function isDestroyed() : bool
207
    {
208
        return $this->sessionManager->isSessionDestroyed();
209
    }
210
211
    /**
212
     * Get session identifier.
213
     *
214
     * @return string
215
     */
216
    public function getId() : string
217
    {
218
        return $this->sessionManager->getSessionId();
219
    }
220
221
    /**
222
     * Set session identifier.
223
     *
224
     * @param string $sessionId
225
     *
226
     * @throws \RuntimeException
227
     */
228
    public function setId(string $sessionId)
229
    {
230
        if ($this->isActive() || $this->isDestroyed()) {
231
            throw new \RuntimeException('Cannot set session id on started or destroyed sessions');
232
        }
233
234
        $this->sessionManager->setSessionId($sessionId);
235
    }
236
237
    /**
238
     * Session parameter existence.
239
     *
240
     * @param string $key
241
     *
242
     * @return bool
243
     */
244
    public function has(string $key) : bool
245
    {
246
        return array_key_exists($key, $this->data);
247
    }
248
249
    /**
250
     * Retrieve session parameter.
251
     *
252
     * @param string     $key
253
     * @param mixed|null $default
254
     *
255
     * @return mixed
256
     */
257
    public function get(string $key, $default = null)
258
    {
259
        return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
260
    }
261
262
    /**
263
     * Set session parameter.
264
     *
265
     * @param string $key
266
     * @param mixed  $value
267
     *
268
     * @throws \InvalidArgumentException
269
     *
270
     * @return self
271
     */
272
    public function set(string $key, $value) : self
273
    {
274
        $this->verifyScalarValue($value);
275
276
        $this->data[$key] = $value;
277
278
        return $this;
279
    }
280
281
    /**
282
     * Verify only scalar values allowed.
283
     *
284
     * @param string|int|float|bool|array $value
285
     *
286
     * @throws \InvalidArgumentException
287
     */
288
    final protected function verifyScalarValue($value)
289
    {
290
        if (is_array($value)) {
291
            foreach ($value as $val) {
292
                $this->verifyScalarValue($val);
293
            }
294
        }
295
296
        if (!is_scalar($value)) {
297
            throw new \InvalidArgumentException(sprintf('Session values must be scalars, %s given', gettype($value)));
298
        }
299
    }
300
301
    /**
302
     * Remove session parameter.
303
     *
304
     * @param string $key
305
     *
306
     * @return self
307
     */
308
    public function remove(string $key) : self
309
    {
310
        if (array_key_exists($key, $this->data)) {
311
            unset($this->data[$key]);
312
        }
313
314
        return $this;
315
    }
316
317
    /**
318
     * Remove all session parameters.
319
     *
320
     * @return self
321
     */
322
    public function clear() : self
323
    {
324
        $timeoutKey = $this->getConfiguration()->getTimeoutKey();
325
        $sessionTimeout = array_key_exists($timeoutKey, $this->data)
326
            ? $this->data[$timeoutKey]
327
            : time() + $this->getConfiguration()->getLifetime();
328
329
        $this->data = [$timeoutKey => $sessionTimeout];
330
331
        return $this;
332
    }
333
334
    /**
335
     * Manage session timeout.
336
     *
337
     * @throws \RuntimeException
338
     */
339
    protected function manageTimeout()
340
    {
341
        $configuration = $this->getConfiguration();
342
        $timeoutKey = $configuration->getTimeoutKey();
343
344
        if (array_key_exists($timeoutKey, $this->data) && $this->data[$timeoutKey] < time()) {
345
            $this->emit(Event::named('pre.session_timeout'), $this);
346
347
            $this->sessionManager->sessionRegenerateId();
348
349
            $this->emit(Event::named('post.session_timeout'), $this);
350
        }
351
352
        $this->data[$timeoutKey] = time() + $configuration->getLifetime();
353
    }
354
355
    /**
356
     * Get cookie header content.
357
     *
358
     * @return string
359
     */
360
    public function getCookieString() : string
361
    {
362
        $configuration = $this->getConfiguration();
363
364
        $timeoutKey = $configuration->getTimeoutKey();
365
        $expireTime = array_key_exists($timeoutKey, $this->data)
366
            ? $this->data[$timeoutKey]
367
            : time() + $configuration->getLifetime();
368
369
        return sprintf(
370
            '%s=%s; %s',
371
            urlencode($configuration->getName()),
372
            urlencode($this->getId()),
373
            $this->getCookieParameters($expireTime)
374
        );
375
    }
376
377
    /**
378
     * Get session cookie parameters.
379
     *
380
     * @param int $expireTime
381
     *
382
     * @return string
383
     */
384
    protected function getCookieParameters(int $expireTime) : string
385
    {
386
        $configuration = $this->getConfiguration();
387
388
        $cookieParams = [
389
            sprintf(
390
                'expires=%s; max-age=%s',
391
                gmdate('D, d M Y H:i:s T', $expireTime),
392
                $configuration->getLifetime()
393
            ),
394
        ];
395
396
        if (!empty($configuration->getCookiePath())) {
397
            $cookieParams[] = 'path=' . $configuration->getCookiePath();
398
        }
399
400
        if (!empty($configuration->getCookieDomain())) {
401
            $cookieParams[] = 'domain=' . $configuration->getCookieDomain();
402
        }
403
404
        if ($configuration->isCookieSecure()) {
405
            $cookieParams[] = 'secure';
406
        }
407
408
        if ($configuration->isCookieHttpOnly()) {
409
            $cookieParams[] = 'httponly';
410
        }
411
412
        return implode('; ', $cookieParams);
413
    }
414
415
    /**
416
     * Get session configuration.
417
     *
418
     * @return Configuration
419
     */
420
    protected function getConfiguration() : Configuration
421
    {
422
        return $this->sessionManager->getConfiguration();
423
    }
424
}
425