Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
CookieJar
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
8 / 8
16
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 lock
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isLocked
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 set
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 remove
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 save
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\DataStorage\Cookie
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\DataStorage\Cookie;
16
17use phpOMS\DataStorage\LockException;
18
19/**
20 * CookieJar class
21 *
22 * @package phpOMS\DataStorage\Cookie
23 * @license OMS License 2.0
24 * @link    https://jingga.app
25 * @since   1.0.0
26 */
27final class CookieJar
28{
29    /**
30     * Locked.
31     *
32     * @var bool
33     * @since 1.0.0
34     */
35    private static bool $isLocked = false;
36
37    /**
38     * Cookie values.
39     *
40     * @var array
41     * @since 1.0.0
42     */
43    private array $cookies = [];
44
45    /**
46     * Constructor.
47     *
48     * @since  1.0.0
49     *
50     * @SuppressWarnings(PHPMD.Superglobals)
51     */
52    public function __construct()
53    {
54        $this->cookies = $_COOKIE;
55    }
56
57    /**
58     * Lock
59     *
60     * @return void
61     *
62     * @since 1.0.0
63     */
64    public static function lock() : void
65    {
66        self::$isLocked = true;
67    }
68
69    /**
70     * Is locked?
71     *
72     * @return bool
73     *
74     * @since 1.0.0
75     */
76    public static function isLocked() : bool
77    {
78        return self::$isLocked;
79    }
80
81    /**
82     * Set pending cookie
83     *
84     * @param string $id        Cookie id
85     * @param mixed  $value     Cookie value
86     * @param int    $expire    Expire time
87     * @param string $path      Path
88     * @param string $domain    Domain
89     * @param bool   $secure    Is secure
90     * @param bool   $httpOnly  Allow only http access
91     * @param bool   $overwrite Overwrite if already set
92     *
93     * @return bool
94     *
95     * @since 1.0.0
96     */
97    public function set(
98        string $id,
99        mixed $value,
100        int $expire = 86400,
101        string $path = '/',
102        string $domain = null,
103        bool $secure = false,
104        bool $httpOnly = true,
105        bool $overwrite = true
106    ) : bool
107    {
108        if ($overwrite || !isset($this->cookies[$id])) {
109            $this->cookies[$id] = [
110                'value'    => $value,
111                'expires'  => $expire,
112                'path'     => $path,
113                'domain'   => $domain,
114                'secure'   => $secure,
115                'httponly' => $httpOnly,
116            ];
117
118            return true;
119        }
120
121        return false;
122    }
123
124    /**
125     * Get cookie value
126     *
127     * @param string $id Cookie id
128     *
129     * @return mixed
130     *
131     * @since 1.0.0
132     */
133    public function get(string $id) : mixed
134    {
135        return $this->cookies[$id] ?? null;
136    }
137
138    /**
139     * Delete already set cookie
140     *
141     * @param string $id Cookie id to remove
142     *
143     * @return bool
144     *
145     * @throws LockException Throws this exception if the cookie is already sent
146     *
147     * @since 1.0.0
148     */
149    public function delete(string $id) : bool
150    {
151        if ($this->remove($id)) {
152            if (self::$isLocked) {
153                throw new LockException('CookieJar');
154            }
155
156            // @codeCoverageIgnoreStart
157            if (!\headers_sent()) {
158                \setcookie($id, '', \time() - 3600);
159
160                return true;
161            }
162
163            return false;
164            // @codeCoverageIgnoreEnd
165        }
166
167        return false;
168    }
169
170    /**
171     * Remove pending cookie
172     *
173     * @param string $id Cookie id to remove
174     *
175     * @return bool
176     *
177     * @since 1.0.0
178     */
179    public function remove(string $id) : bool
180    {
181        if (isset($this->cookies[$id])) {
182            unset($this->cookies[$id]);
183
184            return true;
185        }
186
187        return false;
188    }
189
190    /**
191     * Save cookie
192     *
193     * @return void
194     *
195     * @throws LockException Throws this exception if the cookie is already sent
196     *
197     * @since 1.0.0
198     */
199    public function save() : void
200    {
201        if (self::$isLocked) {
202            throw new LockException('CookieJar');
203        }
204
205        // @codeCoverageIgnoreStart
206        foreach ($this->cookies as $key => $cookie) {
207            \setcookie($key, $cookie['value'], [
208                'expires'  => $cookie['expires'],
209                'path'     => $cookie['path'],
210                'domain'   => $cookie['domain'],
211                'secure'   => $cookie['secure'],
212                'httponly' => $cookie['httponly'],
213                'samesite' => 'Strict',
214            ]);
215        }
216        // @codeCoverageIgnoreEnd
217    }
218}