Completed
Pull Request — master (#1)
by Ben
02:04
created

Root   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 66
c 3
b 0
f 0
dl 0
loc 172
ccs 42
cts 42
cp 1
rs 10
wmc 29

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
A composeScheme() 0 3 3
A parseHost() 0 19 6
A port() 0 3 1
A scheme() 0 3 1
A fromString() 0 5 1
A secure() 0 6 1
A host() 0 3 1
A __toString() 0 3 1
A replaceScheme() 0 8 1
A parse() 0 22 3
A isAnonymousScheme() 0 5 3
A defaultScheme() 0 8 1
A get() 0 5 2
A valid() 0 3 1
1
<?php
2
3
namespace Thinktomorrow\Url;
4
5
use Thinktomorrow\Url\Exceptions\InvalidUrl;
6
7
class Root
8
{
9
    /** @var string|null */
10
    private $scheme;
11
12
    /** @var string|null */
13
    private $host;
14
15
    /** @var string|null */
16
    private $port;
17
18
    /** @var bool */
19 10
    private $anonymousScheme;
20
21 10
    /** @var null|string */
22
    private $defaultScheme;
23 9
24 9
    /** @var bool */
25
    private $secure = false;
26 9
27
    /** @var bool */
28 10
    private $valid = false;
29
30 10
    private function __construct(?string $scheme = null, ?string $host = null, ?string $port = null, bool $anonymousScheme, ?string $defaultScheme = 'http://')
31
    {
32
        $this->scheme = $scheme;
33 9
        $this->host = $host;
34
        $this->port = $port;
35 9
        $this->anonymousScheme = $anonymousScheme;
36 9
        $this->defaultScheme = $defaultScheme;
37
38 9
        if (false !== filter_var($this->get(), FILTER_VALIDATE_URL)) {
39
            $this->valid = true;
40
        }
41 1
42
        if ($this->composeScheme() == 'https://') {
43 1
            $this->secure();
44
        }
45
    }
46 7
47
    public static function fromString(string $host)
48 7
    {
49 7
        $parsed = static::parse($host);
50
51 7
        return new static($parsed['scheme'], $parsed['host'], $parsed['port'], $parsed['anonymousScheme']);
52
    }
53
54 3
    public function get()
55
    {
56 3
        return $this->composeScheme() .
57
                $this->host() .
58
                ( $this->port() ? ':'.$this->port : null );
59 1
    }
60
61 1
    public function valid(): bool
62
    {
63
        return $this->valid;
64 10
    }
65
66
    public function secure(): self
67 10
    {
68 1
        $this->secure = true;
69
        $this->scheme = 'https';
70
71 10
        return $this;
72 10
    }
73 1
74
    private function composeScheme()
75
    {
76
        return $this->scheme() ? $this->scheme().'://' : ($this->anonymousScheme ? '//' : $this->defaultScheme);
77
    }
78 9
79
    public function replaceScheme(string $scheme): self
80 9
    {
81 9
        return new static(
82 7
            $scheme,
83
            $this->host,
84
            $this->port,
85 9
            false,
86 9
            $this->defaultScheme,
87 9
        );
88
    }
89 1
90
    public function defaultScheme(?string $scheme = null): self
91 1
    {
92
        return new static(
93
            $this->scheme,
94 9
            $this->host,
95
            $this->port,
96 9
            $this->anonymousScheme,
97 7
            $scheme
98
        );
99
    }
100 6
101 1
    public function host(): ?string
102 6
    {
103
        return $this->host;
104
    }
105
106
    public function scheme(): ?string
107
    {
108
        return $this->scheme;
109
    }
110
111
    public function port(): ?string
112
    {
113
        return $this->port;
114
    }
115
116
    private static function parse(string $url)
117
    {
118
        if (in_array($url, ['//','/'])){
119
            return [
120
                'scheme'          => null,
121
                'host'            => null,
122
                'port'            => null,
123
                'anonymousScheme' => false,
124
            ];
125
        }
126
127
        $parsed = parse_url($url);
128
129
        if (false === $parsed) {
130
            throw new InvalidUrl('Failed to parse url. Invalid url ['.$url.'] passed as parameter.');
131
        }
132
133
        return [
134
            'scheme'          => $parsed['scheme'] ?? null,
135
            'host'            => static::parseHost($parsed),
136
            'port'            => $parsed['port'] ?? null,
137
            'anonymousScheme' => static::isAnonymousScheme($url),
138
        ];
139
    }
140
141
    public function __toString(): string
142
    {
143
        return $this->get();
144
    }
145
146
    /**
147
     * If an url is passed with anonymous scheme, e.g. //example.com, parse_url will ignore this and
148
     * strip the first tags so we need to explicitly reassemble the 'anonymous scheme' manually
149
     *
150
     * @param string $host
151
     * @return bool
152
     */
153
    private static function isAnonymousScheme(string $host): bool
154
    {
155
        $parsed = parse_url($host);
156
157
        return !isset($parsed['scheme']) && (0 === strpos($host, '//') && isset($parsed['host']));
158
    }
159
160
    private static function parseHost(array $parsed): ?string
161
    {
162
        if (isset($parsed['host'])) {
163
            return $parsed['host'];
164
        }
165
166
        if(!isset($parsed['path'])) return null;
167
168
        // e.g. /foo/bar
169
        if((0 === strpos($parsed['path'], '/'))) return null;
170
171
        // Invalid tld (missing .tld)
172
        if(false == strpos($parsed['path'], '.')) return null;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($parsed['path'], '.') of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
173
174
        // e.g. example.com/foo
175
        if( (0 < strpos($parsed['path'], '/')) ) return substr($parsed['path'], 0, strpos($parsed['path'], '/'));
176
177
        // e.g. foo or example.com
178
        return $parsed['path'];
179
    }
180
}
181