Passed
Push — master ( 5c25b8...502eee )
by Gabor
05:16
created

ServiceAdapter   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 93.1%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 1
dl 0
loc 293
ccs 54
cts 58
cp 0.931
rs 8.8
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 2
A __destruct() 0 10 2
A read() 0 6 2
A write() 0 8 2
A sessionStarted() 0 9 2
B start() 0 24 3
A regenerateId() 0 19 4
A getSessionId() 0 8 2
A set() 0 18 4
A has() 0 8 2
A get() 0 14 4
A delete() 0 16 4
A unlock() 0 8 2
A toArray() 0 4 1
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Session\ServiceAdapter\Base;
15
16
use RuntimeException;
17
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
18
use WebHemi\Session\ServiceInterface;
19
20
/**
21
 * Class ServiceAdapter.
22
 */
23
class ServiceAdapter implements ServiceInterface
24
{
25
    /** @var string */
26
    private $namespace;
27
    /** @var string */
28
    private $cookiePrefix;
29
    /** @var string */
30
    private $sessionNameSalt;
31
    /** @var array */
32
    private $readOnly = [];
33
    /** @var array */
34
    private $data = [];
35
36
    /**
37
     * ServiceAdapter constructor.
38
     *
39
     * @param ConfigurationInterface $configuration
40
     */
41 8
    public function __construct(ConfigurationInterface $configuration)
42
    {
43 8
        $configuration = $configuration->getData('session');
44
45 8
        $this->namespace = $configuration['namespace'];
46 8
        $this->cookiePrefix = $configuration['cookie_prefix'];
47 8
        $this->sessionNameSalt = $configuration['session_name_salt'];
48
49
        // @codeCoverageIgnoreStart
50
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
51
            ini_set('session.entropy_file', '/dev/urandom');
52
            ini_set('session.entropy_length', '16');
53
            ini_set('session.hash_function', (string) $configuration['hash_function']);
54
            ini_set('session.use_only_cookies', (string) $configuration['use_only_cookies']);
55
            ini_set('session.use_cookies', (string) $configuration['use_cookies']);
56
            ini_set('session.use_trans_sid', (string) $configuration['use_trans_sid']);
57
            ini_set('session.cookie_httponly', (string) $configuration['cookie_http_only']);
58
            ini_set('session.save_path', (string) $configuration['save_path']);
59
        }
60
        // @codeCoverageIgnoreEnd
61 8
    }
62
63
    /**
64
     * Saves data back to session.
65
     */
66 8
    public function __destruct()
67
    {
68 8
        $this->write();
69
70
        // @codeCoverageIgnoreStart
71
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
72
            session_write_close();
73
        }
74
        // @codeCoverageIgnoreEnd
75 8
    }
76
77
    /**
78
     * Reads PHP Session array to class property.
79
     *
80
     * @return void
81
     *
82
     * @codeCoverageIgnore
83
     */
84
    private function read() : void
85
    {
86
        if (isset($_SESSION[$this->namespace])) {
87
            $this->data = $_SESSION[$this->namespace];
88
        }
89
    }
90
91
    /**
92
     * Writes class property to PHP Session array.
93
     *
94
     * @return void
95
     *
96
     * @codeCoverageIgnore
97
     */
98
    private function write() : void
99
    {
100
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
101
            return;
102
        }
103
104
        $_SESSION[$this->namespace] = $this->data;
105
    }
106
107
    /**
108
     * Check whether the session has already been started.
109
     *
110
     * @return bool
111
     *
112
     * @codeCoverageIgnore
113
     */
114
    private function sessionStarted() : bool
115
    {
116
        // For unit test we give controllable result.
117
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
118
            return $this->namespace == 'TEST';
119
        }
120
121
        return session_status() === PHP_SESSION_ACTIVE;
122
    }
123
124
    /**
125
     * Starts a session.
126
     *
127
     * @param string $name
128
     * @param int    $timeOut
129
     * @param string $path
130
     * @param string $domain
131
     * @param bool   $secure
132
     * @param bool   $httpOnly
133
     * @return ServiceInterface
134
     */
135 1
    public function start(
136
        string $name,
137
        int $timeOut = 3600,
138
        string $path = '/',
139
        ? string $domain = null,
140
        bool $secure = false,
141
        bool $httpOnly = false
142
    ) : ServiceInterface {
143 1
        if ($this->sessionStarted()) {
144 1
            throw new RuntimeException('Cannot start session. Session is already started.', 1000);
145
        }
146
147
        // @codeCoverageIgnoreStart
148
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
149
            session_name($this->cookiePrefix.'-'.bin2hex($name.$this->sessionNameSalt));
150
            session_set_cookie_params($timeOut, $path, $domain, $secure, $httpOnly);
151
            session_start();
152
        }
153
        // @codeCoverageIgnoreEnd
154
155 1
        $this->read();
156
157 1
        return $this;
158
    }
159
160
    /**
161
     * Regenerates session identifier.
162
     *
163
     * @return ServiceInterface
164
     */
165 2
    public function regenerateId() : ServiceInterface
166
    {
167 2
        if (!$this->sessionStarted()) {
168 1
            throw new RuntimeException('Cannot regenerate session identifier. Session is not started yet.', 1001);
169
        }
170
171
        // first save data.
172 2
        $this->write();
173
174
        // @codeCoverageIgnoreStart
175
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
176
            if (!session_regenerate_id(true)) {
177
                throw new RuntimeException('Cannot regenerate session identifier. Unknown error.', 1002);
178
            }
179
        }
180
        // @codeCoverageIgnoreEnd
181
182 2
        return $this;
183
    }
184
185
    /**
186
     * Returns the session id.
187
     *
188
     * @return string
189
     */
190
    public function getSessionId() : string
191
    {
192
        if (!$this->sessionStarted()) {
193
            throw new RuntimeException('Cannot retrieve session identifier. Session is not started yet.', 1010);
194
        }
195
196
        return session_id();
197
    }
198
199
    /**
200
     * Sets session data.
201
     *
202
     * @param string $name
203
     * @param mixed  $value
204
     * @param bool   $readOnly
205
     * @throws RuntimeException
206
     * @return ServiceInterface
207
     */
208 5
    public function set(string $name, $value, bool $readOnly = false) : ServiceInterface
209
    {
210 5
        if (!$this->sessionStarted()) {
211 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1005);
212
        }
213
214 5
        if (isset($this->readOnly[$name])) {
215 1
            throw new RuntimeException('Unable to overwrite data. Permission denied.', 1006);
216
        }
217
218 5
        if ($readOnly) {
219 2
            $this->readOnly[$name] = $name;
220
        }
221
222 5
        $this->data[$name] = $value;
223
224 5
        return $this;
225
    }
226
227
    /**
228
     * Checks whether a session data exists or not.
229
     *
230
     * @param string $name
231
     * @throws RuntimeException
232
     * @return bool
233
     */
234 2
    public function has(string $name) : bool
235
    {
236 2
        if (!$this->sessionStarted()) {
237 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1009);
238
        }
239
240 2
        return isset($this->data[$name]);
241
    }
242
243
    /**
244
     * Gets session data.
245
     *
246
     * @param string $name
247
     * @param bool   $skipMissing
248
     * @throws RuntimeException
249
     * @return mixed
250
     */
251 4
    public function get(string $name, bool $skipMissing = true)
252
    {
253 4
        if (!$this->sessionStarted()) {
254 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1003);
255
        }
256
257 4
        if (isset($this->data[$name])) {
258 4
            return $this->data[$name];
259 3
        } elseif ($skipMissing) {
260 3
            return null;
261
        }
262
263 1
        throw new RuntimeException('Cannot retrieve session data. Data is not set', 1004);
264
    }
265
266
    /**
267
     * Deletes session data.
268
     *
269
     * @param string $name
270
     * @param bool   $forceDelete
271
     * @throws RuntimeException
272
     * @return ServiceInterface
273
     */
274 2
    public function delete(string $name, bool $forceDelete = false) : ServiceInterface
275
    {
276 2
        if (!$this->sessionStarted()) {
277 1
            throw new RuntimeException('Cannot delete session data. Session is not started.', 1007);
278
        }
279
280 2
        if (!$forceDelete && isset($this->readOnly[$name])) {
281 1
            throw new RuntimeException('Unable to delete data. Permission denied.', 1008);
282
        }
283
284
        // hide errors if data not exists.
285 2
        unset($this->readOnly[$name]);
286 2
        unset($this->data[$name]);
287
288 2
        return $this;
289
    }
290
291
    /**
292
     * Unlocks readOnly data.
293
     *
294
     * @param string $name
295
     * @return ServiceInterface
296
     */
297 1
    public function unlock(string $name) : ServiceInterface
298
    {
299 1
        if (isset($this->readOnly[$name])) {
300 1
            unset($this->readOnly[$name]);
301
        }
302
303 1
        return $this;
304
    }
305
306
    /**
307
     * Returns the internal storage.
308
     *
309
     * @return array
310
     */
311 1
    public function toArray() : array
312
    {
313 1
        return $this->data;
314
    }
315
}
316