Root::get()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 2
rs 10
c 2
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Thinktomorrow\Url;
5
6
use JetBrains\PhpStorm\Pure;
7
use Thinktomorrow\Url\Exceptions\InvalidUrl;
8
9
class Root
10
{
11
    private ?string $scheme;
12
    private ?string $host;
13
    private ?string $port;
14
15
    private bool $anonymousScheme;
16
    private ?string $defaultScheme;
17
    private bool $valid;
18
19
    private function __construct(?string $scheme = null, ?string $host = null, ?string $port = null, bool $anonymousScheme = false, ?string $defaultScheme = 'https://')
20
    {
21
        $this->scheme = $scheme;
22
        $this->host = $host;
23
        $this->port = $port;
24
        $this->anonymousScheme = $anonymousScheme;
25
        $this->defaultScheme = $defaultScheme;
26
27
        $this->valid = (false !== filter_var($this->get(), FILTER_VALIDATE_URL));
28
29
//        if ($this->composeScheme() == 'https://') {
30 26
//            $this->secure();
31
//        }
32 26
    }
33 26
34 26
    public static function fromString(string $host): self
35 26
    {
36 26
        return new static(...array_values(static::parse($host)));
0 ignored issues
show
Bug introduced by
array_values(static::parse($host)) is expanded, but the parameter $scheme of Thinktomorrow\Url\Root::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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