Passed
Push — master ( d49c11...6804d3 )
by Teodoro
03:59
created

FindRoutes   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 12
eloc 24
c 2
b 0
f 0
dl 0
loc 97
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A factorizeMiddleware() 0 8 2
A matchRoute() 0 7 2
A extractClosure() 0 3 1
A __construct() 0 6 1
A find() 0 15 3
A removeIntegerKeyParam() 0 10 3
1
<?php
2
3
namespace Tleckie\Router;
4
5
use Tleckie\Router\Exception\RouteNotFoundException;
6
use function array_pop;
7
use function is_numeric;
8
use function preg_match_all;
9
10
/**
11
 * Class FindRoutes
12
 *
13
 * @package Tleckie\Router
14
 * @author  Teodoro Leckie Westberg <[email protected]>
15
 */
16
class FindRoutes
17
{
18
    /** @var MiddlewareFactory */
19
    private MiddlewareFactory $middlewareFactory;
20
21
    /** @var array */
22
    private array $routes;
23
24
    /**
25
     * FindRoutes constructor.
26
     *
27
     * @param MiddlewareFactory $middlewareFactory
28
     * @param array             $routes
29
     */
30
    public function __construct(
31
        MiddlewareFactory $middlewareFactory,
32
        array $routes
33
    ) {
34
        $this->middlewareFactory = $middlewareFactory;
35
        $this->routes = $routes;
36
    }
37
38
    /**
39
     * @param string $method
40
     * @param string $path
41
     * @return Item
42
     * @throws RouteNotFoundException
43
     */
44
    public function find(string $method, string $path): Item
45
    {
46
        foreach ($this->routes[$method] ?? [] as $pattern => $closures) {
47
            if (null !== $params = $this->matchRoute($pattern, $path)) {
48
                $params = $this->removeIntegerKeyParam($params);
49
50
                $closure = $this->extractClosure($closures);
51
52
                $middlewares = $this->factorizeMiddleware($closures, $params);
53
54
                return new Item($closure, $params, $middlewares);
55
            }
56
        }
57
58
        throw new RouteNotFoundException('Routes not match');
59
    }
60
61
    /**
62
     * @param string $pattern
63
     * @param string $path
64
     * @return array|bool
65
     */
66
    private function matchRoute(string $pattern, string $path): ?array
67
    {
68
        if (preg_match_all("#^{$pattern}$#", $path, $matches, PREG_SET_ORDER)) {
69
            return $matches[0] ?? [];
70
        }
71
72
        return null;
73
    }
74
75
    /**
76
     * @param array $params
77
     * @return array
78
     */
79
    private function removeIntegerKeyParam(array $params): array
80
    {
81
        $returnParams = [];
82
        foreach ($params as $paramKey => $paramValue) {
83
            if (!is_numeric($paramKey)) {
84
                $returnParams[$paramKey] = $paramValue;
85
            }
86
        }
87
88
        return $returnParams;
89
    }
90
91
    /**
92
     * @param array $closures
93
     * @return Closure|callable
94
     */
95
    private function extractClosure(array &$closures): Closure|callable
0 ignored issues
show
Bug introduced by
The type Tleckie\Router\Closure was not found. Did you mean Closure? If so, make sure to prefix the type with \.
Loading history...
96
    {
97
        return array_pop($closures);
98
    }
99
100
    /**
101
     * @param array $closures
102
     * @param array $params
103
     * @return array
104
     */
105
    private function factorizeMiddleware(array $closures, array $params): array
106
    {
107
        $middleware = [];
108
        foreach ($closures as $closure) {
109
            $middleware[] = $this->middlewareFactory->create($closure, $params);
110
        }
111
112
        return $middleware;
113
    }
114
}
115