Completed
Push — master ( a5481a...3fd81a )
by Alejandro
15s queued 10s
created

HostAndPortValidator::isValid()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 15
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 26
rs 8.8333
ccs 16
cts 16
cp 1
crap 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Common\Validation;
6
7
use Zend\Validator\AbstractValidator;
8
use Zend\Validator\Between;
9
use Zend\Validator\Digits;
10
use Zend\Validator\Exception;
11
use Zend\Validator\Hostname;
12
use Zend\Validator\ValidatorChain;
13
use Zend\Validator\ValidatorInterface;
14
15
use function count;
16
use function explode;
17
use function get_class;
18
use function gettype;
19
use function is_object;
20
use function is_string;
21
use function sprintf;
22
23
class HostAndPortValidator extends AbstractValidator
24
{
25
    private const INVALID_AMOUNT_OF_PARTS = 'INVALID_AMOUNT_OF_PARTS';
26
    private const INVALID_HOST = 'INVALID_HOST';
27
    private const INVALID_PORT = 'INVALID_PORT';
28
29
    protected $messageTemplates = [
30
        self::INVALID_AMOUNT_OF_PARTS =>
31
            'Provided value, once split using the ":" separator, returned more than 2 parts',
32
        self::INVALID_HOST => 'The host part of the value is not valid',
33
        self::INVALID_PORT => 'The port part of the value is not valid. Must be a number between 1 and 65535',
34
    ];
35
36
    /** @var ValidatorInterface */
37
    private $hostValidator;
38
    /** @var ValidatorInterface */
39
    private $portValidator;
40
41 19
    public function __construct($options = null)
42
    {
43 19
        parent::__construct($options);
44
45 19
        $this->hostValidator = new Hostname([
46 19
            'allow' => Hostname::ALLOW_DNS | Hostname::ALLOW_LOCAL,
47
        ]);
48 19
        $this->portValidator = (new ValidatorChain())->attach(new Digits())
49 19
                                                     ->attach(new Between([
50 19
                                                         'min' => 1,
51
                                                         'max' => 65535,
52
                                                         'inclusive' => true,
53
                                                     ]));
54
    }
55
56
    /**
57
     * Returns true if and only if $value meets the validation requirements
58
     *
59
     * If $value fails validation, then this method returns false, and
60
     * getMessages() will return an array of messages that explain why the
61
     * validation failed.
62
     *
63
     * @param mixed $value
64
     * @return bool
65
     * @throws Exception\RuntimeException If validation of $value is impossible
66
     */
67 19
    public function isValid($value): bool
68
    {
69 19
        if (! is_string($value)) {
70 4
            throw new Exception\RuntimeException(sprintf(
71 4
                'Expected value to be a string. %s provided',
72 4
                is_object($value) ? get_class($value) : gettype($value)
73
            ));
74
        }
75
76 15
        $parts = explode(':', $value);
77 15
        if (count($parts) > 2) {
78 2
            $this->error(self::INVALID_AMOUNT_OF_PARTS);
79 2
            return false;
80
        }
81
82 13
        if (! $this->hostValidator->isValid($parts[0])) {
83 3
            $this->error(self::INVALID_HOST);
84 3
            return false;
85
        }
86
87 10
        if (isset($parts[1]) && ! $this->portValidator->isValid($parts[1])) {
88 6
            $this->error(self::INVALID_PORT);
89 6
            return false;
90
        }
91
92 4
        return true;
93
    }
94
}
95