Completed
Push — master ( b9920c...064526 )
by Jan-Petter
03:06
created

UriParser::uriValidateHost()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.2
cc 4
eloc 6
nc 4
nop 1
1
<?php
2
namespace vipnytt\RobotsTxtParser\Parser;
3
4
use vipnytt\RobotsTxtParser\Exceptions\ClientException;
5
6
/**
7
 * Trait UriParser
8
 *
9
 * @package vipnytt\RobotsTxtParser\Parser
10
 */
11
trait UriParser
12
{
13
    /**
14
     * Convert relative to full uri
15
     *
16
     * @param string $uri
17
     * @param string $base
18
     * @return string
19
     * @throws ClientException
20
     */
21
    private function uriConvertToFull($uri, $base)
22
    {
23
        $uri = $this->uriEncode($uri);
24
        if ($this->uriValidate($uri)) {
25
            return $uri;
26
        } elseif (stripos($uri, '/') === 0) {
27
            return $this->uriBase($base) . $uri;
28
        }
29
        throw new ClientException("Invalid URI `$uri`");
30
    }
31
32
    /**
33
     * URI encoder according to RFC 3986
34
     * Returns a string containing the encoded URI with disallowed characters converted to their percentage encodings.
35
     * @link http://publicmind.in/blog/url-encoding/
36
     *
37
     * @param string $uri
38
     * @return string
39
     */
40
    protected function uriEncode($uri)
41
    {
42
        $reserved = [
43
            '!%21!ui' => "!",
44
            '!%23!ui' => "#",
45
            '!%24!ui' => "$",
46
            '!%25!ui' => "%",
47
            '!%26!ui' => "&",
48
            '!%27!ui' => "'",
49
            '!%28!ui' => "(",
50
            '!%29!ui' => ")",
51
            '!%2A!ui' => "*",
52
            '!%2B!ui' => "+",
53
            '!%2C!ui' => ",",
54
            '!%2F!ui' => "/",
55
            '!%3A!ui' => ":",
56
            '!%3B!ui' => ";",
57
            '!%3D!ui' => "=",
58
            '!%3F!ui' => "?",
59
            '!%40!ui' => "@",
60
            '!%5B!ui' => "[",
61
            '!%5D!ui' => "]",
62
        ];
63
        return preg_replace(array_keys($reserved), array_values($reserved), rawurlencode($uri));
64
    }
65
66
    /**
67
     * Validate uri
68
     *
69
     * @param string $uri
70
     * @return bool
71
     */
72
    private function uriValidate($uri)
73
    {
74
        return (
75
            (
76
                filter_var($uri, FILTER_VALIDATE_URL) ||
77
                // PHP 5.x bug fix: FILTER_VALIDATE_URL doesn't support IPv6 urls. IP check not needed in the future.
78
                $this->uriValidateIP(parse_url($uri, PHP_URL_HOST))
0 ignored issues
show
Security Bug introduced by
It seems like parse_url($uri, PHP_URL_HOST) targeting parse_url() can also be of type false; however, vipnytt\RobotsTxtParser\...Parser::uriValidateIP() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
79
            ) &&
80
            ($parsed = parse_url($uri)) !== false &&
81
            (
82
                $this->uriValidateHost($parsed['host']) ||
83
                $this->uriValidateIP($parsed['host'])
84
            ) &&
85
            $this->uriValidateScheme($parsed['scheme'])
86
        );
87
    }
88
89
    /**
90
     * Validate IPv4 or IPv6
91
     *
92
     * @param  string $ip
93
     * @return bool
94
     */
95
    private function uriValidateIP($ip)
96
    {
97
        return (
98
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
99
            filter_var(trim($ip, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
100
        );
101
    }
102
103
    /**
104
     * Validate host name
105
     *
106
     * @link http://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
107
     *
108
     * @param  string $host
109
     * @return bool
110
     */
111
    private function uriValidateHost($host)
112
    {
113
        return (
114
            preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $host) //valid chars check
115
            && preg_match("/^.{1,253}$/", $host) //overall length check
116
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $host) //length of each label
117
            && !$this->uriValidateIP($host)
118
        );
119
    }
120
121
    /**
122
     * Validate uri scheme
123
     *
124
     * @param  string $scheme
125
     * @return bool
126
     */
127
    private function uriValidateScheme($scheme)
128
    {
129
        return in_array(strtolower($scheme), [
130
                'http',
131
                'https',
132
                'ftp',
133
                'ftps',
134
                'sftp',
135
            ]
136
        );
137
    }
138
139
    /**
140
     * Base uri
141
     *
142
     * @param string $uri
143
     * @return string
144
     * @throws ClientException
145
     */
146
    protected function uriBase($uri)
147
    {
148
        if ($this->uriValidate($uri) === false) {
149
            throw new ClientException("Invalid or unsupported URI `$uri`");
150
        }
151
        $parts = [
152
            'scheme' => parse_url($uri, PHP_URL_SCHEME),
153
            'host' => parse_url($uri, PHP_URL_HOST),
154
        ];
155
        $parts['port'] = is_int($port = parse_url($uri, PHP_URL_PORT)) ? $port : getservbyname($parts['scheme'], 'tcp');
156
        return $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port'];
157
    }
158
}
159