Passed
Pull Request — master (#244)
by
unknown
03:18
created

CookieCollection::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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