Completed
Pull Request — master (#399)
by Anton
04:24
created

Session::setNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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