Completed
Push — master ( 228ec0...89e623 )
by Jan-Petter
04:43
created

UriParser   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 204
Duplicated Lines 5.88 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 7
Bugs 4 Features 1
Metric Value
wmc 31
c 7
b 4
f 1
lcom 1
cbo 1
dl 12
loc 204
rs 9.8

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A convertToFull() 0 12 3
B encode() 0 26 1
A baseToLowercase() 0 8 2
B validate() 0 16 7
A validateIP() 4 11 4
B validateHost() 4 13 6
A validateScheme() 4 8 3
A base() 0 12 3
A stripFragment() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * vipnytt/RobotsTxtParser
4
 *
5
 * @link https://github.com/VIPnytt/RobotsTxtParser
6
 * @license https://github.com/VIPnytt/RobotsTxtParser/blob/master/LICENSE The MIT License (MIT)
7
 */
8
9
namespace vipnytt\RobotsTxtParser\Parser;
10
11
use vipnytt\RobotsTxtParser\Exceptions\ClientException;
12
13
class UriParser
14
{
15
    /**
16
     * Scheme white-list
17
     * @var string[]
18
     */
19
    protected $schemes = [
20
        'http',
21
        'https',
22
        'ftp',
23
        'ftps',
24
        'sftp',
25
    ];
26
27
    /**
28
     * URI
29
     * @var string
30
     */
31
    private $uri;
32
33
    /**
34
     * UriParser constructor.
35
     *
36
     * @param $uri
37
     */
38
    public function __construct($uri)
39
    {
40
        $this->uri = $uri;
41
    }
42
43
    /**
44
     * Convert relative to full
45
     *
46
     * @param string $fallbackBase
47
     * @return string
48
     * @throws ClientException
49
     */
50
    public function convertToFull($fallbackBase)
51
    {
52
        $this->encode();
53
        if ($this->validate()) {
54
            return $this->uri;
55
        } elseif (strpos($this->uri, '/') === 0) {
56
            $relative = $this->uri;
57
            $this->uri = $fallbackBase;
58
            return $this->base() . $relative;
59
        }
60
        throw new ClientException("Invalid URI `$this->uri`");
61
    }
62
63
    /**
64
     * URI encoder according to RFC 3986
65
     * Returns a string containing the encoded URI with disallowed characters converted to their percentage encodings.
66
     * @link http://publicmind.in/blog/url-encoding/
67
     *
68
     * @return string
69
     */
70
    public function encode()
71
    {
72
        $reserved = [
73
            '!%21!ui' => "!",
74
            '!%23!ui' => "#",
75
            '!%24!ui' => "$",
76
            '!%25!ui' => "%",
77
            '!%26!ui' => "&",
78
            '!%27!ui' => "'",
79
            '!%28!ui' => "(",
80
            '!%29!ui' => ")",
81
            '!%2A!ui' => "*",
82
            '!%2B!ui' => "+",
83
            '!%2C!ui' => ",",
84
            '!%2F!ui' => "/",
85
            '!%3A!ui' => ":",
86
            '!%3B!ui' => ";",
87
            '!%3D!ui' => "=",
88
            '!%3F!ui' => "?",
89
            '!%40!ui' => "@",
90
            '!%5B!ui' => "[",
91
            '!%5D!ui' => "]",
92
        ];
93
        $this->uri = preg_replace(array_keys($reserved), array_values($reserved), rawurlencode($this->uri));
94
        return $this->baseToLowercase();
95
    }
96
97
    /**
98
     * Base uri to lowercase
99
     *
100
     * @return string
101
     */
102
    private function baseToLowercase()
103
    {
104
        if (($host = parse_url($this->uri, PHP_URL_HOST)) === null) {
105
            return $this->uri;
106
        }
107
        $pos = strpos($this->uri, $host) + strlen($host);
108
        return $this->uri = substr_replace($this->uri, strtolower(substr($this->uri, 0, $pos)), 0, $pos);
109
    }
110
111
    /**
112
     * Validate
113
     *
114
     * @return bool
115
     */
116
    public function validate()
117
    {
118
        return (
119
            (
120
                filter_var($this->uri, FILTER_VALIDATE_URL) ||
121
                // PHP 5.x bug fix: FILTER_VALIDATE_URL doesn't support IPv6 urls. IP check not needed in the future.
122
                $this->validateIP(($parsed = parse_url($this->uri, PHP_URL_HOST)) === false ? '' : $parsed)
123
            ) &&
124
            ($parsed = parse_url($this->uri)) !== false &&
125
            (
126
                $this->validateHost($parsed['host']) ||
127
                $this->validateIP($parsed['host'])
128
            ) &&
129
            $this->validateScheme($parsed['scheme'])
130
        );
131
    }
132
133
    /**
134
     * Validate IPv4 or IPv6
135
     *
136
     * @param  string|null $ipAddress
137
     * @return bool
138
     */
139
    public function validateIP($ipAddress = null)
140
    {
141 View Code Duplication
        if ($ipAddress === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
            $parsed = parse_url($this->uri);
143
            $ipAddress = isset($parsed['host']) ? $parsed['host'] : $parsed['path'];
144
        }
145
        return (
146
            filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
147
            filter_var(trim($ipAddress, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
148
        );
149
    }
150
151
    /**
152
     * Validate host name
153
     *
154
     * @link http://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
155
     *
156
     * @param  string|null $host
157
     * @return bool
158
     */
159
    public function validateHost($host = null)
160
    {
161 View Code Duplication
        if ($host === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
            $parsed = parse_url($this->uri);
163
            $host = isset($parsed['host']) ? $parsed['host'] : $parsed['path'];
164
        }
165
        return (
166
            preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $host) //valid chars check
167
            && preg_match("/^.{1,253}$/", $host) //overall length check
168
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $host) //length of each label
169
            && !$this->validateIP($host)
170
        );
171
    }
172
173
    /**
174
     * Validate scheme
175
     *
176
     * @param  string|null $scheme
177
     * @return bool
178
     */
179
    public function validateScheme($scheme = null)
180
    {
181 View Code Duplication
        if ($scheme === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
            $parsed = parse_url($this->uri);
183
            $scheme = isset($parsed['host']) ? $parsed['host'] : $parsed['path'];
184
        }
185
        return in_array($scheme, $this->schemes);
186
    }
187
188
    /**
189
     * Base
190
     *
191
     * @return string
192
     * @throws ClientException
193
     */
194
    public function base()
195
    {
196
        if (!$this->validate()) {
197
            throw new ClientException("Invalid URI: $this->uri");
198
        }
199
        $parts = [
200
            'scheme' => parse_url($this->uri, PHP_URL_SCHEME),
201
            'host' => parse_url($this->uri, PHP_URL_HOST),
202
        ];
203
        $parts['port'] = is_int($port = parse_url($this->uri, PHP_URL_PORT)) ? $port : getservbyname($parts['scheme'], 'tcp');
204
        return strtolower($parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port']);
205
    }
206
207
    /**
208
     * Strip fragment
209
     *
210
     * @return string
211
     */
212
    public function stripFragment()
213
    {
214
        return explode('#', $this->uri, 2)[0];
215
    }
216
}
217