Completed
Pull Request — 3.x (#127)
by Joschi
02:04
created

Generator   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 14
Bugs 0 Features 1
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 276
c 14
b 0
f 1
ccs 65
cts 65
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A generate() 0 4 1
A generateRaw() 0 4 1
A buildUrl() 0 17 4
A buildWildcardReplacement() 0 10 4
A __construct() 0 5 1
A build() 0 15 1
A buildTokenReplacements() 0 6 2
A buildOptionalReplacements() 0 17 2
A buildOptionalReplacement() 0 15 3
A encode() 0 8 3
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Router;
10
11
/**
12
 *
13
 * Generates URL paths from routes.
14
 *
15
 * @package Aura.Router
16
 *
17
 */
18
class Generator
19
{
20
    /**
21
     *
22
     * The map of all routes.
23
     *
24
     * @var Map
25
     *
26
     */
27
    protected $map;
28
29
    /**
30
     *
31
     * The route from which the URL is being generated.
32
     *
33
     * @var Route
34
     *
35
     */
36
    protected $route;
37
38
    /**
39
     *
40
     * The URL being generated.
41
     *
42
     * @var string
43
     *
44
     */
45
    protected $url;
46
47
    /**
48
     *
49
     * Data being interpolated into the URL.
50
     *
51
     * @var array
52
     *
53
     */
54
    protected $data;
55
56
    /**
57
     *
58
     * Replacement data.
59
     *
60
     * @var array
61
     *
62
     */
63
    protected $repl;
64
65
    /**
66
     *
67
     * Leave values raw?
68
     *
69
     * @var bool
70
     *
71
     */
72
    protected $raw;
73
74
    /**
75
     *
76
     * The basepath to prefix to generated paths.
77
     *
78
     * @var string
79
     *
80
     */
81
    protected $basepath;
82
83
    /**
84
     *
85
     * Constructor.
86
     *
87
     * @param Map $map A route collection object.
88
     *
89
     * @param string $basepath The basepath to prefix to generated paths.
90
     *
91
     */
92 12
    public function __construct(Map $map, $basepath = null)
93
    {
94 12
        $this->map = $map;
95 12
        $this->basepath = $basepath;
96 12
    }
97
98
    /**
99
     *
100
     * Looks up a route by name, and interpolates data into it to return
101
     * a URI path.
102
     *
103
     * @param string $name The route name to look up.
104
     *
105
     * @param array $data The data to interpolate into the URI; data keys
106
     * map to attribute tokens in the path.
107
     *
108
     * @return string|false A URI path string if the route name is found, or
109
     * boolean false if not.
110
     *
111
     * @throws Exception\RouteNotFound
112
     *
113
     */
114 11
    public function generate($name, $data = [])
115
    {
116 11
        return $this->build($name, $data, false);
117
    }
118
119
    /**
120
     *
121
     * Generate the route without url encoding.
122
     *
123
     * @param string $name The route name to look up.
124
     *
125
     * @param array $data The data to interpolate into the URI; data keys
126
     * map to attribute tokens in the path.
127
     *
128
     * @return string|false A URI path string if the route name is found, or
129
     * boolean false if not.
130
     *
131
     * @throws Exception\RouteNotFound
132
     *
133
     */
134 1
    public function generateRaw($name, $data = [])
135
    {
136 1
        return $this->build($name, $data, true);
137
    }
138
139
    /**
140
     *
141
     * Gets the URL for a Route.
142
     *
143
     * @param string $name The route name to look up.
144
     *
145
     * @param array $data An array of key-value pairs to interpolate into the
146
     * attribute tokens in the path for the Route.
147
     *
148
     * @param bool $raw Leave the data unencoded?
149
     *
150
     * @return string
151
     *
152
     */
153 12
    protected function build($name, $data, $raw)
154
    {
155 12
        $this->raw = $raw;
156 12
        $this->route = $this->map->getRoute($name);
157 11
        $this->buildUrl();
158 11
        $this->repl = [];
159 11
        $this->data = array_merge($this->route->defaults, $data);
160
161 11
        $this->buildTokenReplacements();
162 11
        $this->buildOptionalReplacements();
163 11
        $this->url = strtr($this->url, $this->repl);
164 11
        $this->buildWildcardReplacement();
165
166 11
        return $this->url;
167
    }
168
169
    /**
170
     *
171
     * Builds the URL property.
172
     *
173
     * @return null
174
     *
175
     */
176 11
    protected function buildUrl()
177
    {
178 11
        $this->url = $this->basepath . $this->route->path;
179
180 11
        $host = $this->route->host;
181 11
        if (! $host) {
182 8
            return;
183
        }
184 3
        $this->url = '//' . $host . $this->url;
185
186 3
        $secure = $this->route->secure;
187 3
        if ($secure === null) {
188 1
            return;
189
        }
190 2
        $protocol = $secure ? 'https:' : 'http:';
191 2
        $this->url = $protocol . $this->url;
192 2
    }
193
194
    /**
195
     *
196
     * Builds urlencoded data for token replacements.
197
     *
198
     * @return array
199
     *
200
     */
201 11
    protected function buildTokenReplacements()
202
    {
203 11
        foreach ($this->data as $key => $val) {
204 11
            $this->repl["{{$key}}"] = $this->encode($val);
205 11
        }
206 11
    }
207
208
    /**
209
     *
210
     * Builds replacements for attributes in the generated path.
211
     *
212
     * @return string
213
     *
214
     */
215 11
    protected function buildOptionalReplacements()
216
    {
217
        // replacements for optional attributes, if any
218 11
        preg_match('#{/([a-z][a-zA-Z0-9_,]*)}#', $this->url, $matches);
219 11
        if (! $matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
220 10
            return;
221
        }
222
223
        // the optional attribute names in the token
224 1
        $names = explode(',', $matches[1]);
225
226
        // this is the full token to replace in the path
227 1
        $key = $matches[0];
228
229
        // build the replacement string
230 1
        $this->repl[$key] = $this->buildOptionalReplacement($names);
231 1
    }
232
233
    /**
234
     *
235
     * Builds the optional replacement for attribute names.
236
     *
237
     * @param array $names The optional replacement names.
238
     *
239
     * @return string
240
     *
241
     */
242 1
    protected function buildOptionalReplacement($names)
243
    {
244 1
        $repl = '';
245 1
        foreach ($names as $name) {
246
            // is there data for this optional attribute?
247 1
            if (! isset($this->data[$name])) {
248
                // options are *sequentially* optional, so if one is
249
                // missing, we're done
250 1
                return $repl;
251
            }
252
            // encode the optional value
253 1
            $repl .= '/' . $this->encode($this->data[$name]);
254 1
        }
255 1
        return $repl;
256
    }
257
258
    /**
259
     *
260
     * Builds a wildcard replacement in the generated path.
261
     *
262
     * @return string
263
     *
264
     */
265 11
    protected function buildWildcardReplacement()
266
    {
267 11
        $wildcard = $this->route->wildcard;
268 11
        if ($wildcard && isset($this->data[$wildcard])) {
269 1
            $this->url = rtrim($this->url, '/');
270 1
            foreach ($this->data[$wildcard] as $val) {
271 1
                $this->url .= '/' . $this->encode($val);
272 1
            }
273 1
        }
274 11
    }
275
276
    /**
277
     *
278
     * Encodes values, or leaves them raw.
279
     *
280
     * @param string $val The value to encode or leave raw.
281
     *
282
     * @return mixed
283
     *
284
     */
285 11
    protected function encode($val)
286
    {
287 11
        if ($this->raw) {
288 1
            return $val;
289
        }
290
291 10
        return is_scalar($val) ? rawurlencode($val) : null;
292
    }
293
}
294