Passed
Push — refactor ( 0a349f...63f2db )
by Florian
02:41 queued 31s
created

Host::compile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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