Completed
Pull Request — master (#68)
by Antonio J.
07:16
created

Cookie::createWithoutValidation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
ccs 4
cts 4
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 57
    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 57
        $this->validateName($name);
79 53
        $this->validateValue($value);
80 50
        $this->validateMaxAge($maxAge);
81
82 50
        $this->name = $name;
83 50
        $this->value = $value;
84 50
        $this->maxAge = $maxAge;
85 50
        $this->expires = $expires;
86 50
        $this->domain = $this->normalizeDomain($domain);
87 50
        $this->path = $this->normalizePath($path);
88 50
        $this->secure = (bool) $secure;
89 50
        $this->httpOnly = (bool) $httpOnly;
90 50
    }
91
92
    /**
93
     * Creates a new cookie without any attribute validation.
94
     *
95
     * @param string         $name
96
     * @param string|null    $value
97 2
     * @param int            $maxAge
98
     * @param string|null    $domain
99 2
     * @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
    public static function createWithoutValidation(
105
        $name,
106
        $value = null,
107 5
        $maxAge = null,
108
        $domain = null,
109 5
        $path = null,
110
        $secure = false,
111
        $httpOnly = false,
112
        \DateTime $expires = null
113
    ) {
114
        $cookie = new self('name', null, null, $domain, $path, $secure, $httpOnly, $expires);
115
        $cookie->name = $name;
116
        $cookie->value = $value;
117 13
        $cookie->maxAge = $maxAge;
118
119 13
        return $cookie;
120
    }
121
122
    /**
123
     * Returns the name.
124
     *
125
     * @return string
126
     */
127
    public function getName()
128
    {
129 8
        return $this->name;
130
    }
131 8
132
    /**
133 5
     * Returns the value.
134 5
     *
135
     * @return string|null
136 5
     */
137
    public function getValue()
138
    {
139
        return $this->value;
140
    }
141
142
    /**
143
     * Checks if there is a value.
144 3
     *
145
     * @return bool
146 3
     */
147
    public function hasValue()
148
    {
149
        return isset($this->value);
150
    }
151
152
    /**
153
     * Sets the value.
154 1
     *
155
     * @param string|null $value
156 1
     *
157
     * @return Cookie
158
     */
159
    public function withValue($value)
160
    {
161
        $this->validateValue($value);
162
163
        $new = clone $this;
164
        $new->value = $value;
165
166 2
        return $new;
167
    }
168 2
169
    /**
170 1
     * Returns the max age.
171 1
     *
172
     * @return int|null
173 1
     */
174
    public function getMaxAge()
175
    {
176
        return $this->maxAge;
177
    }
178
179
    /**
180
     * Checks if there is a max age.
181 3
     *
182
     * @return bool
183 3
     */
184
    public function hasMaxAge()
185
    {
186
        return isset($this->maxAge);
187
    }
188
189
    /**
190
     * Sets the max age.
191 2
     *
192
     * @param int|null $maxAge
193 2
     *
194
     * @return Cookie
195
     */
196
    public function withMaxAge($maxAge)
197
    {
198
        $this->validateMaxAge($maxAge);
199
200
        $new = clone $this;
201
        $new->maxAge = $maxAge;
202
203 1
        return $new;
204
    }
205 1
206 1
    /**
207
     * Returns the expiration time.
208 1
     *
209
     * @return \DateTime|null
210
     */
211
    public function getExpires()
212
    {
213
        return $this->expires;
214
    }
215
216 2
    /**
217
     * Checks if there is an expiration time.
218 2
     *
219
     * @return bool
220
     */
221
    public function hasExpires()
222
    {
223
        return isset($this->expires);
224
    }
225
226 3
    /**
227
     * Sets the expires.
228 3
     *
229
     * @param \DateTime|null $expires
230
     *
231
     * @return Cookie
232
     */
233
    public function withExpires(\DateTime $expires = null)
234
    {
235
        $new = clone $this;
236 4
        $new->expires = $expires;
237
238 4
        return $new;
239
    }
240
241
    /**
242
     * Checks if the cookie is expired.
243
     *
244
     * @return bool
245
     */
246
    public function isExpired()
247
    {
248 1
        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 1
251 1
    /**
252
     * Returns the domain.
253 1
     *
254
     * @return string|null
255
     */
256
    public function getDomain()
257
    {
258
        return $this->domain;
259
    }
260
261
    /**
262
     * Checks if there is a domain.
263
     *
264
     * @return bool
265 2
     */
266
    public function hasDomain()
267
    {
268 2
        return isset($this->domain);
269 2
    }
270
271
    /**
272
     * Sets the domain.
273 1
     *
274 1
     * @param string|null $domain
275
     *
276
     * @return Cookie
277 1
     */
278
    public function withDomain($domain)
279
    {
280
        $new = clone $this;
281
        $new->domain = $this->normalizeDomain($domain);
282
283
        return $new;
284
    }
285 2
286
    /**
287 2
     * 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
    public function matchDomain($domain)
296
    {
297 1
        // Domain is not set or exact match
298
        if (!$this->hasDomain() || strcasecmp($domain, $this->domain) === 0) {
299 1
            return true;
300 1
        }
301
302 1
        // Domain is not an IP address
303
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
304
            return false;
305
        }
306
307
        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 3
     */
315
    public function getPath()
316 3
    {
317
        return $this->path;
318
    }
319
320
    /**
321
     * Sets the path.
322
     *
323
     * @param string|null $path
324 2
     *
325
     * @return Cookie
326 2
     */
327
    public function withPath($path)
328
    {
329
        $new = clone $this;
330
        $new->path = $this->normalizePath($path);
331
332
        return $new;
333
    }
334
335
    /**
336 1
     * Checks whether this cookie is meant for this path.
337
     *
338 1
     * @see http://tools.ietf.org/html/rfc6265#section-5.1.4
339 1
     *
340
     * @param string $path
341 1
     *
342
     * @return bool
343
     */
344
    public function matchPath($path)
345
    {
346
        return $this->path === $path || (strpos($path, rtrim($this->path, '/').'/') === 0);
347
    }
348
349 2
    /**
350
     * Checks whether this cookie may only be sent over HTTPS.
351 2
     *
352
     * @return bool
353
     */
354
    public function isSecure()
355
    {
356
        return $this->secure;
357
    }
358
359
    /**
360
     * Sets whether this cookie should only be sent over HTTPS.
361 1
     *
362
     * @param bool $secure
363 1
     *
364 1
     * @return Cookie
365
     */
366 1
    public function withSecure($secure)
367
    {
368
        $new = clone $this;
369
        $new->secure = (bool) $secure;
370
371
        return $new;
372
    }
373
374
    /**
375
     * Check whether this cookie may not be accessed through Javascript.
376
     *
377
     * @return bool
378 10
     */
379
    public function isHttpOnly()
380 10
    {
381
        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
    public function withHttpOnly($httpOnly)
392 57
    {
393
        $new = clone $this;
394 57
        $new->httpOnly = (bool) $httpOnly;
395 1
396
        return $new;
397
    }
398
399 56
    /**
400 3
     * Checks if this cookie represents the same cookie as $cookie.
401
     *
402 53
     * This does not compare the values, only name, domain and path.
403
     *
404
     * @param Cookie $cookie
405
     *
406
     * @return bool
407
     */
408
    public function match(Cookie $cookie)
409
    {
410
        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 53
    /**
414
     * Validates cookie attributes.
415 53
     *
416 49
     * @return bool
417 6
     */
418
    public function isValid()
419 46
    {
420 50
        try {
421
            $this->validateName($this->name);
422
            $this->validateValue($this->value);
423
            $this->validateMaxAge($this->maxAge);
424
        } catch (\InvalidArgumentException $e) {
425
            return false;
426
        }
427
428
        return true;
429 50
    }
430
431 50
    /**
432 7
     * Validates the name attribute.
433 1
     *
434
     * @see http://tools.ietf.org/search/rfc2616#section-2.2
435 6
     *
436 50
     * @param string $name
437
     *
438
     * @throws \InvalidArgumentException If the name is empty or contains invalid characters.
439
     */
440
    private function validateName($name)
441
    {
442
        if (strlen($name) < 1) {
443
            throw new \InvalidArgumentException('The name cannot be empty');
444
        }
445
446
        // Name attribute is a token as per spec in RFC 2616
447
        if (preg_match('/[\x00-\x20\x22\x28-\x29\x2C\x2F\x3A-\x40\x5B-\x5D\x7B\x7D\x7F]/', $name)) {
448
            throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
449 50
        }
450
    }
451 50
452 6
    /**
453 6
     * Validates a value.
454
     *
455 50
     * @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
    private function validateValue($value)
462
    {
463
        if (isset($value)) {
464
            if (preg_match('/[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/', $value)) {
465
                throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value));
466
            }
467
        }
468 50
    }
469
470 50
    /**
471
     * Validates a Max-Age attribute.
472 50
     *
473 49
     * @param int|null $maxAge
474 49
     *
475
     * @throws \InvalidArgumentException If the Max-Age is not an empty or integer value.
476 50
     */
477
    private function validateMaxAge($maxAge)
478
    {
479
        if (isset($maxAge)) {
480
            if (!is_int($maxAge)) {
481
                throw new \InvalidArgumentException('Max-Age must be integer');
482
            }
483
        }
484
    }
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
    private function normalizeDomain($domain)
498
    {
499
        if (isset($domain)) {
500
            $domain = ltrim(strtolower($domain), '.');
501
        }
502
503
        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
    private function normalizePath($path)
517
    {
518
        $path = rtrim($path, '/');
519
520
        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
            $path = '/';
522
        }
523
524
        return $path;
525
    }
526
}
527