Host::_compile()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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