Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.29% |
64 / 85 |
|
66.67% |
12 / 18 |
CRAP | |
0.00% |
0 / 1 |
Session | |
75.29% |
64 / 85 |
|
66.67% |
12 / 18 |
61.94 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
3 | |||
getInstance | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
regenerateId | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
init | |
50.00% |
5 / 10 |
|
0.00% |
0 / 1 |
4.12 | |||
kill | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
5.03 | |||
setTimedValue | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
setRequestValue | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
checkRequest | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
checkRequests | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
checkExpiration | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
checkExpirations | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
toArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
__set | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
__get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
3 | |||
__isset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
__unset | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 |
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-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace 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-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 4.0.0 |
25 | */ |
26 | class Session extends AbstractSession |
27 | { |
28 | |
29 | /** |
30 | * Instance of the session |
31 | * @var ?object |
32 | */ |
33 | private static ?object $instance = null; |
34 | |
35 | /** |
36 | * Session Name |
37 | * @var ?string |
38 | */ |
39 | private ?string $sessionName = null; |
40 | |
41 | /** |
42 | * Session ID |
43 | * @var ?string |
44 | */ |
45 | private ?string $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 = []): Session |
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(): string |
105 | { |
106 | return $this->sessionName; |
107 | } |
108 | |
109 | /** |
110 | * Return the current the session id |
111 | * |
112 | * @return string |
113 | */ |
114 | public function getId(): string |
115 | { |
116 | return $this->sessionId; |
117 | } |
118 | |
119 | /** |
120 | * Regenerate the session id |
121 | * |
122 | * @param bool $deleteOldSession |
123 | * @return void |
124 | */ |
125 | public function regenerateId(bool $deleteOldSession = true): void |
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(): void |
138 | { |
139 | if (!isset($_SESSION['_POP_SESSION_'])) { |
140 | $_SESSION['_POP_SESSION_'] = [ |
141 | 'requests' => [], |
142 | 'expirations' => [] |
143 | ]; |
144 | } else if (!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(): void |
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 | self::$instance = null; |
169 | $this->sessionId = null; |
170 | $this->sessionName = null; |
171 | } |
172 | |
173 | /** |
174 | * Set a time-based value |
175 | * |
176 | * @param string $key |
177 | * @param mixed $value |
178 | * @param int $expire |
179 | * @return Session |
180 | */ |
181 | public function setTimedValue(string $key, mixed $value, int $expire = 300): Session |
182 | { |
183 | $_SESSION[$key] = $value; |
184 | $_SESSION['_POP_SESSION_']['expirations'][$key] = time() + (int)$expire; |
185 | return $this; |
186 | } |
187 | |
188 | /** |
189 | * Set a request-based value |
190 | * |
191 | * @param string $key |
192 | * @param mixed $value |
193 | * @param int $hops |
194 | * @return Session |
195 | */ |
196 | public function setRequestValue(string $key, mixed $value, int $hops = 1): Session |
197 | { |
198 | $_SESSION[$key] = $value; |
199 | $_SESSION['_POP_SESSION_']['requests'][$key] = [ |
200 | 'current' => 0, |
201 | 'limit' => (int)$hops |
202 | ]; |
203 | return $this; |
204 | } |
205 | |
206 | /** |
207 | * Check the request-based session value |
208 | * |
209 | * @return void |
210 | */ |
211 | private function checkRequest($key): void |
212 | { |
213 | if (isset($_SESSION['_POP_SESSION_']['requests'][$key])) { |
214 | $_SESSION['_POP_SESSION_']['requests'][$key]['current']++; |
215 | $current = $_SESSION['_POP_SESSION_']['requests'][$key]['current']; |
216 | $limit = $_SESSION['_POP_SESSION_']['requests'][$key]['limit']; |
217 | if ($current > $limit) { |
218 | unset($_SESSION[$key]); |
219 | unset($_SESSION['_POP_SESSION_']['requests'][$key]); |
220 | } |
221 | } |
222 | } |
223 | |
224 | /** |
225 | * Check the request-based session values |
226 | * |
227 | * @return void |
228 | */ |
229 | private function checkRequests(): void |
230 | { |
231 | foreach ($_SESSION as $key => $value) { |
232 | $this->checkRequest($key); |
233 | } |
234 | } |
235 | |
236 | /** |
237 | * Check the time-based session value |
238 | * |
239 | * @return void |
240 | */ |
241 | private function checkExpiration($key): void |
242 | { |
243 | if (isset($_SESSION['_POP_SESSION_']['expirations'][$key]) && |
244 | (time() > $_SESSION['_POP_SESSION_']['expirations'][$key])) { |
245 | unset($_SESSION[$key]); |
246 | unset($_SESSION['_POP_SESSION_']['expirations'][$key]); |
247 | } |
248 | } |
249 | |
250 | /** |
251 | * Check the time-based session values |
252 | * |
253 | * @return void |
254 | */ |
255 | private function checkExpirations(): void |
256 | { |
257 | foreach ($_SESSION as $key => $value) { |
258 | $this->checkExpiration($key); |
259 | } |
260 | } |
261 | |
262 | /** |
263 | * Get the session values as an array |
264 | * |
265 | * @return array |
266 | */ |
267 | public function toArray(): array |
268 | { |
269 | $session = $_SESSION; |
270 | |
271 | if (isset($session['_POP_SESSION_'])) { |
272 | unset($session['_POP_SESSION_']); |
273 | } |
274 | |
275 | return $session; |
276 | } |
277 | |
278 | /** |
279 | * Set a property in the session object that is linked to the $_SESSION global variable |
280 | * |
281 | * @param string $name |
282 | * @param mixed $value |
283 | * @throws Exception |
284 | * @return void |
285 | */ |
286 | public function __set(string $name, mixed $value): void |
287 | { |
288 | if ($name == '_POP_SESSION_') { |
289 | throw new Exception("Error: Cannot use the reserved name '_POP_SESSION_'."); |
290 | } |
291 | $_SESSION[$name] = $value; |
292 | } |
293 | |
294 | /** |
295 | * Get method to return the value of the $_SESSION global variable |
296 | * |
297 | * @param string $name |
298 | * @return mixed |
299 | */ |
300 | public function __get(string $name): mixed |
301 | { |
302 | return (($name !== '_POP_SESSION_') && isset($_SESSION[$name])) ? $_SESSION[$name] : null; |
303 | } |
304 | |
305 | /** |
306 | * Return the isset value of the $_SESSION global variable |
307 | * |
308 | * @param string $name |
309 | * @return bool |
310 | */ |
311 | public function __isset(string $name): bool |
312 | { |
313 | return (($name !== '_POP_SESSION_') && isset($_SESSION[$name])); |
314 | } |
315 | |
316 | /** |
317 | * Unset the $_SESSION global variable |
318 | * |
319 | * @param string $name |
320 | * @throws Exception |
321 | * @return void |
322 | */ |
323 | public function __unset(string $name): void |
324 | { |
325 | if ($name == '_POP_SESSION_') { |
326 | throw new Exception("Error: Cannot use the reserved name '_POP_SESSION_'."); |
327 | } |
328 | |
329 | $_SESSION[$name] = null; |
330 | unset($_SESSION[$name]); |
331 | } |
332 | |
333 | } |