Host::match()   B
last analyzed

Complexity

Conditions 10
Paths 32

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 22
c 1
b 0
f 0
dl 0
loc 37
ccs 15
cts 15
cp 1
rs 7.6666
cc 10
nc 32
nop 2
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Lead\Router;
5
6
use Lead\Router\Exception\RouterException;
7
8
/**
9
 * Defines a Host Pattern to match
10
 */
11
class Host implements HostInterface
12
{
13
    /**
14
     * Class dependencies.
15
     *
16
     * @var array
17
     */
18
    protected $_classes = [];
19
20
    /**
21
     * The matching scheme.
22
     *
23
     * @var string
24
     */
25
    protected $_scheme = '*';
26
27
    /**
28
     * The matching host.
29
     *
30
     * @var string
31
     */
32
    protected $_pattern = '*';
33
34
    /**
35
     * The tokens structure extracted from host's pattern.
36
     *
37
     * @see Parser::tokenize()
38
     * @var array
39
     */
40
    protected $_token = null;
41
42
    /**
43
     * The host's regular expression pattern.
44
     *
45
     * @see Parser::compile()
46
     * @var string
47
     */
48
    protected $_regex = null;
49
50
    /**
51
     * The host's variables.
52
     *
53
     * @see Parser::compile()
54
     * @var array
55
     */
56
    protected $_variables = null;
57
58
    /**
59
     * Constructs a route
60
     *
61
     * @param array $config The config array.
62
     */
63
    public function __construct($config = [])
64
    {
65
        $defaults = [
66
            'scheme'     => '*',
67
            'pattern'     => '*',
68
            'classes'    => [
69
                'parser' => 'Lead\Router\Parser'
70
            ]
71 25
        ];
72 25
        $config += $defaults;
73
74 25
        $this->_classes = $config['classes'];
75
76 25
        $this->setScheme($config['scheme']);
77 25
        $this->setPattern($config['pattern']);
78
    }
79
80
    /**
81
     * Sets the scheme
82
     *
83
     * @param string $scheme Scheme to set.
84
     * @return $this
85
     */
86
    public function setScheme(string $scheme)
87
    {
88 25
        $this->_scheme = $scheme;
89
90 25
        return $this;
91
    }
92
93
    /**
94
     * Gets the scheme
95
     *
96
     * @return string|null
97
     */
98
    public function getScheme(): ?string
99
    {
100 21
        return $this->_scheme;
101
    }
102
103
    /**
104
     * Sets the hosts pattern
105
     *
106
     * @param string $pattern Pattern
107
     * @return $this
108
     */
109
    public function setPattern(string $pattern)
110
    {
111 25
        $this->_token = null;
112 25
        $this->_regex = null;
113 25
        $this->_variables = null;
114 25
        $this->_pattern = $pattern;
115
116 25
        return $this;
117
    }
118
119
    /**
120
     * Gets the hosts pattern
121
     *
122
     * @return string
123
     */
124
    public function getPattern(): string
125
    {
126 15
        return $this->_pattern;
127
    }
128
129
    /**
130
     * Returns the route's token structures.
131
     *
132
     * @return array A collection route's token structure.
133
     */
134
    protected function getToken()
135
    {
136
        if ($this->_token === null) {
137 20
            $parser = $this->_classes['parser'];
138 20
            $this->_token = [];
139 20
            $this->_regex = null;
140 20
            $this->_variables = null;
141 20
            $this->_token = $parser::tokenize($this->_pattern, '.');
142
        }
143 20
        return $this->_token;
144
    }
145
146
    /**
147
     * Gets the route's regular expression pattern.
148
     *
149
     * @return string the route's regular expression pattern.
150
     */
151
    public function getRegex(): string
152
    {
153
        if ($this->_regex !== null) {
154 7
            return $this->_regex;
155
        }
156 11
        $this->_compile();
157
158 11
        return $this->_regex;
159
    }
160
161
    /**
162
     * Gets the route's variables and their associated pattern in case of array variables.
163
     *
164
     * @return array The route's variables and their associated pattern.
165
     */
166
    public function getVariables()
167
    {
168
        if ($this->_variables !== null) {
169 10
            return $this->_variables;
170
        }
171 4
        $this->_compile();
172 4
        return $this->_variables;
173
    }
174
175
    /**
176
     * Compiles the host's patten.
177
     */
178
    protected function _compile()
179
    {
180
        if ($this->getPattern() === '*') {
181 2
            return;
182
        }
183 13
        $parser = $this->_classes['parser'];
184 13
        $rule = $parser::compile($this->getToken());
185 13
        $this->_regex = $rule[0];
186 13
        $this->_variables = $rule[1];
187
    }
188
189
    /**
190
     * @inheritDoc
191
     */
192
    public function match($request, &$hostVariables = null): bool
193
    {
194
        $defaults = [
195
            'host'   => '*',
196
            'scheme' => '*'
197 12
        ];
198 12
        $request += $defaults;
199 12
        $scheme = $request['scheme'];
200 12
        $host = $request['host'];
201
202 12
        $hostVariables = [];
203
204 12
        $anyHost = $this->getPattern() === '*' || $host === '*';
205 12
        $anyScheme = $this->getScheme() === '*' || $scheme === '*';
206
207
208
        if ($anyHost) {
209
            if ($this->getVariables()) {
210 1
                $hostVariables = array_fill_keys(array_keys($this->getVariables()), null);
211
            }
212 3
            return $anyScheme || $this->getScheme() === $scheme;
213
        }
214
215
        if (!$anyScheme && $this->getScheme() !== $scheme) {
216 1
            return false;
217
        }
218
219
        if (!preg_match('~^' . $this->getRegex() . '$~', $host, $matches)) {
220 6
            $hostVariables = null;
221 6
            return false;
222
        }
223 8
        $i = 0;
224
225
        foreach ($this->getVariables() as $name => $pattern) {
226 8
            $hostVariables[$name] = $matches[++$i];
227
        }
228 8
        return true;
229
    }
230
231
    /**
232
     * @inheritDoc
233
     */
234
    public function link($params = [], $options = []): string
235
    {
236
        $defaults = [
237
            'scheme'   => $this->getScheme()
238 8
        ];
239 8
        $options += $defaults;
240
241
        if (!isset($options['host'])) {
242 7
            $options['host'] = $this->_link($this->getToken(), $params);
243
        }
244
245 7
        $scheme = $options['scheme'] !== '*' ? $options['scheme'] . '://' : '//';
246 7
        return $scheme . $options['host'];
247
    }
248
249
    /**
250
     * Helper for `Host::link()`.
251
     *
252
     * @param  array $token  The token structure array.
253
     * @param  array $params The route parameters.
254
     * @return string The URL path representation of the token structure array.
255
     */
256
    protected function _link($token, $params): string
257
    {
258 7
        $link = '';
259
        foreach ($token['tokens'] as $child) {
260
            if (is_string($child)) {
261 7
                $link .= $child;
262 7
                continue;
263
            }
264
            if (isset($child['tokens'])) {
265
                if ($child['repeat']) {
266 1
                    $name = $child['repeat'];
267 1
                    $values = isset($params[$name]) && $params[$name] !== null ? (array) $params[$name] : [];
268
                    foreach ($values as $value) {
269 1
                        $link .= $this->_link($child, [$name => $value] + $params);
270
                    }
271
                } else {
272
                    $link .= $this->_link($child, $params);
273
                }
274 1
                continue;
275
            }
276
            if (!array_key_exists($child['name'], $params)) {
277
                if (!$token['optional']) {
278 1
                    throw new RouterException("Missing parameters `'{$child['name']}'` for host: `'{$this->_pattern}'`.");
279
                }
280
                return '';
281
            }
282 6
            $link .= $params[$child['name']];
283
        }
284 6
        return $link;
285
    }
286
}
287