Test Failed
Pull Request — master (#1)
by Ben
01:43
created

Root::isAnonymousScheme()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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