Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
25 / 25
CRAP
100.00% covered (success)
100.00%
1 / 1
Cookie
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
25 / 25
47
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
 getOptions
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 setOptions
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
11
 getInstance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 set
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getExpires
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSecure
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isHttpOnly
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSamesite
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 clear
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __set
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 __get
100.00% covered (success)
100.00%
4 / 4
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
1
 __unset
100.00% covered (success)
100.00%
3 / 3
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
 offsetGet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetUnset
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\Cookie;
15
16use ReturnTypeWillChange;
17
18/**
19 * Cookie class
20 *
21 * @category   Pop
22 * @package    Pop\Cookie
23 * @author     Nick Sagona, III <dev@nolainteractive.com>
24 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    3.3.0
27 */
28class Cookie implements \ArrayAccess, \Countable, \IteratorAggregate
29{
30
31    /**
32     * Instance of the cookie object
33     * @var Cookie
34     */
35    static private $instance;
36
37    /**
38     * Cookie IP
39     * @var string
40     */
41    private $ip = null;
42
43    /**
44     * Cookie Expiration
45     * @var int
46     */
47    private $expires = 0;
48
49    /**
50     * Cookie Path
51     * @var string
52     */
53    private $path = '/';
54
55    /**
56     * Cookie Domain
57     * @var string
58     */
59    private $domain = null;
60
61    /**
62     * Cookie Secure Flag
63     * @var boolean
64     */
65    private $secure = false;
66
67    /**
68     * Cookie HTTP Only Flag
69     * @var boolean
70     */
71    private $httponly = false;
72
73    /**
74     * Cookie SameSite Flag (None, Lax, Strict)
75     * @var boolean
76     */
77    private $samesite = 'Lax';
78
79    /**
80     * Constructor
81     *
82     * Private method to instantiate the cookie object
83     *
84     * @param  array $options
85     */
86    private function __construct(array $options = [])
87    {
88        $this->setOptions($options);
89    }
90
91    /**
92     * Method to create options array
93     *
94     * @return array
95     */
96    public function getOptions()
97    {
98        return [
99            'expires'  => $this->expires,
100            'path'     => $this->path,
101            'domain'   => $this->domain,
102            'secure'   => $this->secure,
103            'httponly' => $this->httponly,
104            'samesite' => $this->samesite
105        ];
106    }
107
108    /**
109     * Private method to set options
110     *
111     * @param  array $options
112     * @return Cookie
113     */
114    public function setOptions(array $options = [])
115    {
116        // Set the cookie owner's IP address and domain.
117        $this->ip     = $_SERVER['REMOTE_ADDR'];
118        $this->domain = (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']);
119
120        if (isset($options['expires'])) {
121            $this->expires = (int)$options['expires'];
122        }
123        if (isset($options['path'])) {
124            $this->path = $options['path'];
125        }
126        if (isset($options['domain'])) {
127            $this->domain = $options['domain'];
128        }
129        if (isset($options['secure'])) {
130            $this->secure = (bool)$options['secure'];
131        }
132        if (isset($options['httponly'])) {
133            $this->httponly = (bool)$options['httponly'];
134        }
135        if (isset($options['samesite'])) {
136            if (($options['samesite'] == 'None') || ($options['samesite'] == 'Lax') || ($options['samesite'] == 'Strict')) {
137                $this->samesite = $options['samesite'];
138            }
139
140        }
141
142        return $this;
143    }
144
145    /**
146     * Determine whether or not an instance of the cookie object exists
147     * already, and instantiate the object if it does not exist.
148     *
149     * @param  array $options
150     * @return Cookie
151     */
152    public static function getInstance(array $options = [])
153    {
154        if (empty(self::$instance)) {
155            self::$instance = new Cookie($options);
156        }
157
158        return self::$instance;
159    }
160
161    /**
162     * Set a cookie
163     *
164     * @param  string  $name
165     * @param  mixed   $value
166     * @param  array   $options
167     * @return Cookie
168     */
169    public function set($name, $value, array $options = null)
170    {
171        if (null !== $options) {
172            $this->setOptions($options);
173        }
174
175        if (!is_string($value) && !is_numeric($value)) {
176            $value = json_encode($value);
177        }
178
179        setcookie($name, $value, $this->getOptions());
180        return $this;
181    }
182
183    /**
184     * Return the current cookie expiration
185     *
186     * @return int
187     */
188    public function getExpires()
189    {
190        return $this->expires;
191    }
192
193    /**
194     * Return the current cookie path.
195     *
196     * @return string
197     */
198    public function getPath()
199    {
200        return $this->path;
201    }
202
203    /**
204     * Return the current cookie domain
205     *
206     * @return string
207     */
208    public function getDomain()
209    {
210        return $this->domain;
211    }
212
213    /**
214     * Return if the cookie is secure
215     *
216     * @return boolean
217     */
218    public function isSecure()
219    {
220        return $this->secure;
221    }
222
223    /**
224     * Return if the cookie is HTTP only
225     *
226     * @return boolean
227     */
228    public function isHttpOnly()
229    {
230        return $this->httponly;
231    }
232
233    /**
234     * Return if the cookie's samesite flag
235     *
236     * @return string
237     */
238    public function getSamesite()
239    {
240        return $this->samesite;
241    }
242
243    /**
244     * Return the current IP address.
245     *
246     * @return string
247     */
248    public function getIp()
249    {
250        return $this->ip;
251    }
252
253    /**
254     * Delete a cookie
255     *
256     * @param  string $name
257     * @param  array  $options
258     * @return void
259     */
260    public function delete($name, array $options = null)
261    {
262        if (null !== $options) {
263            $this->setOptions($options);
264        }
265        if (isset($_COOKIE[$name])) {
266            $this->expires = time() - 3600;
267            setcookie($name, $_COOKIE[$name], $this->getOptions());
268        }
269    }
270
271    /**
272     * Clear (delete) all cookies
273     *
274     * @param  array $options
275     * @return void
276     */
277    public function clear(array $options = null)
278    {
279        if (null !== $options) {
280            $this->setOptions($options);
281        }
282
283        $this->expires = time() - 3600;
284
285        foreach ($_COOKIE as $name => $value) {
286            if (isset($_COOKIE[$name])) {
287                setcookie($name, $_COOKIE[$name], $this->getOptions());
288            }
289        }
290    }
291
292    /**
293     * Method to get the count of cookie data
294     *
295     * @return int
296     */
297    public function count(): int
298    {
299        return count($this->toArray());
300    }
301    /**
302     * Method to iterate over the cookie
303     *
304     * @return \ArrayIterator
305     */
306    public function getIterator(): \ArrayIterator
307    {
308        return new \ArrayIterator($this->toArray());
309    }
310    /**
311     * Get the cookie values as an array
312     *
313     * @return array
314     */
315    public function toArray()
316    {
317        return $_COOKIE;
318    }
319
320    /**
321     * Set method to set the value of the $_COOKIE global variable
322     *
323     * @param  string $name
324     * @param  mixed $value
325     * @return void
326     */
327    public function __set($name, $value)
328    {
329        $options = [
330            'expires'  => $this->expires,
331            'path'     => $this->path,
332            'domain'   => $this->domain,
333            'secure'   => $this->secure,
334            'httponly' => $this->httponly
335        ];
336        $this->set($name, $value, $options);
337    }
338
339    /**
340     * Get method to return the value of the $_COOKIE global variable
341     *
342     * @param  string $name
343     * @return mixed
344     */
345    public function __get($name)
346    {
347        $value = null;
348        if (isset($_COOKIE[$name])) {
349            $value = (substr($_COOKIE[$name], 0, 1) == '{') ? json_decode($_COOKIE[$name], true) : $_COOKIE[$name];
350        }
351        return $value;
352    }
353
354    /**
355     * Return the isset value of the $_COOKIE global variable
356     *
357     * @param  string $name
358     * @return boolean
359     */
360    public function __isset($name)
361    {
362        return isset($_COOKIE[$name]);
363    }
364
365    /**
366     * Unset the value in the $_COOKIE global variable
367     *
368     * @param  string $name
369     * @return void
370     */
371    public function __unset($name)
372    {
373        if (isset($_COOKIE[$name])) {
374            $this->expires = time() - 3600;
375            setcookie($name, $_COOKIE[$name], $this->getOptions());
376        }
377    }
378
379    /**
380     * ArrayAccess offsetSet
381     *
382     * @param  mixed $offset
383     * @param  mixed $value
384     * @return void
385     */
386    #[ReturnTypeWillChange]
387    public function offsetSet($offset, $value)
388    {
389        $this->__set($offset, $value);
390    }
391
392    /**
393     * ArrayAccess offsetGet
394     *
395     * @param  mixed $offset
396     * @return mixed
397     */
398    #[ReturnTypeWillChange]
399    public function offsetGet($offset)
400    {
401        return $this->__get($offset);
402    }
403
404    /**
405     * ArrayAccess offsetExists
406     *
407     * @param  mixed $offset
408     * @return boolean
409     */
410    public function offsetExists($offset): bool
411    {
412        return $this->__isset($offset);
413    }
414
415    /**
416     * ArrayAccess offsetUnset
417     *
418     * @param  mixed $offset
419     * @return void
420     */
421    #[ReturnTypeWillChange]
422    public function offsetUnset($offset)
423    {
424        $this->__unset($offset);
425    }
426
427}