Completed
Push — master ( aef2ed...440238 )
by Anton
12s
created

Session::sessionExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
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
    public function getId()
140
    {
141
        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
        }
158
        return false;
159
    }
160
161
    /**
162
     * Returns true if session ID is set
163
     *
164
     * @return bool
165
     */
166 638
    public function cookieExists()
167
    {
168 638
        return isset($_COOKIE[session_name()]);
169
    }
170
171
    /**
172
     * Does a session started and is it currently active?
173
     *
174
     * @return bool
175
     */
176 638
    public function sessionExists()
177
    {
178 638
        return session_status() === PHP_SESSION_ACTIVE;
179
    }
180
181
    /**
182
     * Start session
183
     *
184
     * if No session currently exists, attempt to start it. Calls
185
     * {@link isValid()} once session_start() is called, and raises an
186
     * exception if validation fails.
187
     *
188
     * @return void
189
     * @throws SessionException
190
     */
191 638
    public function start()
192
    {
193 638
        if ($this->sessionExists()) {
194 638
            return;
195
        }
196
197 1
        $this->initAdapter();
198
199 1
        session_start();
200 1
    }
201
202
    /**
203
     * Destroy/end a session
204
     *
205
     * @return void
206
     */
207
    public function destroy()
208
    {
209
        if (!$this->cookieExists() || !$this->sessionExists()) {
210
            return;
211
        }
212
213
        session_destroy();
214
215
        // send expire cookies
216
        $this->expireSessionCookie();
217
218
        // clear session data
219
        unset($_SESSION[$this->getNamespace()]);
220
    }
221
222
    /**
223
     * Set session save handler object
224
     *
225
     * @param  \SessionHandlerInterface $saveHandler
226
     * @return Session
227
     */
228
    public function setAdapter($saveHandler)
229
    {
230
        $this->adapter = $saveHandler;
231
        return $this;
232
    }
233
234
    /**
235
     * Get SaveHandler Object
236
     *
237
     * @return \SessionHandlerInterface
238
     */
239
    public function getAdapter()
240
    {
241
        return $this->adapter;
242
    }
243
244
    /**
245
     * Register Save Handler with ext/session
246
     *
247
     * Since ext/session is coupled to this particular session manager
248
     * register the save handler with ext/session.
249
     *
250
     * @return bool
251
     * @throws ComponentException
252
     */
253 1
    protected function initAdapter()
254
    {
255 1
        if (is_null($this->adapter) || $this->adapter === 'files') {
256
            // try to apply settings
257 1
            if ($settings = $this->getOption('settings', 'files')) {
258 1
                $this->setSavePath($settings['save_path']);
259
            }
260 1
            return true;
261
        } elseif (is_string($this->adapter)) {
262
            $adapterClass = '\\Bluz\\Session\\Adapter\\'.ucfirst($this->adapter);
263
            if (!class_exists($adapterClass) || !is_subclass_of($adapterClass, '\SessionHandlerInterface')) {
264
                throw new ComponentException("Class for session adapter `{$this->adapter}` not found");
265
            }
266
            $settings = $this->getOption('settings', $this->adapter) ?: [];
267
268
            $this->adapter = new $adapterClass($settings);
269
        }
270
271
        return session_set_save_handler($this->adapter);
272
    }
273
274
    /**
275
     * Set the session cookie lifetime
276
     *
277
     * If a session already exists, destroys it (without sending an expiration
278
     * cookie), regenerates the session ID, and restarts the session.
279
     *
280
     * @param  integer $ttl TTL in seconds
281
     * @return void
282
     */
283
    public function setSessionCookieLifetime($ttl)
284
    {
285
        // Set new cookie TTL
286
        session_set_cookie_params($ttl);
287
288
        if ($this->sessionExists()) {
289
            // There is a running session so we'll regenerate id to send a new cookie
290
            $this->regenerateId();
291
        }
292
    }
293
294
    /**
295
     * Expire the session cookie
296
     *
297
     * Sends a session cookie with no value, and with an expiry in the past.
298
     *
299
     * @return void
300
     */
301
    public function expireSessionCookie()
302
    {
303
        if (ini_get('session.use_cookies')) {
304
            $params = session_get_cookie_params();
305
            setcookie(
306
                $this->getName(),
307
                '',
308
                $_SERVER['REQUEST_TIME'] - 42000,
309
                $params['path'],
310
                $params['domain'],
311
                $params['secure'],
312
                $params['httponly']
313
            );
314
        }
315
    }
316
317
    /**
318
     * Set session save path
319
     *
320
     * @param  string $savePath
321
     * @return Session
322
     * @throws ComponentException
323
     */
324 1
    protected function setSavePath($savePath)
325
    {
326 1
        if (!is_dir($savePath)
327 1
            || !is_writable($savePath)
328
        ) {
329
            throw new ComponentException('Session path is not writable');
330
        }
331 1
        session_save_path($savePath);
332 1
        return $this;
333
    }
334
335
    /**
336
     * Set key/value pair
337
     *
338
     * @param  string $key
339
     * @param  mixed  $value
340
     * @return void
341
     */
342 7
    public function set($key, $value)
343
    {
344 7
        $this->start();
345
        // check storage
346 7
        if (!isset($_SESSION[$this->getNamespace()])) {
347 7
            $_SESSION[$this->getNamespace()] = [];
348
        }
349 7
        $_SESSION[$this->namespace][$key] = $value;
350 7
    }
351
352
    /**
353
     * Get value by key
354
     *
355
     * @param  string $key
356
     * @return mixed
357
     */
358 639
    public function get($key)
359
    {
360 639
        if ($this->contains($key)) {
361 5
            return $_SESSION[$this->namespace][$key];
362
        }
363 639
        return null;
364
    }
365
366
    /**
367
     * Isset
368
     *
369
     * @param  string $key
370
     * @return bool
371
     */
372 639
    public function contains($key)
373
    {
374 639
        if ($this->cookieExists()) {
375 639
            $this->start();
376
        } elseif (!$this->sessionExists()) {
377
            return false;
378
        }
379 639
        return isset($_SESSION[$this->namespace][$key]);
380
    }
381
382
    /**
383
     * Unset
384
     *
385
     * @param  string $key
386
     * @return void
387
     */
388 638
    public function delete($key)
389
    {
390 638
        if ($this->contains($key)) {
391 3
            unset($_SESSION[$this->namespace][$key]);
392
        }
393 638
    }
394
}
395