Completed
Pull Request — master (#68)
by Antonio J.
07:10 queued 05:22
created

Cookie::getValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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
     * Validation state.
58
     *
59
     * @var bool
60
     */
61
    private $valid;
62
63
    /**
64
     * @param string         $name
65
     * @param string|null    $value
66
     * @param int            $maxAge
67
     * @param string|null    $domain
68
     * @param string|null    $path
69
     * @param bool           $secure
70
     * @param bool           $httpOnly
71
     * @param \DateTime|null $expires           Expires attribute is HTTP 1.0 only and should be avoided.
72
     * @param bool           $requireValidation deprecated since version 1.5. Will be removed in 2.0
73
     *
74
     * @throws \InvalidArgumentException If name, value or max age is not valid. Attributes validation during instantiation is deprecated since 1.5 and will be removed in 2.0.
75
     */
76 73
    public function __construct(
77
        $name,
78
        $value = null,
79
        $maxAge = null,
80
        $domain = null,
81
        $path = null,
82
        $secure = false,
83
        $httpOnly = false,
84
        \DateTime $expires = null,
85
        $requireValidation = true
86
    ) {
87 73
        if ($requireValidation) {
88 58
            @trigger_error('Attributes validation during instantiation is deprecated since 1.5 and will be removed in 2.0', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

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