Completed
Push — master ( 95e117...43f948 )
by David
02:23
created

Cookie::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 1

Importance

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