Passed
Pull Request — master (#244)
by Alexander
02:43
created

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