Completed
Push — development ( 67765c...7029e6 )
by Andrij
18:12
created

UrlParser   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 302
rs 8.3999
c 0
b 0
f 0
wmc 38
lcom 1
cbo 1

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A parse() 0 11 2
B getFullUrl() 0 20 6
A getUrl() 0 3 1
A getLocale() 0 3 1
A getFilterSegments() 0 3 1
A getFilterSegment() 0 3 1
A getProperties() 0 3 1
A getBrands() 0 3 1
A getValues() 0 3 2
A getFirstProperty() 0 3 1
A getFirstValue() 0 4 1
A countBrands() 0 3 1
A countProperties() 0 3 1
A countValues() 0 3 1
A createSegments() 0 4 1
A detectLanguage() 0 8 3
A brandSegmentIsFirst() 0 6 3
B fetchFilterSegments() 0 21 5
A fetchBrands() 0 4 1
A fetchProperties() 0 14 1
A fetchUrl() 0 12 2
1
<?php namespace core\src;
2
3
use core\src\Exception\PageNotFoundException;
4
5
class UrlParser
6
{
0 ignored issues
show
introduced by
Opening brace of a class must be on the same line as the definition
Loading history...
7
8
    const SEPARATOR = '/';
9
10
    const VALUE_SEPARATOR = '-or-';
11
12
    const PREFIX_BRAND = 'brand-';
13
14
    const PREFIX_PROPERTY = 'property-';
15
16
    /**
17
     * @var array
18
     */
19
    private $properties = [];
20
21
    /**
22
     * @var array
23
     */
24
    private $brands = [];
25
26
    /**
27
     * Only filter segments
28
     * @var array
29
     */
30
    private $filterSegments;
31
32
    /**
33
     * Url segments without filter
34
     * @var array
35
     */
36
    private $urlSegments;
37
38
    /**
39
     * All segments
40
     * @var array
41
     */
42
    private $segments = [];
43
44
    /**
45
     * @var string
46
     */
47
    private $locale;
48
49
    /**
50
     * @var string
51
     */
52
    private $url;
53
54
    /**
55
     * @var string
56
     */
57
    private $fullUrl;
58
59
    /**
60
     * @var CoreConfiguration
61
     */
62
    private $coreConfiguration;
63
64
    /**
65
     * @var int
66
     */
67
    private $brandSegmentPosition = 0;
68
69
    /**
70
     * @var array
71
     */
72
    private $propertySegmentPositions = [];
73
74
    /**
75
     * @var string
76
     */
77
    private $paramsString;
78
79
    public function __construct(CoreConfiguration $coreConfiguration) {
80
81
        $this->coreConfiguration = $coreConfiguration;
82
    }
83
84
    /**
85
     * @param $url
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
86
     */
87
    public function parse($url) {
88
        list($this->fullUrl, $this->paramsString) = explode('?', $url);
89
        if (strpos($this->fullUrl, '/') == 0) {
90
            $this->fullUrl = (string) substr($this->fullUrl, 1);
91
        }
92
93
        $this->segments = $this->createSegments();
94
        $this->locale = $this->detectLanguage();
95
        $this->filterSegments = $this->fetchFilterSegments($this->segments);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->fetchFilterSegments($this->segments) can be null. However, the property $filterSegments is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
96
        $this->url = $this->fetchUrl();
97
    }
98
99
    public function getFullUrl($locale = true, $filterSegments = true, $getParams = true) {
100
101
        if ($filterSegments) {
102
            $url = $this->segments;
103
        } else {
104
            $url = $this->urlSegments;
105
        }
106
107
        if ($locale && $this->locale) {
108
            array_unshift($url, $this->locale);
109
        }
110
111
        $url = implode('/', $url);
112
113
        if ($getParams && $this->paramsString) {
114
            $url = $url . '?' . $this->paramsString;
115
        }
116
117
        return $url;
118
    }
119
120
    /**
121
     * Url without locale and filter parameters
122
     * @return string
123
     */
124
    public function getUrl() {
125
        return $this->url;
126
    }
127
128
    /**
129
     * Locale from url
130
     * @return string
131
     */
132
    public function getLocale() {
133
        return $this->locale;
134
    }
135
136
    /**
137
     * @return array
138
     */
139
    public function getFilterSegments() {
140
        return $this->filterSegments;
141
    }
142
143
    /**
144
     * @return string
145
     */
146
    public function getFilterSegment() {
147
        return implode(self::SEPARATOR, $this->filterSegments);
148
    }
149
150
    /**
151
     * @return array
152
     */
153
    public function getProperties() {
154
        return $this->properties;
155
    }
156
157
    /**
158
     * @return array
159
     */
160
    public function getBrands() {
161
        return $this->brands;
162
    }
163
164
    /**
165
     * @param $propertyName
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
166
     * @return array|null
167
     */
168
    public function getValues($propertyName) {
169
        return isset($this->properties[$propertyName]) ? $this->properties[$propertyName] : null;
170
    }
171
172
    /**
173
     * @return string csv_name
174
     */
175
    public function getFirstProperty() {
176
        return key($this->properties);
177
    }
178
179
    /**
180
     * @param string $property
181
     * @return string value
182
     */
183
    public function getFirstValue($property) {
184
        $values = $this->getValues($property);
185
        return reset($values);
186
    }
187
188
    /**
189
     * @return int
190
     */
191
    public function countBrands() {
192
        return count($this->brands);
193
    }
194
195
    /**
196
     * @return int
197
     */
198
    public function countProperties() {
199
        return count($this->properties);
200
    }
201
202
    /**
203
     * @param $propertyName
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
204
     * @return int
205
     */
206
    public function countValues($propertyName) {
207
        return count($this->getValues($propertyName));
208
    }
209
210
    /**
211
     * Explode url segments and check denied
212
     * @return array
213
     * @throws PageNotFoundException
0 ignored issues
show
introduced by
Comment missing or not on the next line for @throws tag in function comment
Loading history...
214
     */
215
    private function createSegments() {
216
        $segments = explode('/', $this->fullUrl);
217
        return $segments;
218
    }
219
220
    /**
221
     * @return string|null
222
     */
223
    private function detectLanguage() {
224
225
        $languageExists = count($this->segments) >= 1 && array_key_exists($this->segments[0], $this->coreConfiguration->getLanguages());
226
227
        if ($languageExists) {
228
            return array_shift($this->segments);
229
        }
230
    }
231
232
    public function brandSegmentIsFirst() {
233
        return
234
            $this->brandSegmentPosition < min($this->propertySegmentPositions)
235
            || empty($this->propertySegmentPositions)
236
            || $this->brandSegmentPosition == 0;
237
    }
238
239
    /**
240
     * @param $segments
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
241
     * @return array|null
242
     */
243
    private function fetchFilterSegments($segments) {
244
        $this->segments = $segments;
245
246
        foreach ($segments as $key => $segment) {
247
            if (stripos($segment, self::PREFIX_BRAND) === 0) {
248
                $this->fetchBrands($segment);
249
                $filterSegments[] = $segment;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$filterSegments was never initialized. Although not strictly required by PHP, it is generally a good practice to add $filterSegments = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
250
                $this->brandSegmentPosition = $key;
0 ignored issues
show
Documentation Bug introduced by
It seems like $key can also be of type string. However, the property $brandSegmentPosition is declared as type integer. 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...
251
            } elseif (stripos($segment, self::PREFIX_PROPERTY) === 0) {
252
                $this->fetchProperties($segment);
253
                $filterSegments[] = $segment;
254
                $this->propertySegmentPositions[] = $key;
255
            } else {
256
                $this->urlSegments[] = $segment;
257
            }
258
259
        }
260
261
        return is_array($filterSegments) ? $filterSegments : null;
262
263
    }
264
265
    /**
266
     * @param array $segment
267
     */
268
    private function fetchBrands($segment) {
269
        $segment = substr($segment, strlen(self::PREFIX_BRAND));
270
        $this->brands = explode(self::VALUE_SEPARATOR, $segment);
271
    }
272
273
    /**
274
     * @param array $segment
275
     */
276
    private function fetchProperties($segment) {
277
        $segment = substr($segment, strlen(self::PREFIX_PROPERTY));
278
        $values = explode(self::VALUE_SEPARATOR, $segment);
279
280
        $firstValue = $values[0];
281
        $valueSeparator = strrpos($firstValue, '-');
282
        $property = substr($firstValue, 0, $valueSeparator);
283
        $value = substr($firstValue, $valueSeparator + 1);
284
285
        $values[0] = $value;
286
287
        $this->properties[$property] = $values;
288
289
    }
290
291
    /**
292
     * @return string
293
     */
294
    private function fetchUrl() {
295
296
        $segments = $this->segments;
297
298
        if (($filters = $this->filterSegments)) {
299
            $segments = array_slice($segments, 0, count($segments) - count($filters));
300
        }
301
302
        $categoryPath = implode('/', $segments);
303
304
        return $categoryPath;
305
    }
306
}