Completed
Push — issue#666 ( 5e6b12...7229dc )
by Guilherme
09:55
created

TagUri::getSpecific()   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
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\RemoteClaimsBundle\Model;
12
13
use Egulias\EmailValidator\EmailValidator;
14
use Psr\Http\Message\UriInterface;
15
16
class TagUri implements UriInterface
17
{
18
    const REGEX_OVERALL = '^tag:(?<taggingEntity>[^:]+):(?<specific>[^#]+)(?:#(?<fragment>[\w\d&?@:_]+))?$';
19
    const REGEX_DATE = '\d{4}(?:-(?:0\d|1[012])(?:-(?:[012]\d|3[01]))?)?';
20
    const REGEX_DATE_YEAR = '(?<year>\d{4})';
21
    const REGEX_DATE_MONTH = '(?<month>0\d|1[012])';
22
    const REGEX_DATE_DAY = '(?<day>[012]\d|3[01])';
23
    const REGEX_DNScomp = '(?:[\w\d](?:[\w\d-]*[\w\d])?)';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected REGEX_DNSCOMP).
Loading history...
24
    const REGEX_date = '(?:\d{4}(?:-\d{2}(?:-\d{2})?)?)';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected REGEX_DATE).
Loading history...
25
26
    protected static $supportedSchemes = ['tag'];
27
28
    /** @var string */
29
    private $authorityName;
30
31
    /** @var string */
32
    private $host;
33
34
    /** @var string */
35
    private $date;
36
37
    /** @var string */
38
    private $specific;
39
40
    /** @var string */
41
    private $fragment;
42
43
    private function OLDgetAuthorityName()
0 ignored issues
show
Coding Style introduced by
function OLDgetAuthorityName() does not seem to conform to the naming convention (^(?:[a-z]|__)[a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
44
    {
45
        $path = $this->getPath();
46
        $taggingEntity = $this->getTaggingEntityRegex();
47
        if (preg_match("/^{$taggingEntity}/", $path, $m) !== 1) {
48
            throw new \InvalidArgumentException('Invalid taggingEntity');
49
        }
50
51
        return $m['authorityName'];
52
    }
53
54
    private function isValid()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
55
    {
56
        $authorityName = $this->getAuthorityName();
57
        if (strstr($authorityName, '@') === false) {
58
            $validAuthorityName = new Host($authorityName);
59
        } else {
60
            $validator = new EmailValidator();
61
            $validAuthorityName = $validator->isValid($authorityName);
62
        }
63
64
        return !$this->userInfo->__toString()
65
            && !$this->host->__toString()
0 ignored issues
show
Bug introduced by
The method __toString cannot be called on $this->host (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
66
            && $validAuthorityName;
67
    }
68
69 21
    private static function getDnsNameRegex()
70
    {
71 21
        return '(?:'.self::REGEX_DNScomp.'(?:[.]'.self::REGEX_DNScomp.')*)';
72
    }
73
74 21
    private static function getTaggingEntityRegex()
75
    {
76 21
        return '(?<authorityName>'.self::getDnsNameRegex().'|'.static::getEmailAddressRegex().'),(?<date>'.self::REGEX_date.')';
77
    }
78
79 21
    private static function getEmailAddressRegex()
80
    {
81 21
        return '(?:[\w\d-._+]*@'.self::getDnsNameRegex().')';
82
    }
83
84 19
    private static function getDateRegex()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
85
    {
86 19
        $day = self::REGEX_DATE_DAY;
87 19
        $month = self::REGEX_DATE_MONTH;
88 19
        $year = self::REGEX_DATE_YEAR;
89
90 19
        return "$year(?:-$month(?:-$day)?)?";
91
    }
92
93
    /**
94
     * Retrieve the scheme component of the URI.
95
     *
96
     * If no scheme is present, this method MUST return an empty string.
97
     *
98
     * The value returned MUST be normalized to lowercase, per RFC 3986
99
     * Section 3.1.
100
     *
101
     * The trailing ":" character is not part of the scheme and MUST NOT be
102
     * added.
103
     *
104
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
105
     * @return string The URI scheme.
106
     */
107
    public function getScheme()
108
    {
109
        return 'tag';
110
    }
111
112
    /**
113
     * Retrieve the authority component of the URI.
114
     *
115
     * If no authority information is present, this method MUST return an empty
116
     * string.
117
     *
118
     * The authority syntax of the URI is:
119
     *
120
     * <pre>
121
     * [user-info@]host[:port]
122
     * </pre>
123
     *
124
     * If the port component is not set or is the standard port for the current
125
     * scheme, it SHOULD NOT be included.
126
     *
127
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
128
     * @return string The URI authority, in "[user-info@]host[:port]" format.
129
     */
130 14
    public function getAuthority()
131
    {
132 14
        return $this->authorityName;
133
    }
134
135 7
    public function getAuthorityName()
136
    {
137 7
        return $this->getAuthority();
138
    }
139
140
    /**
141
     * Retrieve the user information component of the URI.
142
     *
143
     * If no user information is present, this method MUST return an empty
144
     * string.
145
     *
146
     * If a user is present in the URI, this will return that value;
147
     * additionally, if the password is also present, it will be appended to the
148
     * user value, with a colon (":") separating the values.
149
     *
150
     * The trailing "@" character is not part of the user information and MUST
151
     * NOT be added.
152
     *
153
     * @return string The URI user information, in "username[:password]" format.
154
     */
155
    public function getUserInfo()
156
    {
157
        return '';
158
    }
159
160
    /**
161
     * Retrieve the host component of the URI.
162
     *
163
     * If no host is present, this method MUST return an empty string.
164
     *
165
     * The value returned MUST be normalized to lowercase, per RFC 3986
166
     * Section 3.2.2.
167
     *
168
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
169
     * @return string The URI host.
170
     */
171
    public function getHost()
172
    {
173
        return $this->extractHost($this->getAuthority());
174
    }
175
176
    /**
177
     * Retrieve the port component of the URI.
178
     *
179
     * If a port is present, and it is non-standard for the current scheme,
180
     * this method MUST return it as an integer. If the port is the standard port
181
     * used with the current scheme, this method SHOULD return null.
182
     *
183
     * If no port is present, and no scheme is present, this method MUST return
184
     * a null value.
185
     *
186
     * If no port is present, but a scheme is present, this method MAY return
187
     * the standard port for that scheme, but SHOULD return null.
188
     *
189
     * @return null|int The URI port.
190
     */
191
    public function getPort()
192
    {
193
        return null;
194
    }
195
196
    /**
197
     * Retrieve the path component of the URI.
198
     *
199
     * The path can either be empty or absolute (starting with a slash) or
200
     * rootless (not starting with a slash). Implementations MUST support all
201
     * three syntaxes.
202
     *
203
     * Normally, the empty path "" and absolute path "/" are considered equal as
204
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
205
     * do this normalization because in contexts with a trimmed base path, e.g.
206
     * the front controller, this difference becomes significant. It's the task
207
     * of the user to handle both "" and "/".
208
     *
209
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
210
     * any characters. To determine what characters to encode, please refer to
211
     * RFC 3986, Sections 2 and 3.3.
212
     *
213
     * As an example, if the value should include a slash ("/") not intended as
214
     * delimiter between path segments, that value MUST be passed in encoded
215
     * form (e.g., "%2F") to the instance.
216
     *
217
     * @see https://tools.ietf.org/html/rfc3986#section-2
218
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
219
     * @return string The URI path.
220
     */
221
    public function getPath()
222
    {
223
        return '';
224
    }
225
226
    /**
227
     * Retrieve the query string of the URI.
228
     *
229
     * If no query string is present, this method MUST return an empty string.
230
     *
231
     * The leading "?" character is not part of the query and MUST NOT be
232
     * added.
233
     *
234
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
235
     * any characters. To determine what characters to encode, please refer to
236
     * RFC 3986, Sections 2 and 3.4.
237
     *
238
     * As an example, if a value in a key/value pair of the query string should
239
     * include an ampersand ("&") not intended as a delimiter between values,
240
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
241
     *
242
     * @see https://tools.ietf.org/html/rfc3986#section-2
243
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
244
     * @return string The URI query string.
245
     */
246
    public function getQuery()
247
    {
248
        return '';
249
    }
250
251
    /**
252
     * Retrieve the fragment component of the URI.
253
     *
254
     * If no fragment is present, this method MUST return an empty string.
255
     *
256
     * The leading "#" character is not part of the fragment and MUST NOT be
257
     * added.
258
     *
259
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
260
     * any characters. To determine what characters to encode, please refer to
261
     * RFC 3986, Sections 2 and 3.5.
262
     *
263
     * @see https://tools.ietf.org/html/rfc3986#section-2
264
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
265
     * @return string The URI fragment.
266
     */
267 10
    public function getFragment()
268
    {
269 10
        return $this->fragment;
270
    }
271
272
    /**
273
     * Retrieve the specific component of the tag URI.
274
     *
275
     * If no specific is present, this method MUST return an empty string.
276
     *
277
     * The leading ":" character is not part of the specific and MUST NOT be
278
     * added.
279
     *
280
     * @see https://tools.ietf.org/html/rfc4151#section-2
281
     * @return string The tag URI specific.
282
     */
283 10
    public function getSpecific()
284
    {
285 10
        return $this->specific;
286
    }
287
288
    /**
289
     * Retrieve the date component of the tag URI.
290
     *
291
     * The leading "," character is not part of the date and MUST NOT be
292
     * added.
293
     *
294
     * @see https://tools.ietf.org/html/rfc4151#section-2
295
     * @return string The tag URI date.
296
     */
297 10
    public function getDate()
298
    {
299 10
        return $this->date;
300
    }
301
302 10
    public function getTaggingEntity()
303
    {
304 10
        return sprintf("%s,%s", $this->getAuthority(), $this->getDate());
305
    }
306
307
    /**
308
     * Return an instance with the specified scheme.
309
     *
310
     * This method MUST retain the state of the current instance, and return
311
     * an instance that contains the specified scheme.
312
     *
313
     * Implementations MUST support the schemes "http" and "https" case
314
     * insensitively, and MAY accommodate other schemes if required.
315
     *
316
     * An empty scheme is equivalent to removing the scheme.
317
     *
318
     * @param string $scheme The scheme to use with the new instance.
319
     * @return static A new instance with the specified scheme.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NoType.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
320
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
321
     */
322
    public function withScheme($scheme)
323
    {
324
        throw new \BadMethodCallException("This method is not supported");
325
    }
326
327
    /**
328
     * Return an instance with the specified user information.
329
     *
330
     * This method MUST retain the state of the current instance, and return
331
     * an instance that contains the specified user information.
332
     *
333
     * Password is optional, but the user information MUST include the
334
     * user; an empty string for the user is equivalent to removing user
335
     * information.
336
     *
337
     * @param string $user The user name to use for authority.
338
     * @param null|string $password The password associated with $user.
339
     * @return static A new instance with the specified user information.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
340
     */
341
    public function withUserInfo($user, $password = null)
342
    {
343
        // TODO: Implement withUserInfo() method.
344
    }
345
346
    /**
347
     * Return an instance with the specified host.
348
     *
349
     * This method MUST retain the state of the current instance, and return
350
     * an instance that contains the specified host.
351
     *
352
     * An empty host value is equivalent to removing the host.
353
     *
354
     * @param string $host The hostname to use with the new instance.
355
     * @return static A new instance with the specified host.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
356
     * @throws \InvalidArgumentException for invalid hostnames.
357
     */
358
    public function withHost($host)
359
    {
360
        // TODO: Implement withHost() method.
361
    }
362
363
    /**
364
     * Return an instance with the specified port.
365
     *
366
     * This method MUST retain the state of the current instance, and return
367
     * an instance that contains the specified port.
368
     *
369
     * Implementations MUST raise an exception for ports outside the
370
     * established TCP and UDP port ranges.
371
     *
372
     * A null value provided for the port is equivalent to removing the port
373
     * information.
374
     *
375
     * @param null|int $port The port to use with the new instance; a null value
376
     *     removes the port information.
377
     * @return static A new instance with the specified port.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
378
     * @throws \InvalidArgumentException for invalid ports.
379
     */
380
    public function withPort($port)
381
    {
382
        // TODO: Implement withPort() method.
383
    }
384
385
    /**
386
     * Return an instance with the specified path.
387
     *
388
     * This method MUST retain the state of the current instance, and return
389
     * an instance that contains the specified path.
390
     *
391
     * The path can either be empty or absolute (starting with a slash) or
392
     * rootless (not starting with a slash). Implementations MUST support all
393
     * three syntaxes.
394
     *
395
     * If the path is intended to be domain-relative rather than path relative then
396
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
397
     * are assumed to be relative to some base path known to the application or
398
     * consumer.
399
     *
400
     * Users can provide both encoded and decoded path characters.
401
     * Implementations ensure the correct encoding as outlined in getPath().
402
     *
403
     * @param string $path The path to use with the new instance.
404
     * @return static A new instance with the specified path.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
405
     * @throws \InvalidArgumentException for invalid paths.
406
     */
407
    public function withPath($path)
408
    {
409
        // TODO: Implement withPath() method.
410
    }
411
412
    /**
413
     * Return an instance with the specified query string.
414
     *
415
     * This method MUST retain the state of the current instance, and return
416
     * an instance that contains the specified query string.
417
     *
418
     * Users can provide both encoded and decoded query characters.
419
     * Implementations ensure the correct encoding as outlined in getQuery().
420
     *
421
     * An empty query string value is equivalent to removing the query string.
422
     *
423
     * @param string $query The query string to use with the new instance.
424
     * @return static A new instance with the specified query string.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
425
     * @throws \InvalidArgumentException for invalid query strings.
426
     */
427
    public function withQuery($query)
428
    {
429
        // TODO: Implement withQuery() method.
430
    }
431
432
    /**
433
     * Return an instance with the specified URI fragment.
434
     *
435
     * This method MUST retain the state of the current instance, and return
436
     * an instance that contains the specified URI fragment.
437
     *
438
     * Users can provide both encoded and decoded fragment characters.
439
     * Implementations ensure the correct encoding as outlined in getFragment().
440
     *
441
     * An empty fragment value is equivalent to removing the fragment.
442
     *
443
     * @param string $fragment The fragment to use with the new instance.
444
     * @return static A new instance with the specified fragment.
0 ignored issues
show
Documentation introduced by
Should the return type not be TagUri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
445
     */
446
    public function withFragment($fragment)
447
    {
448
        // TODO: Implement withFragment() method.
449
    }
450
451
    /**
452
     * Return the string representation as a URI reference.
453
     *
454
     * Depending on which components of the URI are present, the resulting
455
     * string is either a full URI or relative reference according to RFC 3986,
456
     * Section 4.1. The method concatenates the various components of the URI,
457
     * using the appropriate delimiters:
458
     *
459
     * - If a scheme is present, it MUST be suffixed by ":".
460
     * - If an authority is present, it MUST be prefixed by "//".
461
     * - The path can be concatenated without delimiters. But there are two
462
     *   cases where the path has to be adjusted to make the URI reference
463
     *   valid as PHP does not allow to throw an exception in __toString():
464
     *     - If the path is rootless and an authority is present, the path MUST
465
     *       be prefixed by "/".
466
     *     - If the path is starting with more than one "/" and no authority is
467
     *       present, the starting slashes MUST be reduced to one.
468
     * - If a query is present, it MUST be prefixed by "?".
469
     * - If a fragment is present, it MUST be prefixed by "#".
470
     *
471
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
472
     * @return string
473
     */
474 10
    public function __toString()
475
    {
476 10
        $tagURI = sprintf("tag:%s:%s", $this->getTaggingEntity(), $this->getSpecific());
477
478 10
        if ('' !== $fragment = $this->getFragment()) {
479
            $tagURI = sprintf("%s#%s", $tagURI, $fragment);
480
        }
481
482 10
        return $tagURI;
483
    }
484
485 19
    public function setAuthorityName($authorityName)
486
    {
487 19
        $this->authorityName = $authorityName;
488
489 19
        return $this;
490
    }
491
492 19
    public function setDate($date)
493
    {
494 19
        $dateRegex = self::getDateRegex();
495 19
        if (!preg_match("/^$dateRegex$/", $date, $m)) {
496 1
            throw new \InvalidArgumentException('Invalid date: '.$date);
497
        }
498
499 18
        $parts = array_merge([
500 18
            'year' => $m['year'],
501 18
            'month' => '01',
502 18
            'day' => '01',
503 18
        ], $m);
504
505 18
        if (!checkdate($parts['month'], $parts['day'], $parts['year'])) {
506 1
            throw new \InvalidArgumentException('Invalid date: '.$date);
507
        }
508 17
        $this->date = $date;
509
510 17
        return $this;
511
    }
512
513 17
    public function setSpecific($specific)
514
    {
515 17
        $this->specific = $specific;
516
517 17
        return $this;
518
    }
519
520 17
    public function setFragment($fragment)
521
    {
522 17
        $this->fragment = $fragment ?: '';
523
524 17
        return $this;
525
    }
526
527
    private function extractHost($authorityName)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
528
    {
529
        $parts = explode('@', $authorityName);
530
531
        return end($parts);
532
    }
533
534 21
    public static function createFromString($string)
535
    {
536 21
        $overallRegex = self::REGEX_OVERALL;
537 21
        if (!preg_match("/{$overallRegex}/", $string, $overall)) {
538 1
            throw new \InvalidArgumentException("The provided tag URI doesn't seem to be valid: {$string}");
539
        }
540
541 21
        $taggingEntity = self::getTaggingEntityRegex();
542 21
        if (preg_match("/^{$taggingEntity}/", $overall['taggingEntity'], $m) !== 1) {
543 2
            throw new \InvalidArgumentException('Invalid taggingEntity: '.$overall['taggingEntity']);
544
        }
545
546 19
        $tagUri = (new TagUri())
547 19
            ->setAuthorityName($m['authorityName'])
548 19
            ->setDate($m['date'])
549 17
            ->setSpecific($overall['specific'])
550 17
            ->setFragment($overall['fragment']);
551
552 17
        return $tagUri;
553
    }
554
}
555