Completed
Push — master ( 480535...7e0bd3 )
by Anton
12s
created

Session   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 384
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 39.81%

Importance

Changes 0
Metric Value
dl 0
loc 384
ccs 43
cts 108
cp 0.3981
rs 8.3673
c 0
b 0
f 0
wmc 45
lcom 1
cbo 3

21 Methods

Rating   Name   Duplication   Size   Complexity  
A setName() 0 18 3
A getName() 0 11 2
A set() 0 9 2
A setSavePath() 0 10 3
A setNamespace() 0 5 1
A getNamespace() 0 4 1
A setId() 0 10 2
A getId() 0 4 1
A regenerateId() 0 7 2
A cookieExists() 0 4 1
A sessionExists() 0 4 1
A start() 0 10 2
A destroy() 0 14 3
A getAdapter() 0 4 1
B initAdapter() 0 20 8
A setSessionCookieLifetime() 0 10 2
A expireSessionCookie() 0 15 2
A get() 0 7 2
A contains() 0 9 3
A delete() 0 6 2
A setAdapter() 0 5 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
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link      https://github.com/bluzphp/framework
7
 */
8
9
declare(strict_types=1);
10
11
namespace Bluz\Session;
12
13
use Bluz\Common\Exception\ComponentException;
14
use Bluz\Common\Options;
15
16
/**
17
 * Session
18
 *
19
 * @package  Bluz\Session
20
 * @author   Anton Shevchuk
21
 * @link     https://github.com/bluzphp/framework/wiki/Session
22
 */
23
class Session
24
{
25
    use Options;
26
27
    /**
28
     * @var string value returned by session_name()
29
     */
30
    protected $name;
31
32
    /**
33
     * @var string namespace
34
     */
35
    protected $namespace = 'bluz';
36
37
    /**
38
     * @var \SessionHandlerInterface Session save handler
39
     */
40
    protected $adapter;
41
42
    /**
43
     * Attempt to set the session name
44
     *
45
     * If the session has already been started, or if the name provided fails
46
     * validation, an exception will be raised.
47
     *
48
     * @param  string $name
49
     *
50
     * @throws SessionException
51
     * @return Session
52
     */
53
    public function setName($name)
54
    {
55
        if ($this->sessionExists()) {
56
            throw new SessionException(
57
                'Cannot set session name after a session has already started'
58
            );
59
        }
60
61
        if (!preg_match('/^[a-zA-Z0-9]+$/', $name)) {
62
            throw new SessionException(
63
                'Name provided contains invalid characters; must be alphanumeric only'
64
            );
65
        }
66
67
        $this->name = $name;
68
        session_name($name);
69
        return $this;
70
    }
71
72
    /**
73
     * Get session name
74
     *
75
     * Proxies to {@link session_name()}.
76
     *
77
     * @return string
78
     */
79
    public function getName()
80
    {
81
        if (null === $this->name) {
82
            // If we're grabbing via session_name(), we don't need our
83
            // validation routine; additionally, calling setName() after
84
            // session_start() can lead to issues, and often we just need the name
85
            // in order to do things such as setting cookies.
86
            $this->name = session_name();
87
        }
88
        return $this->name;
89
    }
90
91
    /**
92
     * Set Namespace
93
     *
94
     * @param  string $namespace
95
     *
96
     * @return Session
97
     */
98
    public function setNamespace($namespace)
99
    {
100
        $this->namespace = $namespace;
101
        return $this;
102
    }
103
104
    /**
105
     * Get Namespace
106
     *
107
     * @return string
108
     */
109 6
    public function getNamespace()
110
    {
111 6
        return $this->namespace;
112
    }
113
114
    /**
115
     * Set session ID
116
     *
117
     * Can safely be called in the middle of a session.
118
     *
119
     * @param  string $id
120
     *
121
     * @return Session
122
     * @throws SessionException
123
     */
124
    public function setId($id)
125
    {
126
        if ($this->sessionExists()) {
127
            throw new SessionException(
128
                'Session has already been started, to change the session ID call regenerateId()'
129
            );
130
        }
131
        session_id($id);
132
        return $this;
133
    }
134
135
    /**
136
     * Get session ID
137
     *
138
     * Proxies to {@link session_id()}
139
     *
140
     * @return string
141
     */
142
    public function getId()
143
    {
144
        return session_id();
145
    }
146
147
    /**
148
     * Regenerate id
149
     *
150
     * Regenerate the session ID, using session save handler's
151
     * native ID generation Can safely be called in the middle of a session.
152
     *
153
     * @param  bool $deleteOldSession
154
     *
155
     * @return bool
156
     */
157
    public function regenerateId($deleteOldSession = true)
158
    {
159
        if ($this->sessionExists()) {
160
            return session_regenerate_id((bool)$deleteOldSession);
161
        }
162
        return false;
163
    }
164
165
    /**
166
     * Returns true if session ID is set
167
     *
168
     * @return bool
169
     */
170 713
    public function cookieExists()
171
    {
172 713
        return isset($_COOKIE[session_name()]);
173
    }
174
175
    /**
176
     * Does a session started and is it currently active?
177
     *
178
     * @return bool
179
     */
180 713
    public function sessionExists()
181
    {
182 713
        return session_status() === PHP_SESSION_ACTIVE;
183
    }
184
185
    /**
186
     * Start session
187
     *
188
     * if No session currently exists, attempt to start it. Calls
189
     * {@link isValid()} once session_start() is called, and raises an
190
     * exception if validation fails.
191
     *
192
     * @return void
193
     * @throws \Bluz\Common\Exception\ComponentException
194
     * @throws SessionException
195
     */
196 713
    public function start()
197
    {
198 713
        if ($this->sessionExists()) {
199 713
            return;
200
        }
201
202 1
        $this->initAdapter();
203
204 1
        session_start();
205 1
    }
206
207
    /**
208
     * Destroy/end a session
209
     *
210
     * @return void
211
     */
212
    public function destroy()
213
    {
214
        if (!$this->cookieExists() || !$this->sessionExists()) {
215
            return;
216
        }
217
218
        session_destroy();
219
220
        // send expire cookies
221
        $this->expireSessionCookie();
222
223
        // clear session data
224
        unset($_SESSION[$this->getNamespace()]);
225
    }
226
227
    /**
228
     * Set session save handler object
229
     *
230
     * @param  \SessionHandlerInterface $saveHandler
231
     *
232
     * @return Session
233
     */
234 1
    public function setAdapter($saveHandler)
235
    {
236 1
        $this->adapter = $saveHandler;
237 1
        return $this;
238
    }
239
240
    /**
241
     * Get SaveHandler Object
242
     *
243
     * @return \SessionHandlerInterface
244
     */
245
    public function getAdapter()
246
    {
247
        return $this->adapter;
248
    }
249
250
    /**
251
     * Register Save Handler with ext/session
252
     *
253
     * Since ext/session is coupled to this particular session manager
254
     * register the save handler with ext/session.
255
     *
256
     * @return bool
257
     * @throws ComponentException
258
     */
259 1
    protected function initAdapter()
260
    {
261 1
        if (is_null($this->adapter) || $this->adapter === 'files') {
262
            // try to apply settings
263 1
            if ($settings = $this->getOption('settings', 'files')) {
264 1
                $this->setSavePath($settings['save_path']);
265
            }
266 1
            return true;
267
        } elseif (is_string($this->adapter)) {
268
            $adapterClass = '\\Bluz\\Session\\Adapter\\' . ucfirst($this->adapter);
269
            if (!class_exists($adapterClass) || !is_subclass_of($adapterClass, '\SessionHandlerInterface')) {
270
                throw new ComponentException("Class for session adapter `{$this->adapter}` not found");
271
            }
272
            $settings = $this->getOption('settings', $this->adapter) ?: [];
273
274
            $this->adapter = new $adapterClass($settings);
275
        }
276
277
        return session_set_save_handler($this->adapter);
278
    }
279
280
    /**
281
     * Set the session cookie lifetime
282
     *
283
     * If a session already exists, destroys it (without sending an expiration
284
     * cookie), regenerates the session ID, and restarts the session.
285
     *
286
     * @param  integer $ttl TTL in seconds
287
     *
288
     * @return void
289
     */
290
    public function setSessionCookieLifetime($ttl)
291
    {
292
        // Set new cookie TTL
293
        session_set_cookie_params($ttl);
294
295
        if ($this->sessionExists()) {
296
            // There is a running session so we'll regenerate id to send a new cookie
297
            $this->regenerateId();
298
        }
299
    }
300
301
    /**
302
     * Expire the session cookie
303
     *
304
     * Sends a session cookie with no value, and with an expiry in the past.
305
     *
306
     * @return void
307
     */
308
    public function expireSessionCookie()
309
    {
310
        if (ini_get('session.use_cookies')) {
311
            $params = session_get_cookie_params();
312
            setcookie(
313
                $this->getName(),
314
                '',
315
                $_SERVER['REQUEST_TIME'] - 42000,
316
                $params['path'],
317
                $params['domain'],
318
                $params['secure'],
319
                $params['httponly']
320
            );
321
        }
322
    }
323
324
    /**
325
     * Set session save path
326
     *
327
     * @param  string $savePath
328
     *
329
     * @return Session
330
     * @throws ComponentException
331
     */
332 1
    protected function setSavePath($savePath)
333
    {
334 1
        if (!is_dir($savePath)
335 1
            || !is_writable($savePath)
336
        ) {
337
            throw new ComponentException('Session path is not writable');
338
        }
339 1
        session_save_path($savePath);
340 1
        return $this;
341
    }
342
343
    /**
344
     * Set key/value pair
345
     *
346
     * @param  string $key
347
     * @param  mixed  $value
348
     *
349
     * @return void
350
     */
351 7
    public function set($key, $value)
352
    {
353 7
        $this->start();
354
        // check storage
355 7
        if (!isset($_SESSION[$this->getNamespace()])) {
356 7
            $_SESSION[$this->getNamespace()] = [];
357
        }
358 7
        $_SESSION[$this->namespace][$key] = $value;
359 7
    }
360
361
    /**
362
     * Get value by key
363
     *
364
     * @param  string $key
365
     *
366
     * @return mixed
367
     */
368 714
    public function get($key)
369
    {
370 714
        if ($this->contains($key)) {
371 5
            return $_SESSION[$this->namespace][$key];
372
        }
373 714
        return null;
374
    }
375
376
    /**
377
     * Isset
378
     *
379
     * @param  string $key
380
     *
381
     * @return bool
382
     */
383 714
    public function contains($key)
384
    {
385 714
        if ($this->cookieExists()) {
386 714
            $this->start();
387
        } elseif (!$this->sessionExists()) {
388
            return false;
389
        }
390 714
        return isset($_SESSION[$this->namespace][$key]);
391
    }
392
393
    /**
394
     * Unset
395
     *
396
     * @param  string $key
397
     *
398
     * @return void
399
     */
400 713
    public function delete($key)
401
    {
402 713
        if ($this->contains($key)) {
403 3
            unset($_SESSION[$this->namespace][$key]);
404
        }
405 713
    }
406
}
407