Passed
Push — refactor ( 885968...d87829 )
by Florian
02:43
created

Host::scheme()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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|null
40
     */
41
    protected $token = null;
42
43
    /**
44
     * The host's regular expression pattern.
45
     *
46
     * @see Parser::compile()
47
     * @var string|null
48
     */
49
    protected $regex = null;
50
51
    /**
52
     * The host's variables.
53
     *
54
     * @see Parser::compile()
55
     * @var array|null
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 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 scheme(): ?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 pattern(): 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 token()
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 regex(): 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 variables()
167
    {
168
        if ($this->variables !== null) {
169 10
            return $this->variables;
170
        }
171
172 4
        $this->compile();
173
174 4
        return $this->variables;
175
    }
176
177
    /**
178
     * Compiles the host's patten.
179
     */
180
    protected function compile(): void
181
    {
182
        if ($this->pattern() === '*') {
183 2
            return;
184
        }
185
186 13
        $parser = $this->classes['parser'];
187 13
        $rule = $parser::compile($this->token());
188 13
        $this->regex = $rule[0];
189 13
        $this->variables = $rule[1];
190
    }
191
192
    /**
193
     * @inheritDoc
194
     */
195
    public function match($request, &$hostVariables = null): bool
196
    {
197
        $defaults = [
198
            'host'   => '*',
199
            'scheme' => '*'
200 12
        ];
201 12
        $request += $defaults;
202 12
        $scheme = $request['scheme'];
203 12
        $host = $request['host'];
204
205 12
        $hostVariables = [];
206
207 12
        $anyHost = $this->pattern() === '*' || $host === '*';
208 12
        $anyScheme = $this->scheme() === '*' || $scheme === '*';
209
210
211
        if ($anyHost) {
212
            if ($this->variables()) {
213 1
                $hostVariables = array_fill_keys(array_keys($this->variables()), null);
214
            }
215 3
            return $anyScheme || $this->scheme() === $scheme;
216
        }
217
218
        if (!$anyScheme && $this->scheme() !== $scheme) {
219 1
            return false;
220
        }
221
222
        if (!preg_match('~^' . $this->regex() . '$~', $host, $matches)) {
223 6
            $hostVariables = null;
224 6
            return false;
225
        }
226 8
        $i = 0;
227
228
        foreach ($this->variables() as $name => $pattern) {
229 8
            $hostVariables[$name] = $matches[++$i];
230
        }
231 8
        return true;
232
    }
233
234
    /**
235
     * @inheritDoc
236
     */
237
    public function link($params = [], $options = []): string
238
    {
239
        $defaults = [
240
            'scheme'   => $this->scheme()
241 8
        ];
242 8
        $options += $defaults;
243
244
        if (!isset($options['host'])) {
245 7
            $options['host'] = $this->_link($this->token(), $params);
246
        }
247
248 7
        $scheme = $options['scheme'] !== '*' ? $options['scheme'] . '://' : '//';
249
250 7
        return $scheme . $options['host'];
251
    }
252
253
    /**
254
     * Helper for `Host::link()`.
255
     *
256
     * @param  array $token  The token structure array.
257
     * @param  array $params The route parameters.
258
     * @return string The URL path representation of the token structure array.
259
     */
260
    protected function _link(array $token, array $params): string
261
    {
262 7
        $link = '';
263
        foreach ($token['tokens'] as $child) {
264
            if (is_string($child)) {
265 7
                $link .= $child;
266 7
                continue;
267
            }
268
            if (isset($child['tokens'])) {
269
                if ($child['repeat']) {
270 1
                    $name = $child['repeat'];
271 1
                    $values = isset($params[$name]) && $params[$name] !== null ? (array) $params[$name] : [];
272
                    foreach ($values as $value) {
273 1
                        $link .= $this->_link($child, [$name => $value] + $params);
274
                    }
275
                } else {
276
                    $link .= $this->_link($child, $params);
277
                }
278 1
                continue;
279
            }
280
            if (!array_key_exists($child['name'], $params)) {
281
                if (!$token['optional']) {
282 1
                    throw new RouterException("Missing parameters `'{$child['name']}'` for host: `'{$this->pattern}'`.");
283
                }
284
                return '';
285
            }
286 6
            $link .= $params[$child['name']];
287
        }
288 6
        return $link;
289
    }
290
}
291