Completed
Push — master ( 922528...99faf5 )
by Ben
04:00
created

UrlParser::absolute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Thinktomorrow\Locale\Parsers;
4
5
use Illuminate\Routing\UrlGenerator;
6
use Thinktomorrow\Locale\Locale;
7
8
class UrlParser implements Parser
9
{
10
    /**
11
     * @var Locale
12
     */
13
    private $locale;
14
15
    /**
16
     * If locale is explicitly passed, we will set it
17
     * If null is passed it means the default locale must be used
18
     *
19
     * @var string
20
     */
21
    private $localeslug = false;
22
23
    /**
24
     * @var array
25
     */
26
    private $parsed;
27
28
    /**
29
     * @var bool
30
     */
31
    private $secure = false;
32
33
    /**
34
     * @var array
35
     */
36
    private $parameters = [];
37
38
    /**
39
     * Internal flag to keep track of schemeless url
40
     * @var bool
41
     */
42
    private $schemeless = false;
43
44
    /**
45
     * @var UrlGenerator
46
     */
47
    private $generator;
48
49 141
    public function __construct(Locale $locale, UrlGenerator $generator)
50
    {
51 141
        $this->locale = $locale;
52 141
        $this->generator = $generator;
53 141
    }
54
55
    /**
56
     * Set the original url/uri
57
     *
58
     * @param $url
59
     * @return $this
60
     */
61 135
    public function set($url)
62
    {
63 135
        $this->parsed = parse_url($url);
0 ignored issues
show
Documentation Bug introduced by
It seems like parse_url($url) can also be of type false. However, the property $parsed is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
64
65 135
        if(false === $this->parsed)
66 135
        {
67 3
            throw new \InvalidArgumentException('Failed to parse url. Invalid url ['.$url.'] passed as parameter.');
68
        }
69
70
        // If a schemeless url is passed, parse_url will ignore this and strip the first tags
71
        // so we keep a reminder to explicitly reassemble the 'anonymous scheme' manually
72 132
        $this->schemeless = (0 === strpos($url,'//') && isset($this->parsed['host']));
73
74 132
        return $this;
75
    }
76
77
    /**
78
     * Get the localized url
79
     *
80
     * @return string
81
     */
82 135
    public function get()
83
    {
84 135
        $this->assertUrlExists();
85
86
        // Only localize the url if a locale is passed.
87 132
        if(false !== $this->localeslug)
88 132
        {
89 123
            $this->localizePath($this->localeslug);
90 123
        }
91
92 132
        $url = $this->generator->to($this->reassemble($this->parsed),$this->parameters,$this->secure);
93
94
        // Secure url is not enforced by the urlgenerator when a valid url is passed
95
        // So we enforce it after url generation
96 132
        if($this->secure && 0 === strpos($url,'http://'))
97 132
        {
98 6
            $url = str_replace('http://','https://',$url);
99 6
        }
100
101 132
        return $url;
102
    }
103
104
    /**
105
     * Resolve the route via the Illuminate UrlGenerator
106
     *
107
     * @param $routekey
108
     * @param array $parameters
109
     * @return string
110
     */
111 78
    public function resolveRoute($routekey, $parameters = [])
112
    {
113 78
        return $this->generator->route($routekey,$parameters,true);
114
    }
115
116
    /**
117
     * Place locale segment in front of url path
118
     * e.g. /foo/bar is transformed into /en/foo/bar
119
     *
120
     * @param null $localeslug
121
     * @return string
122
     */
123 126
    public function localize($localeslug = null)
124
    {
125 126
        $this->localeslug = $localeslug;
126
127 126
        return $this;
128
    }
129
130
    /**
131
     * @param array $parameters
132
     * @return $this
133
     */
134 33
    public function parameters(array $parameters = [])
135
    {
136 33
        $this->parameters = $parameters;
137
138 33
        return $this;
139
    }
140
141
    /**
142
     * @param bool $secure
143
     * @return $this
144
     */
145 39
    public function secure($secure = true)
146
    {
147 39
        $this->secure = !!$secure;
148
149 39
        return $this;
150
    }
151
152
    /**
153
     * Inject the locale slug in the uri
154
     *
155
     * @param null $locale
156
     */
157 123
    private function localizePath($locale = null)
158
    {
159 123
        $this->parsed['path'] = str_replace('//','/',
160 123
            '/'.$this->locale->getSlug($locale).$this->delocalizePath()
161 123
        );
162 123
    }
163
164
    /**
165
     * @return array
166
     */
167 123
    private function delocalizePath()
168
    {
169 123
        if (!isset($this->parsed['path'])) return null;
170
171 123
        $path_segments = explode('/', trim($this->parsed['path'], '/'));
172
173 123
        if (count($path_segments) < 1) return null;
174
175 123
        if ($path_segments[0] == $this->locale->getSlug($path_segments[0]) || $this->locale->isHidden($path_segments[0])) {
176 36
            unset($path_segments[0]);
177 36
        }
178
179 123
        return '/' . implode('/', $path_segments);
180
    }
181
182
    /**
183
     * Construct a full url with the parsed url elements
184
     * resulted from a parse_url() function call
185
     *
186
     * @param array $parsed
187
     * @return string
188
     */
189 132
    private function reassemble(array $parsed)
190
    {
191 132
        $scheme = (isset($parsed['scheme'])) ? $parsed['scheme'] . '://' : ($this->schemeless ? '//' : '');
192
193
        return
194
            $scheme
195 132
            .((isset($parsed['user'])) ? $parsed['user'] . ((isset($parsed['pass'])) ? ':' . $parsed['pass'] : '') .'@' : '')
196 132
            .((isset($parsed['host'])) ? $parsed['host'] : '')
197 132
            .((isset($parsed['port'])) ? ':' . $parsed['port'] : '')
198 132
            .((isset($parsed['path'])) ? $parsed['path'] : '')
199 132
            .((isset($parsed['query'])) ? '?' . $parsed['query'] : '')
200 132
            .((isset($parsed['fragment'])) ? '#' . $parsed['fragment'] : '');
201
    }
202
203 135
    private function assertUrlExists()
204
    {
205 135
        if (!$this->parsed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->parsed of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
206 3
            throw new \LogicException('Url is required. Please run UrlParser::set($url) first.');
207
        }
208
    }
209
}