Completed
Push — master ( 6ffe75...72d7e2 )
by Márk
06:57
created

Cookie::createWithoutValidation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 14
nc 1
nop 8
crap 1

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            $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
     * @param \DateTime|null $expires
230
     *
231
     * @return Cookie
232
     */
233 1
    public function withExpires(\DateTime $expires = null)
234
    {
235 1
        $new = clone $this;
236 1
        $new->expires = $expires;
237
238 1
        return $new;
239
    }
240
241
    /**
242
     * Checks if the cookie is expired.
243
     *
244
     * @return bool
245
     */
246 2
    public function isExpired()
247
    {
248 2
        return isset($this->expires) and $this->expires < new \DateTime();
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
249
    }
250
251
    /**
252
     * Returns the domain.
253
     *
254
     * @return string|null
255
     */
256 3
    public function getDomain()
257
    {
258 3
        return $this->domain;
259
    }
260
261
    /**
262
     * Checks if there is a domain.
263
     *
264
     * @return bool
265
     */
266 4
    public function hasDomain()
267
    {
268 4
        return isset($this->domain);
269
    }
270
271
    /**
272
     * Sets the domain.
273
     *
274
     * @param string|null $domain
275
     *
276
     * @return Cookie
277
     */
278 1
    public function withDomain($domain)
279
    {
280 1
        $new = clone $this;
281 1
        $new->domain = $this->normalizeDomain($domain);
282
283 1
        return $new;
284
    }
285
286
    /**
287
     * Checks whether this cookie is meant for this domain.
288
     *
289
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
290
     *
291
     * @param string $domain
292
     *
293
     * @return bool
294
     */
295 2
    public function matchDomain($domain)
296
    {
297
        // Domain is not set or exact match
298 2
        if (!$this->hasDomain() || strcasecmp($domain, $this->domain) === 0) {
299 2
            return true;
300
        }
301
302
        // Domain is not an IP address
303 1
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
304 1
            return false;
305
        }
306
307 1
        return (bool) preg_match(sprintf('/\b%s$/i', preg_quote($this->domain)), $domain);
308
    }
309
310
    /**
311
     * Returns the path.
312
     *
313
     * @return string
314
     */
315 2
    public function getPath()
316
    {
317 2
        return $this->path;
318
    }
319
320
    /**
321
     * Sets the path.
322
     *
323
     * @param string|null $path
324
     *
325
     * @return Cookie
326
     */
327 1
    public function withPath($path)
328
    {
329 1
        $new = clone $this;
330 1
        $new->path = $this->normalizePath($path);
331
332 1
        return $new;
333
    }
334
335
    /**
336
     * Checks whether this cookie is meant for this path.
337
     *
338
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
339
     *
340
     * @param string $path
341
     *
342
     * @return bool
343
     */
344 3
    public function matchPath($path)
345
    {
346 3
        return $this->path === $path || (strpos($path, rtrim($this->path, '/').'/') === 0);
347
    }
348
349
    /**
350
     * Checks whether this cookie may only be sent over HTTPS.
351
     *
352
     * @return bool
353
     */
354 2
    public function isSecure()
355
    {
356 2
        return $this->secure;
357
    }
358
359
    /**
360
     * Sets whether this cookie should only be sent over HTTPS.
361
     *
362
     * @param bool $secure
363
     *
364
     * @return Cookie
365
     */
366 1
    public function withSecure($secure)
367
    {
368 1
        $new = clone $this;
369 1
        $new->secure = (bool) $secure;
370
371 1
        return $new;
372
    }
373
374
    /**
375
     * Check whether this cookie may not be accessed through Javascript.
376
     *
377
     * @return bool
378
     */
379 2
    public function isHttpOnly()
380
    {
381 2
        return $this->httpOnly;
382
    }
383
384
    /**
385
     * Sets whether this cookie may not be accessed through Javascript.
386
     *
387
     * @param bool $httpOnly
388
     *
389
     * @return Cookie
390
     */
391 1
    public function withHttpOnly($httpOnly)
392
    {
393 1
        $new = clone $this;
394 1
        $new->httpOnly = (bool) $httpOnly;
395
396 1
        return $new;
397
    }
398
399
    /**
400
     * Checks if this cookie represents the same cookie as $cookie.
401
     *
402
     * This does not compare the values, only name, domain and path.
403
     *
404
     * @param Cookie $cookie
405
     *
406
     * @return bool
407
     */
408 10
    public function match(Cookie $cookie)
409
    {
410 10
        return $this->name === $cookie->name && $this->domain === $cookie->domain and $this->path === $cookie->path;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
411
    }
412
413
    /**
414
     * Validates cookie attributes.
415
     *
416
     * @return bool
417
     */
418 4
    public function isValid()
419
    {
420
        try {
421 4
            $this->validateName($this->name);
422 3
            $this->validateValue($this->value);
423 2
            $this->validateMaxAge($this->maxAge);
424 4
        } catch (\InvalidArgumentException $e) {
425 3
            return false;
426
        }
427
428 1
        return true;
429
    }
430
431
    /**
432
     * Validates the name attribute.
433
     *
434
     * @see http://tools.ietf.org/search/rfc2616#section-2.2
435
     *
436
     * @param string $name
437
     *
438
     * @throws \InvalidArgumentException If the name is empty or contains invalid characters.
439
     */
440 61
    private function validateName($name)
441
    {
442 61
        if (strlen($name) < 1) {
443 1
            throw new \InvalidArgumentException('The name cannot be empty');
444
        }
445
446
        // Name attribute is a token as per spec in RFC 2616
447 60
        if (preg_match('/[\x00-\x20\x22\x28-\x29\x2C\x2F\x3A-\x40\x5B-\x5D\x7B\x7D\x7F]/', $name)) {
448 4
            throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
449
        }
450 57
    }
451
452
    /**
453
     * Validates a value.
454
     *
455
     * @see http://tools.ietf.org/html/rfc6265#section-4.1.1
456
     *
457
     * @param string|null $value
458
     *
459
     * @throws \InvalidArgumentException If the value contains invalid characters.
460
     */
461 57
    private function validateValue($value)
462
    {
463 57
        if (isset($value)) {
464 52
            if (preg_match('/[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/', $value)) {
465 7
                throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value));
466
            }
467 48
        }
468 54
    }
469
470
    /**
471
     * Validates a Max-Age attribute.
472
     *
473
     * @param int|null $maxAge
474
     *
475
     * @throws \InvalidArgumentException If the Max-Age is not an empty or integer value.
476
     */
477 54
    private function validateMaxAge($maxAge)
478
    {
479 54
        if (isset($maxAge)) {
480 8
            if (!is_int($maxAge)) {
481 2
                throw new \InvalidArgumentException('Max-Age must be integer');
482
            }
483 6
        }
484 54
    }
485
486
    /**
487
     * Remove the leading '.' and lowercase the domain as per spec in RFC 6265.
488
     *
489
     * @see http://tools.ietf.org/html/rfc6265#section-4.1.2.3
490
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.3
491
     * @see http://tools.ietf.org/html/rfc6265#section-5.2.3
492
     *
493
     * @param string|null $domain
494
     *
495
     * @return string
496
     */
497 54
    private function normalizeDomain($domain)
498
    {
499 54
        if (isset($domain)) {
500 6
            $domain = ltrim(strtolower($domain), '.');
501 6
        }
502
503 54
        return $domain;
504
    }
505
506
    /**
507
     * Processes path as per spec in RFC 6265.
508
     *
509
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
510
     * @see http://tools.ietf.org/html/rfc6265#section-5.2.4
511
     *
512
     * @param string|null $path
513
     *
514
     * @return string
515
     */
516 54
    private function normalizePath($path)
517
    {
518 54
        $path = rtrim($path, '/');
519
520 54
        if (empty($path) or substr($path, 0, 1) !== '/') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
521 53
            $path = '/';
522 53
        }
523
524 54
        return $path;
525
    }
526
}
527