Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
77.78% |
56 / 72 |
|
61.54% |
8 / 13 |
CRAP | |
0.00% |
0 / 1 |
CookieManager | |
77.78% |
56 / 72 |
|
61.54% |
8 / 13 |
28.81 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getCookies | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDomain | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isSecure | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isHttpOnly | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSessionName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSameSite | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
proxySetCookie | |
14.29% |
2 / 14 |
|
0.00% |
0 / 1 |
4.52 | |||
setGlobalCookie | |
96.67% |
29 / 30 |
|
0.00% |
0 / 1 |
6 | |||
set | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
clear | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
4.03 | |||
get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Cookie Manager |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2015. |
9 | * Copyright (C) The National Library of Finland 2020. |
10 | * |
11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2, |
13 | * as published by the Free Software Foundation. |
14 | * |
15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 | * |
24 | * @category VuFind |
25 | * @package Cookie |
26 | * @author Demian Katz <demian.katz@villanova.edu> |
27 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
28 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
29 | * @link https://vufind.org/wiki/development Wiki |
30 | */ |
31 | |
32 | namespace VuFind\Cookie; |
33 | |
34 | use function is_array; |
35 | |
36 | /** |
37 | * Cookie Manager |
38 | * |
39 | * @category VuFind |
40 | * @package Cookie |
41 | * @author Demian Katz <demian.katz@villanova.edu> |
42 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
43 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
44 | * @link https://vufind.org/wiki/development Wiki |
45 | */ |
46 | class CookieManager |
47 | { |
48 | /** |
49 | * Cookie array to work with |
50 | * |
51 | * @var array |
52 | */ |
53 | protected $cookies; |
54 | |
55 | /** |
56 | * Cookie base path |
57 | * |
58 | * @var string |
59 | */ |
60 | protected $path; |
61 | |
62 | /** |
63 | * Cookie domain |
64 | * |
65 | * @var string |
66 | */ |
67 | protected $domain; |
68 | |
69 | /** |
70 | * Are cookies secure only? |
71 | * |
72 | * @var bool |
73 | */ |
74 | protected $secure; |
75 | |
76 | /** |
77 | * Are cookies HTTP only? |
78 | * |
79 | * @var bool |
80 | */ |
81 | protected $httpOnly; |
82 | |
83 | /** |
84 | * The name of the session cookie |
85 | * |
86 | * @var ?string |
87 | */ |
88 | protected $sessionName; |
89 | |
90 | /** |
91 | * Default SameSite attribute |
92 | * |
93 | * @var string |
94 | */ |
95 | protected $sameSite; |
96 | |
97 | /** |
98 | * Constructor |
99 | * |
100 | * @param array $cookies Cookie array to manipulate (e.g. $_COOKIE) |
101 | * @param string $path Cookie base path (default = /) |
102 | * @param string $domain Cookie domain |
103 | * @param bool $secure Are cookies secure only? (default = false) |
104 | * @param ?string $sessionName Session cookie name (if null defaults to PHP |
105 | * settings) |
106 | * @param bool $httpOnly Are cookies HTTP only? (default = true) |
107 | * @param string $sameSite Default SameSite attribute (defaut = 'Lax') |
108 | */ |
109 | public function __construct( |
110 | $cookies, |
111 | $path = '/', |
112 | $domain = null, |
113 | $secure = false, |
114 | $sessionName = null, |
115 | $httpOnly = true, |
116 | $sameSite = 'Lax' |
117 | ) { |
118 | $this->cookies = $cookies; |
119 | $this->path = $path; |
120 | $this->domain = $domain; |
121 | $this->secure = $secure; |
122 | $this->httpOnly = $httpOnly; |
123 | $this->sessionName = $sessionName; |
124 | $this->sameSite = $sameSite; |
125 | } |
126 | |
127 | /** |
128 | * Get all cookie values. |
129 | * |
130 | * @return array |
131 | */ |
132 | public function getCookies() |
133 | { |
134 | return $this->cookies; |
135 | } |
136 | |
137 | /** |
138 | * Get the cookie domain. |
139 | * |
140 | * @return string |
141 | */ |
142 | public function getDomain() |
143 | { |
144 | return $this->domain; |
145 | } |
146 | |
147 | /** |
148 | * Get the cookie path. |
149 | * |
150 | * @return string |
151 | */ |
152 | public function getPath() |
153 | { |
154 | return $this->path; |
155 | } |
156 | |
157 | /** |
158 | * Are cookies set to "secure only" mode? |
159 | * |
160 | * @return bool |
161 | */ |
162 | public function isSecure() |
163 | { |
164 | return $this->secure; |
165 | } |
166 | |
167 | /** |
168 | * Are cookies set to "HTTP only" mode? |
169 | * |
170 | * @return bool |
171 | */ |
172 | public function isHttpOnly() |
173 | { |
174 | return $this->httpOnly; |
175 | } |
176 | |
177 | /** |
178 | * Get the name of the cookie |
179 | * |
180 | * @return ?string |
181 | */ |
182 | public function getSessionName() |
183 | { |
184 | return $this->sessionName; |
185 | } |
186 | |
187 | /** |
188 | * Get the cookie SameSite attribute. |
189 | * |
190 | * @return string |
191 | */ |
192 | public function getSameSite() |
193 | { |
194 | return $this->sameSite; |
195 | } |
196 | |
197 | /** |
198 | * Support method for setGlobalCookie -- proxy PHP's setcookie() function |
199 | * for compatibility with unit testing. |
200 | * |
201 | * @param string $key Name of cookie to set |
202 | * @param mixed $value Value to set |
203 | * @param int $expire Cookie expiration time |
204 | * @param string $path Path |
205 | * @param string $domain Domain |
206 | * @param bool $secure Whether the cookie is secure only |
207 | * @param bool $httpOnly Whether the cookie should be "HTTP only" |
208 | * @param string $sameSite SameSite attribute to use (Lax, Strict or None) |
209 | * |
210 | * @return bool |
211 | */ |
212 | public function proxySetCookie( |
213 | $key, |
214 | $value, |
215 | $expire, |
216 | $path, |
217 | $domain, |
218 | $secure, |
219 | $httpOnly, |
220 | $sameSite |
221 | ) { |
222 | // Special case: in CLI -- don't actually write headers! |
223 | if ('cli' === PHP_SAPI) { |
224 | return true; |
225 | } |
226 | return setcookie( |
227 | $key, |
228 | $value ?? '', |
229 | [ |
230 | 'expires' => $expire, |
231 | 'path' => $path, |
232 | 'domain' => $domain, |
233 | 'samesite' => $sameSite, |
234 | 'secure' => $secure, |
235 | 'httponly' => $httpOnly, |
236 | ] |
237 | ); |
238 | } |
239 | |
240 | /** |
241 | * Support method for set() -- set the actual cookie in PHP. |
242 | * |
243 | * @param string $key Name of cookie to set |
244 | * @param mixed $value Value to set |
245 | * @param int $expire Cookie expiration time |
246 | * @param null|bool $httpOnly Whether the cookie should be "HTTP only" |
247 | * @param string $sameSite SameSite attribute to use (Lax, Strict or None) |
248 | * |
249 | * @return bool |
250 | */ |
251 | public function setGlobalCookie( |
252 | $key, |
253 | $value, |
254 | $expire, |
255 | $httpOnly = null, |
256 | $sameSite = null |
257 | ) { |
258 | if (null === $httpOnly) { |
259 | $httpOnly = $this->httpOnly; |
260 | } |
261 | if (null === $sameSite) { |
262 | $sameSite = $this->sameSite; |
263 | } |
264 | // Simple case: flat value. |
265 | if (!is_array($value)) { |
266 | return $this->proxySetCookie( |
267 | $key, |
268 | $value, |
269 | $expire, |
270 | $this->path, |
271 | $this->domain, |
272 | $this->secure, |
273 | $httpOnly, |
274 | $sameSite |
275 | ); |
276 | } |
277 | |
278 | // Complex case: array of values. |
279 | $success = true; |
280 | foreach ($value as $i => $curr) { |
281 | $lastSuccess = $this->proxySetCookie( |
282 | $key . '[' . $i . ']', |
283 | $curr, |
284 | $expire, |
285 | $this->path, |
286 | $this->domain, |
287 | $this->secure, |
288 | $httpOnly, |
289 | $sameSite |
290 | ); |
291 | if (!$lastSuccess) { |
292 | $success = false; |
293 | } |
294 | } |
295 | return $success; |
296 | } |
297 | |
298 | /** |
299 | * Set a cookie. |
300 | * |
301 | * @param string $key Name of cookie to set |
302 | * @param mixed $value Value to set |
303 | * @param int $expire Cookie expiration time |
304 | * @param null|bool $httpOnly Whether the cookie should be "HTTP only" |
305 | * @param string $sameSite SameSite attribute to use (Lax, Strict or None) |
306 | * |
307 | * @return bool |
308 | */ |
309 | public function set( |
310 | $key, |
311 | $value, |
312 | $expire = 0, |
313 | $httpOnly = null, |
314 | $sameSite = null |
315 | ) { |
316 | $success = $this |
317 | ->setGlobalCookie($key, $value, $expire, $httpOnly, $sameSite); |
318 | if ($success) { |
319 | $this->cookies[$key] = $value; |
320 | } |
321 | return $success; |
322 | } |
323 | |
324 | /** |
325 | * Clear a cookie. |
326 | * |
327 | * @param string $key Name of cookie to unset |
328 | * |
329 | * @return bool |
330 | */ |
331 | public function clear($key) |
332 | { |
333 | $value = $this->get($key); |
334 | if (is_array($value)) { |
335 | $success = true; |
336 | foreach (array_keys($value) as $i) { |
337 | if (!$this->clear($key . '[' . $i . ']')) { |
338 | $success = false; |
339 | } |
340 | } |
341 | return $success; |
342 | } |
343 | return $this->set($key, null, time() - 3600); |
344 | } |
345 | |
346 | /** |
347 | * Retrieve a cookie value (or null if unset). |
348 | * |
349 | * @param string $key Name of cookie to retrieve |
350 | * |
351 | * @return mixed |
352 | */ |
353 | public function get($key) |
354 | { |
355 | return $this->cookies[$key] ?? null; |
356 | } |
357 | } |