Url   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 119
dl 0
loc 290
ccs 125
cts 125
cp 1
rs 3.6
c 0
b 0
f 0
wmc 60

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getQuery() 0 5 1
A createDefaultParser() 0 3 1
A getParser() 0 7 2
A setFragment() 0 5 1
A getNetloc() 0 5 5
A setPath() 0 5 1
A setUrl() 0 5 1
F fromCurrent() 0 39 18
A parse() 0 3 1
A getPath() 0 5 1
A join() 0 22 5
A getUrl() 0 11 2
A getFragment() 0 5 1
A httpBuildRelativeUrl() 0 9 3
A extract() 0 11 2
A httpBuildUrl() 0 15 4
A setParser() 0 3 1
A doInitialize() 0 14 4
A isAbsolute() 0 5 2
A __construct() 0 4 1
A __toString() 0 3 1
A setQuery() 0 5 1
A set() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like Url often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Url, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Purl;
6
7
use function array_map;
8
use function explode;
9
use function ltrim;
10
use function preg_match_all;
11
use function sprintf;
12
use function strpos;
13
14
/**
15
 * Url is a simple OO class for manipulating Urls in PHP.
16
 *
17
 * @property string $scheme
18
 * @property string $host
19
 * @property int $port
20
 * @property string $user
21
 * @property string $pass
22
 * @property Path|string $path
23
 * @property Query|string $query
24
 * @property Fragment|string $fragment
25
 * @property string $canonical
26
 * @property string $resource
27
 */
28
class Url extends AbstractPart
29
{
30
    /** @var string|null The original url string. */
31
    private $url;
32
33
    /** @var ParserInterface|null */
34
    private $parser;
35
36
    /** @var mixed[] */
37
    protected $data = [
38
        'scheme'             => null,
39
        'host'               => null,
40
        'port'               => null,
41
        'user'               => null,
42
        'pass'               => null,
43
        'path'               => null,
44
        'query'              => null,
45
        'fragment'           => null,
46
        'publicSuffix'       => null,
47
        'registerableDomain' => null,
48
        'subdomain'          => null,
49
        'canonical'          => null,
50
        'resource'           => null,
51
    ];
52
53
    /** @var string[] */
54
    protected $partClassMap = [
55
        'path' => 'Purl\Path',
56
        'query' => 'Purl\Query',
57
        'fragment' => 'Purl\Fragment',
58
    ];
59
60 31
    public function __construct(?string $url = null, ?ParserInterface $parser = null)
61
    {
62 31
        $this->url    = $url;
63 31
        $this->parser = $parser;
64 31
    }
65
66 4
    public static function parse(string $url) : Url
67
    {
68 4
        return new self($url);
69
    }
70
71
    /**
72
     * @return Url[] $urls
73
     */
74 1
    public static function extract(string $string) : array
75
    {
76 1
        $regex = '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/\S*)?/';
77
78 1
        preg_match_all($regex, $string, $matches);
79 1
        $urls = [];
80 1
        foreach ($matches[0] as $url) {
81 1
            $urls[] = self::parse($url);
82
        }
83
84 1
        return $urls;
85
    }
86
87 1
    public static function fromCurrent() : Url
88
    {
89 1
        $scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443 ? 'https' : 'http';
90
91 1
        $host    = $_SERVER['HTTP_HOST'];
92 1
        $baseUrl = sprintf('%s://%s', $scheme, $host);
93
94 1
        $url = new self($baseUrl);
95
96 1
        if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI']) {
97 1
            if (strpos($_SERVER['REQUEST_URI'], '?') !== false) {
98 1
                [$path, $query] = explode('?', $_SERVER['REQUEST_URI'], 2);
99
            } else {
100 1
                $path  = $_SERVER['REQUEST_URI'];
101 1
                $query = '';
102
            }
103
104 1
            $url->set('path', $path);
105 1
            $url->set('query', $query);
106
        }
107
108
        // Only set port if different from default (80 or 443)
109 1
        if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT']) {
110 1
            $port = $_SERVER['SERVER_PORT'];
111 1
            if (($scheme === 'http' && $port !== 80) ||
112 1
                ($scheme === 'https' && $port !== 443)) {
113 1
                $url->set('port', $port);
114
            }
115
        }
116
117
        // Authentication
118 1
        if (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER']) {
119 1
            $url->set('user', $_SERVER['PHP_AUTH_USER']);
120 1
            if (isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW']) {
121 1
                $url->set('pass', $_SERVER['PHP_AUTH_PW']);
122
            }
123
        }
124
125 1
        return $url;
126
    }
127
128 30
    public function getParser() : ParserInterface
129
    {
130 30
        if ($this->parser === null) {
131 29
            $this->parser = self::createDefaultParser();
132
        }
133
134 30
        return $this->parser;
135
    }
136
137 1
    public function setParser(ParserInterface $parser) : void
138
    {
139 1
        $this->parser = $parser;
140 1
    }
141
142
    /**
143
     * @param string|Url $url
144
     */
145 2
    public function join($url) : Url
146
    {
147 2
        $this->initialize();
148 2
        $parts = $this->getParser()->parseUrl($url);
149
150 2
        if ($this->data['scheme'] !== null) {
151 2
            $parts['scheme'] = $this->data['scheme'];
152
        }
153
154 2
        foreach ($parts as $key => $value) {
155 2
            if ($value === null) {
156 2
                continue;
157
            }
158
159 2
            $this->data[$key] = $value;
160
        }
161
162 2
        foreach ($this->data as $key => $value) {
163 2
            $this->data[$key] = $this->preparePartValue($key, $value);
164
        }
165
166 2
        return $this;
167
    }
168
169
    /**
170
     * @param mixed $value
171
     */
172 8
    public function set(string $key, $value) : AbstractPart
173
    {
174 8
        $this->initialize();
175
176 8
        $this->data[$key] = $this->preparePartValue($key, $value);
177
178 8
        return $this;
179
    }
180
181 1
    public function setPath(Path $path) : AbstractPart
182
    {
183 1
        $this->data['path'] = $path;
184
185 1
        return $this;
186
    }
187
188 1
    public function getPath() : Path
189
    {
190 1
        $this->initialize();
191
192 1
        return $this->data['path'];
193
    }
194
195 1
    public function setQuery(Query $query) : AbstractPart
196
    {
197 1
        $this->data['query'] = $query;
198
199 1
        return $this;
200
    }
201
202 1
    public function getQuery() : Query
203
    {
204 1
        $this->initialize();
205
206 1
        return $this->data['query'];
207
    }
208
209 1
    public function setFragment(Fragment $fragment) : AbstractPart
210
    {
211 1
        $this->data['fragment'] = $fragment;
212
213 1
        return $this;
214
    }
215
216 1
    public function getFragment() : Fragment
217
    {
218 1
        $this->initialize();
219
220 1
        return $this->data['fragment'];
221
    }
222
223 1
    public function getNetloc() : string
224
    {
225 1
        $this->initialize();
226
227 1
        return ($this->user !== null && $this->pass !== null ? $this->user . ($this->pass !== null ? ':' . $this->pass : '') . '@' : '') . $this->host . ($this->port !== null ? ':' . $this->port : '');
228
    }
229
230 17
    public function getUrl() : string
231
    {
232 17
        $this->initialize();
233
234 17
        $parts = array_map('strval', $this->data);
235
236 17
        if (! $this->isAbsolute()) {
237 1
            return self::httpBuildRelativeUrl($parts);
238
        }
239
240 16
        return self::httpBuildUrl($parts);
241
    }
242
243 2
    public function setUrl(string $url) : void
244
    {
245 2
        $this->initialized = false;
246 2
        $this->data        = [];
247 2
        $this->url         = $url;
248 2
    }
249
250 18
    public function isAbsolute() : bool
251
    {
252 18
        $this->initialize();
253
254 18
        return $this->scheme !== null && $this->host !== null;
255
    }
256
257 14
    public function __toString() : string
258
    {
259 14
        return $this->getUrl();
260
    }
261
262 29
    protected function doInitialize() : void
263
    {
264 29
        $parts = $this->getParser()->parseUrl($this->url);
265
266 29
        foreach ($parts as $k => $v) {
267 29
            if (isset($this->data[$k])) {
268 1
                continue;
269
            }
270
271 29
            $this->data[$k] = $v;
272
        }
273
274 29
        foreach ($this->data as $key => $value) {
275 29
            $this->data[$key] = $this->preparePartValue($key, $value);
276
        }
277 29
    }
278
279
    /**
280
     * @param string[] $parts
281
     */
282 16
    private static function httpBuildUrl(array $parts) : string
283
    {
284 16
        $relative = self::httpBuildRelativeUrl($parts);
285
286 16
        $pass = $parts['pass'] !== '' ? sprintf(':%s', $parts['pass']) : '';
287 16
        $auth = $parts['user'] !== '' ? sprintf('%s%s@', $parts['user'], $pass) : '';
288 16
        $port = $parts['port'] !== '' ? sprintf(':%d', $parts['port']) : '';
289
290 16
        return sprintf(
291 16
            '%s://%s%s%s%s',
292 16
            $parts['scheme'],
293 16
            $auth,
294 16
            $parts['host'],
295 16
            $port,
296 16
            $relative
297
        );
298
    }
299
300
    /**
301
     * @param string[] $parts
302
     */
303 17
    private static function httpBuildRelativeUrl(array $parts) : string
304
    {
305 17
        $parts['path'] = ltrim($parts['path'], '/');
306
307 17
        return sprintf(
308 17
            '/%s%s%s',
309 17
            $parts['path'],
310 17
            $parts['query'] !== '' ? '?' . $parts['query'] : '',
311 17
            $parts['fragment'] !== '' ? '#' . $parts['fragment'] : ''
312
        );
313
    }
314
315 29
    private static function createDefaultParser() : Parser
316
    {
317 29
        return new Parser();
318
    }
319
}
320