Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
64.00% covered (warning)
64.00%
48 / 75
70.59% covered (success)
70.59%
12 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
Session
64.00% covered (warning)
64.00%
48 / 75
70.59% covered (success)
70.59%
12 / 17
129.27
0.00% covered (danger)
0.00%
0 / 1
 __construct
42.86% covered (warning)
42.86%
6 / 14
0.00% covered (danger)
0.00%
0 / 1
4.68
 getInstance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 regenerateId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 init
37.50% covered (warning)
37.50%
3 / 8
0.00% covered (danger)
0.00%
0 / 1
7.91
 kill
85.71% covered (success)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
5.07
 setTimedValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setRequestValue
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 checkRequests
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 checkExpirations
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 toArray
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
6
 __set
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 __get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 __isset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 __unset
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 offsetSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Session;
15
16/**
17 * Session class
18 *
19 * @category   Pop
20 * @package    Pop\Session
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    3.3.0
25 */
26class Session extends AbstractSession
27{
28
29    /**
30     * Instance of the session
31     * @var object
32     */
33    private static $instance = null;
34
35    /**
36     * Session Name
37     * @var string
38     */
39    private $sessionName = null;
40
41    /**
42     * Session ID
43     * @var string
44     */
45    private $sessionId = null;
46
47    /**
48     * Constructor
49     *
50     * @param array $options
51     *
52     * Private method to instantiate the session object
53     */
54    private function __construct(array $options = [])
55    {
56        // Start a session and set the session id.
57        if (session_id() == '') {
58            if (!empty($options)) {
59                $sessionParams = session_get_cookie_params();
60                $lifetime      = $options['lifetime'] ?? $sessionParams['lifetime'];
61                $path          = $options['path']     ?? $sessionParams['lifetime'];
62                $domain        = $options['domain']   ?? $sessionParams['domain'];
63                $secure        = $options['secure']   ??  $sessionParams['secure'];
64                $httponly      = $options['httponly'] ??  $sessionParams['httponly'];
65                $sameSite      = $options['samesite'] ??  $sessionParams['samesite'];
66
67                session_set_cookie_params([
68                    'lifetime' => $lifetime,
69                    'path'     => $path,
70                    'domain'   => $domain,
71                    'secure'   => $secure,
72                    'httponly' => $httponly,
73                    'samesite' => $sameSite
74                ]);
75            }
76            session_start();
77            $this->sessionId   = session_id();
78            $this->sessionName = session_name();
79            $this->init();
80        }
81    }
82
83    /**
84     * Determine whether or not an instance of the session object exists already,
85     * and instantiate the object if it does not exist.
86     *
87     * @param  array $options
88     * @return Session
89     */
90    public static function getInstance(array $options = [])
91    {
92        if (null === self::$instance) {
93            self::$instance = new Session($options);
94        }
95
96        return self::$instance;
97    }
98
99    /**
100     * Return the current the session name
101     *
102     * @return string
103     */
104    public function getName()
105    {
106        return $this->sessionName;
107    }
108
109    /**
110     * Return the current the session id
111     *
112     * @return string
113     */
114    public function getId()
115    {
116        return $this->sessionId;
117    }
118
119    /**
120     * Regenerate the session id
121     *
122     * @param  boolean $deleteOldSession
123     * @return void
124     */
125    public function regenerateId($deleteOldSession = true)
126    {
127        session_regenerate_id($deleteOldSession);
128        $this->sessionId   = session_id();
129        $this->sessionName = session_name();
130    }
131
132    /**
133     * Init the session
134     *
135     * @return void
136     */
137    private function init()
138    {
139        if (!isset($_SESSION['_POP_SESSION_'])) {
140            $_SESSION['_POP_SESSION_'] = [
141                'requests'    => [],
142                'expirations' => []
143            ];
144        } else if (isset($_SESSION['_POP_SESSION_']) && !isset($_SESSION['_POP_SESSION_']['requests'])) {
145            $_SESSION['_POP_SESSION_']['requests']    = [];
146            $_SESSION['_POP_SESSION_']['expirations'] = [];
147        } else {
148            $this->checkRequests();
149            $this->checkExpirations();
150        }
151    }
152
153    /**
154     * Destroy the session
155     *
156     * @return void
157     */
158    public function kill()
159    {
160        if (!empty($this->sessionName) && !empty($this->sessionId) &&
161            isset($_COOKIE[$this->sessionName]) && ($_COOKIE[$this->sessionName] == $this->sessionId)) {
162            setcookie($this->sessionName, $this->sessionId, time() - 3600);
163        }
164
165        $_SESSION = null;
166        session_unset();
167        session_destroy();
168        unset($this->sessionId);
169    }
170
171    /**
172     * Set a time-based value
173     *
174     * @param  string $key
175     * @param  mixed  $value
176     * @param  int    $expire
177     * @return Session
178     */
179    public function setTimedValue($key, $value, $expire = 300)
180    {
181        $_SESSION[$key] = $value;
182        $_SESSION['_POP_SESSION_']['expirations'][$key] = time() + (int)$expire;
183        return $this;
184    }
185
186    /**
187     * Set a request-based value
188     *
189     * @param  string $key
190     * @param  mixed  $value
191     * @param  int    $hops
192     * @return Session
193     */
194    public function setRequestValue($key, $value, $hops = 1)
195    {
196        $_SESSION[$key] = $value;
197        $_SESSION['_POP_SESSION_']['requests'][$key] = [
198            'current' => 0,
199            'limit'   => (int)$hops
200        ];
201        return $this;
202    }
203
204    /**
205     * Check the request-based session values
206     *
207     * @return void
208     */
209    private function checkRequests()
210    {
211        foreach ($_SESSION as $key => $value) {
212            if (isset($_SESSION['_POP_SESSION_']['requests'][$key])) {
213                $_SESSION['_POP_SESSION_']['requests'][$key]['current']++;
214                $current = $_SESSION['_POP_SESSION_']['requests'][$key]['current'];
215                $limit   = $_SESSION['_POP_SESSION_']['requests'][$key]['limit'];
216                if ($current > $limit) {
217                    unset($_SESSION[$key]);
218                    unset($_SESSION['_POP_SESSION_']['requests'][$key]);
219                }
220            }
221        }
222    }
223
224    /**
225     * Check the time-based session values
226     *
227     * @return void
228     */
229    private function checkExpirations()
230    {
231        foreach ($_SESSION as $key => $value) {
232            if (isset($_SESSION['_POP_SESSION_']['expirations'][$key]) &&
233                (time() > $_SESSION['_POP_SESSION_']['expirations'][$key])) {
234                unset($_SESSION[$key]);
235                unset($_SESSION['_POP_SESSION_']['expirations'][$key]);
236            }
237        }
238    }
239
240    /**
241     * Get the session values as an array
242     *
243     * @return array
244     */
245    public function toArray()
246    {
247        $session = $_SESSION;
248
249        if (isset($session['_POP_SESSION_'])) {
250            foreach ($session['_POP_SESSION_'] as $key => $value) {
251                if (($key != 'expirations') && ($key != 'requests')) {
252                    if (isset($session[$key])) {
253                        unset($session[$key]);
254                    }
255                }
256            }
257            unset($session['_POP_SESSION_']);
258        }
259
260        return $session;
261    }
262
263    /**
264     * Set a property in the session object that is linked to the $_SESSION global variable
265     *
266     * @param  string $name
267     * @param  mixed $value
268     * @throws Exception
269     * @return void
270     */
271    public function __set($name, $value)
272    {
273        if ($name == '_POP_SESSION_') {
274            throw new Exception("Error: Cannot use the reserved name '_POP_SESSION_'.");
275        }
276        $_SESSION[$name] = $value;
277    }
278
279    /**
280     * Get method to return the value of the $_SESSION global variable
281     *
282     * @param  string $name
283     * @return mixed
284     */
285    public function __get($name)
286    {
287        return (($name !== '_POP_SESSION_') && isset($_SESSION[$name])) ? $_SESSION[$name] : null;
288    }
289
290    /**
291     * Return the isset value of the $_SESSION global variable
292     *
293     * @param  string $name
294     * @return boolean
295     */
296    public function __isset($name)
297    {
298        return (($name !== '_POP_SESSION_') && isset($_SESSION[$name]));
299    }
300
301    /**
302     * Unset the $_SESSION global variable
303     *
304     * @param  string $name
305     * @throws Exception
306     * @return void
307     */
308    public function __unset($name)
309    {
310        if ($name == '_POP_SESSION_') {
311            throw new Exception("Error: Cannot use the reserved name '_POP_SESSION_'.");
312        }
313
314        $_SESSION[$name] = null;
315        unset($_SESSION[$name]);
316    }
317
318    /**
319     * ArrayAccess offsetSet
320     *
321     * @param  mixed $offset
322     * @param  mixed $value
323     * @throws Exception
324     * @return void
325     */
326    public function offsetSet($offset, $value): void
327    {
328        $this->__set($offset, $value);
329    }
330
331}