Passed
Push — master ( 439edc...3c5346 )
by Alexander
09:35
created

CookieCollection::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 3
rs 10
c 1
b 0
f 1
eloc 4
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Web;
6
7
use ArrayAccess;
8
use ArrayIterator;
9
use Closure;
10
use Countable;
11
use Exception;
12
use InvalidArgumentException;
13
use IteratorAggregate;
14
use Psr\Http\Message\ResponseInterface;
15
16
use function array_keys;
17
use function array_values;
18
use function array_walk;
19
use function count;
20
use function in_array;
21
22
/**
23
 * A CookieCollection helps to work with many cookies at once and to read / modify response cookies.
24
 *
25
 * @see Cookie
26
 */
27
final class CookieCollection implements IteratorAggregate, ArrayAccess, Countable
28
{
29
    /**
30
     * @var Cookie[] the cookies in this collection (indexed by the cookie name)
31
     */
32
    private array $cookies = [];
33
34
    /**
35
     * CookieCollection constructor.
36
     *
37
     * @param array $cookies the cookies that this collection initially contains.
38
     */
39 26
    public function __construct(array $cookies = [])
40
    {
41 26
        foreach ($cookies as $cookie) {
42 5
            if (!($cookie instanceof Cookie)) {
43 1
                throw new InvalidArgumentException('CookieCollection can contain only Cookie instances.');
44
            }
45
46 5
            $this->cookies[$cookie->getName()] = $cookie;
47
        }
48 26
    }
49
50
    /**
51
     * Returns the collection as a PHP array.
52
     * The array keys are cookie names, and the array values are the corresponding cookie objects.
53
     *
54
     * @return Cookie[]
55
     */
56 1
    public function toArray(): array
57
    {
58 1
        return $this->cookies;
59
    }
60
61
    /**
62
     * Returns an iterator for traversing the cookies in the collection.
63
     * This method is required by the SPL interface [[\IteratorAggregate]].
64
     * It will be implicitly called when you use `foreach` to traverse the collection.
65
     *
66
     * @return ArrayIterator
67
     */
68
    public function getIterator(): ArrayIterator
69
    {
70
        return new ArrayIterator($this->cookies);
71
    }
72
73
    /**
74
     * Returns whether there is a cookie with the specified name.
75
     * This method is required by the SPL interface [[\ArrayAccess]].
76
     * It is implicitly called when you use something like `isset($collection[$name])`.
77
     * This is equivalent to [[has()]].
78
     *
79
     * @param string $name the cookie name
80
     * @return bool whether the named cookie exists
81
     */
82 1
    public function offsetExists($name): bool
83
    {
84 1
        return $this->has($name);
85
    }
86
87
    /**
88
     * Returns the cookie with the specified name.
89
     * This method is required by the SPL interface [[\ArrayAccess]].
90
     * It is implicitly called when you use something like `$cookie = $collection[$name];`.
91
     * This is equivalent to [[get()]].
92
     *
93
     * @param string $name the cookie name
94
     * @return Cookie the cookie with the specified name, null if the named cookie does not exist.
95
     */
96 2
    public function offsetGet($name): Cookie
97
    {
98 2
        return $this->get($name);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get($name) could return the type null which is incompatible with the type-hinted return Yiisoft\Yii\Web\Cookie. Consider adding an additional type-check to rule them out.
Loading history...
99
    }
100
101
    /**
102
     * Adds the cookie to the collection.
103
     * This method is required by the SPL interface [[\ArrayAccess]].
104
     * It is implicitly called when you use something like `$collection[$name] = $cookie;`.
105
     * This is equivalent to [[add()]].
106
     *
107
     * @param string $name the cookie name
108
     * @param Cookie $cookie the cookie to be added
109
     */
110 3
    public function offsetSet($name, $cookie): void
111
    {
112 3
        $this->add($cookie);
113 3
    }
114
115
    /**
116
     * Removes the named cookie.
117
     * This method is required by the SPL interface [[\ArrayAccess]].
118
     * It is implicitly called when you use something like `unset($collection[$name])`.
119
     * This is equivalent to [[remove()]].
120
     *
121
     * @param string $name the cookie name
122
     */
123 1
    public function offsetUnset($name): void
124
    {
125 1
        $this->remove($name);
126 1
    }
127
128
    /**
129
     * Returns the number of cookies in the collection.
130
     * This method is required by the SPL `Countable` interface.
131
     * It will be implicitly called when you use `count($collection)`.
132
     *
133
     * @return int the number of cookies in the collection.
134
     */
135 7
    public function count(): int
136
    {
137 7
        return count($this->cookies);
138
    }
139
140
    /**
141
     * Returns the cookie with the specified name.
142
     *
143
     * @param string $name the cookie name
144
     * @return Cookie the cookie with the specified name. Null if the named cookie does not exist.
145
     * @see getValue()
146
     */
147 5
    public function get(string $name): ?Cookie
148
    {
149 5
        return $this->cookies[$name] ?? null;
150
    }
151
152
    /**
153
     * Returns the value of the named cookie.
154
     *
155
     * @param string $name the cookie name
156
     * @param mixed $defaultValue the value that should be returned when the named cookie does not exist.
157
     * @return string|null the value of the named cookie or the default value if cookie is not set.
158
     * @see get()
159
     */
160 1
    public function getValue(string $name, $defaultValue = null): ?string
161
    {
162 1
        return isset($this->cookies[$name]) ? $this->cookies[$name]->getValue() : $defaultValue;
163
    }
164
165
    /**
166
     * Adds a cookie to the collection.
167
     * If there is already a cookie with the same name in the collection, it will be removed first.
168
     *
169
     * @param Cookie $cookie the cookie to be added
170
     */
171 15
    public function add(Cookie $cookie): void
172
    {
173 15
        $this->cookies[$cookie->getName()] = $cookie;
174 15
    }
175
176
    /**
177
     * Returns whether there is a cookie with the specified name.
178
     *
179
     * @param string $name the cookie name
180
     * @return bool whether the named cookie exists
181
     * @see remove()
182
     */
183 2
    public function has(string $name): bool
184
    {
185 2
        return isset($this->cookies[$name]);
186
    }
187
188
    /**
189
     * Removes a cookie.
190
     *
191
     * @param string $name the name of the cookie to be removed.
192
     * @return Cookie|null cookie that is removed
193
     */
194 3
    public function remove(string $name): ?Cookie
195
    {
196 3
        if (!isset($this->cookies[$name])) {
197 2
            return null;
198
        }
199
200 2
        $removed = $this->cookies[$name];
201 2
        unset($this->cookies[$name]);
202
203 2
        return $removed;
204
    }
205
206
    /**
207
     * Removes all cookies.
208
     */
209 1
    public function clear(): void
210
    {
211 1
        $this->cookies = [];
212 1
    }
213
214
    /**
215
     * Returns whether the collection already contains the cookie.
216
     *
217
     * @param Cookie $cookie the cookie to check for
218
     * @return bool whether cookie exists
219
     * @see has()
220
     */
221 1
    public function contains(Cookie $cookie): bool
222
    {
223 1
        return in_array($cookie, $this->cookies, true);
224
    }
225
226
    /**
227
     * Tests for the existence of the cookie that satisfies the given predicate.
228
     *
229
     * @param Closure $p The predicate.
230
     * @return bool whether the predicate is true for at least on cookie.
231
     */
232 1
    public function exists(Closure $p): bool
233
    {
234 1
        foreach ($this->cookies as $name => $cookie) {
235 1
            if ($p($cookie, $name)) {
236 1
                return true;
237
            }
238
        }
239
240 1
        return false;
241
    }
242
243
    /**
244
     * Expire the cookie with the specified name
245
     *
246
     * @param string $name the cookie name
247
     */
248 1
    public function expire(string $name): void
249
    {
250 1
        if (!isset($this->cookies[$name])) {
251
            return;
252
        }
253
254 1
        $this->cookies[$name] = $this->cookies[$name]->expire();
255 1
    }
256
257
    /**
258
     * Apply user supplied function to every cookie in the collection.
259
     * If you want to modify the cookie in the collection, specify the first
260
     * parameter of Closure as reference.
261
     *
262
     * @param Closure $p
263
     */
264 1
    public function walk(Closure $p): void
265
    {
266 1
        array_walk($this->cookies, $p);
267 1
    }
268
269
    /**
270
     * Gets all keys/indices of the collection.
271
     *
272
     * @return string[] The keys/indices of the collection.
273
     */
274 1
    public function getKeys(): array
275
    {
276 1
        return array_keys($this->cookies);
277
    }
278
279
    /**
280
     * Gets all cookies of the collection as an indexed array.
281
     *
282
     * @return Cookie[] The cookies in the collection, in the order they appear in the collection.
283
     */
284 1
    public function getValues(): array
285
    {
286 1
        return array_values($this->cookies);
287
    }
288
289
    /**
290
     * Checks whether the collection is empty (contains no cookies).
291
     *
292
     * @return bool whether the collection is empty.
293
     */
294 1
    public function isEmpty(): bool
295
    {
296 1
        return empty($this->cookies);
297
    }
298
299
    /**
300
     * Populates the cookie collection from an array of 'name' => 'value' pairs.
301
     *
302
     * @param array $array the cookies to populate from
303
     * @return static collection created from array
304
     */
305 5
    public static function fromArray(array $array): self
306
    {
307 5
        if (empty($array)) {
308 1
            return new self();
309
        }
310
311
        // check if associative array with 'name' => 'value' pairs is passed
312 4
        if (count(array_filter(array_keys($array), 'is_string')) !== count($array)) {
313 3
            throw new InvalidArgumentException('Array in wrong format is passed.');
314
        }
315
316 1
        return new self(array_map(static fn ($name, $value) => new Cookie($name, $value), array_keys($array), $array));
317
    }
318
319
    /**
320
     * Adds the cookies in the collection to response and returns it.
321
     *
322
     * @param ResponseInterface $response
323
     * @return ResponseInterface response with added cookies.
324
     */
325 2
    public function addToResponse(ResponseInterface $response): ResponseInterface
326
    {
327 2
        foreach ($this->cookies as $cookie) {
328 2
            $response = $cookie->addToResponse($response);
329
        }
330
331 2
        return $response;
332
    }
333
334
    /**
335
     * Creates a copy of the response with cookies set from the collection.
336
     *
337
     * @param ResponseInterface $response
338
     * @return ResponseInterface response with new cookies.
339
     */
340 1
    public function setToResponse(ResponseInterface $response): ResponseInterface
341
    {
342 1
        $response = $response->withoutHeader('Set-Cookie');
343 1
        return $this->addToResponse($response);
344
    }
345
346
    /**
347
     * Populates the cookie collection from a ResponseInterface.
348
     *
349
     * @param ResponseInterface $response the response object to populate from
350
     * @return static collection created from response
351
     * @throws Exception
352
     */
353 1
    public static function fromResponse(ResponseInterface $response): self
354
    {
355 1
        $collection = new self();
356 1
        foreach ($response->getHeader('Set-Cookie') as $setCookieString) {
357 1
            $cookie = Cookie::fromCookieString($setCookieString);
358 1
            $collection->add($cookie);
359
        }
360 1
        return $collection;
361
    }
362
}
363