Cookie::createWithoutValidation()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
nc 1
nop 8
dl 0
loc 16
ccs 6
cts 6
cp 1
crap 1
rs 10
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Http\Message;
4
5
/**
6
 * Cookie Value Object.
7
 *
8
 * @author Márk Sági-Kazár <[email protected]>
9
 *
10
 * @see http://tools.ietf.org/search/rfc6265
11
 */
12
final class Cookie
13
{
14
    /**
15
     * @var string
16
     */
17
    private $name;
18
19
    /**
20
     * @var string|null
21
     */
22
    private $value;
23
24
    /**
25
     * @var int|null
26
     */
27
    private $maxAge;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $domain;
33
34
    /**
35
     * @var string
36
     */
37
    private $path;
38
39
    /**
40
     * @var bool
41
     */
42
    private $secure;
43
44
    /**
45
     * @var bool
46
     */
47
    private $httpOnly;
48
49
    /**
50
     * Expires attribute is HTTP 1.0 only and should be avoided.
51
     *
52
     * @var \DateTime|null
53
     */
54
    private $expires;
55
56
    /**
57
     * @param string         $name
58
     * @param string|null    $value
59
     * @param int|null       $maxAge
60
     * @param string|null    $domain
61
     * @param string|null    $path
62
     * @param bool           $secure
63
     * @param bool           $httpOnly
64
     * @param \DateTime|null $expires  Expires attribute is HTTP 1.0 only and should be avoided.
65
     *
66
     * @throws \InvalidArgumentException if name, value or max age is not valid
67
     */
68 61
    public function __construct(
69
        $name,
70
        $value = null,
71
        $maxAge = null,
72
        $domain = null,
73
        $path = null,
74
        $secure = false,
75
        $httpOnly = false,
76
        \DateTime $expires = null
77
    ) {
78 61
        $this->validateName($name);
79 57
        $this->validateValue($value);
80 54
        $this->validateMaxAge($maxAge);
81
82 54
        $this->name = $name;
83 54
        $this->value = $value;
84 54
        $this->maxAge = $maxAge;
85 54
        $this->expires = $expires;
86 54
        $this->domain = $this->normalizeDomain($domain);
87 54
        $this->path = $this->normalizePath($path);
88 54
        $this->secure = (bool) $secure;
89 54
        $this->httpOnly = (bool) $httpOnly;
90 54
    }
91
92
    /**
93
     * Creates a new cookie without any attribute validation.
94
     *
95
     * @param string         $name
96
     * @param string|null    $value
97
     * @param int            $maxAge
98
     * @param string|null    $domain
99
     * @param string|null    $path
100
     * @param bool           $secure
101
     * @param bool           $httpOnly
102
     * @param \DateTime|null $expires  Expires attribute is HTTP 1.0 only and should be avoided.
103
     */
104 3
    public static function createWithoutValidation(
105
        $name,
106
        $value = null,
107
        $maxAge = null,
108
        $domain = null,
109
        $path = null,
110
        $secure = false,
111
        $httpOnly = false,
112
        \DateTime $expires = null
113
    ) {
114 3
        $cookie = new self('name', null, null, $domain, $path, $secure, $httpOnly, $expires);
115 3
        $cookie->name = $name;
116 3
        $cookie->value = $value;
117 3
        $cookie->maxAge = $maxAge;
118
119 3
        return $cookie;
120
    }
121
122
    /**
123
     * Returns the name.
124
     *
125
     * @return string
126
     */
127 2
    public function getName()
128
    {
129 2
        return $this->name;
130
    }
131
132
    /**
133
     * Returns the value.
134
     *
135
     * @return string|null
136
     */
137 5
    public function getValue()
138
    {
139 5
        return $this->value;
140
    }
141
142
    /**
143
     * Checks if there is a value.
144
     *
145
     * @return bool
146
     */
147 13
    public function hasValue()
148
    {
149 13
        return isset($this->value);
150
    }
151
152
    /**
153
     * Sets the value.
154
     *
155
     * @param string|null $value
156
     *
157
     * @return Cookie
158
     */
159 8
    public function withValue($value)
160
    {
161 8
        $this->validateValue($value);
162
163 5
        $new = clone $this;
164 5
        $new->value = $value;
165
166 5
        return $new;
167
    }
168
169
    /**
170
     * Returns the max age.
171
     *
172
     * @return int|null
173
     */
174 3
    public function getMaxAge()
175
    {
176 3
        return $this->maxAge;
177
    }
178
179
    /**
180
     * Checks if there is a max age.
181
     *
182
     * @return bool
183
     */
184 1
    public function hasMaxAge()
185
    {
186 1
        return isset($this->maxAge);
187
    }
188
189
    /**
190
     * Sets the max age.
191
     *
192
     * @param int|null $maxAge
193
     *
194
     * @return Cookie
195
     */
196 2
    public function withMaxAge($maxAge)
197
    {
198 2
        $this->validateMaxAge($maxAge);
199
200 1
        $new = clone $this;
201 1
        $new->maxAge = $maxAge;
202
203 1
        return $new;
204
    }
205
206
    /**
207
     * Returns the expiration time.
208
     *
209
     * @return \DateTime|null
210
     */
211 3
    public function getExpires()
212
    {
213 3
        return $this->expires;
214
    }
215
216
    /**
217
     * Checks if there is an expiration time.
218
     *
219
     * @return bool
220
     */
221 2
    public function hasExpires()
222
    {
223 2
        return isset($this->expires);
224
    }
225
226
    /**
227
     * Sets the expires.
228
     *
229
     * @return Cookie
230
     */
231
    public function withExpires(\DateTime $expires = null)
232
    {
233 1
        $new = clone $this;
234
        $new->expires = $expires;
235 1
236 1
        return $new;
237
    }
238 1
239
    /**
240
     * Checks if the cookie is expired.
241
     *
242
     * @return bool
243
     */
244
    public function isExpired()
245
    {
246 2
        return isset($this->expires) and $this->expires < new \DateTime();
247
    }
248 2
249
    /**
250
     * Returns the domain.
251
     *
252
     * @return string|null
253
     */
254
    public function getDomain()
255
    {
256 3
        return $this->domain;
257
    }
258 3
259
    /**
260
     * Checks if there is a domain.
261
     *
262
     * @return bool
263
     */
264
    public function hasDomain()
265
    {
266 4
        return isset($this->domain);
267
    }
268 4
269
    /**
270
     * Sets the domain.
271
     *
272
     * @param string|null $domain
273
     *
274
     * @return Cookie
275
     */
276
    public function withDomain($domain)
277
    {
278 1
        $new = clone $this;
279
        $new->domain = $this->normalizeDomain($domain);
280 1
281 1
        return $new;
282
    }
283 1
284
    /**
285
     * Checks whether this cookie is meant for this domain.
286
     *
287
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
288
     *
289
     * @param string $domain
290
     *
291
     * @return bool
292
     */
293
    public function matchDomain($domain)
294
    {
295 2
        // Domain is not set or exact match
296
        if (!$this->hasDomain() || 0 === strcasecmp($domain, $this->domain)) {
297
            return true;
298 2
        }
299 2
300
        // Domain is not an IP address
301
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
302
            return false;
303 1
        }
304 1
305
        return (bool) preg_match(sprintf('/\b%s$/i', preg_quote($this->domain)), $domain);
306
    }
307 1
308
    /**
309
     * Returns the path.
310
     *
311
     * @return string
312
     */
313
    public function getPath()
314
    {
315 2
        return $this->path;
316
    }
317 2
318
    /**
319
     * Sets the path.
320
     *
321
     * @param string|null $path
322
     *
323
     * @return Cookie
324
     */
325
    public function withPath($path)
326
    {
327 1
        $new = clone $this;
328
        $new->path = $this->normalizePath($path);
329 1
330 1
        return $new;
331
    }
332 1
333
    /**
334
     * Checks whether this cookie is meant for this path.
335
     *
336
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
337
     *
338
     * @param string $path
339
     *
340
     * @return bool
341
     */
342
    public function matchPath($path)
343
    {
344 3
        return $this->path === $path || (0 === strpos($path, rtrim($this->path, '/').'/'));
345
    }
346 3
347
    /**
348
     * Checks whether this cookie may only be sent over HTTPS.
349
     *
350
     * @return bool
351
     */
352
    public function isSecure()
353
    {
354 2
        return $this->secure;
355
    }
356 2
357
    /**
358
     * Sets whether this cookie should only be sent over HTTPS.
359
     *
360
     * @param bool $secure
361
     *
362
     * @return Cookie
363
     */
364
    public function withSecure($secure)
365
    {
366 1
        $new = clone $this;
367
        $new->secure = (bool) $secure;
368 1
369 1
        return $new;
370
    }
371 1
372
    /**
373
     * Check whether this cookie may not be accessed through Javascript.
374
     *
375
     * @return bool
376
     */
377
    public function isHttpOnly()
378
    {
379 2
        return $this->httpOnly;
380
    }
381 2
382
    /**
383
     * Sets whether this cookie may not be accessed through Javascript.
384
     *
385
     * @param bool $httpOnly
386
     *
387
     * @return Cookie
388
     */
389
    public function withHttpOnly($httpOnly)
390
    {
391 1
        $new = clone $this;
392
        $new->httpOnly = (bool) $httpOnly;
393 1
394 1
        return $new;
395
    }
396 1
397
    /**
398
     * Checks if this cookie represents the same cookie as $cookie.
399
     *
400
     * This does not compare the values, only name, domain and path.
401
     *
402
     * @param Cookie $cookie
403
     *
404
     * @return bool
405
     */
406
    public function match(self $cookie)
407
    {
408 10
        return $this->name === $cookie->name && $this->domain === $cookie->domain and $this->path === $cookie->path;
409
    }
410 10
411
    /**
412
     * Validates cookie attributes.
413
     *
414
     * @return bool
415
     */
416
    public function isValid()
417
    {
418 4
        try {
419
            $this->validateName($this->name);
420
            $this->validateValue($this->value);
421 4
            $this->validateMaxAge($this->maxAge);
422 3
        } catch (\InvalidArgumentException $e) {
423 2
            return false;
424 3
        }
425 3
426
        return true;
427
    }
428 1
429
    /**
430
     * Validates the name attribute.
431
     *
432
     * @see http://tools.ietf.org/search/rfc2616#section-2.2
433
     *
434
     * @param string $name
435
     *
436
     * @throws \InvalidArgumentException if the name is empty or contains invalid characters
437
     */
438
    private function validateName($name)
439
    {
440 61
        if (strlen($name) < 1) {
441
            throw new \InvalidArgumentException('The name cannot be empty');
442 61
        }
443 1
444
        // Name attribute is a token as per spec in RFC 2616
445
        if (preg_match('/[\x00-\x20\x22\x28-\x29\x2C\x2F\x3A-\x40\x5B-\x5D\x7B\x7D\x7F]/', $name)) {
446
            throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
447 60
        }
448 4
    }
449
450 57
    /**
451
     * Validates a value.
452
     *
453
     * @see http://tools.ietf.org/html/rfc6265#section-4.1.1
454
     *
455
     * @param string|null $value
456
     *
457
     * @throws \InvalidArgumentException if the value contains invalid characters
458
     */
459
    private function validateValue($value)
460
    {
461 57
        if (isset($value)) {
462
            if (preg_match('/[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/', $value)) {
463 57
                throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value));
464 52
            }
465 7
        }
466
    }
467
468 54
    /**
469
     * Validates a Max-Age attribute.
470
     *
471
     * @param int|null $maxAge
472
     *
473
     * @throws \InvalidArgumentException if the Max-Age is not an empty or integer value
474
     */
475
    private function validateMaxAge($maxAge)
476
    {
477 54
        if (isset($maxAge)) {
478
            if (!is_int($maxAge)) {
0 ignored issues
show
introduced by
The condition is_int($maxAge) is always true.
Loading history...
479 54
                throw new \InvalidArgumentException('Max-Age must be integer');
480 8
            }
481 2
        }
482
    }
483
484 54
    /**
485
     * Remove the leading '.' and lowercase the domain as per spec in RFC 6265.
486
     *
487
     * @see http://tools.ietf.org/html/rfc6265#section-4.1.2.3
488
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
489
     * @see http://tools.ietf.org/html/rfc6265#section-5.2.3
490
     *
491
     * @param string|null $domain
492
     *
493
     * @return string
494
     */
495
    private function normalizeDomain($domain)
496
    {
497 54
        if (isset($domain)) {
498
            $domain = ltrim(strtolower($domain), '.');
499 54
        }
500 6
501
        return $domain;
502
    }
503 54
504
    /**
505
     * Processes path as per spec in RFC 6265.
506
     *
507
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
508
     * @see http://tools.ietf.org/html/rfc6265#section-5.2.4
509
     *
510
     * @param string|null $path
511
     *
512
     * @return string
513
     */
514
    private function normalizePath($path)
515
    {
516 54
        $path = rtrim($path, '/');
517
518 54
        if (empty($path) or '/' !== substr($path, 0, 1)) {
519
            $path = '/';
520 54
        }
521 53
522
        return $path;
523
    }
524
}
525