Test Failed
Branch master (3ad9ef)
by Gabor
05:05
created

ServiceAdapter::start()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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