Completed
Pull Request — master (#148)
by ARCANEDEV
01:19
created

Url::getFragment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arcanedev\Localization\Utilities;
6
7
use Arcanedev\Localization\Contracts\RouteBindable;
8
use Arcanedev\Localization\Contracts\Url as UrlContract;
9
use Illuminate\Http\Request;
10
11
/**
12
 * Class     Url
13
 *
14
 * @package  Arcanedev\Localization\Utilities
15
 * @author   ARCANEDEV <[email protected]>
16
 */
17
class Url implements UrlContract
18
{
19
    /* -----------------------------------------------------------------
20
     |  Main Methods
21
     | -----------------------------------------------------------------
22
     */
23
24
    /**
25
     * Extract attributes for current url.
26
     *
27
     * @param  bool|false|string  $url
28
     *
29
     * @return array
30
     */
31 318
    public static function extractAttributes($url = false)
32
    {
33 318
        $parse = parse_url((string) $url);
34 318
        $path  = isset($parse['path']) ? explode('/', $parse['path']) : [];
35 318
        $url   = [];
36
37 318
        foreach ($path as $segment) {
38 318
            if ( ! empty($segment))
39 318
                $url[] = $segment;
40
        }
41
42 318
        return self::extractAttributesFromRoutes($url, app('router')->getRoutes());
43
    }
44
45
    /**
46
     * Change uri attributes (wildcards) for the ones in the $attributes array.
47
     *
48
     * @param  array   $attributes
49
     * @param  string  $uri
50
     *
51
     * @return string
52
     */
53 330
    public static function substituteAttributes(array $attributes, $uri)
54
    {
55 330
        foreach ($attributes as $key => $value) {
56 114
            if ($value instanceof RouteBindable)
57 6
                $value = $value->getWildcardValue();
58
59 114
            $uri = str_replace(['{'.$key.'?}', '{'.$key.'}'], $value, $uri);
60
        }
61
62
        // delete empty optional arguments that are not in the $attributes array
63 330
        return preg_replace('/\/{[^)]+\?}/', '', $uri);
64
    }
65
66
    /**
67
     * Build URL using array data from parse_url.
68
     *
69
     * @param  array|false  $parsed
70
     *
71
     * @return string
72
     */
73 276
    public static function unparse($parsed)
74
    {
75 276
        if (empty($parsed)) return '';
76
77 276
        $parsed = self::checkParsedUrl($parsed);
78
79 276
        $url  = self::getUrl($parsed);
80 276
        $url .= self::getQuery($parsed);
81 276
        $url .= self::getFragment($parsed);
82
83 276
        return $url;
84
    }
85
86
    /* -----------------------------------------------------------------
87
     |  Extract Methods
88
     | -----------------------------------------------------------------
89
     */
90
91
    /**
92
     * Extract attributes from routes.
93
     *
94
     * @param  array                                $url
95
     * @param  \Illuminate\Routing\RouteCollection  $routes
96
     *
97
     * @return array
98
     */
99 318
    private static function extractAttributesFromRoutes(array $url, $routes): array
100
    {
101 318
        $attributes = [];
102
103 318
        foreach ($routes as $route) {
104
            /**
105
             * @var  \Illuminate\Routing\Route  $route
106
             * @var  \Illuminate\Http\Request   $request
107
             */
108 318
            $request = Request::create(implode('/', $url));
109
110 318
            if ( ! $route->matches($request))
111 318
                continue;
112
113 252
            $match = self::hasAttributesFromUriPath($url, $route->uri(), $attributes);
114
115 252
            if ($match)
116 204
                break;
117
        }
118
119 318
        return $attributes;
120
    }
121
122
    /**
123
     * Check if has attributes from a route.
124
     *
125
     * @param  array   $url
126
     * @param  string  $path
127
     * @param  array   $attributes
128
     *
129
     * @return bool
130
     */
131 252
    private static function hasAttributesFromUriPath($url, $path, &$attributes): bool
132
    {
133 252
        $i     = 0;
134 252
        $match = true;
135 252
        $path  = explode('/', $path);
136
137 252
        foreach ($path as $j => $segment) {
138 252
            if (isset($url[$i])) {
139 204
                if ($segment !== $url[$i]) {
140 96
                    self::extractAttributesFromSegment($url, $path, $i, $j, $segment, $attributes);
141
                }
142
143 204
                $i++;
144 204
                continue;
145
            }
146 120
            elseif ( ! preg_match('/{[\w]+\?}/', $segment)) {
147
                // No optional parameters but no more $url given this route does not match the url
148 72
                $match = false;
149 72
                break;
150
            }
151
        }
152
153 252
        if (isset($url[$i + 1]))
154
            $match = false;
155
156 252
        return $match;
157
    }
158
159
    /**
160
     * Extract attribute from a segment.
161
     *
162
     * @param  array   $url
163
     * @param  array   $path
164
     * @param  int     $i
165
     * @param  int     $j
166
     * @param  string  $segment
167
     * @param  array   $attributes
168
     */
169 96
    private static function extractAttributesFromSegment($url, $path, $i, $j, $segment, &$attributes): void
170
    {
171
        // Required parameters
172 96
        if (preg_match('/{[\w]+}/', $segment)) {
173 96
            $attributeName              = preg_replace(['/{/', '/\?/', '/}/'], '', $segment);
174 96
            $attributes[$attributeName] = $url[$i];
175
        }
176
177
        // Optional parameter
178
        if (
179 96
            preg_match('/{[\w]+\?}/', $segment) &&
180 96
            ( ! isset($path[$j + 1]) || $path[$j + 1] !== $url[$i])
181
        ) {
182 48
            $attributeName              = preg_replace(['/{/', '/\?/', '/}/'], '', $segment);
183 48
            $attributes[$attributeName] = $url[$i];
184
        }
185 96
    }
186
187
    /* -----------------------------------------------------------------
188
     |  Other Methods
189
     | -----------------------------------------------------------------
190
     */
191
192
    /**
193
     * Check parsed URL.
194
     *
195
     * @param  array  $parsed
196
     *
197
     * @return array
198
     */
199 276
    private static function checkParsedUrl(array $parsed): array
200
    {
201 276
        $scheme   =& $parsed['scheme'];
202 276
        $user     =& $parsed['user'];
203 276
        $pass     =& $parsed['pass'];
204 276
        $host     =& $parsed['host'];
205 276
        $port     =& $parsed['port'];
206 276
        $path     =& $parsed['path'];
207 276
        $path     = '/'.ltrim($path, '/'); // If / is missing for path.
208 276
        $query    =& $parsed['query'];
209 276
        $fragment =& $parsed['fragment'];
210
211 276
        return compact(
212 276
            'scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment'
213
        );
214
    }
215
216
    /**
217
     * Get URL.
218
     *
219
     * @param  array  $parsed
220
     *
221
     * @return string
222
     */
223 276
    private static function getUrl(array $parsed): string
224
    {
225 276
        return strlen((string) $parsed['scheme'])
226 276
            ? $parsed['scheme'].':'.self::getHierPart($parsed)
227 276
            : '';
228
    }
229
230
    /**
231
     * Get hier part.
232
     *
233
     * @param  array  $parsed
234
     *
235
     * @return string
236
     */
237 276
    private static function getHierPart(array $parsed): string
238
    {
239 276
        return strlen($authority = self::getAuthority($parsed))
240 276
            ? '//'.$authority.$parsed['path']
241 276
            : $parsed['path'];
242
    }
243
244
    /**
245
     * Get authority.
246
     *
247
     * @param  array  $parsed
248
     *
249
     * @return string
250
     */
251 276
    private static function getAuthority(array $parsed): string
252
    {
253 276
        $host = self::getHost($parsed);
254
255 276
        return strlen($userInfo = self::getUserInfo($parsed)) ? $userInfo.'@'.$host : $host;
256
    }
257
258
    /**
259
     * Get user info.
260
     *
261
     * @param  array  $parsed
262
     *
263
     * @return string
264
     */
265 276
    private static function getUserInfo(array $parsed): string
266
    {
267 276
        return strlen((string) $parsed['pass'])
268 6
            ? $parsed['user'].':'.$parsed['pass']
269 276
            : '';
270
    }
271
272
    /**
273
     * Get host.
274
     *
275
     * @param  array  $parsed
276
     *
277
     * @return string
278
     */
279 276
    private static function getHost(array $parsed): string
280
    {
281 276
        return empty((string) $parsed['port'])
282 276
            ? $parsed['host']
283 276
            : $parsed['host'].':'.$parsed['port'];
284
    }
285
286
    /**
287
     * Get Query.
288
     *
289
     * @param  array  $parsed
290
     *
291
     * @return string
292
     */
293 276
    private static function getQuery(array $parsed): string
294
    {
295 276
        return strlen((string) $parsed['query'])
296 6
            ? '?'.$parsed['query']
297 276
            : '';
298
    }
299
300
    /**
301
     * Get fragment.
302
     *
303
     * @param  array  $parsed
304
     *
305
     * @return string
306
     */
307 276
    private static function getFragment(array $parsed): string
308
    {
309 276
        return strlen((string) $parsed['fragment'])
310 6
            ? '#'.$parsed['fragment']
311 276
            : '';
312
    }
313
}
314