Passed
Push — master ( 8d94c2...c3251a )
by Gabor
07:26
created

ServiceAdapter::set()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 4
nop 3
crap 4
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 1
    public function getSessionId() : string
191
    {
192 1
        if (!$this->sessionStarted()) {
193 1
            throw new RuntimeException('Cannot retrieve session identifier. Session is not started yet.', 1010);
194
        }
195
196
        // @codeCoverageIgnoreStart
197
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
198
            return '';
199
        }
200
201
        return session_id();
202
        // @codeCoverageIgnoreEnd
203
    }
204
205
    /**
206
     * Sets session data.
207
     *
208
     * @param string $name
209
     * @param mixed  $value
210
     * @param bool   $readOnly
211
     * @throws RuntimeException
212
     * @return ServiceInterface
213
     */
214 5
    public function set(string $name, $value, bool $readOnly = false) : ServiceInterface
215
    {
216 5
        if (!$this->sessionStarted()) {
217 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1005);
218
        }
219
220 5
        if (isset($this->readOnly[$name])) {
221 1
            throw new RuntimeException('Unable to overwrite data. Permission denied.', 1006);
222
        }
223
224 5
        if ($readOnly) {
225 2
            $this->readOnly[$name] = $name;
226
        }
227
228 5
        $this->data[$name] = $value;
229
230 5
        return $this;
231
    }
232
233
    /**
234
     * Checks whether a session data exists or not.
235
     *
236
     * @param string $name
237
     * @throws RuntimeException
238
     * @return bool
239
     */
240 2
    public function has(string $name) : bool
241
    {
242 2
        if (!$this->sessionStarted()) {
243 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1009);
244
        }
245
246 2
        return isset($this->data[$name]);
247
    }
248
249
    /**
250
     * Gets session data.
251
     *
252
     * @param string $name
253
     * @param bool   $skipMissing
254
     * @throws RuntimeException
255
     * @return mixed
256
     */
257 4
    public function get(string $name, bool $skipMissing = true)
258
    {
259 4
        if (!$this->sessionStarted()) {
260 1
            throw new RuntimeException('Cannot set session data. Session is not started yet.', 1003);
261
        }
262
263 4
        if (isset($this->data[$name])) {
264 4
            return $this->data[$name];
265 3
        } elseif ($skipMissing) {
266 3
            return null;
267
        }
268
269 1
        throw new RuntimeException('Cannot retrieve session data. Data is not set', 1004);
270
    }
271
272
    /**
273
     * Deletes session data.
274
     *
275
     * @param string $name
276
     * @param bool   $forceDelete
277
     * @throws RuntimeException
278
     * @return ServiceInterface
279
     */
280 2
    public function delete(string $name, bool $forceDelete = false) : ServiceInterface
281
    {
282 2
        if (!$this->sessionStarted()) {
283 1
            throw new RuntimeException('Cannot delete session data. Session is not started.', 1007);
284
        }
285
286 2
        if (!$forceDelete && isset($this->readOnly[$name])) {
287 1
            throw new RuntimeException('Unable to delete data. Permission denied.', 1008);
288
        }
289
290
        // hide errors if data not exists.
291 2
        unset($this->readOnly[$name]);
292 2
        unset($this->data[$name]);
293
294 2
        return $this;
295
    }
296
297
    /**
298
     * Unlocks readOnly data.
299
     *
300
     * @param string $name
301
     * @return ServiceInterface
302
     */
303 1
    public function unlock(string $name) : ServiceInterface
304
    {
305 1
        if (isset($this->readOnly[$name])) {
306 1
            unset($this->readOnly[$name]);
307
        }
308
309 1
        return $this;
310
    }
311
312
    /**
313
     * Returns the internal storage.
314
     *
315
     * @return array
316
     */
317 1
    public function toArray() : array
318
    {
319 1
        return $this->data;
320
    }
321
}
322