Url::match()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
namespace Weew\Url;
4
5
use Weew\Collections\IDictionary;
6
use Weew\UrlMatcher\IUrlMatcher;
7
use Weew\UrlMatcher\UrlMatcher;
8
9
class Url implements IUrl {
10
    /**
11
     * @var IUrlParser
12
     */
13
    protected $parser;
14
15
    /**
16
     * @var IUrlMatcher
17
     */
18
    protected $matcher;
19
20
    /**
21
     * @var IUrlBuilder
22
     */
23
    protected $builder;
24
25
    /**
26
     * @var string
27
     */
28
    protected $protocol;
29
30
    /**
31
     * @var string
32
     */
33
    protected $tld;
34
35
    /**
36
     * @var string
37
     */
38
    protected $domain;
39
40
    /**
41
     * @var string
42
     */
43
    protected $subdomain;
44
45
    /**
46
     * @var string
47
     */
48
    protected $port;
49
50
    /**
51
     * @var string
52
     */
53
    protected $username;
54
55
    /**
56
     * @var string
57
     */
58
    protected $password;
59
60
    /**
61
     * @var string
62
     */
63
    protected $path;
64
65
    /**
66
     * @var IUrlQuery
67
     */
68
    protected $query;
69
70
    /**
71
     * @var string
72
     */
73
    protected $fragment;
74
75
    /**
76
     * @param string ...$url
77
     */
78
    public function __construct($url = '') {
0 ignored issues
show
Unused Code introduced by
The parameter $url is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
79
        $this->builder = $this->createBuilder();
80
        $this->parser = $this->createParser();
81
        $this->matcher = $this->createMatcher();
82
83
        $this->build(implode('/', func_get_args()));
84
    }
85
86
    /**
87
     * @return string
88
     */
89
    public function getProtocol() {
90
        return $this->protocol;
91
    }
92
93
    /**
94
     * @param string $protocol
95
     */
96
    public function setProtocol($protocol) {
97
        $this->protocol = $protocol;
98
    }
99
100
    /**
101
     * @return string
102
     */
103
    public function getHost() {
104
        return $this->builder->buildHost(
105
            $this->getTLD(), $this->getDomain(), $this->getSubdomain()
106
        );
107
    }
108
109
    /**
110
     * @param string $host
111
     */
112
    public function setHost($host) {
113
        $parts = $this->parser->parseHost($host);
114
        $this->setTLD(array_get($parts, 'tld'));
115
        $this->setDomain(array_get($parts, 'domain'));
116
        $this->setSubdomain(array_get($parts, 'subdomain'));
117
    }
118
119
    /**
120
     * @return string
121
     */
122
    public function getTLD() {
123
        return $this->tld;
124
    }
125
126
    /**
127
     * @param string $tld
128
     */
129
    public function setTLD($tld) {
130
        $this->tld = $tld;
131
    }
132
133
    /**
134
     * @return string
135
     */
136
    public function getDomain() {
137
        return $this->domain;
138
    }
139
140
    /**
141
     * @param string $domain
142
     */
143
    public function setDomain($domain) {
144
        $this->domain = $domain;
145
    }
146
147
    /**
148
     * @return string
149
     */
150
    public function getSubdomain() {
151
        return $this->subdomain;
152
    }
153
154
    /**
155
     * @param string $subdomain
156
     */
157
    public function setSubdomain($subdomain) {
158
        $this->subdomain = $subdomain;
159
    }
160
161
    /**
162
     * @return string
163
     */
164
    public function getPort() {
165
        return $this->port;
166
    }
167
168
    /**
169
     * @param string $port
170
     */
171
    public function setPort($port) {
172
        $this->port = $port;
173
    }
174
175
    /**
176
     * @return string
177
     */
178
    public function getUsername() {
179
        return $this->username;
180
    }
181
182
    /**
183
     * @param string $username
184
     */
185
    public function setUsername($username) {
186
        $this->username = $username;
187
    }
188
189
    /**
190
     * @return string
191
     */
192
    public function getPassword() {
193
        return $this->password;
194
    }
195
196
    /**
197
     * @param string $password
198
     */
199
    public function setPassword($password) {
200
        $this->password = $password;
201
    }
202
203
    /**
204
     * @return string
205
     */
206
    public function getPath() {
207
        return $this->path;
208
    }
209
210
    /**
211
     * @param string $path
212
     */
213
    public function setPath($path) {
214
        $this->path = $this->addLeadingSlash($path);
215
    }
216
217
    /**
218
     * @param string $path
219
     */
220
    public function addPath($path) {
221
        $this->setPath(
222
            $this->getPath() . $this->addLeadingSlash($path)
223
        );
224
    }
225
226
    /**
227
     * @param string $pattern
228
     * @param array $patterns
229
     *
230
     * @return bool
231
     */
232
    public function match($pattern, array $patterns = []) {
233
        return $this->matcher->match(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->matcher->m...>getPath(), $patterns); (boolean) is incompatible with the return type declared by the interface Weew\Url\IUrl::match of type Weew\Url\true.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
234
            $this->addLeadingSlash($pattern), $this->getPath(), $patterns
235
        );
236
    }
237
238
    /**
239
     * @param string $pattern
240
     * @param array $patterns
241
     *
242
     * @return IDictionary
243
     */
244
    public function parse($pattern, array $patterns = []) {
245
        return $this->matcher->parse(
246
            $pattern, $this->getPath(), $patterns
247
        );
248
    }
249
250
    /**
251
     * @param string $key
252
     * @param string $value
253
     */
254
    public function replace($key, $value) {
255
        $url = $this->matcher->replace(
256
            $this->toString(), $key, $value
257
        );
258
259
        $this->build($url);
260
    }
261
262
    /**
263
     * @param array $replacements
264
     */
265
    public function replaceAll(array $replacements) {
266
        $url = $this->matcher->replaceAll(
267
            $this->toString(), $replacements
268
        );
269
270
        $this->build($url);
271
    }
272
273
    /**
274
     * @return IUrlQuery
275
     */
276
    public function getQuery() {
277
        return $this->query;
278
    }
279
280
    /**
281
     * @param IUrlQuery $query
282
     */
283
    public function setQuery(IUrlQuery $query) {
284
        $this->query = $query;
285
    }
286
287
    /**
288
     * @return string
289
     */
290
    public function getFragment() {
291
        return $this->fragment;
292
    }
293
294
    /**
295
     * @param string $fragment
296
     */
297
    public function setFragment($fragment) {
298
        $this->fragment = $fragment;
299
    }
300
301
    /**
302
     * @param bool $encode
303
     *
304
     * @return string
305
     */
306
    public function toString($encode = false) {
307
        return $this->buildUrl($encode);
308
    }
309
310
    /**
311
     * @return string
312
     */
313
    public function __toString() {
314
        return $this->toString();
315
    }
316
317
    /**
318
     * @return array
319
     */
320
    public function toArray() {
321
        return [
322
            'protocol' => $this->getProtocol(),
323
            'tld' => $this->getTLD(),
324
            'domain' => $this->getDomain(),
325
            'subdomain' => $this->getSubdomain(),
326
            'host' => $this->getHost(),
327
            'port' => $this->getPort(),
328
            'path' => $this->getPath(),
329
            'query' => $this->getQuery()->toArray(),
330
            'username' => $this->getUsername(),
331
            'password' => $this->getPassword(),
332
            'fragment' => $this->getFragment(),
333
            'full' => $this->toString(),
334
        ];
335
    }
336
337
    /**
338
     * @param string $url
339
     */
340
    protected function build($url) {
341
        $parts = $this->parser->parse($url);
342
343
        $this->setProtocol(array_get($parts, 'protocol'));
344
        $this->setTLD(array_get($parts, 'tld'));
345
        $this->setDomain(array_get($parts, 'domain'));
346
        $this->setSubdomain(array_get($parts, 'subdomain'));
347
        $this->setPath(array_get($parts, 'path'));
348
        $this->setPort(array_get($parts, 'port'));
349
        $this->setUsername(array_get($parts, 'username'));
350
        $this->setPassword(array_get($parts, 'password'));
351
        $this->setQuery(new UrlQuery(array_get($parts, 'query')));
352
        $this->setFragment(array_get($parts, 'fragment'));
353
    }
354
355
    /**
356
     * @param $segment
357
     *
358
     * @return string
359
     */
360
    protected function addLeadingSlash($segment) {
361
        if ($segment && substr($segment, 0, 1) != '/') {
362
            $segment = s('/%s', $segment);
363
        }
364
365
        return $segment;
366
    }
367
368
    /**
369
     * @param bool $encode
370
     *
371
     * @return string
372
     */
373
    protected function buildUrl($encode = false) {
374
        return $this->builder->build($this, $encode);
375
    }
376
377
    /**
378
     * @return IUrlBuilder
379
     */
380
    protected function createBuilder() {
381
        return new UrlBuilder();
382
    }
383
384
    /**
385
     * @return IUrlParser
386
     */
387
    protected function createParser() {
388
        return new UrlParser();
389
    }
390
391
    /**
392
     * @return IUrlMatcher
393
     */
394
    protected function createMatcher() {
395
        return new UrlMatcher();
396
    }
397
}
398