Completed
Push — master ( 0eceaa...aee3a6 )
by Ben
15:21 queued 05:47
created

Url::parse()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 10
nop 1
dl 0
loc 21
ccs 11
cts 11
cp 1
crap 5
rs 8.7624
c 0
b 0
f 0
1
<?php
2
3
namespace Thinktomorrow\Locale\Values;
4
5
use Thinktomorrow\Locale\Exceptions\InvalidUrl;
6
7
class Url
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
8
{
9
    private $parsed;
10
    private $root;
11
12
    private $secure;
13
    private $schemeless = false;
14
    private $absolute = false;
15
16 75
    private function __construct(string $url)
17
    {
18 75
        $this->parse($url);
19 74
    }
20
21 75
    public static function fromString(string $url)
22
    {
23 75
        return new self($url);
24
    }
25
26 9
    public function setCustomRoot(Root $root)
27
    {
28 9
        $this->root = $root;
29
30 9
        return $this;
31
    }
32
33 30
    public function secure($secure = true)
34
    {
35 30
        $this->secure = (bool) $secure;
36
37 30
        return $this;
38
    }
39
40 46
    public function get()
41
    {
42 46
        if ($this->root) {
43 9
            if ($this->secure) {
44 1
                $this->root->secure($this->secure);
0 ignored issues
show
Unused Code introduced by
The call to Root::secure() has too many arguments starting with $this->secure.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
45
            }
46
47
            // Path is reconstructed. Taken care of possible double slashes
48 9
            $path = str_replace('//', '/', '/'.trim($this->reassembleWithoutRoot(), '/'));
49 9
            if ($path == '/') {
50
                $path = '';
51
            }
52
53 9
            return $this->root->get().$path;
54
        }
55
56 39
        return $this->reassemble();
57
    }
58
59 1
    public function isAbsolute(): bool
60
    {
61 1
        return $this->absolute;
62
    }
63
64 44
    public function localize(string $localeSegment = null, array $available_locales = [])
65
    {
66 44
        $this->parsed['path'] = str_replace('//', '/',
67 44
            rtrim(
68 44
                '/'.trim($localeSegment.$this->delocalizePath($available_locales), '/'),
69 44
                '/'
70
            )
71
72
        );
73
74 44
        return $this;
75
    }
76
77 44
    private function delocalizePath(array $available_locales)
78
    {
79 44
        if (!isset($this->parsed['path'])) {
80 5
            return;
81
        }
82
83 44
        $path_segments = explode('/', trim($this->parsed['path'], '/'));
84
85
        // Remove the locale segment if present
86 44
        if (in_array($path_segments[0], array_keys($available_locales))) {
87 5
            unset($path_segments[0]);
88
        }
89
90 44
        return '/'.implode('/', $path_segments);
91
    }
92
93 75
    private function parse(string $url)
94
    {
95
        // Sanitize url input a bit to remove double slashes, but do not remove first slashes
96 75
        if ($url == '//') {
97 2
            $url = '/';
98
        }
99
100 75
        $this->parsed = parse_url($url);
101
102 75
        if (false === $this->parsed) {
103 2
            throw new InvalidUrl('Failed to parse url. Invalid url ['.$url.'] passed as parameter.');
104
        }
105
106
        // If a schemeless url is passed, parse_url will ignore this and strip the first tags
107
        // so we keep a reminder to explicitly reassemble the 'anonymous scheme' manually
108 74
        $this->schemeless = (0 === strpos($url, '//') && isset($this->parsed['host']));
109
110 74
        $this->absolute = (!preg_match('~^(#|//|https?://|mailto:|tel:)~', $url))
111 20
                ? filter_var($url, FILTER_VALIDATE_URL) !== false
112 71
                : true;
113 74
    }
114
115 1
    public function __toString(): string
116
    {
117 1
        return $this->get();
118
    }
119
120 39
    private function reassemble(): string
121
    {
122 39
        $scheme = (isset($this->parsed['scheme']))
123 29
            ? $this->parsed['scheme'].'://'
124 39
            : ($this->schemeless ? '//' : '');
125
126
        // Convert to secure scheme if needed or vice versa
127 39
        if ($scheme == 'http://' && $this->secure) {
128 6
            $scheme = 'https://';
129 35
        } elseif ($scheme == 'https://' && false === $this->secure) {
130 1
            $scheme = 'http://';
131
        }
132
133
        return
134
            $scheme
135 39
            .((isset($this->parsed['user'])) ? $this->parsed['user'].((isset($this->parsed['pass'])) ? ':'.$this->parsed['pass'] : '').'@' : '')
136 39
            .((isset($this->parsed['host'])) ? $this->parsed['host'] : '')
137 39
            .((isset($this->parsed['port'])) ? ':'.$this->parsed['port'] : '')
138 39
            .((isset($this->parsed['path'])) ? $this->parsed['path'] : '')
139 39
            .((isset($this->parsed['query'])) ? '?'.$this->parsed['query'] : '')
140 39
            .((isset($this->parsed['fragment'])) ? '#'.$this->parsed['fragment'] : '');
141
    }
142
143
    /**
144
     * Construct a full url with the parsed url elements
145
     * resulted from a parse_url() function call.
146
     *
147
     * @return string
148
     */
149 9
    private function reassembleWithoutRoot()
150
    {
151 9
        if (!$this->root) {
152
            return $this->reassemble();
153
        }
154
155
        /**
156
         * In some rare conditions the path in interpreted as the host when there is no domain.tld format given.
157
         * This is still considered a valid url, be it with only a tld as indication.
158
         */
159 9
        $path = (isset($this->parsed['path']) && $this->parsed['path'] != $this->root->host())
160 9
                    ? $this->parsed['path']
161 9
                    : '';
162
163
        return $path
164 9
            .((isset($this->parsed['query'])) ? '?'.$this->parsed['query'] : '')
165 9
            .((isset($this->parsed['fragment'])) ? '#'.$this->parsed['fragment'] : '');
166
    }
167
}
168