Passed
Push — master ( 65ba04...698d84 )
by Ben
01:03 queued 11s
created

Root::isAnonymousScheme()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 2
nc 3
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 3
rs 10
c 0
b 0
f 0
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
    private $anonymousScheme;
20
21
    /** @var null|string */
22
    private $defaultScheme;
23
24
    /** @var bool */
25
    private $secure = false;
26
27
    /** @var bool */
28
    private $valid = false;
29
30 23
    private function __construct(?string $scheme = null, ?string $host = null, ?string $port = null, bool $anonymousScheme, ?string $defaultScheme = 'http://')
31
    {
32 23
        $this->scheme = $scheme;
33 23
        $this->host = $host;
34 23
        $this->port = $port;
35 23
        $this->anonymousScheme = $anonymousScheme;
36 23
        $this->defaultScheme = $defaultScheme;
37
38 23
        if (false !== filter_var($this->get(), FILTER_VALIDATE_URL)) {
39 21
            $this->valid = true;
40
        }
41
42 23
        if ($this->composeScheme() == 'https://') {
43 14
            $this->secure();
44
        }
45 23
    }
46
47 24
    public static function fromString(string $host)
48
    {
49 24
        return new static(...array_values(static::parse($host)));
50
    }
51
52 23
    public function get()
53
    {
54 23
        return $this->composeScheme() .
55 23
                $this->host() .
56 23
                ( $this->port() ? ':'.$this->port : null );
57
    }
58
59 1
    public function valid(): bool
60
    {
61 1
        return $this->valid;
62
    }
63
64 14
    public function secure(): self
65
    {
66 14
        $this->secure = true;
67 14
        $this->scheme = 'https';
68
69 14
        return $this;
70
    }
71
72 23
    private function composeScheme()
73
    {
74 23
        return $this->scheme() ? $this->scheme().'://' : ($this->anonymousScheme ? '//' : $this->defaultScheme);
75
    }
76
77 6
    public function replaceScheme(string $scheme): self
78
    {
79 6
        return new static(
80 6
            $scheme,
81 6
            $this->host,
82 6
            $this->port,
83 6
            false,
84 6
            $this->defaultScheme
85
        );
86
    }
87
88 17
    public function defaultScheme(?string $scheme = null): self
89
    {
90 17
        return new static(
91 17
            $this->scheme,
92 17
            $this->host,
93 17
            $this->port,
94 17
            $this->anonymousScheme,
95 17
            $scheme
96
        );
97
    }
98
99 23
    public function host(): ?string
100
    {
101 23
        return $this->host;
102
    }
103
104 23
    public function scheme(): ?string
105
    {
106 23
        return $this->scheme;
107
    }
108
109 23
    public function port(): ?string
110
    {
111 23
        return $this->port;
112
    }
113
114 24
    private static function parse(string $url)
115
    {
116 24
        if (in_array($url, ['//','/'])){
117
            return [
118 3
                'scheme'          => null,
119
                'host'            => null,
120
                'port'            => null,
121
                'anonymousScheme' => false,
122
            ];
123
        }
124
125 24
        $parsed = parse_url($url);
126
127 24
        if (false === $parsed) {
128 1
            throw new InvalidUrl('Failed to parse url. Invalid url ['.$url.'] passed as parameter.');
129
        }
130
131
        return [
132 23
            'scheme'          => $parsed['scheme'] ?? null,
133 23
            'host'            => static::parseHost($parsed),
134 23
            'port'            => $parsed['port'] ?? null,
135 23
            'anonymousScheme' => static::isAnonymousScheme($url),
136
        ];
137
    }
138
139 1
    public function __toString(): string
140
    {
141 1
        return $this->get();
142
    }
143
144
    /**
145
     * If an url is passed with anonymous scheme, e.g. //example.com, parse_url will ignore this and
146
     * strip the first tags so we need to explicitly reassemble the 'anonymous scheme' manually
147
     *
148
     * @param string $host
149
     * @return bool
150
     */
151 23
    private static function isAnonymousScheme(string $host): bool
152
    {
153 23
        $parsed = parse_url($host);
154
155 23
        return !isset($parsed['scheme']) && (0 === strpos($host, '//') && isset($parsed['host']));
156
    }
157
158 23
    private static function parseHost(array $parsed): ?string
159
    {
160 23
        if (isset($parsed['host'])) {
161 18
            return $parsed['host'];
162
        }
163
164 18
        if(!isset($parsed['path'])) return null;
165
166
        // e.g. /foo/bar
167 18
        if((0 === strpos($parsed['path'], '/'))) return null;
168
169
        // Invalid tld (missing .tld)
170 15
        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...
171
172
        // e.g. example.com/foo
173 10
        if( (0 < strpos($parsed['path'], '/')) ) return substr($parsed['path'], 0, strpos($parsed['path'], '/'));
174
175
        // e.g. foo or example.com
176 10
        return $parsed['path'];
177
    }
178
}
179